Skip to main content

tokio/sync/mpsc/
list.rs

1//! A concurrent, lock-free, FIFO list.
2
3use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize};
4use crate::loom::thread;
5use crate::sync::mpsc::block::{self, Block};
6
7use std::fmt;
8use std::ptr::NonNull;
9use std::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
10
11/// List queue transmit handle.
12pub(crate) struct Tx<T> {
13    /// Tail in the `Block` mpmc list.
14    block_tail: AtomicPtr<Block<T>>,
15
16    /// Position to push the next message. This references a block and offset
17    /// into the block.
18    tail_position: AtomicUsize,
19}
20
21/// List queue receive handle
22pub(crate) struct Rx<T> {
23    /// Pointer to the block being processed.
24    head: NonNull<Block<T>>,
25
26    /// Next slot index to process.
27    index: usize,
28
29    /// Pointer to the next block pending release.
30    free_head: NonNull<Block<T>>,
31}
32
33/// Return value of `Rx::try_pop`.
34pub(crate) enum TryPopResult<T> {
35    /// Successfully popped a value.
36    Ok(T),
37    /// The channel is empty.
38    ///
39    /// Note that `list.rs` only tracks the close state set by senders. If the
40    /// channel is closed by `Rx::close()`, then `TryPopResult::Empty` is still
41    /// returned, and the close state needs to be handled by `chan.rs`.
42    Empty,
43    /// The channel is empty and closed.
44    ///
45    /// Returned when the send half is closed (all senders dropped).
46    Closed,
47    /// The channel is not empty, but the first value is being written.
48    Busy,
49}
50
51pub(crate) fn channel<T>() -> (Tx<T>, Rx<T>) {
52    channel_from_index(0)
53}
54
55pub(crate) fn channel_from_index<T>(start_index: usize) -> (Tx<T>, Rx<T>) {
56    debug_assert_eq!(block::offset(start_index), 0);
57
58    // Create the initial block shared between the tx and rx halves.
59    let initial_block = Block::new(start_index);
60    let initial_block_ptr = Box::into_raw(initial_block);
61
62    let tx = Tx {
63        block_tail: AtomicPtr::new(initial_block_ptr),
64        tail_position: AtomicUsize::new(start_index),
65    };
66
67    let head = NonNull::new(initial_block_ptr).unwrap();
68
69    let rx = Rx {
70        head,
71        index: start_index,
72        free_head: head,
73    };
74
75    (tx, rx)
76}
77
78impl<T> Tx<T> {
79    /// Pushes a value into the list.
80    pub(crate) fn push(&self, value: T) {
81        // First, claim a slot for the value. `Acquire` is used here to
82        // synchronize with the `fetch_add` in `reclaim_blocks`.
83        let slot_index = self.tail_position.fetch_add(1, Acquire);
84
85        // Load the current block and write the value
86        let block = self.find_block(slot_index);
87
88        unsafe {
89            // Write the value to the block
90            block.as_ref().write(slot_index, value);
91        }
92    }
93
94    /// Closes the send half of the list.
95    ///
96    /// Similar process as pushing a value, but instead of writing the value &
97    /// setting the ready flag, the `TX_CLOSED` flag is set on the block.
98    pub(crate) fn close(&self) {
99        // First, claim a slot for the value. This is the last slot that will be
100        // claimed.
101        let slot_index = self.tail_position.fetch_add(1, Acquire);
102
103        let block = self.find_block(slot_index);
104
105        unsafe { block.as_ref().tx_close() }
106    }
107
108    fn find_block(&self, slot_index: usize) -> NonNull<Block<T>> {
109        // The start index of the block that contains `index`.
110        let start_index = block::start_index(slot_index);
111
112        // The index offset into the block
113        let offset = block::offset(slot_index);
114
115        // Load the current head of the block
116        let mut block_ptr = self.block_tail.load(Acquire);
117
118        let block = unsafe { &*block_ptr };
119
120        // Calculate the distance between the tail ptr and the target block
121        let distance = block.distance(start_index);
122
123        // Decide if this call to `find_block` should attempt to update the
124        // `block_tail` pointer.
125        //
126        // Updating `block_tail` is not always performed in order to reduce
127        // contention.
128        //
129        // When set, as the routine walks the linked list, it attempts to update
130        // `block_tail`. If the update cannot be performed, `try_updating_tail`
131        // is unset.
132        let mut try_updating_tail = distance > offset;
133
134        // Walk the linked list of blocks until the block with `start_index` is
135        // found.
136        loop {
137            let block = unsafe { &(*block_ptr) };
138
139            if block.is_at_index(start_index) {
140                return unsafe { NonNull::new_unchecked(block_ptr) };
141            }
142
143            let next_block = block
144                .load_next(Acquire)
145                // There is no allocated next block, grow the linked list.
146                .unwrap_or_else(|| block.grow());
147
148            // If the block is **not** final, then the tail pointer cannot be
149            // advanced any more.
150            try_updating_tail &= block.is_final();
151
152            if try_updating_tail {
153                // Advancing `block_tail` must happen when walking the linked
154                // list. `block_tail` may not advance passed any blocks that are
155                // not "final". At the point a block is finalized, it is unknown
156                // if there are any prior blocks that are unfinalized, which
157                // makes it impossible to advance `block_tail`.
158                //
159                // While walking the linked list, `block_tail` can be advanced
160                // as long as finalized blocks are traversed.
161                //
162                // Release ordering is used to ensure that any subsequent reads
163                // are able to see the memory pointed to by `block_tail`.
164                //
165                // Acquire is not needed as any "actual" value is not accessed.
166                // At this point, the linked list is walked to acquire blocks.
167                if self
168                    .block_tail
169                    .compare_exchange(block_ptr, next_block.as_ptr(), Release, Relaxed)
170                    .is_ok()
171                {
172                    // Synchronize with any senders
173                    let tail_position = self.tail_position.fetch_add(0, Release);
174
175                    unsafe {
176                        block.tx_release(tail_position);
177                    }
178                } else {
179                    // A concurrent sender is also working on advancing
180                    // `block_tail` and this thread is falling behind.
181                    //
182                    // Stop trying to advance the tail pointer
183                    try_updating_tail = false;
184                }
185            }
186
187            block_ptr = next_block.as_ptr();
188
189            thread::yield_now();
190        }
191    }
192
193    /// # Safety
194    ///
195    /// Behavior is undefined if any of the following conditions are violated:
196    ///
197    /// - The `block` was created by [`Box::into_raw`].
198    /// - The `block` is not currently part of any linked list.
199    /// - The `block` is a valid pointer to a [`Block<T>`].
200    pub(crate) unsafe fn reclaim_block(&self, mut block: NonNull<Block<T>>) {
201        // The block has been removed from the linked list and ownership
202        // is reclaimed.
203        //
204        // Before dropping the block, see if it can be reused by
205        // inserting it back at the end of the linked list.
206        //
207        // First, reset the data
208        //
209        // Safety: caller guarantees the block is valid and not in any list.
210        unsafe {
211            block.as_mut().reclaim();
212        }
213
214        let mut reused = false;
215
216        // Attempt to insert the block at the end
217        //
218        // Walk at most three times
219        let curr_ptr = self.block_tail.load(Acquire);
220
221        // The pointer can never be null
222        debug_assert!(!curr_ptr.is_null());
223
224        // Safety: curr_ptr is never null.
225        let mut curr = unsafe { NonNull::new_unchecked(curr_ptr) };
226
227        // TODO: Unify this logic with Block::grow
228        for _ in 0..3 {
229            match unsafe { curr.as_ref().try_push(&mut block, AcqRel, Acquire) } {
230                Ok(()) => {
231                    reused = true;
232                    break;
233                }
234                Err(next) => {
235                    curr = next;
236                }
237            }
238        }
239
240        if !reused {
241            // Safety:
242            //
243            // 1. Caller guarantees the block is valid and not in any list.
244            // 2. The block was created by `Box::into_raw`.
245            let _ = unsafe { Box::from_raw(block.as_ptr()) };
246        }
247    }
248}
249
250impl<T> fmt::Debug for Tx<T> {
251    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
252        fmt.debug_struct("Tx")
253            .field("block_tail", &self.block_tail.load(Relaxed))
254            .field("tail_position", &self.tail_position.load(Relaxed))
255            .finish()
256    }
257}
258
259impl<T> Rx<T> {
260    pub(crate) fn is_empty(&self, tx: &Tx<T>) -> bool {
261        let block = unsafe { self.head.as_ref() };
262        if block.has_value(self.index) {
263            return false;
264        }
265
266        // It is possible that a block has no value "now" but the list is still not empty.
267        // To be sure, it is necessary to check the length of the list.
268        self.len(tx) == 0
269    }
270
271    // Guaranteed to return true if `slot_index` is the fake message sent on channel close.
272    // Guaranteed to return false if `slot_index` is a fully sent message.
273    //
274    // For messages that are partially sent, may return either true or false.
275    fn is_maybe_closed(&self, tx: &Tx<T>, slot_index: usize) -> bool {
276        let start_index = block::start_index(slot_index);
277
278        let tail = tx.block_tail.load(Acquire);
279        // SAFETY: Only the receiver frees blocks, so since we are the receiver, this will not be
280        // freed right now.
281        let tail_ref = unsafe { &*tail };
282        if tail_ref.is_at_index(start_index) {
283            return !tail_ref.has_value(slot_index);
284        }
285
286        // This method is optimized for checking whether the last value is present, so most of the
287        // time it is in `block_tail`. However, this isn't always the case since it's possible
288        // that the list was grown with an empty block, in which case `block_tail` points one block
289        // too far. To handle this case, we walk the list from the head.
290        let mut block_ptr = Some(self.head);
291
292        while let Some(block) = block_ptr {
293            // SAFETY: Only the receiver frees blocks, so since we are the receiver, this will not
294            // be freed right now.
295            let block_ref = unsafe { block.as_ref() };
296            if block_ref.is_at_index(start_index) {
297                return !block_ref.has_value(slot_index);
298            }
299            block_ptr = block_ref.load_next(Acquire);
300        }
301        true
302    }
303
304    pub(crate) fn len(&self, tx: &Tx<T>) -> usize {
305        let tail_position = tx.tail_position.load(Acquire);
306        let mut len = tail_position.wrapping_sub(self.index);
307        debug_assert!(0 <= len as isize);
308        if len == 0 {
309            return 0;
310        }
311        // There are messages present in the queue. However, it's possible that the last message is
312        // a fake "closed" message that we do not wish to count. To avoid counting it, we do not
313        // count the last message if the ready bit is unset.
314        //
315        // Note that it is also possible for the ready bit to be unset on a normal message, but
316        // this happens only if that message is currently being sent *right now* in parallel on
317        // another thread. That is okay because it is optional to count messages that are currently
318        // being sent.
319        if self.is_maybe_closed(tx, tail_position.wrapping_sub(1)) {
320            len -= 1;
321        }
322        len
323    }
324
325    /// Pops the next value off the queue.
326    pub(crate) fn pop(&mut self, tx: &Tx<T>) -> Option<block::Read<T>> {
327        // Advance `head`, if needed
328        if !self.try_advancing_head() {
329            return None;
330        }
331
332        self.reclaim_blocks(tx);
333
334        unsafe {
335            let block = self.head.as_ref();
336
337            let ret = block.read(self.index);
338
339            if let Some(block::Read::Value(..)) = ret {
340                self.index = self.index.wrapping_add(1);
341            }
342
343            ret
344        }
345    }
346
347    /// Pops the next value off the queue, detecting whether the block
348    /// is busy or empty on failure.
349    ///
350    /// This function exists because `Rx::pop` can return `None` even if the
351    /// channel's queue contains a message that has been completely written.
352    /// This can happen if the fully delivered message is behind another message
353    /// that is in the middle of being written to the block, since the channel
354    /// can't return the messages out of order.
355    pub(crate) fn try_pop(&mut self, tx: &Tx<T>) -> TryPopResult<T> {
356        let tail_position = tx.tail_position.load(Acquire);
357        let result = self.pop(tx);
358
359        match result {
360            Some(block::Read::Value(t)) => TryPopResult::Ok(t),
361            Some(block::Read::Closed) => TryPopResult::Closed,
362            None if tail_position == self.index => TryPopResult::Empty,
363            None => TryPopResult::Busy,
364        }
365    }
366
367    /// Tries advancing the block pointer to the block referenced by `self.index`.
368    ///
369    /// Returns `true` if successful, `false` if there is no next block to load.
370    fn try_advancing_head(&mut self) -> bool {
371        let block_index = block::start_index(self.index);
372
373        loop {
374            let next_block = {
375                let block = unsafe { self.head.as_ref() };
376
377                if block.is_at_index(block_index) {
378                    return true;
379                }
380
381                block.load_next(Acquire)
382            };
383
384            let next_block = match next_block {
385                Some(next_block) => next_block,
386                None => {
387                    return false;
388                }
389            };
390
391            self.head = next_block;
392
393            thread::yield_now();
394        }
395    }
396
397    fn reclaim_blocks(&mut self, tx: &Tx<T>) {
398        while self.free_head != self.head {
399            unsafe {
400                // Get a handle to the block that will be freed and update
401                // `free_head` to point to the next block.
402                let block = self.free_head;
403
404                let observed_tail_position = block.as_ref().observed_tail_position();
405
406                let required_index = match observed_tail_position {
407                    Some(i) => i,
408                    None => return,
409                };
410
411                if required_index.wrapping_sub(self.index) as isize > 0 {
412                    return;
413                }
414
415                // We may read the next pointer with `Relaxed` ordering as it is
416                // guaranteed that the `reclaim_blocks` routine trails the `recv`
417                // routine. Any memory accessed by `reclaim_blocks` has already
418                // been acquired by `recv`.
419                let next_block = block.as_ref().load_next(Relaxed);
420
421                // Update the free list head
422                self.free_head = next_block.unwrap();
423
424                // Push the emptied block onto the back of the queue, making it
425                // available to senders.
426                tx.reclaim_block(block);
427            }
428
429            thread::yield_now();
430        }
431    }
432
433    /// Effectively `Drop` all the blocks. Should only be called once, when
434    /// the list is dropping.
435    pub(super) unsafe fn free_blocks(&mut self) {
436        debug_assert_ne!(self.free_head, NonNull::dangling());
437
438        let mut cur = Some(self.free_head);
439
440        #[cfg(debug_assertions)]
441        {
442            // to trigger the debug assert above so as to catch that we
443            // don't call `free_blocks` more than once.
444            self.free_head = NonNull::dangling();
445            self.head = NonNull::dangling();
446        }
447
448        while let Some(block) = cur {
449            cur = unsafe { block.as_ref() }.load_next(Relaxed);
450            drop(unsafe { Box::from_raw(block.as_ptr()) });
451        }
452    }
453}
454
455impl<T> fmt::Debug for Rx<T> {
456    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
457        fmt.debug_struct("Rx")
458            .field("head", &self.head)
459            .field("index", &self.index)
460            .field("free_head", &self.free_head)
461            .finish()
462    }
463}
464
465#[cfg(all(test, not(loom)))]
466mod tests {
467    use crate::sync::mpsc::unbounded::unbounded_channel_from_index;
468    use crate::sync::mpsc::BLOCK_CAP;
469
470    #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
471    use wasm_bindgen_test::wasm_bindgen_test as test;
472
473    #[test]
474    #[cfg(not(target_family = "wasm"))]
475    fn wraparound() {
476        use super::*;
477
478        let (tx, mut rx) = channel_from_index(0usize.wrapping_sub(2 * BLOCK_CAP));
479        let head = rx.free_head;
480
481        for i in 0..BLOCK_CAP {
482            tx.push(i);
483            assert!(matches!(rx.pop(&tx), Some(block::Read::Value(v)) if v == i));
484        }
485
486        // Simulate a slow sender claiming the first slot of the second block
487        // while still holding a pointer to the first block.
488        let slow_slot = tx.tail_position.fetch_add(1, Acquire);
489        let slow_tail = AtomicPtr::new(tx.block_tail.load(Acquire));
490
491        // Fill the rest of the second block (wrapping `tail_position` to 0),
492        // then push one item into the third block to retire the first block.
493        for i in 1..BLOCK_CAP {
494            tx.push(i);
495        }
496        assert_eq!(rx.len(&tx), BLOCK_CAP);
497        tx.push(BLOCK_CAP);
498
499        // Advancing `rx` to the second block must not reclaim the first block yet,
500        // because `rx.index` has not reached the wrapped `required_index` (1).
501        std::thread::scope(|s| {
502            s.spawn(|| unsafe {
503                let slow_tail = &*slow_tail.load(Relaxed);
504                let slow_block = slow_tail.load_next(Acquire).unwrap();
505                assert!(slow_block
506                    .as_ref()
507                    .is_at_index(block::start_index(slow_slot)));
508            });
509            assert!(rx.pop(&tx).is_none());
510        });
511        assert_eq!(rx.free_head, head);
512
513        unsafe {
514            let slow_tail = &*slow_tail.load(Relaxed);
515            let slow_block = slow_tail.load_next(Acquire).unwrap();
516            slow_block.as_ref().write(slow_slot, 0);
517        }
518
519        for i in 0..=BLOCK_CAP {
520            assert!(matches!(rx.pop(&tx), Some(block::Read::Value(v)) if v == i));
521        }
522        unsafe { rx.free_blocks() };
523    }
524
525    #[test]
526    fn wraparound_unbounded() {
527        let (tx, mut rx) = unbounded_channel_from_index(0usize.wrapping_sub(2 * BLOCK_CAP));
528
529        for i in 0..4 * BLOCK_CAP {
530            tx.send(i).unwrap();
531            assert_eq!(rx.len(), i + 1);
532            assert!(!rx.is_empty());
533        }
534
535        for i in 0..4 * BLOCK_CAP {
536            assert_eq!(rx.try_recv().unwrap(), i);
537            assert_eq!(rx.len(), 4 * BLOCK_CAP - 1 - i);
538        }
539        assert!(rx.is_empty());
540    }
541}