Skip to main content

simoom_sys/
mpsc.rs

1//! A lock-free multi-producer, single-consumer queue: any thread pushes, one thread takes
2//! everything at once.
3//!
4//! Producers push onto an atomic stack with one compare-and-swap; the consumer swaps the whole
5//! stack out in one atomic exchange and reverses it, so items come out in the order they were
6//! pushed (per producer, and in push order overall). There is no lock, so a producer never
7//! waits on the consumer or on another producer's critical section, and nothing ever calls into
8//! the kernel. Taking the whole stack at once has no ABA problem: a node is only ever freed by
9//! the consumer after it left the shared stack.
10//!
11//! The event loops use it for messages from other threads (`Responder::send`, offloaded jobs),
12//! together with [`crate::driver::Waker::post`], which wakes a loop only when it is parked.
13
14use core::ptr;
15use std::marker::PhantomData;
16use std::sync::atomic::{AtomicPtr, Ordering};
17
18struct Node<T> {
19    next: *mut Node<T>,
20    value: T,
21}
22
23/// The queue (see the module documentation).
24pub struct Mpsc<T> {
25    head: AtomicPtr<Node<T>>,
26    _owns: PhantomData<Box<Node<T>>>,
27}
28
29// SAFETY: the queue moves `T` values between threads (so `T: Send`), and every access to the
30// shared head is atomic; nodes are owned by exactly one side at a time (a producer until its
31// compare-and-swap succeeds, then the stack, then the consumer after its exchange).
32unsafe impl<T: Send> Send for Mpsc<T> {}
33// SAFETY: as above; `push` and `take` only touch the head through atomics.
34unsafe impl<T: Send> Sync for Mpsc<T> {}
35
36impl<T> Default for Mpsc<T> {
37    fn default() -> Self {
38        Mpsc::new()
39    }
40}
41
42impl<T> std::fmt::Debug for Mpsc<T> {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("Mpsc").field("empty", &self.is_empty()).finish()
45    }
46}
47
48impl<T> Mpsc<T> {
49    /// An empty queue.
50    pub const fn new() -> Mpsc<T> {
51        Mpsc { head: AtomicPtr::new(ptr::null_mut()), _owns: PhantomData }
52    }
53
54    /// Adds `value` (any thread). Sequentially consistent, so a consumer that announces it is
55    /// about to sleep and then checks [`Mpsc::is_empty`] either sees the value or is seen by
56    /// the producer's following check of that announcement.
57    pub fn push(&self, value: T) {
58        let node = Box::into_raw(Box::new(Node { next: ptr::null_mut(), value }));
59        let mut head = self.head.load(Ordering::Relaxed);
60        loop {
61            // SAFETY: `node` came from `Box::into_raw` above and is not shared until the
62            // compare-and-swap below succeeds, so this thread has exclusive access to it.
63            unsafe { (*node).next = head };
64            match self.head.compare_exchange_weak(head, node, Ordering::SeqCst, Ordering::Relaxed) {
65                Ok(_) => return,
66                Err(h) => head = h,
67            }
68        }
69    }
70
71    /// Whether the queue looks empty (a push may be landing concurrently).
72    #[inline]
73    pub fn is_empty(&self) -> bool {
74        self.head.load(Ordering::SeqCst).is_null()
75    }
76
77    /// Moves every queued value into `into`, oldest first (the consumer thread).
78    pub fn take(&self, into: &mut Vec<T>) {
79        if self.head.load(Ordering::Relaxed).is_null() {
80            return;
81        }
82        let mut p = self.head.swap(ptr::null_mut(), Ordering::Acquire);
83        let start = into.len();
84        while !p.is_null() {
85            // SAFETY: the exchange above took the whole list out of the shared head, so no
86            // other thread can reach these nodes any more; each was made by `Box::into_raw` in
87            // `push` and is freed exactly once here.
88            let node = unsafe { Box::from_raw(p) };
89            p = node.next;
90            into.push(node.value);
91        }
92        // The stack holds the newest first.
93        into[start..].reverse();
94    }
95}
96
97impl<T> Drop for Mpsc<T> {
98    fn drop(&mut self) {
99        let mut p = *self.head.get_mut();
100        while !p.is_null() {
101            // SAFETY: `&mut self` means no producer or consumer is running; every node in the
102            // list came from `Box::into_raw` and is freed exactly once.
103            let node = unsafe { Box::from_raw(p) };
104            p = node.next;
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use std::sync::Arc;
113
114    #[test]
115    fn values_come_out_in_push_order() {
116        let q = Mpsc::new();
117        let mut v = Vec::new();
118        q.take(&mut v);
119        assert!(v.is_empty() && q.is_empty());
120        for i in 0..5 {
121            q.push(i);
122        }
123        assert!(!q.is_empty());
124        v.push(-1);
125        q.take(&mut v);
126        assert_eq!(v, [-1, 0, 1, 2, 3, 4]);
127        assert!(q.is_empty());
128    }
129
130    #[test]
131    fn many_producers_lose_nothing_and_keep_their_own_order() {
132        let q = Arc::new(Mpsc::new());
133        let producers: Vec<_> = (0..8u64)
134            .map(|p| {
135                let q = q.clone();
136                std::thread::spawn(move || {
137                    for i in 0..20_000u64 {
138                        q.push((p, i));
139                    }
140                })
141            })
142            .collect();
143        let mut seen = vec![0u64; 8];
144        let mut got = 0;
145        let mut buf = Vec::new();
146        while got < 8 * 20_000 {
147            q.take(&mut buf);
148            for (p, i) in buf.drain(..) {
149                assert_eq!(seen[p as usize], i, "producer {p} out of order");
150                seen[p as usize] += 1;
151                got += 1;
152            }
153        }
154        for t in producers {
155            t.join().unwrap();
156        }
157        assert!(q.is_empty());
158    }
159
160    #[test]
161    fn dropping_a_full_queue_drops_its_values() {
162        let marker = Arc::new(());
163        let q = Mpsc::new();
164        for _ in 0..10 {
165            q.push(marker.clone());
166        }
167        assert_eq!(Arc::strong_count(&marker), 11);
168        drop(q);
169        assert_eq!(Arc::strong_count(&marker), 1);
170    }
171}