1use std::{cell::Cell, panic, ptr};
2
3thread_local! {
4 static CB: Cell<*const Callbacks> = const { Cell::new(ptr::null()) };
5}
6
7struct Callbacks {
8 before: Box<dyn Fn() -> Option<*const ()>>,
9 enter: Box<dyn Fn(*const ()) -> *const ()>,
10 exit: Box<dyn Fn(*const ())>,
11 after: Box<dyn Fn(*const ())>,
12}
13
14pub(crate) struct Data {
15 cb: &'static Callbacks,
16 ptr: *const (),
17}
18
19impl Data {
20 pub(crate) fn load() -> Option<Data> {
21 let cb = CB.with(|cb| cb.get());
22
23 if let Some(cb) = unsafe { cb.as_ref() } {
24 if let Some(ptr) = (*cb.before)() {
25 return Some(Data { cb, ptr });
26 }
27 }
28 None
29 }
30
31 pub(crate) fn run<F, R>(&mut self, f: F) -> R
32 where
33 F: FnOnce() -> R,
34 {
35 let ptr = (*self.cb.enter)(self.ptr);
36 let result = f();
37 (*self.cb.exit)(ptr);
38 result
39 }
40}
41
42impl Drop for Data {
43 fn drop(&mut self) {
44 (*self.cb.after)(self.ptr)
45 }
46}
47
48pub unsafe fn task_callbacks<FBefore, FEnter, FExit, FAfter>(
59 before: FBefore,
60 enter: FEnter,
61 exit: FExit,
62 after: FAfter,
63) where
64 FBefore: Fn() -> Option<*const ()> + 'static,
65 FEnter: Fn(*const ()) -> *const () + 'static,
66 FExit: Fn(*const ()) + 'static,
67 FAfter: Fn(*const ()) + 'static,
68{
69 CB.with(|cb| {
70 if !cb.get().is_null() {
71 panic!("Spawn callbacks already set");
72 }
73
74 let new: *mut Callbacks = Box::leak(Box::new(Callbacks {
75 before: Box::new(before),
76 enter: Box::new(enter),
77 exit: Box::new(exit),
78 after: Box::new(after),
79 }));
80 cb.replace(new);
81 });
82}
83
84pub unsafe fn task_opt_callbacks<FBefore, FEnter, FExit, FAfter>(
93 before: FBefore,
94 enter: FEnter,
95 exit: FExit,
96 after: FAfter,
97) -> bool
98where
99 FBefore: Fn() -> Option<*const ()> + 'static,
100 FEnter: Fn(*const ()) -> *const () + 'static,
101 FExit: Fn(*const ()) + 'static,
102 FAfter: Fn(*const ()) + 'static,
103{
104 CB.with(|cb| {
105 if !cb.get().is_null() {
106 false
107 } else {
108 unsafe {
109 task_callbacks(before, enter, exit, after);
110 }
111 true
112 }
113 })
114}