magnetic/
lib.rs

1//! Magnetic contains a set of high-performance queues useful for developing
2//! low-latency applications. All queues are FIFO unless otherwise specified.
3//!
4//! # Examples
5//!
6//! ```
7//! use std::thread::spawn;
8//! use magnetic::spsc::spsc_queue;
9//! use magnetic::buffer::dynamic::DynamicBuffer;
10//! use magnetic::{Producer, Consumer};
11//!
12//! let (p, c) = spsc_queue(DynamicBuffer::new(32).unwrap());
13//!
14//! // Push and pop within a single thread
15//! p.push(1).unwrap();
16//! assert_eq!(c.pop(), Ok(1));
17//!
18//! // Push and pop from multiple threads. Since this example is using the
19//! // SPSC queue, only one producer and one consumer are allowed.
20//! let t1 = spawn(move || {
21//!     for i in 0..10 {
22//!         println!("Producing {}", i);
23//!         p.push(i).unwrap();
24//!     }
25//!     p
26//! });
27//!
28//! let t2 = spawn(move || {
29//!     loop {
30//!         let i = c.pop().unwrap();
31//!         println!("Consumed {}", i);
32//!         if i == 9 { break; }
33//!     }
34//! });
35//!
36//! t1.join().unwrap();
37//! t2.join().unwrap();
38//! ```
39
40#![deny(missing_docs)]
41
42use std::fmt;
43
44pub mod buffer;
45pub mod mpmc;
46pub mod mpsc;
47pub mod spmc;
48pub mod spsc;
49mod util;
50
51/// Possible errors for `Producer::push`
52#[derive(Clone, Copy, PartialEq, Eq)]
53pub enum PushError<T> {
54    /// Consumer was destroyed
55    Disconnected(T),
56}
57
58impl<T> fmt::Debug for PushError<T> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            Self::Disconnected(_) => f.pad("Disconnected(_)"),
62        }
63    }
64}
65
66impl<T> fmt::Display for PushError<T> {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            Self::Disconnected(_) => "queue abandoned".fmt(f),
70        }
71    }
72}
73
74impl<T> std::error::Error for PushError<T> {}
75
76/// Possible errors for `Producer::try_push`
77#[derive(Clone, Copy, PartialEq, Eq)]
78pub enum TryPushError<T> {
79    /// Queue was full
80    Full(T),
81    /// Consumer was destroyed
82    Disconnected(T),
83}
84
85impl<T> fmt::Debug for TryPushError<T> {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            Self::Full(_) => f.pad("Full(_)"),
89            Self::Disconnected(_) => f.pad("Disconnected(_)"),
90        }
91    }
92}
93
94impl<T> fmt::Display for TryPushError<T> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        match self {
97            Self::Full(_) => "queue full".fmt(f),
98            Self::Disconnected(_) => "queue abandoned".fmt(f),
99        }
100    }
101}
102
103impl<T> std::error::Error for TryPushError<T> {}
104
105/// Possible errors for `Consumer::pop`
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum PopError {
108    /// Producer was destroyed
109    Disconnected,
110}
111
112impl fmt::Display for PopError {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        match self {
115            Self::Disconnected => "queue abandoned".fmt(f),
116        }
117    }
118}
119
120impl std::error::Error for PopError {}
121
122/// Possible errors for `Consumer::try_pop`
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum TryPopError {
125    /// Queue was empty
126    Empty,
127    /// Producer was destroyed
128    Disconnected,
129}
130
131impl fmt::Display for TryPopError {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        match self {
134            Self::Empty => "queue empty".fmt(f),
135            Self::Disconnected => "queue abandoned".fmt(f),
136        }
137    }
138}
139
140impl std::error::Error for TryPopError {}
141
142/// The consumer end of the queue allows for sending data. `Producer<T>` is
143/// always `Send`, but is only `Sync` for multi-producer (MPSC, MPMC) queues.
144pub trait Producer<T> {
145    /// Add value to front of the queue. This method will block if the queue
146    /// is currently full.
147    fn push(&self, value: T) -> Result<(), PushError<T>>;
148
149    /// Attempt to add a value to the front of the queue. If the value was
150    /// added successfully, `None` will be returned. If unsuccessful, `value`
151    /// will be returned. An unsuccessful push indicates that the queue was
152    /// full.
153    fn try_push(&self, value: T) -> Result<(), TryPushError<T>>;
154}
155
156/// The consumer end of the queue allows for receiving data. `Consumer<T>` is
157/// always `Send`, but is only `Sync` for multi-consumer (SPMC, MPMC) queues.
158pub trait Consumer<T> {
159    /// Remove value from the end of the queue. This method will block if the
160    /// queue is currently empty.
161    fn pop(&self) -> Result<T, PopError>;
162
163    /// Attempt to remove a value from the end of the queue. If the value was
164    /// removed successfully, `Some(T)` will be returned. If unsuccessful,
165    /// `None` will be returned. An unsuccessful pop indicates that the queue
166    /// was empty.
167    fn try_pop(&self) -> Result<T, TryPopError>;
168}