spawned_rt/threads/
mod.rs1pub mod mpsc;
4pub mod oneshot;
5
6#[cfg(spawned_loom)]
9use loom::sync::{
10 atomic::{AtomicBool, Ordering},
11 Arc, Mutex,
12};
13#[cfg(not(spawned_loom))]
14use std::sync::{
15 atomic::{AtomicBool, Ordering},
16 Arc, Mutex,
17};
18use std::sync::{mpsc as std_mpsc, OnceLock};
19pub use std::{
20 future::Future,
21 thread::{sleep, spawn, JoinHandle},
22};
23
24#[cfg(feature = "async")]
25use crate::tasks::Runtime;
26use crate::tracing::init_tracing;
27
28static CTRL_C_SUBSCRIBERS: OnceLock<Mutex<Vec<std_mpsc::Sender<()>>>> = OnceLock::new();
30
31pub fn run(f: fn()) {
33 init_tracing();
34
35 f()
36}
37
38#[cfg(feature = "async")]
45pub fn block_on<F: Future>(future: F) -> F::Output {
46 #[cfg(feature = "backend-experiments")]
47 if std::env::var("SPAWNED_EXPERIMENT_RUNTIME").as_deref() == Ok("managed") {
48 static POOL: OnceLock<Runtime> = OnceLock::new();
50 return POOL
51 .get_or_init(|| {
52 tokio::runtime::Builder::new_multi_thread()
53 .worker_threads(2)
54 .enable_all()
55 .build()
56 .unwrap()
57 })
58 .block_on(future);
59 }
60 #[cfg(feature = "backend-experiments")]
61 let rt = match std::env::var("SPAWNED_EXPERIMENT_RUNTIME").as_deref() {
62 Ok("current") => tokio::runtime::Builder::new_current_thread()
63 .enable_all()
64 .build()
65 .unwrap(),
66 Ok("one-worker") => tokio::runtime::Builder::new_multi_thread()
67 .worker_threads(1)
68 .enable_all()
69 .build()
70 .unwrap(),
71 _ => isolated_runtime(),
72 };
73 #[cfg(not(feature = "backend-experiments"))]
74 let rt = isolated_runtime();
75 rt.block_on(future)
76}
77
78#[cfg(feature = "async")]
81fn isolated_runtime() -> Runtime {
82 tokio::runtime::Builder::new_multi_thread()
83 .worker_threads(1)
84 .enable_all()
85 .build()
86 .unwrap()
87}
88
89pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
91where
92 F: FnOnce() -> R + Send + 'static,
93 R: Send + 'static,
94{
95 spawn(f)
96}
97
98type CancelCallback = Box<dyn FnOnce() + Send>;
99
100#[derive(Clone)]
105pub struct CancellationToken {
106 is_cancelled: Arc<AtomicBool>,
107 callbacks: Arc<Mutex<Vec<CancelCallback>>>,
108}
109
110impl std::fmt::Debug for CancellationToken {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 f.debug_struct("CancellationToken")
113 .field("is_cancelled", &self.is_cancelled())
114 .finish()
115 }
116}
117
118impl Default for CancellationToken {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl CancellationToken {
125 pub fn new() -> Self {
126 CancellationToken {
127 is_cancelled: Arc::new(false.into()),
128 callbacks: Arc::new(Mutex::new(Vec::new())),
129 }
130 }
131
132 pub fn is_cancelled(&self) -> bool {
133 self.is_cancelled.load(Ordering::SeqCst)
134 }
135
136 pub fn cancel(&self) {
137 self.is_cancelled.store(true, Ordering::SeqCst);
138 let callbacks: Vec<_> = self
140 .callbacks
141 .lock()
142 .unwrap_or_else(|e| e.into_inner())
143 .drain(..)
144 .collect();
145 for cb in callbacks {
146 cb();
147 }
148 }
149
150 pub fn on_cancel(&self, callback: CancelCallback) {
156 let mut callbacks = self.callbacks.lock().unwrap_or_else(|e| e.into_inner());
161 if self.is_cancelled() {
162 drop(callbacks);
163 callback();
164 } else {
165 callbacks.push(callback);
166 }
167 }
168}
169
170pub fn ctrl_c() -> impl FnOnce() + Send + 'static {
187 let subscribers = CTRL_C_SUBSCRIBERS.get_or_init(|| {
189 ctrlc::set_handler(|| {
190 if let Some(subs) = CTRL_C_SUBSCRIBERS.get() {
191 let mut guard = subs.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
192 guard.retain(|tx| tx.send(()).is_ok());
194 }
195 })
196 .expect("Ctrl+C handler already set. Use ctrl_c() instead of ctrlc::set_handler()");
197 Mutex::new(Vec::new())
198 });
199
200 let (tx, rx) = std_mpsc::channel();
202 subscribers
203 .lock()
204 .unwrap_or_else(|poisoned| poisoned.into_inner())
205 .push(tx);
206
207 move || {
208 let _ = rx.recv();
209 }
210}
211
212#[cfg(not(feature = "async"))]
215pub fn block_on<F: Future>(future: F) -> F::Output {
216 use std::task::{Context, Poll, Wake, Waker};
217 struct Parker {
218 notified: std::sync::Mutex<bool>,
219 signal: std::sync::Condvar,
220 }
221 impl Parker {
222 fn notify(&self) {
223 *self.notified.lock().unwrap_or_else(|p| p.into_inner()) = true;
224 self.signal.notify_one();
225 }
226 fn wait(&self) {
227 let mut notified = self.notified.lock().unwrap_or_else(|p| p.into_inner());
228 while !*notified {
229 notified = self
230 .signal
231 .wait(notified)
232 .unwrap_or_else(|p| p.into_inner());
233 }
234 *notified = false;
235 }
236 }
237 impl Wake for Parker {
238 fn wake(self: std::sync::Arc<Self>) {
239 self.notify();
240 }
241 fn wake_by_ref(self: &std::sync::Arc<Self>) {
242 self.notify();
243 }
244 }
245 let parker = std::sync::Arc::new(Parker {
248 notified: std::sync::Mutex::new(false),
249 signal: std::sync::Condvar::new(),
250 });
251 let waker = Waker::from(parker.clone());
252 let mut context = Context::from_waker(&waker);
253 let mut future = std::pin::pin!(future);
254 loop {
255 match future.as_mut().poll(&mut context) {
256 Poll::Ready(value) => return value,
257 Poll::Pending => parker.wait(),
258 }
259 }
260}
261
262#[cfg(all(test, not(feature = "async")))]
263mod parking_tests {
264 #[test]
265 fn nested_block_on_does_not_consume_the_outer_futures_wakeup() {
266 let (tx, rx) = std::sync::mpsc::channel();
267 let worker = std::thread::spawn(move || {
268 let mut outer_polled = false;
269 let value = super::block_on(std::future::poll_fn(|cx| {
270 if outer_polled {
271 return std::task::Poll::Ready(42);
272 }
273 outer_polled = true;
274 cx.waker().wake_by_ref();
275 let mut inner_polled = false;
276 super::block_on(std::future::poll_fn(|inner| {
277 if inner_polled {
278 std::task::Poll::Ready(())
279 } else {
280 inner_polled = true;
281 inner.waker().wake_by_ref();
282 std::task::Poll::Pending
283 }
284 }));
285 std::task::Poll::Pending
286 }));
287 tx.send(value).unwrap();
288 });
289 let result = rx.recv_timeout(std::time::Duration::from_secs(1));
290 if result.is_err() {
291 worker.thread().unpark();
294 let _ = rx.recv_timeout(std::time::Duration::from_secs(1));
295 }
296 let value = result.expect("inner executor consumed the outer wake");
297 worker.join().unwrap();
298 assert_eq!(value, 42);
299 }
300}