tsan/
tsan.rs

1use any_intern::UnsafeLock;
2use std::{cell::Cell, rc::Rc, thread};
3
4fn main() {
5    test_unsafelock();
6}
7
8#[allow(unused_mut)]
9fn test_unsafelock() {
10    let Ok(num_cpus) = thread::available_parallelism() else {
11        return;
12    };
13    let mut num_threads = num_cpus.get();
14
15    let val = Rc::new(Cell::new(0_usize));
16
17    // UnsafeLock requires that there's no copies of the value.
18    // We're cloning the value here, but we're not going to use the original value so it's fine.
19    let lock = unsafe { UnsafeLock::new(val.clone()) };
20
21    // Threads will increase the value. The value will become N * num_threads.
22    const N: usize = 10_000;
23    let mut handles = Vec::new();
24    for _ in 0..num_threads {
25        let c_lock = lock.clone();
26        let handle = thread::spawn(move || {
27            for _ in 0..N {
28                unsafe {
29                    let val = c_lock.lock().as_mut();
30                    val.set(val.get() + 1);
31                    c_lock.unlock();
32                }
33            }
34        });
35        handles.push(handle);
36    }
37
38    // UnsafeLock's guarantee is upheld when all data is under its protection. But, this block of
39    // code violate the safety. You can check this through thread sanitizer.
40    //
41    // num_threads += 1;
42    // for _ in 0..N {
43    //     val.set(val.get() + 1); // val is outside the UnsafeLock
44    // }
45
46    for handle in handles {
47        handle.join().unwrap();
48    }
49
50    unsafe {
51        let val = lock.lock().as_ref();
52        assert_eq!(val.get(), N * num_threads);
53        lock.unlock();
54    }
55}