projects/linux·blob master · refs

121 lines · 3.8 KiB

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3#![allow(clippy::undocumented_unsafe_blocks)]
4#![cfg_attr(feature = "alloc", feature(allocator_api))]
5#![allow(unused_imports)]
6
7use core::{
8 cell::{Cell, UnsafeCell},
9 mem::MaybeUninit,
10 ops,
11 pin::Pin,
12 time::Duration,
13};
14use pin_init::*;
15#[cfg(feature = "std")]
16use std::{
17 sync::Arc,
18 thread::{sleep, Builder},
19};
20
21#[allow(unused_attributes)]
22mod mutex;
23use mutex::*;
24
25pub struct StaticInit<T, I> {
26 cell: UnsafeCell<MaybeUninit<T>>,
27 init: Cell<Option<I>>,
28 lock: SpinLock,
29 present: Cell<bool>,
30}
31
32unsafe impl<T: Sync, I> Sync for StaticInit<T, I> {}
33unsafe impl<T: Send, I> Send for StaticInit<T, I> {}
34
35impl<T, I: PinInit<T>> StaticInit<T, I> {
36 pub const fn new(init: I) -> Self {
37 Self {
38 cell: UnsafeCell::new(MaybeUninit::uninit()),
39 init: Cell::new(Some(init)),
40 lock: SpinLock::new(),
41 present: Cell::new(false),
42 }
43 }
44}
45
46impl<T, I: PinInit<T>> ops::Deref for StaticInit<T, I> {
47 type Target = T;
48 fn deref(&self) -> &Self::Target {
49 if self.present.get() {
50 unsafe { (*self.cell.get()).assume_init_ref() }
51 } else {
52 println!("acquire spinlock on static init");
53 let _guard = self.lock.acquire();
54 println!("rechecking present...");
55 std::thread::sleep(std::time::Duration::from_millis(200));
56 if self.present.get() {
57 return unsafe { (*self.cell.get()).assume_init_ref() };
58 }
59 println!("doing init");
60 let ptr = self.cell.get().cast::<T>();
61 match self.init.take() {
62 Some(f) => unsafe { pin_init::raw_init(ptr, f) },
63 None => unsafe { core::hint::unreachable_unchecked() },
64 }
65 self.present.set(true);
66 unsafe { (*self.cell.get()).assume_init_ref() }
67 }
68 }
69}
70
71pub struct CountInit;
72
73unsafe impl PinInit<CMutex<usize>> for CountInit {
74 unsafe fn __init(self, slot: *mut CMutex<usize>) -> Result<(), core::convert::Infallible> {
75 let init = CMutex::new(0);
76 std::thread::sleep(std::time::Duration::from_millis(1000));
77 unsafe { pin_init::raw_init(slot, init) };
78 Ok(())
79 }
80}
81
82pub static COUNT: StaticInit<CMutex<usize>, CountInit> = StaticInit::new(CountInit);
83
84fn main() {
85 #[cfg(feature = "std")]
86 {
87 let mtx: Pin<Arc<CMutex<usize>>> = Arc::pin_init(CMutex::new(0)).unwrap();
88 let mut handles = vec![];
89 let thread_count = 20;
90 let workload = 1_000;
91 for i in 0..thread_count {
92 let mtx = mtx.clone();
93 handles.push(
94 Builder::new()
95 .name(format!("worker #{i}"))
96 .spawn(move || {
97 for _ in 0..workload {
98 *COUNT.lock() += 1;
99 std::thread::sleep(std::time::Duration::from_millis(10));
100 *mtx.lock() += 1;
101 std::thread::sleep(std::time::Duration::from_millis(10));
102 *COUNT.lock() += 1;
103 }
104 println!("{i} halfway");
105 sleep(Duration::from_millis((i as u64) * 10));
106 for _ in 0..workload {
107 std::thread::sleep(std::time::Duration::from_millis(10));
108 *mtx.lock() += 1;
109 }
110 println!("{i} finished");
111 })
112 .expect("should not fail"),
113 );
114 }
115 for h in handles {
116 h.join().expect("thread panicked");
117 }
118 println!("{:?}, {:?}", *mtx.lock(), *COUNT.lock());
119 assert_eq!(*mtx.lock(), workload * thread_count * 2);
120 }
121}