use crate::runtime::execution::ExecutionState;
use crate::runtime::thread;
use std::marker::PhantomData;
pub fn yield_now() {
let waker = ExecutionState::with(|state| state.current().waker());
waker.wake_by_ref();
ExecutionState::request_yield();
thread::switch();
}
pub fn thread_fn<F, T>(
f: F,
switch_before_exit: bool,
result: std::sync::Arc<std::sync::Mutex<Option<std::thread::Result<T>>>>,
) where
F: FnOnce() -> T,
{
let ret = f();
if switch_before_exit && ExecutionState::with(|s| s.exit_current_truncates_execution()) {
thread::switch();
}
let ret = if std::sync::Arc::strong_count(&result) > 1 {
Some(ret)
} else {
drop(ret);
None
};
tracing::trace!("thread finished, dropping thread locals");
ExecutionState::drop_task_locals();
tracing::trace!("done dropping thread locals");
if let Some(ret) = ret {
*result.lock().unwrap() = Some(Ok(ret));
}
ExecutionState::with(|state| {
if let Some(waiter) = state.current_mut().take_waiter() {
state.get_mut(waiter).unblock();
}
});
}
pub struct LocalKey<T: 'static> {
#[doc(hidden)]
pub init: fn() -> T,
#[doc(hidden)]
pub _p: PhantomData<fn() -> T>,
}
#[doc(hidden)]
pub struct DestroyedLastKey<T: 'static> {
key: LocalKey<T>,
}
impl<T: 'static> DestroyedLastKey<T> {
pub const fn new(init: fn() -> T) -> Self {
Self {
key: LocalKey { init, _p: PhantomData },
}
}
pub fn try_with<F, R>(&'static self, f: F) -> std::result::Result<R, AccessError>
where
F: FnOnce(&T) -> R,
{
self.key.try_with_slot(f, true)
}
}
impl<T: 'static> std::fmt::Debug for DestroyedLastKey<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DestroyedLastKey").finish_non_exhaustive()
}
}
impl<T: 'static> std::fmt::Debug for LocalKey<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LocalKey").finish_non_exhaustive()
}
}
impl<T: 'static> LocalKey<T> {
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
self.try_with(f).expect(
"cannot access a Thread Local Storage value \
during or after destruction",
)
}
pub fn try_with<F, R>(&'static self, f: F) -> std::result::Result<R, AccessError>
where
F: FnOnce(&T) -> R,
{
self.try_with_slot(f, false)
}
fn try_with_slot<F, R>(&'static self, f: F, destroyed_last: bool) -> std::result::Result<R, AccessError>
where
F: FnOnce(&T) -> R,
{
let value = self.get().unwrap_or_else(|| {
let value = (self.init)();
let unused = ExecutionState::with(move |state| {
if state.current().local(self).is_none() {
if destroyed_last {
state.current_mut().init_local_destroyed_last(self, value);
} else {
state.current_mut().init_local(self, value);
}
None
} else {
Some(value)
}
});
drop(unused);
self.get().unwrap()
})?;
Ok(f(value))
}
fn get(&'static self) -> Option<std::result::Result<&'static T, AccessError>> {
unsafe fn extend_lt<'b, T>(t: &'_ T) -> &'b T {
std::mem::transmute(t)
}
ExecutionState::with(|state| {
if let Ok(value) = state.current().local(self)? {
Some(Ok(unsafe { extend_lt(value) }))
} else {
Some(Err(AccessError))
}
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub struct AccessError;
impl std::fmt::Display for AccessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt("already destroyed", f)
}
}
impl std::error::Error for AccessError {}