// SPDX-License-Identifier: Apache-2.0 OR MIT #![allow(clippy::undocumented_unsafe_blocks)]#![cfg_attr(feature = "alloc", feature(allocator_api))]#![allow(unused_imports)] use core::{ cell::{Cell, UnsafeCell}, mem::MaybeUninit, ops, pin::Pin, time::Duration,};use pin_init::*;#[cfg(feature = "std")]use std::{ sync::Arc, thread::{sleep, Builder},}; #[allow(unused_attributes)]mod mutex;use mutex::*; pub struct StaticInit<T, I> { cell: UnsafeCell<MaybeUninit<T>>, init: Cell<Option<I>>, lock: SpinLock, present: Cell<bool>,} unsafe impl<T: Sync, I> Sync for StaticInit<T, I> {}unsafe impl<T: Send, I> Send for StaticInit<T, I> {} impl<T, I: PinInit<T>> StaticInit<T, I> { pub const fn new(init: I) -> Self { Self { cell: UnsafeCell::new(MaybeUninit::uninit()), init: Cell::new(Some(init)), lock: SpinLock::new(), present: Cell::new(false), } }} impl<T, I: PinInit<T>> ops::Deref for StaticInit<T, I> { type Target = T; fn deref(&self) -> &Self::Target { if self.present.get() { unsafe { (*self.cell.get()).assume_init_ref() } } else { println!("acquire spinlock on static init"); let _guard = self.lock.acquire(); println!("rechecking present..."); std::thread::sleep(std::time::Duration::from_millis(200)); if self.present.get() { return unsafe { (*self.cell.get()).assume_init_ref() }; } println!("doing init"); let ptr = self.cell.get().cast::<T>(); match self.init.take() { Some(f) => unsafe { f.__pinned_init(ptr).unwrap() }, None => unsafe { core::hint::unreachable_unchecked() }, } self.present.set(true); unsafe { (*self.cell.get()).assume_init_ref() } } }} pub struct CountInit; unsafe impl PinInit<CMutex<usize>> for CountInit { unsafe fn __pinned_init( self, slot: *mut CMutex<usize>, ) -> Result<(), core::convert::Infallible> { let init = CMutex::new(0); std::thread::sleep(std::time::Duration::from_millis(1000)); unsafe { init.__pinned_init(slot) } }} pub static COUNT: StaticInit<CMutex<usize>, CountInit> = StaticInit::new(CountInit); fn main() { #[cfg(feature = "std")] { let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap(); let mut handles = vec![]; let thread_count = 20; let workload = 1_000; for i in 0..thread_count { let mtx = mtx.clone(); handles.push( Builder::new() .name(format!("worker #{i}")) .spawn(move || { for _ in 0..workload { *COUNT.lock() += 1; std::thread::sleep(std::time::Duration::from_millis(10)); *mtx.lock() += 1; std::thread::sleep(std::time::Duration::from_millis(10)); *COUNT.lock() += 1; } println!("{i} halfway"); sleep(Duration::from_millis((i as u64) * 10)); for _ in 0..workload { std::thread::sleep(std::time::Duration::from_millis(10)); *mtx.lock() += 1; } println!("{i} finished"); }) .expect("should not fail"), ); } for h in handles { h.join().expect("thread panicked"); } println!("{:?}, {:?}", *mtx.lock(), *COUNT.lock()); assert_eq!(*mtx.lock(), workload * thread_count * 2); }}
// SPDX-License-Identifier: Apache-2.0 OR MIT
#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]
#![allow(unused_imports)]
use core::{
cell::{Cell, UnsafeCell},
mem::MaybeUninit,
ops,
pin::Pin,
time::Duration,
};
use pin_init::*;
#[cfg(feature = "std")]
use std::{
sync::Arc,
thread::{sleep, Builder},
};
#[allow(unused_attributes)]
mod mutex;
use mutex::*;
pub struct StaticInit<T, I> {
cell: UnsafeCell<MaybeUninit<T>>,
init: Cell<Option<I>>,
lock: SpinLock,
present: Cell<bool>,
}
unsafe impl<T: Sync, I> Sync for StaticInit<T, I> {}
unsafe impl<T: Send, I> Send for StaticInit<T, I> {}
impl<T, I: PinInit<T>> StaticInit<T, I> {
pub const fn new(init: I) -> Self {
Self {
cell: UnsafeCell::new(MaybeUninit::uninit()),
init: Cell::new(Some(init)),
lock: SpinLock::new(),
present: Cell::new(false),
}
}
}
impl<T, I: PinInit<T>> ops::Deref for StaticInit<T, I> {
type Target = T;
fn deref(&self) -> &Self::Target {
if self.present.get() {
unsafe { (*self.cell.get()).assume_init_ref() }
} else {
println!("acquire spinlock on static init");
let _guard = self.lock.acquire();
println!("rechecking present...");
std::thread::sleep(std::time::Duration::from_millis(200));
if self.present.get() {
return unsafe { (*self.cell.get()).assume_init_ref() };
}
println!("doing init");
let ptr = self.cell.get().cast::<T>();
match self.init.take() {
Some(f) => unsafe { f.__pinned_init(ptr).unwrap() },
None => unsafe { core::hint::unreachable_unchecked() },
}
self.present.set(true);
unsafe { (*self.cell.get()).assume_init_ref() }
}
}
}
pub struct CountInit;
unsafe impl PinInit<CMutex<usize>> for CountInit {
unsafe fn __pinned_init(
self,
slot: *mut CMutex<usize>,
) -> Result<(), core::convert::Infallible> {
let init = CMutex::new(0);
std::thread::sleep(std::time::Duration::from_millis(1000));
unsafe { init.__pinned_init(slot) }
}
}
pub static COUNT: StaticInit<CMutex<usize>, CountInit> = StaticInit::new(CountInit);
fn main() {
#[cfg(feature = "std")]
{
let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
let mut handles = vec![];
let thread_count = 20;
let workload = 1_000;
for i in 0..thread_count {
let mtx = mtx.clone();
handles.push(
Builder::new()
.name(format!("worker #{i}"))
.spawn(move || {
for _ in 0..workload {
*COUNT.lock() += 1;
std::thread::sleep(std::time::Duration::from_millis(10));
*mtx.lock() += 1;
std::thread::sleep(std::time::Duration::from_millis(10));
*COUNT.lock() += 1;
}
println!("{i} halfway");
sleep(Duration::from_millis((i as u64) * 10));
for _ in 0..workload {
std::thread::sleep(std::time::Duration::from_millis(10));
*mtx.lock() += 1;
}
println!("{i} finished");
})
.expect("should not fail"),
);
}
for h in handles {
h.join().expect("thread panicked");
}
println!("{:?}, {:?}", *mtx.lock(), *COUNT.lock());
assert_eq!(*mtx.lock(), workload * thread_count * 2);
}
}