openraft_rt/async_runtime.rs
1//! `async` runtime interface.
2//!
3//! `async` runtime is an abstraction over different asynchronous runtimes, such as `tokio`,
4//! `async-std`, etc.
5
6use std::fmt::Debug;
7use std::fmt::Display;
8use std::future::Future;
9use std::io;
10use std::time::Duration;
11
12use openraft_macros::since;
13
14use crate::Instant;
15use crate::Mpsc;
16use crate::MpscReceiver;
17use crate::Mutex;
18use crate::Oneshot;
19use crate::OptionalSend;
20use crate::OptionalSync;
21use crate::TryRecvError;
22use crate::Watch;
23
24/// A trait defining interfaces with an asynchronous runtime.
25///
26/// The intention of this trait is to allow an application using this crate to bind an asynchronous
27/// runtime that suits it the best.
28///
29/// Some additional related functions are also exposed by this trait.
30///
31/// ## Note
32///
33/// The default asynchronous runtime is `tokio`.
34pub trait AsyncRuntime: Debug + OptionalSend + OptionalSync + 'static {
35 /// The error type of [`Self::JoinHandle`].
36 type JoinError: Debug + Display + OptionalSend;
37
38 /// The return type of [`Self::spawn`].
39 type JoinHandle<T: OptionalSend + 'static>: Future<Output = Result<T, Self::JoinError>>
40 + OptionalSend
41 + OptionalSync
42 + Unpin;
43
44 /// The type that enables the user to sleep in an asynchronous runtime.
45 type Sleep: Future<Output = ()> + OptionalSend + OptionalSync;
46
47 /// A measurement of a monotonically non-decreasing clock.
48 type Instant: Instant;
49
50 /// The timeout error type.
51 type TimeoutError: Debug + Display + OptionalSend;
52
53 /// The timeout type used by [`Self::timeout`] and [`Self::timeout_at`] that enables the user
54 /// to await the outcome of a [`Future`].
55 type Timeout<R, T: Future<Output = R> + OptionalSend>: Future<Output = Result<R, Self::TimeoutError>> + OptionalSend;
56
57 /// Type of thread-local random number generator.
58 type ThreadLocalRng: rand::Rng;
59
60 /// Spawn a new task.
61 #[track_caller]
62 fn spawn<T>(future: T) -> Self::JoinHandle<T::Output>
63 where
64 T: Future + OptionalSend + 'static,
65 T::Output: OptionalSend + 'static;
66
67 /// Wait until `duration` has elapsed.
68 #[track_caller]
69 fn sleep(duration: Duration) -> Self::Sleep;
70
71 /// Wait until `deadline` is reached.
72 #[track_caller]
73 fn sleep_until(deadline: Self::Instant) -> Self::Sleep;
74
75 /// Require a [`Future`] to complete before the specified duration has elapsed.
76 #[track_caller]
77 fn timeout<R, F: Future<Output = R> + OptionalSend>(duration: Duration, future: F) -> Self::Timeout<R, F>;
78
79 /// Require a [`Future`] to complete before the specified instant in time.
80 #[track_caller]
81 fn timeout_at<R, F: Future<Output = R> + OptionalSend>(deadline: Self::Instant, future: F) -> Self::Timeout<R, F>;
82
83 /// Check if the [`Self::JoinError`] is `panic`.
84 #[track_caller]
85 fn is_panic(join_error: &Self::JoinError) -> bool;
86
87 /// Get the random number generator to use for generating random numbers.
88 ///
89 /// # Note
90 ///
91 /// This is a per-thread instance, which cannot be shared across threads or
92 /// sent to another thread.
93 #[track_caller]
94 fn thread_rng() -> Self::ThreadLocalRng;
95
96 /// The bounded MPSC channel implementation.
97 type Mpsc: Mpsc;
98
99 /// The watch channel implementation.
100 type Watch: Watch;
101
102 /// The oneshot channel implementation.
103 type Oneshot: Oneshot;
104
105 /// The async mutex implementation.
106 type Mutex<T: OptionalSend + 'static>: Mutex<T>;
107
108 /// Create a new runtime instance for testing purposes.
109 ///
110 /// **Note**: This method is primarily intended for testing and is not used by Openraft
111 /// internally. In production applications, the runtime should be created and managed
112 /// by the application itself, with Openraft running within that runtime.
113 ///
114 /// # Arguments
115 ///
116 /// * `threads` - Number of worker threads. Multi-threaded runtimes (like Tokio) will use this
117 /// value; single-threaded runtimes (like Monoio, Compio) may ignore it.
118 fn new(threads: usize) -> Self;
119
120 /// Run a future to completion on this runtime.
121 ///
122 /// This runs synchronously on the current thread, so `Send` is not required.
123 fn block_on<F, T>(&mut self, future: F) -> T
124 where
125 F: Future<Output = T>,
126 T: OptionalSend;
127
128 /// Convenience method: create a runtime and run the future to completion.
129 ///
130 /// Creates a runtime with default configuration (8 threads) and runs the future.
131 /// For simple cases where you don't need to reuse the runtime.
132 /// If you need to run multiple futures, consider using [`Self::new`] and
133 /// [`Self::block_on`] directly.
134 ///
135 /// This runs synchronously on the current thread, so `Send` is not required.
136 fn run<F, T>(future: F) -> T
137 where
138 Self: Sized,
139 F: Future<Output = T>,
140 T: OptionalSend,
141 {
142 Self::new(8).block_on(future)
143 }
144
145 /// Run a blocking function on a separate thread.
146 ///
147 /// The default implementation spawns a new OS thread for each call.
148 /// Runtime implementations may override this with their own thread pool
149 /// (e.g., tokio's `spawn_blocking`) for better resource management.
150 fn spawn_blocking<F, T>(f: F) -> impl Future<Output = Result<T, io::Error>> + Send
151 where
152 F: FnOnce() -> T + Send + 'static,
153 T: Send + 'static,
154 {
155 let (tx, rx) = futures_channel::oneshot::channel();
156 std::thread::spawn(move || {
157 tx.send(f()).ok();
158 });
159 async { rx.await.map_err(|_| io::Error::other("spawn_blocking task cancelled")) }
160 }
161
162 /// Try to poll a value from the channel within the given deadline.
163 ///
164 /// By default, this first checks synchronously if a value is already available.
165 /// If not, it uses the AsyncRuntime timeout mechanism to wait until:
166 /// - a new element arrives
167 /// - the deadline is reached
168 #[since(version = "0.10.0")]
169 fn mpsc_recv_deadline<T: OptionalSend>(
170 receiver: &mut <Self::Mpsc as Mpsc>::Receiver<T>,
171 deadline: Self::Instant,
172 ) -> impl Future<Output = Result<T, TryRecvError>> + OptionalSend {
173 async move {
174 match receiver.try_recv() {
175 Ok(value) => Ok(value),
176 Err(TryRecvError::Disconnected) => Err(TryRecvError::Disconnected),
177 Err(TryRecvError::Empty) if <Self::Instant as Instant>::now() >= deadline => Err(TryRecvError::Empty),
178 Err(TryRecvError::Empty) => match Self::timeout_at(deadline, receiver.recv()).await {
179 Ok(None) => Err(TryRecvError::Disconnected),
180 Ok(Some(value)) => Ok(value),
181 Err(_) => Err(TryRecvError::Empty),
182 },
183 }
184 }
185 }
186}