Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #![feature(const_fn)]
- macro_rules! manually_init_static {(
- $(
- static $NAME:ident: $T:ty = $expr:expr;
- )*
- ) => (
- $(
- static $NAME: $crate::manually_init_lazy::ManuallyInitLazy<$T> =
- <$crate::manually_init_lazy::ManuallyInitLazy<$T>>::new({
- fn non_const_static_init () -> $T
- {
- thread_local! {
- static NOT_REENTRANT: ::core::cell::Cell<bool> =
- ::core::cell::Cell::new(true)
- ;
- }
- NOT_REENTRANT.with(|not_reentrant| {
- assert!(not_reentrant.replace(false),
- "static initialization cannot be reentrant!",
- );
- let ret = $expr;
- not_reentrant.set(true)
- ret
- })
- }
- non_const_static_init
- })
- ;
- )*
- )}
- manually_init_static! {
- static FOO: i32 = {
- eprintln!(" FOO.init();");
- 42
- };
- }
- fn main ()
- {
- eprintln!("fn main ()\n{{");
- unsafe {
- // # Safety
- //
- // - Not yet multithreaded, so there cannot be parallel calls to .get()
- FOO.init();
- }
- ::crossbeam::thread::scope(|scope| (0 .. 10).for_each(|_| {
- scope.spawn(|_| {
- eprintln!(
- " <{:02?}> FOO = {:?}",
- ::std::thread::current().id(),
- FOO.get(),
- );
- });
- })).expect("Some thread panicked");
- eprintln!("}}");
- }
- mod manually_init_lazy {
- use ::core::cell::UnsafeCell;
- enum Lazy<T>
- {
- Value(T),
- Factory(fn() -> T),
- }
- pub
- struct ManuallyInitLazy<T> /* = */ (
- UnsafeCell<Lazy<T>>,
- );
- impl<T> ManuallyInitLazy<T> {
- pub
- const
- fn new (factory: fn() -> T) -> Self
- {
- Self(
- UnsafeCell::new(Lazy::Factory(factory))
- )
- }
- /// # Safety
- ///
- /// - it can only be called if there are no other parallel calls
- /// to `.init()` or `.get()` from other threads.
- pub
- unsafe
- fn init (self: &'_ Self)
- {
- if let Lazy::Factory(factory) = *self.0.get() {
- *self.0.get() = Lazy::Value(factory())
- } else {
- panic!("Attempted to init an already init value")
- }
- }
- pub
- fn get (self: &'_ Self) -> &'_ T
- {
- let at_this: &Lazy<T> = unsafe {
- // # Safety
- //
- // - there cannot be a data race unless `.init()` contract is
- // violated.
- &*self.0.get()
- };
- match at_this {
- | &Lazy::Value(ref inner) => inner,
- | _ => panic!("Attempted to fetch an uninit value"),
- }
- }
- /// Skips the init check.
- ///
- /// # Safety
- ///
- /// - `.init()` must have been already called
- pub
- unsafe
- fn assume_init (self: &'_ Self) -> &'_ T
- {
- let at_this: &Lazy<T> = {
- // # Safety
- //
- // - there cannot be a data race unless `.init()` contract is
- // violated.
- &*self.0.get()
- };
- match at_this {
- | &Lazy::Value(ref inner) => inner,
- | _ => {
- // # Safety
- //
- // - Callers ensure that `.init()` has already been called
- ::std::hint::unreachable_unchecked()
- },
- }
- }
- }
- /// # Safety
- ///
- /// - Lazy<T> owns `T`, so `T` needs to be `Send`.
- unsafe impl<T : Send> Send for ManuallyInitLazy<T> {}
- /// # Safety
- ///
- /// - the API does not offer a non-`unsafe` way to perform unsynchronised
- /// mutation.
- ///
- /// - non-unsafe `&ManuallyInitLazy<T> -> &T` leads to `T` needing to be Sync.
- unsafe impl<T : Sync> Sync for ManuallyInitLazy<T> {}
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement