@@ -12,15 +12,17 @@ use core::{
12
12
#[ doc( hidden) ]
13
13
pub use critical_section;
14
14
use heapless:: Deque ;
15
- use rtic_common:: waker_registration:: CriticalSectionWakerRegistration as WakerRegistration ;
16
15
use rtic_common:: {
17
- dropper:: OnDrop ,
18
- wait_queue :: { Link , WaitQueue } ,
16
+ dropper:: OnDrop , wait_queue :: DoublyLinkedList , wait_queue :: Link ,
17
+ waker_registration :: CriticalSectionWakerRegistration as WakerRegistration ,
19
18
} ;
20
19
21
20
#[ cfg( feature = "defmt-03" ) ]
22
21
use crate :: defmt;
23
22
23
+ type WaitQueueData = ( Waker , SlotPtr ) ;
24
+ type WaitQueue = DoublyLinkedList < WaitQueueData > ;
25
+
24
26
/// An MPSC channel for use in no-alloc systems. `N` sets the size of the queue.
25
27
///
26
28
/// This channel uses critical sections, however there are extremely small and all `memcpy`
@@ -192,11 +194,11 @@ unsafe impl<T, const N: usize> Send for Sender<'_, T, N> {}
192
194
/// This is needed to make the async closure in `send` accept that we "share"
193
195
/// the link possible between threads.
194
196
#[ derive( Clone ) ]
195
- struct LinkPtr ( * mut Option < Link < Waker > > ) ;
197
+ struct LinkPtr ( * mut Option < Link < WaitQueueData > > ) ;
196
198
197
199
impl LinkPtr {
198
200
/// This will dereference the pointer stored within and give out an `&mut`.
199
- unsafe fn get ( & mut self ) -> & mut Option < Link < Waker > > {
201
+ unsafe fn get ( & mut self ) -> & mut Option < Link < WaitQueueData > > {
200
202
& mut * self . 0
201
203
}
202
204
}
@@ -205,6 +207,28 @@ unsafe impl Send for LinkPtr {}
205
207
206
208
unsafe impl Sync for LinkPtr { }
207
209
210
+ /// This is needed to make the async closure in `send` accept that we "share"
211
+ /// the link possible between threads.
212
+ #[ derive( Clone ) ]
213
+ struct SlotPtr ( * mut Option < u8 > ) ;
214
+
215
+ impl SlotPtr {
216
+ /// Replace the value of this slot with `new_value`, and return
217
+ /// the old value.
218
+ fn replace (
219
+ & mut self ,
220
+ new_value : Option < u8 > ,
221
+ _cs : critical_section:: CriticalSection ,
222
+ ) -> Option < u8 > {
223
+ // SAFETY: we are in a critical section.
224
+ unsafe { core:: ptr:: replace ( self . 0 , new_value) }
225
+ }
226
+ }
227
+
228
+ unsafe impl Send for SlotPtr { }
229
+
230
+ unsafe impl Sync for SlotPtr { }
231
+
208
232
impl < T , const N : usize > core:: fmt:: Debug for Sender < ' _ , T , N > {
209
233
fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
210
234
write ! ( f, "Sender" )
@@ -268,44 +292,59 @@ impl<T, const N: usize> Sender<'_, T, N> {
268
292
/// Send a value. If there is no place left in the queue this will wait until there is.
269
293
/// If the receiver does not exist this will return an error.
270
294
pub async fn send ( & mut self , val : T ) -> Result < ( ) , NoReceiver < T > > {
271
- let mut link_ptr: Option < Link < Waker > > = None ;
295
+ let mut free_slot_ptr: Option < u8 > = None ;
296
+ let mut link_ptr: Option < Link < WaitQueueData > > = None ;
272
297
273
298
// Make this future `Drop`-safe.
274
299
// SAFETY(link_ptr): Shadow the original definition of `link_ptr` so we can't abuse it.
275
- let mut link_ptr = LinkPtr ( & mut link_ptr as * mut Option < Link < Waker > > ) ;
300
+ let mut link_ptr = LinkPtr ( core:: ptr:: addr_of_mut!( link_ptr) ) ;
301
+ // SAFETY(freed_slot): Shadow the original definition of `free_slot_ptr` so we can't abuse it.
302
+ let mut free_slot_ptr = SlotPtr ( core:: ptr:: addr_of_mut!( free_slot_ptr) ) ;
276
303
277
304
let mut link_ptr2 = link_ptr. clone ( ) ;
305
+ let mut free_slot_ptr2 = free_slot_ptr. clone ( ) ;
278
306
let dropper = OnDrop :: new ( || {
279
307
// SAFETY: We only run this closure and dereference the pointer if we have
280
308
// exited the `poll_fn` below in the `drop(dropper)` call. The other dereference
281
309
// of this pointer is in the `poll_fn`.
282
310
if let Some ( link) = unsafe { link_ptr2. get ( ) } {
283
311
link. remove_from_list ( & self . 0 . wait_queue ) ;
284
312
}
313
+
314
+ // Potentially unnecessary c-s because our link was already popped, so there
315
+ // is no way for anything else to access the free slot ptr. Gotta think
316
+ // about this a bit more...
317
+ critical_section:: with ( |cs| {
318
+ if let Some ( freed_slot) = free_slot_ptr2. replace ( None , cs) {
319
+ debug_assert ! ( !self . 0 . access( cs) . freeq. is_full( ) ) ;
320
+ // SAFETY: freeq is not full.
321
+ unsafe {
322
+ self . 0 . access ( cs) . freeq . push_back_unchecked ( freed_slot) ;
323
+ }
324
+ }
325
+ } ) ;
285
326
} ) ;
286
327
287
328
let idx = poll_fn ( |cx| {
288
- if self . is_closed ( ) {
289
- return Poll :: Ready ( Err ( ( ) ) ) ;
290
- }
291
-
292
329
// Do all this in one critical section, else there can be race conditions
293
- let queue_idx = critical_section:: with ( |cs| {
330
+ critical_section:: with ( |cs| {
294
331
let wq_empty = self . 0 . wait_queue . is_empty ( ) ;
295
332
let fq_empty = self . 0 . access ( cs) . freeq . is_empty ( ) ;
333
+
296
334
if !wq_empty || fq_empty {
297
335
// SAFETY: This pointer is only dereferenced here and on drop of the future
298
336
// which happens outside this `poll_fn`'s stack frame.
299
337
let link = unsafe { link_ptr. get ( ) } ;
300
338
if let Some ( link) = link {
301
339
if !link. is_popped ( ) {
302
- return None ;
340
+ return Poll :: Pending ;
303
341
} else {
304
342
// Fall through to dequeue
305
343
}
306
344
} else {
307
345
// Place the link in the wait queue on first run.
308
- let link_ref = link. insert ( Link :: new ( cx. waker ( ) . clone ( ) ) ) ;
346
+ let link_ref =
347
+ link. insert ( Link :: new ( ( cx. waker ( ) . clone ( ) , free_slot_ptr. clone ( ) ) ) ) ;
309
348
310
349
// SAFETY(new_unchecked): The address to the link is stable as it is defined
311
350
// outside this stack frame.
@@ -315,23 +354,21 @@ impl<T, const N: usize> Sender<'_, T, N> {
315
354
// `link_ptr` lives until the end of the stack frame.
316
355
unsafe { self . 0 . wait_queue . push ( Pin :: new_unchecked ( link_ref) ) } ;
317
356
318
- return None ;
357
+ return Poll :: Pending ;
319
358
}
320
359
}
321
360
322
- assert ! ( !self . 0 . access( cs) . freeq. is_empty( ) ) ;
323
- // Get index as the queue is guaranteed not empty and the wait queue is empty
324
- let idx = unsafe { self . 0 . access ( cs) . freeq . pop_front_unchecked ( ) } ;
325
-
326
- Some ( idx)
327
- } ) ;
361
+ let slot = free_slot_ptr
362
+ . replace ( None , cs)
363
+ . or_else ( || self . 0 . access ( cs) . freeq . pop_back ( ) ) ;
328
364
329
- if let Some ( idx) = queue_idx {
330
- // Return the index
331
- Poll :: Ready ( Ok ( idx) )
332
- } else {
333
- Poll :: Pending
334
- }
365
+ if let Some ( slot) = slot {
366
+ Poll :: Ready ( Ok ( slot) )
367
+ } else {
368
+ debug_assert ! ( self . is_closed( ) ) ;
369
+ Poll :: Ready ( Err ( ( ) ) )
370
+ }
371
+ } )
335
372
} )
336
373
. await ;
337
374
@@ -430,14 +467,15 @@ impl<T, const N: usize> Receiver<'_, T, N> {
430
467
431
468
// Return the index to the free queue after we've read the value.
432
469
critical_section:: with ( |cs| {
433
- assert ! ( !self . 0 . access( cs) . freeq. is_full( ) ) ;
434
- unsafe { self . 0 . access ( cs) . freeq . push_back_unchecked ( rs) }
435
-
436
470
fence ( Ordering :: SeqCst ) ;
437
471
438
- // If someone is waiting in the WaiterQueue, wake the first one up.
439
- if let Some ( wait_head) = self . 0 . wait_queue . pop ( ) {
472
+ // If someone is waiting in the WaiterQueue, wake the first one up & hand it the free slot.
473
+ if let Some ( ( wait_head, mut freeq_slot) ) = self . 0 . wait_queue . pop ( ) {
474
+ freeq_slot. replace ( Some ( rs) , cs) ;
440
475
wait_head. wake ( ) ;
476
+ } else {
477
+ assert ! ( !self . 0 . access( cs) . freeq. is_full( ) ) ;
478
+ unsafe { self . 0 . access ( cs) . freeq . push_back_unchecked ( rs) }
441
479
}
442
480
443
481
Ok ( r)
@@ -495,7 +533,7 @@ impl<T, const N: usize> Drop for Receiver<'_, T, N> {
495
533
// Mark the receiver as dropped and wake all waiters
496
534
critical_section:: with ( |cs| * self . 0 . access ( cs) . receiver_dropped = true ) ;
497
535
498
- while let Some ( waker) = self . 0 . wait_queue . pop ( ) {
536
+ while let Some ( ( waker, _ ) ) = self . 0 . wait_queue . pop ( ) {
499
537
waker. wake ( ) ;
500
538
}
501
539
}
0 commit comments