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}