use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::num::Wrapping;
use std::thread::LocalKey;
#[doc(hidden)]
#[macro_export]
macro_rules! declare_thread_stacks_inner {
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => {
thread_local! {
$(#[$attr])* $vis static $name: $crate::ThreadStackWithInitialValue<$t> = $crate::ThreadStackWithInitialValue::new($init);
}
};
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
thread_local! {
$(#[$attr])* $vis static $name: $crate::ThreadStack<$t> = $crate::ThreadStack::new();
}
};
}
#[macro_export]
macro_rules! declare_thread_stacks {
() => {};
($(#[$attr:meta])* $vis:vis $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
$crate::declare_thread_stacks_inner!($(#[$attr])* $vis $name, $t, $init);
$crate::declare_thread_stacks!($($rest)*);
);
($(#[$attr:meta])* $vis:vis $name:ident: $t:ty = $init:expr;) => (
$crate::declare_thread_stacks_inner!($(#[$attr])* $vis $name, $t, $init);
);
($(#[$attr:meta])* $vis:vis $name:ident: $t:ty; $($rest:tt)*) => (
$crate::declare_thread_stacks_inner!($(#[$attr])* $vis $name, $t);
$crate::declare_thread_stacks!($($rest)*);
);
($(#[$attr:meta])* $vis:vis $name:ident: $t:ty;) => (
$crate::declare_thread_stacks_inner!($(#[$attr])* $vis $name, $t);
);
}
#[doc(hidden)]
pub struct ThreadStackInner<T> {
data: [UnsafeCell<MaybeUninit<T>>; 64],
current: UnsafeCell<Wrapping<usize>>,
}
pub struct ThreadStack<T> {
inner: ThreadStackInner<T>,
}
pub struct ThreadStackWithInitialValue<T> {
inner: ThreadStackInner<T>,
}
#[doc(hidden)]
pub trait IsThreadStack<T> {
fn get_data(&self) -> &[UnsafeCell<MaybeUninit<T>>; 64];
fn get_current(&self) -> &UnsafeCell<Wrapping<usize>>;
fn get_inner(&self) -> &ThreadStackInner<T>;
unsafe fn push_value_impl<'a, 'b>(
&self,
_stack_lifetime_hack: &'a (),
new_value: T,
) -> ThreadStackGuard<'a, T> {
let old_index = *self.get_current().get();
*self.get_current().get() = old_index + Wrapping(1);
let new_index = *self.get_current().get();
let data = &mut *self.get_data()[new_index.0].get();
data.as_mut_ptr().write(new_value);
ThreadStackGuard {
stack: self.get_inner() as *const ThreadStackInner<T>,
stack_lifetime_hack: std::marker::PhantomData,
}
}
unsafe fn get_value_impl<'a, 'b>(self: &'b Self, _hack: &'a ()) -> &'a T;
}
impl<T> IsThreadStack<T> for ThreadStack<T> {
fn get_data(&self) -> &[UnsafeCell<MaybeUninit<T>>; 64] {
&self.inner.data
}
fn get_current(&self) -> &UnsafeCell<Wrapping<usize>> {
&self.inner.current
}
fn get_inner(&self) -> &ThreadStackInner<T> {
&self.inner
}
unsafe fn get_value_impl<'a, 'b>(self: &'b ThreadStack<T>, _hack: &'a ()) -> &'a T {
let index = *self.inner.current.get();
let data = &*self.inner.data[index.0].get();
&*data.as_ptr()
}
}
impl<T> IsThreadStack<T> for ThreadStackWithInitialValue<T> {
fn get_data(&self) -> &[UnsafeCell<MaybeUninit<T>>; 64] {
&self.inner.data
}
fn get_current(&self) -> &UnsafeCell<Wrapping<usize>> {
&self.inner.current
}
fn get_inner(&self) -> &ThreadStackInner<T> {
&self.inner
}
unsafe fn get_value_impl<'a, 'b>(
self: &'b ThreadStackWithInitialValue<T>,
_hack: &'a (),
) -> &'a T {
let index = *self.inner.current.get();
let data = &*self.inner.data.get_unchecked(index.0).get();
&*data.as_ptr()
}
}
impl<T> ThreadStack<T> {
pub const fn new() -> Self {
let stack = ThreadStackInner {
data: [
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
],
current: UnsafeCell::new(Wrapping(usize::MAX)),
};
ThreadStack { inner: stack }
}
}
impl<T> ThreadStackWithInitialValue<T> {
#[doc(hidden)]
pub const fn new(initial: T) -> Self {
let stack = ThreadStackInner {
data: [
UnsafeCell::new(MaybeUninit::new(initial)),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
UnsafeCell::new(MaybeUninit::uninit()),
],
current: UnsafeCell::new(Wrapping(0)),
};
ThreadStackWithInitialValue { inner: stack }
}
}
#[macro_export]
macro_rules! let_ref_thread_stack_value {
($new_variable:ident, $thread_stack:expr) => {
let stack_lifetime_hack = ();
let s = &$thread_stack;
$crate::compile_time_assert_is_thread_stack(s);
let $new_variable = s.with(|stack| unsafe { stack.get_value_impl(&stack_lifetime_hack) });
};
}
#[doc(hidden)]
pub fn compile_time_assert_is_thread_stack<U, T: IsThreadStack<U>>(_t: &LocalKey<T>) -> () {
()
}
#[doc(hidden)]
pub struct ThreadStackGuard<'a, T> {
stack: *const ThreadStackInner<T>,
stack_lifetime_hack: std::marker::PhantomData<&'a ()>,
}
impl<'a, T> Drop for ThreadStackGuard<'a, T> {
fn drop(&mut self) {
let stack = unsafe { &*self.stack };
let old_index = unsafe { *stack.current.get() };
let data = unsafe { &mut *stack.data[old_index.0].get() };
let old = unsafe { std::ptr::drop_in_place(data.as_mut_ptr()) };
std::mem::drop(old);
unsafe { *stack.current.get() = old_index - Wrapping(1) };
}
}
pub fn clone_thread_stack_value<T: Clone, U: IsThreadStack<T>>(stack: &'static LocalKey<U>) -> T {
let_ref_thread_stack_value!(the_value, stack);
the_value.clone()
}
#[macro_export]
macro_rules! push_thread_stack_value {
($new_value:expr, $thread_stack:expr) => {
let stack_lifetime_hack = ();
let s = &$thread_stack;
$crate::compile_time_assert_is_thread_stack(s);
let _push_guard =
s.with(|stack| unsafe { stack.push_value_impl(&stack_lifetime_hack, $new_value) });
};
}
#[cfg(test)]
mod tests {
use super::*;
declare_thread_stacks!(
STACK: u32 = 0xDEADBEEFu32;
);
#[test]
fn it_works() {
let_ref_thread_stack_value!(stack_value, STACK);
assert!(stack_value == &0xDEADBEEFu32);
{
push_thread_stack_value!(stack_value + 1, STACK);
let_ref_thread_stack_value!(stack_value, STACK);
assert!(stack_value == &0xDEADBEF0u32);
}
let_ref_thread_stack_value!(stack_value, STACK);
assert!(stack_value == &0xDEADBEEFu32);
assert!(clone_thread_stack_value(&STACK) == 0xDEADBEEFu32);
}
declare_thread_stacks!(
STARTS_EMPTY: u32;
);
#[test]
#[should_panic(expected = "index out of bounds")]
fn no_initial_value_test() {
let_ref_thread_stack_value!(wont_work, STARTS_EMPTY);
assert!(wont_work == &100);
}
#[test]
#[should_panic(expected = "index out of bounds")]
fn revert_to_no_initial() {
{
push_thread_stack_value!(50, STARTS_EMPTY);
}
let_ref_thread_stack_value!(wont_work, STARTS_EMPTY);
assert!(wont_work == &100);
}
#[test]
fn it_works_no_initial() {
{
push_thread_stack_value!(50, STARTS_EMPTY);
let_ref_thread_stack_value!(stack_value, STARTS_EMPTY);
assert!(stack_value == &50);
}
push_thread_stack_value!(51, STARTS_EMPTY);
let_ref_thread_stack_value!(stack_value, STARTS_EMPTY);
assert!(stack_value == &51);
assert!(clone_thread_stack_value(&STARTS_EMPTY) == 51);
push_thread_stack_value!(52, STARTS_EMPTY);
let_ref_thread_stack_value!(stack_value, STARTS_EMPTY);
assert!(stack_value == &52);
assert!(clone_thread_stack_value(&STARTS_EMPTY) == 52);
}
}