1use std::cell::Cell;
34use std::marker::PhantomData;
35use std::path::Path;
36use std::sync::Arc;
37use std::sync::atomic::{AtomicUsize, Ordering};
38
39use crate::shared_ring::{RingError, SharedRing};
40use crate::spsc_ring::SpscRingCore;
41
42pub struct SharedRingMpsc;
44
45pub struct MpscProducer {
49 inner: Arc<SpscRingCore>,
50 _not_sync: PhantomData<Cell<()>>,
51}
52
53pub struct MpscConsumer {
57 rings: Vec<Arc<SpscRingCore>>,
58 next_drain: AtomicUsize,
63 _not_sync: PhantomData<Cell<()>>,
64}
65
66impl SharedRingMpsc {
67 pub fn create_anon_pool(
70 n_producers: usize,
71 capacity: usize,
72 ) -> Result<(Vec<MpscProducer>, MpscConsumer), RingError> {
73 assert!(n_producers >= 1, "n_producers must be >= 1");
74 let mut rings: Vec<Arc<SpscRingCore>> = Vec::with_capacity(n_producers);
75 for _ in 0..n_producers {
76 rings.push(Arc::new(SpscRingCore::create_anon(capacity)?));
77 }
78 let producers: Vec<MpscProducer> = rings
79 .iter()
80 .map(|r| MpscProducer {
81 inner: Arc::clone(r),
82 _not_sync: PhantomData,
83 })
84 .collect();
85 let consumer = MpscConsumer {
86 rings,
87 next_drain: AtomicUsize::new(0),
88 _not_sync: PhantomData,
89 };
90 Ok((producers, consumer))
91 }
92
93 pub fn create_pool(
96 path_prefix: impl AsRef<Path>,
97 n_producers: usize,
98 capacity: usize,
99 ) -> Result<(Vec<MpscProducer>, MpscConsumer), RingError> {
100 assert!(n_producers >= 1, "n_producers must be >= 1");
101 let mut rings: Vec<Arc<SpscRingCore>> = Vec::with_capacity(n_producers);
102 let base = path_prefix.as_ref().to_path_buf();
103 for i in 0..n_producers {
104 let path = ring_path(&base, i);
105 rings.push(Arc::new(SpscRingCore::create(&path, capacity)?));
106 }
107 let producers: Vec<MpscProducer> = rings
108 .iter()
109 .map(|r| MpscProducer {
110 inner: Arc::clone(r),
111 _not_sync: PhantomData,
112 })
113 .collect();
114 let consumer = MpscConsumer {
115 rings,
116 next_drain: AtomicUsize::new(0),
117 _not_sync: PhantomData,
118 };
119 Ok((producers, consumer))
120 }
121
122 pub fn open_pool(
126 path_prefix: impl AsRef<Path>,
127 n_producers: usize,
128 expected_capacity: usize,
129 ) -> Result<(Vec<MpscProducer>, MpscConsumer), RingError> {
130 assert!(n_producers >= 1, "n_producers must be >= 1");
131 let mut rings: Vec<Arc<SpscRingCore>> = Vec::with_capacity(n_producers);
132 let base = path_prefix.as_ref().to_path_buf();
133 for i in 0..n_producers {
134 let path = ring_path(&base, i);
135 rings.push(Arc::new(SpscRingCore::open(&path, expected_capacity)?));
136 }
137 let producers: Vec<MpscProducer> = rings
138 .iter()
139 .map(|r| MpscProducer {
140 inner: Arc::clone(r),
141 _not_sync: PhantomData,
142 })
143 .collect();
144 let consumer = MpscConsumer {
145 rings,
146 next_drain: AtomicUsize::new(0),
147 _not_sync: PhantomData,
148 };
149 Ok((producers, consumer))
150 }
151}
152
153fn ring_path(prefix: &std::path::Path, i: usize) -> std::path::PathBuf {
154 let mut s = prefix.as_os_str().to_owned();
155 s.push(format!(".{i}.bin"));
156 std::path::PathBuf::from(s)
157}
158
159impl MpscProducer {
160 pub fn try_push(&self, payload: &[u8]) -> Result<(), RingError> {
162 self.inner.try_push(payload)
163 }
164
165 pub fn capacity(&self) -> usize {
167 self.inner.capacity()
168 }
169
170 pub fn head(&self) -> u64 {
172 self.inner.head()
173 }
174}
175
176impl MpscConsumer {
177 pub fn try_pop(&self, out: &mut [u8]) -> Result<usize, RingError> {
181 let n = self.rings.len();
182 let start = self.next_drain.load(Ordering::Relaxed);
183 for i in 0..n {
184 let idx = (start + i) % n;
185 if let Ok(bytes) = self.rings[idx].try_pop(out) {
186 self.next_drain.store((idx + 1) % n, Ordering::Relaxed);
189 return Ok(bytes);
190 }
191 }
192 Err(RingError::Empty)
193 }
194
195 pub fn n_producers(&self) -> usize {
197 self.rings.len()
198 }
199
200 pub fn approx_total_len(&self) -> usize {
202 self.rings.iter().map(|r| r.approx_len()).sum()
203 }
204}
205
206pub struct SharedRingMpscFifo;
234
235pub struct MpscFifoProducer {
238 inner: Arc<SharedRing>,
239 _not_sync: PhantomData<Cell<()>>,
240}
241
242pub struct MpscFifoConsumer {
245 inner: Arc<SharedRing>,
246 _not_sync: PhantomData<Cell<()>>,
247}
248
249impl SharedRingMpscFifo {
250 pub fn create_anon_pool(
252 n_producers: usize,
253 capacity: usize,
254 ) -> Result<(Vec<MpscFifoProducer>, MpscFifoConsumer), RingError> {
255 assert!(n_producers >= 1, "n_producers must be >= 1");
256 let ring = Arc::new(SharedRing::create_anon(capacity)?);
257 let producers: Vec<MpscFifoProducer> = (0..n_producers)
258 .map(|_| MpscFifoProducer {
259 inner: Arc::clone(&ring),
260 _not_sync: PhantomData,
261 })
262 .collect();
263 let consumer = MpscFifoConsumer {
264 inner: ring,
265 _not_sync: PhantomData,
266 };
267 Ok((producers, consumer))
268 }
269
270 pub fn create_pool(
274 path: impl AsRef<Path>,
275 n_producers: usize,
276 capacity: usize,
277 ) -> Result<(Vec<MpscFifoProducer>, MpscFifoConsumer), RingError> {
278 assert!(n_producers >= 1, "n_producers must be >= 1");
279 let ring = Arc::new(SharedRing::create(path, capacity)?);
280 let producers: Vec<MpscFifoProducer> = (0..n_producers)
281 .map(|_| MpscFifoProducer {
282 inner: Arc::clone(&ring),
283 _not_sync: PhantomData,
284 })
285 .collect();
286 let consumer = MpscFifoConsumer {
287 inner: ring,
288 _not_sync: PhantomData,
289 };
290 Ok((producers, consumer))
291 }
292
293 pub fn open_pool(
295 path: impl AsRef<Path>,
296 n_producers: usize,
297 expected_capacity: usize,
298 ) -> Result<(Vec<MpscFifoProducer>, MpscFifoConsumer), RingError> {
299 assert!(n_producers >= 1, "n_producers must be >= 1");
300 let ring = Arc::new(SharedRing::open(path, expected_capacity)?);
301 let producers: Vec<MpscFifoProducer> = (0..n_producers)
302 .map(|_| MpscFifoProducer {
303 inner: Arc::clone(&ring),
304 _not_sync: PhantomData,
305 })
306 .collect();
307 let consumer = MpscFifoConsumer {
308 inner: ring,
309 _not_sync: PhantomData,
310 };
311 Ok((producers, consumer))
312 }
313}
314
315impl MpscFifoProducer {
316 pub fn try_push(&self, payload: &[u8]) -> Result<(), RingError> {
320 self.inner.try_push(payload)
321 }
322}
323
324impl MpscFifoConsumer {
325 pub fn try_pop(&self, out: &mut [u8]) -> Result<usize, RingError> {
331 self.inner.try_pop_spsc(out)
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use crate::spsc_ring::SPSC_PAYLOAD_BYTES;
339 use std::thread;
340
341 #[test]
342 fn create_anon_pool_round_trip() {
343 let (producers, consumer) = SharedRingMpsc::create_anon_pool(4, 8).unwrap();
344 assert_eq!(producers.len(), 4);
345 assert_eq!(consumer.n_producers(), 4);
346
347 for (i, p) in producers.iter().enumerate() {
349 let mut buf = [0u8; SPSC_PAYLOAD_BYTES];
350 buf[..4].copy_from_slice(&(i as u32).to_le_bytes());
351 p.try_push(&buf).unwrap();
352 }
353
354 let mut seen = [false; 4];
356 let mut out = [0u8; SPSC_PAYLOAD_BYTES];
357 for _ in 0..4 {
358 consumer.try_pop(&mut out).unwrap();
359 let v = u32::from_le_bytes(out[..4].try_into().unwrap()) as usize;
360 assert!(v < 4, "got value {v} outside producer index range");
361 assert!(!seen[v], "value {v} appeared twice");
362 seen[v] = true;
363 }
364 assert!(seen.iter().all(|&s| s), "not every producer delivered");
365 assert_eq!(consumer.try_pop(&mut out).unwrap_err(), RingError::Empty);
366 }
367
368 #[test]
369 fn concurrent_producers_lose_no_items() {
370 let (producers, consumer) = SharedRingMpsc::create_anon_pool(4, 64).unwrap();
371 const PER_PRODUCER: u32 = 10_000;
372
373 let producer_handles: Vec<_> = producers
375 .into_iter()
376 .enumerate()
377 .map(|(pid, p)| {
378 thread::spawn(move || {
379 for i in 0..PER_PRODUCER {
380 let mut buf = [0u8; SPSC_PAYLOAD_BYTES];
381 buf[..4].copy_from_slice(&(pid as u32).to_le_bytes());
384 buf[4..8].copy_from_slice(&i.to_le_bytes());
385 while p.try_push(&buf).is_err() {
386 std::hint::spin_loop();
387 }
388 }
389 })
390 })
391 .collect();
392
393 let n_producers = consumer.n_producers();
395 let consumer_handle = thread::spawn(move || -> (u32, Vec<u32>) {
396 let mut next: Vec<u32> = vec![0; n_producers];
397 let mut total: u32 = 0;
398 let target = PER_PRODUCER * n_producers as u32;
399 let mut out = [0u8; SPSC_PAYLOAD_BYTES];
400 while total < target {
401 if consumer.try_pop(&mut out).is_ok() {
402 let pid = u32::from_le_bytes(out[..4].try_into().unwrap()) as usize;
403 let seq = u32::from_le_bytes(out[4..8].try_into().unwrap());
404 assert_eq!(
405 seq, next[pid],
406 "per-producer FIFO violated for producer {pid}: expected {} got {}",
407 next[pid], seq,
408 );
409 next[pid] += 1;
410 total += 1;
411 } else {
412 std::hint::spin_loop();
413 }
414 }
415 (total, next)
416 });
417
418 for h in producer_handles {
419 h.join().unwrap();
420 }
421 let (total, next) = consumer_handle.join().unwrap();
422 assert_eq!(total, PER_PRODUCER * 4);
423 assert_eq!(next, vec![PER_PRODUCER; 4]);
424 }
425
426 #[test]
427 fn round_robin_is_fair() {
428 let (producers, consumer) = SharedRingMpsc::create_anon_pool(3, 4).unwrap();
432 for (i, p) in producers.iter().enumerate() {
433 let mut buf = [0u8; SPSC_PAYLOAD_BYTES];
434 buf[..4].copy_from_slice(&(i as u32).to_le_bytes());
435 p.try_push(&buf).unwrap();
436 }
437 let mut order = Vec::new();
438 let mut out = [0u8; SPSC_PAYLOAD_BYTES];
439 while consumer.try_pop(&mut out).is_ok() {
440 order.push(u32::from_le_bytes(out[..4].try_into().unwrap()));
441 }
442 assert_eq!(order, vec![0, 1, 2]);
445 }
446}