concurrency_traits/
lib.rs

1//! Traits for concurrent primitives.
2
3#![cfg_attr(not(any(feature = "std", test)), no_std)]
4#![warn(missing_docs, missing_debug_implementations, unused_import_braces)]
5#![cfg_attr(feature = "nightly", feature(option_result_unwrap_unchecked))]
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9
10pub mod mutex;
11pub mod queue;
12pub mod rw_lock;
13pub mod semaphore;
14pub mod stack;
15
16use core::convert::Infallible;
17use core::fmt::Debug;
18use core::ops::{Add, AddAssign, Sub, SubAssign};
19use core::time::Duration;
20
21trait EnsureSend: Send {}
22trait EnsureSync: Sync {}
23
24/// Functions to interact with system time.
25pub trait TimeFunctions {
26    /// The type of an instant for this system. Analog for [`std::time::Instant`].
27    type InstantType: Add<Duration, Output = Self::InstantType>
28        + AddAssign<Duration>
29        + Sub<Duration, Output = Self::InstantType>
30        + SubAssign<Duration>
31        + Sub<Self::InstantType, Output = Duration>
32        + Ord
33        + Copy;
34
35    /// Get the current instant. Analog for [`std::time::Instant::now`].
36    fn current_time() -> Self::InstantType;
37}
38/// Functions to allow the current thread to interact in ways a thread might need to.
39pub trait ThreadFunctions {
40    /// Sleeps the current thread for a specified duration. Analog for [`std::thread::sleep`].
41    fn sleep(duration: Duration);
42    /// Yields the current thread to the OS. Analog for [`std::thread::yield_now`].
43    fn yield_now();
44}
45/// Functions to spawn new threads. If infallibility is required look to [`ThreadSpawner`]. If a result is needed from the launched thread look to [`TryResultThreadSpawner`] or [`ResultThreadSpawner`]. `O` is the result of the thread function.
46pub trait TryThreadSpawner<O>
47where
48    O: Send + 'static,
49{
50    /// The handle that is returned from spawning. Analog to [`std::thread::JoinHandle`].
51    type ThreadHandle: ThreadHandle;
52    /// The error that can occur from starting the thread.
53    type SpawnError;
54
55    /// Attempts to spawn a thread returning a result of [`Self::ThreadHandle`] and [`Self::SpawnError`].
56    fn try_spawn(
57        func: impl FnOnce() -> O + 'static + Send,
58    ) -> Result<Self::ThreadHandle, Self::SpawnError>;
59}
60/// Same as a [`TryThreadSpawner`] with an [`Infallible`] [`TryThreadSpawner::SpawnError`]. This is auto-implemented with [`TryThreadSpawner`] when possible. If a result is needed from the launched thread look to [`ResultThreadSpawner`].
61pub trait ThreadSpawner<O>: TryThreadSpawner<O, SpawnError = Infallible>
62where
63    O: Send + 'static,
64{
65    /// Spawns a thread returning a [`TryThreadSpawner::ThreadHandle`]. Analog to [`std::thread::spawn`]. Will be faster on nightly due to [`Result::unwrap_unchecked`].
66    fn spawn(func: impl FnOnce() -> O + 'static + Send) -> Self::ThreadHandle {
67        #[cfg(not(feature = "nightly"))]
68        {
69            Self::try_spawn(func).unwrap()
70        }
71        #[cfg(feature = "nightly")]
72        unsafe {
73            Self::try_spawn(func).unwrap_unchecked()
74        }
75    }
76}
77impl<T, O> ThreadSpawner<O> for T
78where
79    T: TryThreadSpawner<O, SpawnError = Infallible>,
80    O: Send + 'static,
81{
82}
83/// Named version of [`TryThreadSpawner`] where the handle is a [`TryJoinableHandle`]. Auto implemented.
84pub trait TryResultThreadSpawner<O>: TryThreadSpawner<O>
85where
86    Self::ThreadHandle: TryJoinableHandle<Output = O>,
87    O: Send + 'static,
88{
89}
90impl<T, O> TryResultThreadSpawner<O> for T
91where
92    T: TryThreadSpawner<O>,
93    T::ThreadHandle: TryJoinableHandle<Output = O>,
94    O: Send + 'static,
95{
96}
97/// Named version of [`ThreadSpawner`] where the handle is a [`TryJoinableHandle`]. Auto implemented.
98pub trait ResultThreadSpawner<O>: ThreadSpawner<O>
99where
100    Self::ThreadHandle: TryJoinableHandle<Output = O>,
101    O: Send + 'static,
102{
103}
104impl<T, O> ResultThreadSpawner<O> for T
105where
106    T: ThreadSpawner<O>,
107    T::ThreadHandle: TryJoinableHandle<Output = O>,
108    O: Send + 'static,
109{
110}
111/// Functions to allow parking functionality for threads.
112pub trait ThreadParker {
113    /// The type of a thread portable id. Analog for [`std::thread::Thread`].
114    type ThreadId: Debug;
115
116    /// Parks the current thread. Analog for [`std::thread::park`]. This may spuriously wake.
117    fn park();
118    /// Unparks a thread. Analog for [`std::thread::Thread::unpark`].
119    fn unpark(thread: Self::ThreadId);
120    /// Gets the handle to the current thread. Analog for [`std::thread::current`].
121    fn current_thread() -> Self::ThreadId;
122}
123/// Functions to allow parking functionality with timeout for threads.
124pub trait ThreadTimeoutParker: ThreadParker {
125    /// Parks the current thread with a timeout. Analog to [`std::thread::park_timeout`].
126    fn park_timeout(timeout: Duration);
127}
128/// A handle to a spawned thread. Analog for [`std::thread::JoinHandle`].
129pub trait ThreadHandle {
130    /// The type of a thread portable id. Analog for [`std::thread::Thread`].
131    type ThreadId;
132
133    /// Gets a thread id from this handle. Analog for [`std::thread::JoinHandle::thread`].
134    fn thread_id(&self) -> &Self::ThreadId;
135}
136/// A handle to a spawned thread that can be joined, blocking the current thread until the target is finished. Analog for [`std::thread::JoinHandle`]. If infallibility is needed look to [`JoinableHandle`].
137pub trait TryJoinableHandle: Sized + ThreadHandle {
138    /// The output of joining this thread.
139    type Output;
140    /// The possible error when joining this thread,
141    type ThreadError;
142
143    /// Tries to join the target thread blocking the current thread. Analog for [`std::thread::JoinHandle::join`].
144    fn try_join(self) -> Result<Self::Output, Self::ThreadError>;
145}
146/// A handle to a spawned thread that can be joined infallibly. Auto-implemented with [`TryJoinableHandle`] where possible.
147pub trait JoinableHandle: Sized + TryJoinableHandle<ThreadError = Infallible> {
148    /// Joins the target thread blocking the current thread.
149    #[inline]
150    fn join(self) -> Self::Output {
151        #[cfg(not(feature = "nightly"))]
152        {
153            self.try_join().unwrap()
154        }
155        #[cfg(feature = "nightly")]
156        unsafe {
157            self.try_join().unwrap_unchecked()
158        }
159    }
160}
161impl<T> JoinableHandle for T where T: TryJoinableHandle<ThreadError = Infallible> {}
162
163/// A full concurrent system with all functions accessible by reference. This Trait should be implemented where possible.
164pub trait ConcurrentSystem<O>: 'static
165where
166    Self: TimeFunctions
167        + ThreadFunctions
168        + TryThreadSpawner<O>
169        + ThreadParker<
170            ThreadId = <<Self as TryThreadSpawner<O>>::ThreadHandle as ThreadHandle>::ThreadId,
171        >,
172    O: Send + 'static,
173{
174}
175
176/// Std implementations for [`TimeFunctions`], [`ThreadFunctions], [`TryThreadSpawner`], and [`ThreadParker`].
177#[cfg(feature = "std")]
178#[derive(Copy, Clone, Debug)]
179pub struct StdThreadFunctions;
180#[cfg(feature = "std")]
181mod std_thread_impls {
182    use super::*;
183    impl TimeFunctions for StdThreadFunctions {
184        type InstantType = std::time::Instant;
185
186        #[inline]
187        fn current_time() -> Self::InstantType {
188            std::time::Instant::now()
189        }
190    }
191    impl ThreadFunctions for StdThreadFunctions {
192        #[inline]
193        fn sleep(duration: Duration) {
194            std::thread::sleep(duration)
195        }
196
197        fn yield_now() {
198            std::thread::yield_now()
199        }
200    }
201    impl<O> TryThreadSpawner<O> for StdThreadFunctions
202    where
203        O: Send + 'static,
204    {
205        type ThreadHandle = std::thread::JoinHandle<O>;
206        type SpawnError = Infallible;
207
208        fn try_spawn(
209            func: impl FnOnce() -> O + 'static + Send,
210        ) -> Result<Self::ThreadHandle, Self::SpawnError> {
211            Ok(std::thread::spawn(func))
212        }
213    }
214    impl ThreadParker for StdThreadFunctions {
215        type ThreadId = std::thread::Thread;
216
217        #[inline]
218        fn park() {
219            std::thread::park()
220        }
221
222        #[inline]
223        fn unpark(thread: Self::ThreadId) {
224            thread.unpark()
225        }
226
227        #[inline]
228        fn current_thread() -> Self::ThreadId {
229            std::thread::current()
230        }
231    }
232    impl ThreadTimeoutParker for StdThreadFunctions {
233        fn park_timeout(timeout: Duration) {
234            std::thread::park_timeout(timeout)
235        }
236    }
237    impl<O> ThreadHandle for std::thread::JoinHandle<O> {
238        type ThreadId = std::thread::Thread;
239
240        #[inline]
241        fn thread_id(&self) -> &Self::ThreadId {
242            self.thread()
243        }
244    }
245    impl<O> TryJoinableHandle for std::thread::JoinHandle<O> {
246        type Output = O;
247        type ThreadError = Box<dyn std::any::Any + Send + 'static>;
248
249        #[inline]
250        fn try_join(self) -> Result<Self::Output, Self::ThreadError> {
251            self.join()
252        }
253    }
254    impl<O> ConcurrentSystem<O> for StdThreadFunctions where O: Send + 'static {}
255}
256
257// TODO: Replace future associated types and boxed futures with existential types when stabilized https://rust-lang.github.io/rfcs/2071-impl-trait-existential-types.html