123 lines · 3.8 KB · Rust
RawDownload
// 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);    }}