use std::cell::{Cell, Ref, RefCell, RefMut};
use std::fmt;
pub struct ProcessLocal<T: 'static> {
inner: unsafe fn(Option<*mut Option<T>>) -> Option<&'static T>,
}
impl<T: 'static> fmt::Debug for ProcessLocal<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProcessLocal").finish_non_exhaustive()
}
}
#[macro_export]
macro_rules! process_local {
() => {};
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => (
$crate::__process_local_inner!($(#[$attr])* $vis $name, $t, const $init);
$crate::process_local!($($rest)*);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => (
$crate::__process_local_inner!($(#[$attr])* $vis $name, $t, const $init);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
$crate::__process_local_inner!($(#[$attr])* $vis $name, $t, $init);
$crate::process_local!($($rest)*);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
$crate::__process_local_inner!($(#[$attr])* $vis $name, $t, $init);
);
}
#[doc(hidden)]
#[macro_export]
macro_rules! __process_local_inner {
(@key $t:ty, const $init:expr) => {{
#[deny(unsafe_op_in_unsafe_fn)]
unsafe fn __getit(
_init: std::option::Option<&mut std::option::Option<$t>>,
) -> std::option::Option<&'static $t> {
const INIT_EXPR: $t = $init;
static mut VAL: $t = INIT_EXPR;
unsafe { std::option::Option::Some(&VAL) }
}
unsafe {
$crate::ProcessLocal::new(__getit)
}
}};
(@key $t:ty, $init:expr) => {
{
#[inline]
fn __init() -> $t { $init }
#[inline]
unsafe fn __getit(
init: std::option::Option<*mut std::option::Option<$t>>,
) -> std::option::Option<&'static $t> {
static __KEY: $crate::__StaticProcessLocalInner<$t> =
$crate::__StaticProcessLocalInner::new();
#[allow(unused_unsafe)]
unsafe {
__KEY.get(move || {
if let std::option::Option::Some(init) = init {
if let std::option::Option::Some(value) = init.as_mut().unwrap().take() {
return value;
} else if std::cfg!(debug_assertions) {
std::unreachable!("missing default value");
}
}
__init()
})
}
}
unsafe {
$crate::ProcessLocal::new(__getit)
}
}
};
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::ProcessLocal<$t> =
$crate::__process_local_inner!(@key $t, $($init)*);
}
}
impl<T: 'static> ProcessLocal<T> {
#[doc(hidden)]
pub const unsafe fn new(
inner: unsafe fn(Option<*mut Option<T>>) -> Option<&'static T>,
) -> ProcessLocal<T> {
ProcessLocal { inner }
}
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&'static T) -> R,
{
unsafe {
let process_local =
(self.inner)(None).expect("Failed to access process local variable");
f(process_local)
}
}
fn initialize_with<F, R>(&'static self, init: T, f: F) -> R
where
F: FnOnce(Option<T>, &'static T) -> R,
{
unsafe {
let mut init = Some(init);
let reference =
(self.inner)(Some(&mut init)).expect("Failed to access process local variable");
f(init, reference)
}
}
}
impl<T: 'static> ProcessLocal<Cell<T>> {
pub fn set(&'static self, value: T) {
self.initialize_with(Cell::new(value), |value, cell| {
if let Some(value) = value {
cell.set(value.into_inner());
}
});
}
pub fn get(&'static self) -> T
where
T: Copy,
{
self.with(|cell| cell.get())
}
pub fn take(&'static self) -> T
where
T: Default,
{
self.with(|cell| cell.take())
}
pub fn replace(&'static self, value: T) -> T {
self.with(|cell| cell.replace(value))
}
}
impl<T: 'static> ProcessLocal<RefCell<T>> {
pub fn with_borrow<F, R>(&'static self, f: F) -> R
where
F: FnOnce(Ref<'static, T>) -> R,
{
self.with(|cell| f(cell.borrow()))
}
pub fn with_borrow_mut<F, R>(&'static self, f: F) -> R
where
F: FnOnce(RefMut<'static, T>) -> R,
{
self.with(|cell| f(cell.borrow_mut()))
}
pub fn set(&'static self, value: T) {
self.initialize_with(RefCell::new(value), |value, cell| {
if let Some(value) = value {
*cell.borrow_mut() = value.into_inner();
}
});
}
pub fn take(&'static self) -> T
where
T: Default,
{
self.with(|cell| cell.take())
}
pub fn replace(&'static self, value: T) -> T {
self.with(|cell| cell.replace(value))
}
}
#[doc(hidden)]
#[allow(unused_unsafe)]
mod lazy {
use std::cell::UnsafeCell;
use std::{hint, mem};
pub struct LazyKeyInner<T> {
inner: UnsafeCell<Option<T>>,
}
impl<T> LazyKeyInner<T> {
pub const fn new() -> LazyKeyInner<T> {
LazyKeyInner {
inner: UnsafeCell::new(None),
}
}
pub unsafe fn get(&self) -> Option<&'static T> {
unsafe { (*self.inner.get()).as_ref() }
}
pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
let value = init();
let ptr = self.inner.get();
unsafe {
let _ = mem::replace(&mut *ptr, Some(value));
}
unsafe {
match *ptr {
Some(ref x) => x,
None => hint::unreachable_unchecked(),
}
}
}
#[allow(unused)]
pub unsafe fn take(&mut self) -> Option<T> {
unsafe { (*self.inner.get()).take() }
}
}
}
#[doc(hidden)]
#[allow(unused_unsafe)]
pub mod statik {
use std::fmt;
use super::lazy::LazyKeyInner;
pub struct Key<T> {
inner: LazyKeyInner<T>,
}
unsafe impl<T> Sync for Key<T> {}
impl<T> fmt::Debug for Key<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Key").finish_non_exhaustive()
}
}
impl<T> Key<T> {
pub const fn new() -> Key<T> {
Key {
inner: LazyKeyInner::new(),
}
}
pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
let value = unsafe {
match self.inner.get() {
Some(value) => value,
None => self.inner.initialize(init),
}
};
Some(value)
}
}
}