shuttle_engine/
thread_support.rs1use crate::runtime::execution::ExecutionState;
2use crate::runtime::thread;
3use std::marker::PhantomData;
4
5pub fn yield_now() {
7 let waker = ExecutionState::with(|state| state.current().waker());
8 waker.wake_by_ref();
9 ExecutionState::request_yield();
10 thread::switch();
11}
12
13pub fn thread_fn<F, T>(
15 f: F,
16 switch_before_exit: bool,
17 result: std::sync::Arc<std::sync::Mutex<Option<std::thread::Result<T>>>>,
18) where
19 F: FnOnce() -> T,
20{
21 let ret = f();
22
23 if switch_before_exit && ExecutionState::with(|s| s.exit_current_truncates_execution()) {
24 thread::switch();
25 }
26
27 let ret = if std::sync::Arc::strong_count(&result) > 1 {
30 Some(ret)
31 } else {
32 drop(ret);
33 None
34 };
35
36 tracing::trace!("thread finished, dropping thread locals");
37 ExecutionState::drop_task_locals();
38 tracing::trace!("done dropping thread locals");
39
40 if let Some(ret) = ret {
41 *result.lock().unwrap() = Some(Ok(ret));
42 }
43 ExecutionState::with(|state| {
44 if let Some(waiter) = state.current_mut().take_waiter() {
45 state.get_mut(waiter).unblock();
46 }
47 });
48}
49
50pub struct LocalKey<T: 'static> {
52 #[doc(hidden)]
53 pub init: fn() -> T,
54 #[doc(hidden)]
58 pub _p: PhantomData<fn() -> T>,
59}
60
61#[doc(hidden)]
66pub struct DestroyedLastKey<T: 'static> {
67 key: LocalKey<T>,
68}
69
70impl<T: 'static> DestroyedLastKey<T> {
71 pub const fn new(init: fn() -> T) -> Self {
73 Self {
74 key: LocalKey { init, _p: PhantomData },
75 }
76 }
77
78 pub fn try_with<F, R>(&'static self, f: F) -> std::result::Result<R, AccessError>
80 where
81 F: FnOnce(&T) -> R,
82 {
83 self.key.try_with_slot(f, true)
84 }
85}
86
87impl<T: 'static> std::fmt::Debug for DestroyedLastKey<T> {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("DestroyedLastKey").finish_non_exhaustive()
90 }
91}
92
93impl<T: 'static> std::fmt::Debug for LocalKey<T> {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.debug_struct("LocalKey").finish_non_exhaustive()
96 }
97}
98
99impl<T: 'static> LocalKey<T> {
100 pub fn with<F, R>(&'static self, f: F) -> R
104 where
105 F: FnOnce(&T) -> R,
106 {
107 self.try_with(f).expect(
108 "cannot access a Thread Local Storage value \
109 during or after destruction",
110 )
111 }
112
113 pub fn try_with<F, R>(&'static self, f: F) -> std::result::Result<R, AccessError>
119 where
120 F: FnOnce(&T) -> R,
121 {
122 self.try_with_slot(f, false)
123 }
124
125 fn try_with_slot<F, R>(&'static self, f: F, destroyed_last: bool) -> std::result::Result<R, AccessError>
128 where
129 F: FnOnce(&T) -> R,
130 {
131 let value = self.get().unwrap_or_else(|| {
132 let value = (self.init)();
133
134 let unused = ExecutionState::with(move |state| {
138 if state.current().local(self).is_none() {
139 if destroyed_last {
140 state.current_mut().init_local_destroyed_last(self, value);
141 } else {
142 state.current_mut().init_local(self, value);
143 }
144 None
145 } else {
146 Some(value)
147 }
148 });
149 drop(unused);
150
151 self.get().unwrap()
152 })?;
153
154 Ok(f(value))
155 }
156
157 fn get(&'static self) -> Option<std::result::Result<&'static T, AccessError>> {
158 unsafe fn extend_lt<'b, T>(t: &'_ T) -> &'b T {
160 std::mem::transmute(t)
161 }
162
163 ExecutionState::with(|state| {
164 if let Ok(value) = state.current().local(self)? {
165 Some(Ok(unsafe { extend_lt(value) }))
168 } else {
169 Some(Err(AccessError))
170 }
171 })
172 }
173}
174
175#[derive(Clone, Copy, PartialEq, Eq, Debug)]
177#[non_exhaustive]
178pub struct AccessError;
179
180impl std::fmt::Display for AccessError {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 std::fmt::Display::fmt("already destroyed", f)
183 }
184}
185
186impl std::error::Error for AccessError {}