@@ -141,6 +141,8 @@ pub struct Sender<T> {
141
141
flavor : SenderFlavor < T > ,
142
142
}
143
143
144
+ // SAFETY: We implement Send and Sync for the Sender itself, as long as the underlying data can be
145
+ // sent. The underlying zephyr primitives used for the channel provide the Sync safety.
144
146
unsafe impl < T : Send > Send for Sender < T > { }
145
147
unsafe impl < T : Send > Sync for Sender < T > { }
146
148
@@ -158,12 +160,18 @@ impl<T> Sender<T> {
158
160
SenderFlavor :: Unbounded { queue, .. } => {
159
161
let msg = Box :: new ( Message :: new ( msg) ) ;
160
162
let msg = Box :: into_raw ( msg) ;
163
+ // SAFETY: Zephyr requires, for as long as the message remains in the queue, that
164
+ // the first `usize` of the message be available for its use, and that the message
165
+ // not be moved. The `into_raw` of the box consumes the box, so this is entirely a
166
+ // raw pointer with no references from the Rust code. The item is not used until it
167
+ // has been removed from the queue.
161
168
unsafe {
162
169
queue. send ( msg as * mut c_void ) ;
163
170
}
164
171
}
165
172
SenderFlavor :: Bounded ( chan) => {
166
173
// Retrieve a message buffer from the free list.
174
+ // SAFETY: Please see the safety discussion on `Bounded` on what makes this safe.
167
175
let buf = unsafe { chan. free . recv ( timeout) } ;
168
176
if buf. is_null ( ) {
169
177
return Err ( SendError ( msg) ) ;
@@ -200,13 +208,17 @@ impl<T> Drop for Sender<T> {
200
208
fn drop ( & mut self ) {
201
209
match & self . flavor {
202
210
SenderFlavor :: Unbounded { queue, .. } => {
211
+ // SAFETY: It is not possible to free from Zephyr queues. This means drop has to
212
+ // either leak or panic. We will panic for now.
203
213
unsafe {
204
214
queue. release ( |_| {
205
215
panic ! ( "Unbounded queues cannot currently be dropped" ) ;
206
216
} )
207
217
}
208
218
}
209
219
SenderFlavor :: Bounded ( chan) => {
220
+ // SAFETY: It is not possible to free from Zephyr queues. This means drop has to
221
+ // either leak or panic. We will panic for now.
210
222
unsafe {
211
223
chan. release ( |_| {
212
224
panic ! ( "Bounded queues cannot currently be dropped" ) ;
@@ -256,6 +268,8 @@ pub struct Receiver<T> {
256
268
flavor : ReceiverFlavor < T > ,
257
269
}
258
270
271
+ // SAFETY: We implement Send and Sync for the Receiver itself, as long as the underlying data can be
272
+ // sent. The underlying zephyr primitives used for the channel provide the Sync safety.
259
273
unsafe impl < T : Send > Send for Receiver < T > { }
260
274
unsafe impl < T : Send > Sync for Receiver < T > { }
261
275
@@ -270,6 +284,7 @@ impl<T> Receiver<T> {
270
284
{
271
285
match & self . flavor {
272
286
ReceiverFlavor :: Unbounded { queue, .. } => {
287
+ // SAFETY: Messages were sent by converting a Box through `into_raw()`.
273
288
let msg = unsafe {
274
289
let msg = queue. recv ( timeout) ;
275
290
if msg. is_null ( ) {
@@ -278,10 +293,15 @@ impl<T> Receiver<T> {
278
293
msg
279
294
} ;
280
295
let msg = msg as * mut Message < T > ;
296
+ // SAFETY: After receiving the message from the queue's `recv` method, Zephyr will
297
+ // no longer use the `usize` at the beginning, and it is safe for us to convert the
298
+ // message back into a box, copy the field out of it, an allow the Box itself to be
299
+ // freed.
281
300
let msg = unsafe { Box :: from_raw ( msg) } ;
282
301
Ok ( msg. data )
283
302
}
284
303
ReceiverFlavor :: Bounded ( chan) => {
304
+ // SAFETY: Please see the safety discussion on Bounded.
285
305
let rawbuf = unsafe {
286
306
let buf = chan. chan . recv ( timeout) ;
287
307
if buf. is_null ( ) {
@@ -324,14 +344,17 @@ impl<T> Drop for Receiver<T> {
324
344
fn drop ( & mut self ) {
325
345
match & self . flavor {
326
346
ReceiverFlavor :: Unbounded { queue, .. } => {
347
+ // SAFETY: As the Zephyr channel cannot be freed we must either leak or panic.
348
+ // Chose panic for now.
327
349
unsafe {
328
350
queue. release ( |_| {
329
- crate :: printkln!( "Release" ) ;
330
- true
351
+ panic ! ( "Unnbounded channel cannot be dropped" ) ;
331
352
} )
332
353
}
333
354
}
334
355
ReceiverFlavor :: Bounded ( chan) => {
356
+ // SAFETY: As the Zephyr channel cannot be freed we must either leak or panic.
357
+ // Chose panic for now.
335
358
unsafe {
336
359
chan. release ( |_| {
337
360
panic ! ( "Bounded channels cannot be dropped" ) ;
@@ -378,6 +401,20 @@ enum ReceiverFlavor<T> {
378
401
379
402
type Slot < T > = UnsafeCell < MaybeUninit < Message < T > > > ;
380
403
404
+ // SAFETY: A Bounded channel contains an array of messages that are allocated together in a Box.
405
+ // This Box is held for an eventual future implementation that is able to free the messages, once
406
+ // they have all been taken from Zephyr's knowledge. For now, they could also be leaked.
407
+ //
408
+ // There are two `Queue`s used here: `free` is the free list of messages that are not being sent,
409
+ // and `chan` for messages that have been sent but not received. Initially, all slots are placed on
410
+ // the `free` queue. At any time, outside of the calls in this module, each slot must live inside
411
+ // of one of the two queues. This means that the messages cannot be moved or accessed, except
412
+ // inside of the individual send/receive operations. Zephyr makes use of the initial `usize` field
413
+ // at the beginning of each Slot.
414
+ //
415
+ // We use MaybeUninit for the messages to avoid needing to initialize the messages. The individual
416
+ // messages are accessed through pointers when they are retrieved from the Zephyr `Queue`, so these
417
+ // values are never marked as initialized.
381
418
/// Bounded channel implementation.
382
419
struct Bounded < T > {
383
420
/// The messages themselves. This Box owns the allocation of the messages, although it is
@@ -405,6 +442,7 @@ impl<T> Bounded<T> {
405
442
406
443
// Add each of the boxes to the free list.
407
444
for slot in & slots {
445
+ // SAFETY: See safety discussion on `Bounded`.
408
446
unsafe {
409
447
free. send ( slot. get ( ) as * mut c_void ) ;
410
448
}
0 commit comments