forked from m-ou-se/rust-atomics-and-locks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
s1_basic.rs
113 lines (93 loc) · 2.63 KB
/
s1_basic.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::ops::Deref;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::fence;
use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use std::ptr::NonNull;
struct ArcData<T> {
ref_count: AtomicUsize,
data: T,
}
pub struct Arc<T> {
ptr: NonNull<ArcData<T>>,
}
unsafe impl<T: Send + Sync> Send for Arc<T> {}
unsafe impl<T: Send + Sync> Sync for Arc<T> {}
impl<T> Arc<T> {
pub fn new(data: T) -> Arc<T> {
Arc {
ptr: NonNull::from(Box::leak(Box::new(ArcData {
ref_count: AtomicUsize::new(1),
data,
}))),
}
}
fn data(&self) -> &ArcData<T> {
unsafe { self.ptr.as_ref() }
}
pub fn get_mut(arc: &mut Self) -> Option<&mut T> {
if arc.data().ref_count.load(Relaxed) == 1 {
fence(Acquire);
// Safety: Nothing else can access the data, since
// there's only one Arc, to which we have exclusive access.
unsafe { Some(&mut arc.ptr.as_mut().data) }
} else {
None
}
}
}
impl<T> Deref for Arc<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data().data
}
}
impl<T> Clone for Arc<T> {
fn clone(&self) -> Self {
if self.data().ref_count.fetch_add(1, Relaxed) > usize::MAX / 2 {
std::process::abort();
}
Arc {
ptr: self.ptr,
}
}
}
impl<T> Drop for Arc<T> {
fn drop(&mut self) {
if self.data().ref_count.fetch_sub(1, Release) == 1 {
fence(Acquire);
unsafe {
drop(Box::from_raw(self.ptr.as_ptr()));
}
}
}
}
#[test]
fn test() {
static NUM_DROPS: AtomicUsize = AtomicUsize::new(0);
struct DetectDrop;
impl Drop for DetectDrop {
fn drop(&mut self) {
NUM_DROPS.fetch_add(1, Relaxed);
}
}
// Create two Arcs sharing an object containing a string
// and a DetectDrop, to detect when it's dropped.
let x = Arc::new(("hello", DetectDrop));
let y = x.clone();
// Send x to another thread, and use it there.
let t = std::thread::spawn(move || {
assert_eq!(x.0, "hello");
});
// In parallel, y should still be usable here.
assert_eq!(y.0, "hello");
// Wait for the thread to finish.
t.join().unwrap();
// One Arc, x, should be dropped by now.
// We still have y, so the object shouldn't have been dropped yet.
assert_eq!(NUM_DROPS.load(Relaxed), 0);
// Drop the remaining `Arc`.
drop(y);
// Now that `y` is dropped too,
// the object should've been dropped.
assert_eq!(NUM_DROPS.load(Relaxed), 1);
}