| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- // SPDX-License-Identifier: Apache-2.0 OR MIT
- #![allow(clippy::undocumented_unsafe_blocks)]
- #![cfg_attr(feature = "alloc", feature(allocator_api))]
- #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
- #![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);
- }
- }
|