1use std::{future::Future, sync::Arc, sync::atomic::AtomicUsize, sync::atomic::Ordering};
2
3static mut CBS: Option<Arc<dyn CallbacksApi>> = None;
6
7static STATE: AtomicUsize = AtomicUsize::new(0);
8
9const UNINITIALIZED: usize = 0;
13const INITIALIZING: usize = 1;
14const INITIALIZED: usize = 2;
15
16trait CallbacksApi {
17 fn before(&self) -> Option<*const ()>;
18 fn enter(&self, _: *const ()) -> *const ();
19 fn exit(&self, _: *const ());
20 fn after(&self, _: *const ());
21}
22
23#[allow(clippy::struct_field_names)]
24struct Callbacks<A, B, C, D> {
25 f_before: A,
26 f_enter: B,
27 f_exit: C,
28 f_after: D,
29}
30
31impl<A, B, C, D> CallbacksApi for Callbacks<A, B, C, D>
32where
33 A: Fn() -> Option<*const ()> + 'static,
34 B: Fn(*const ()) -> *const () + 'static,
35 C: Fn(*const ()) + 'static,
36 D: Fn(*const ()) + 'static,
37{
38 fn before(&self) -> Option<*const ()> {
39 (self.f_before)()
40 }
41 fn enter(&self, d: *const ()) -> *const () {
42 (self.f_enter)(d)
43 }
44 fn exit(&self, d: *const ()) {
45 (self.f_exit)(d);
46 }
47 fn after(&self, d: *const ()) {
48 (self.f_after)(d);
49 }
50}
51
52pub(crate) struct Data {
53 cb: &'static dyn CallbacksApi,
54 ptr: *const (),
55}
56
57impl Data {
58 #[allow(clippy::if_not_else)]
59 pub(crate) fn load() -> Option<Data> {
60 let cb = if STATE.load(Ordering::Acquire) != INITIALIZED {
62 None
63 } else {
64 #[allow(static_mut_refs)]
65 unsafe {
66 Some(CBS.as_ref().map(AsRef::as_ref).unwrap())
67 }
68 };
69
70 if let Some(cb) = cb
71 && let Some(ptr) = cb.before()
72 {
73 return Some(Data { cb, ptr });
74 }
75 None
76 }
77
78 pub(crate) fn run<F, R>(&mut self, f: F) -> R
79 where
80 F: FnOnce() -> R,
81 {
82 struct Exit(&'static dyn CallbacksApi, *const ());
83
84 impl Drop for Exit {
85 fn drop(&mut self) {
86 self.0.exit(self.1);
87 }
88 }
89
90 let _exit = Exit(self.cb, self.cb.enter(self.ptr));
92 f()
93 }
94}
95
96pub(crate) fn wrap<F: Future>(fut: F) -> impl Future<Output = F::Output> {
101 let mut data = Data::load();
102 async move {
103 let mut f = std::pin::pin!(fut);
104 std::future::poll_fn(|cx| match data.as_mut() {
105 Some(data) => data.run(|| f.as_mut().poll(cx)),
106 None => f.as_mut().poll(cx),
107 })
108 .await
109 }
110}
111
112impl Drop for Data {
113 fn drop(&mut self) {
114 self.cb.after(self.ptr);
115 }
116}
117
118#[track_caller]
139#[must_use = "dropping the returned future cancels the task"]
140pub fn spawn_sendable<F>(
141 fut: F,
142) -> impl Future<Output = Result<F::Output, crate::JoinError>> + Send + 'static
143where
144 F: Future + 'static,
145 F::Output: Send,
146{
147 let (tx, rx) = oneshot::async_channel();
148 let (cancel, canceled) = oneshot::async_channel::<()>();
149 crate::spawn_detached(async move {
150 let output = {
151 let mut fut = std::pin::pin!(fut);
152 let mut canceled = std::pin::pin!(canceled);
153 std::future::poll_fn(|cx| {
154 if canceled.as_mut().poll(cx).is_ready() {
155 std::task::Poll::Ready(None)
156 } else {
157 fut.as_mut().poll(cx).map(Some)
158 }
159 })
160 .await
161 };
162 if let Some(output) = output {
163 let _ = tx.send(output);
164 }
165 });
166 async move {
167 let _cancel = cancel;
168 rx.await.map_err(|_| crate::JoinError)
169 }
170}
171
172pub unsafe fn task_callbacks<FBefore, FEnter, FExit, FAfter>(
198 f_before: FBefore,
199 f_enter: FEnter,
200 f_exit: FExit,
201 f_after: FAfter,
202) where
203 FBefore: Fn() -> Option<*const ()> + 'static + Sync,
204 FEnter: Fn(*const ()) -> *const () + 'static + Sync,
205 FExit: Fn(*const ()) + 'static + Sync,
206 FAfter: Fn(*const ()) + 'static + Sync,
207{
208 let new = Arc::new(Callbacks {
209 f_before,
210 f_enter,
211 f_exit,
212 f_after,
213 });
214 let _ = set_cbs(new);
215}
216
217pub unsafe fn task_opt_callbacks<FBefore, FEnter, FExit, FAfter>(
228 f_before: FBefore,
229 f_enter: FEnter,
230 f_exit: FExit,
231 f_after: FAfter,
232) -> bool
233where
234 FBefore: Fn() -> Option<*const ()> + Sync + 'static,
235 FEnter: Fn(*const ()) -> *const () + Sync + 'static,
236 FExit: Fn(*const ()) + Sync + 'static,
237 FAfter: Fn(*const ()) + Sync + 'static,
238{
239 let new = Arc::new(Callbacks {
240 f_before,
241 f_enter,
242 f_exit,
243 f_after,
244 });
245 set_cbs(new).is_ok()
246}
247
248fn set_cbs(cbs: Arc<dyn CallbacksApi>) -> Result<(), ()> {
249 match STATE.compare_exchange(
250 UNINITIALIZED,
251 INITIALIZING,
252 Ordering::Acquire,
253 Ordering::Relaxed,
254 ) {
255 Ok(UNINITIALIZED) => {
256 unsafe {
257 CBS = Some(cbs);
258 }
259 STATE.store(INITIALIZED, Ordering::Release);
260 Ok(())
261 }
262 Err(INITIALIZING) => {
263 while STATE.load(Ordering::Relaxed) == INITIALIZING {
264 std::hint::spin_loop();
265 }
266 Err(())
267 }
268 _ => Err(()),
269 }
270}
271
272#[cfg(test)]
273#[path = "../tests/unit/task.rs"]
274mod tests;