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
24use crate::{tasks::Runtime, tracing::init_tracing};
25
26static CTRL_C_SUBSCRIBERS: OnceLock<Mutex<Vec<std_mpsc::Sender<()>>>> = OnceLock::new();
28
29pub fn run(f: fn()) {
31 init_tracing();
32
33 f()
34}
35
36pub fn block_on<F: Future>(future: F) -> F::Output {
38 #[cfg(feature = "backend-experiments")]
39 if std::env::var("SPAWNED_EXPERIMENT_RUNTIME").as_deref() == Ok("managed") {
40 static POOL: OnceLock<Runtime> = OnceLock::new();
42 return POOL
43 .get_or_init(|| {
44 tokio::runtime::Builder::new_multi_thread()
45 .worker_threads(2)
46 .enable_all()
47 .build()
48 .unwrap()
49 })
50 .block_on(future);
51 }
52 #[cfg(feature = "backend-experiments")]
53 let rt = match std::env::var("SPAWNED_EXPERIMENT_RUNTIME").as_deref() {
54 Ok("current") => tokio::runtime::Builder::new_current_thread()
55 .enable_all()
56 .build()
57 .unwrap(),
58 Ok("one-worker") => tokio::runtime::Builder::new_multi_thread()
59 .worker_threads(1)
60 .enable_all()
61 .build()
62 .unwrap(),
63 _ => Runtime::new().unwrap(),
64 };
65 #[cfg(not(feature = "backend-experiments"))]
66 let rt = Runtime::new().unwrap();
67 rt.block_on(future)
68}
69
70pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
72where
73 F: FnOnce() -> R + Send + 'static,
74 R: Send + 'static,
75{
76 spawn(f)
77}
78
79type CancelCallback = Box<dyn FnOnce() + Send>;
80
81#[derive(Clone)]
86pub struct CancellationToken {
87 is_cancelled: Arc<AtomicBool>,
88 callbacks: Arc<Mutex<Vec<CancelCallback>>>,
89}
90
91impl std::fmt::Debug for CancellationToken {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 f.debug_struct("CancellationToken")
94 .field("is_cancelled", &self.is_cancelled())
95 .finish()
96 }
97}
98
99impl Default for CancellationToken {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105impl CancellationToken {
106 pub fn new() -> Self {
107 CancellationToken {
108 is_cancelled: Arc::new(false.into()),
109 callbacks: Arc::new(Mutex::new(Vec::new())),
110 }
111 }
112
113 pub fn is_cancelled(&self) -> bool {
114 self.is_cancelled.load(Ordering::SeqCst)
115 }
116
117 pub fn cancel(&self) {
118 self.is_cancelled.store(true, Ordering::SeqCst);
119 let callbacks: Vec<_> = self
121 .callbacks
122 .lock()
123 .unwrap_or_else(|e| e.into_inner())
124 .drain(..)
125 .collect();
126 for cb in callbacks {
127 cb();
128 }
129 }
130
131 pub fn on_cancel(&self, callback: CancelCallback) {
137 let mut callbacks = self.callbacks.lock().unwrap_or_else(|e| e.into_inner());
142 if self.is_cancelled() {
143 drop(callbacks);
144 callback();
145 } else {
146 callbacks.push(callback);
147 }
148 }
149}
150
151pub fn ctrl_c() -> impl FnOnce() + Send + 'static {
168 let subscribers = CTRL_C_SUBSCRIBERS.get_or_init(|| {
170 ctrlc::set_handler(|| {
171 if let Some(subs) = CTRL_C_SUBSCRIBERS.get() {
172 let mut guard = subs.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
173 guard.retain(|tx| tx.send(()).is_ok());
175 }
176 })
177 .expect("Ctrl+C handler already set. Use ctrl_c() instead of ctrlc::set_handler()");
178 Mutex::new(Vec::new())
179 });
180
181 let (tx, rx) = std_mpsc::channel();
183 subscribers
184 .lock()
185 .unwrap_or_else(|poisoned| poisoned.into_inner())
186 .push(tx);
187
188 move || {
189 let _ = rx.recv();
190 }
191}