glib/thread_guard.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4 mem::ManuallyDrop,
5 sync::atomic::{AtomicUsize, Ordering},
6};
7fn next_thread_id() -> usize {
8 static COUNTER: AtomicUsize = AtomicUsize::new(0);
9 COUNTER.fetch_add(1, Ordering::SeqCst)
10}
11
12// rustdoc-stripper-ignore-next
13/// Returns a unique ID for the current thread.
14///
15/// Actual thread IDs can be reused by the OS once the old thread finished.
16/// This works around ambiguity created by ID reuse by using a separate TLS counter for threads.
17pub fn thread_id() -> usize {
18 thread_local!(static THREAD_ID: usize = next_thread_id());
19 THREAD_ID.with(|&x| x)
20}
21
22// rustdoc-stripper-ignore-next
23/// Thread guard that only gives access to the contained value on the thread it was created on.
24pub struct ThreadGuard<T> {
25 thread_id: usize,
26 // This is a `ManuallyDrop` so that the automatic drop glue does not drop `T`
27 // if this `ThreadGuard` is dropped on the wrong thread.
28 value: ManuallyDrop<T>,
29}
30
31impl<T> ThreadGuard<T> {
32 // rustdoc-stripper-ignore-next
33 /// Create a new thread guard around `value`.
34 ///
35 /// The thread guard ensures that access to the value is only allowed from the thread it was
36 /// created on, and otherwise panics.
37 ///
38 /// The thread guard implements the `Send` trait even if the contained value does not.
39 #[inline]
40 pub fn new(value: T) -> Self {
41 Self {
42 thread_id: thread_id(),
43 value: ManuallyDrop::new(value),
44 }
45 }
46
47 // rustdoc-stripper-ignore-next
48 /// Return a reference to the contained value from the thread guard.
49 ///
50 /// # Panics
51 ///
52 /// This function panics if called from a different thread than where the thread guard was
53 /// created.
54 #[inline]
55 pub fn get_ref(&self) -> &T {
56 assert!(
57 self.thread_id == thread_id(),
58 "Value accessed from different thread than where it was created"
59 );
60
61 &self.value
62 }
63
64 // rustdoc-stripper-ignore-next
65 /// Return a mutable reference to the contained value from the thread guard.
66 ///
67 /// # Panics
68 ///
69 /// This function panics if called from a different thread than where the thread guard was
70 /// created.
71 #[inline]
72 pub fn get_mut(&mut self) -> &mut T {
73 assert!(
74 self.thread_id == thread_id(),
75 "Value accessed from different thread than where it was created"
76 );
77
78 &mut self.value
79 }
80
81 // rustdoc-stripper-ignore-next
82 /// Return the contained value from the thread guard.
83 ///
84 /// # Panics
85 ///
86 /// This function panics if called from a different thread than where the thread guard was
87 /// created.
88 #[inline]
89 pub fn into_inner(self) -> T {
90 // We wrap `self` in `ManuallyDrop` to defuse `ThreadGuard`'s `Drop` impl.
91 let mut this = ManuallyDrop::new(self);
92
93 assert!(
94 this.thread_id == thread_id(),
95 "Value accessed from different thread than where it was created"
96 );
97
98 // SAFETY: We are on the right thread, and this.value will not be touched after this
99 unsafe { ManuallyDrop::take(&mut this.value) }
100 }
101
102 // rustdoc-stripper-ignore-next
103 /// Returns `true` if the current thread owns the value, i.e. it can be accessed safely.
104 #[inline]
105 pub fn is_owner(&self) -> bool {
106 self.thread_id == thread_id()
107 }
108}
109
110impl<T> Drop for ThreadGuard<T> {
111 #[inline]
112 fn drop(&mut self) {
113 assert!(
114 self.thread_id == thread_id(),
115 "Value dropped on a different thread than where it was created"
116 );
117
118 // SAFETY: We are on the right thread, and self.value will not be touched after this
119 unsafe { ManuallyDrop::drop(&mut self.value) }
120 }
121}
122
123unsafe impl<T> Send for ThreadGuard<T> {}
124unsafe impl<T> Sync for ThreadGuard<T> {}
125
126impl<T> std::ops::Deref for ThreadGuard<T> {
127 type Target = T;
128
129 fn deref(&self) -> &Self::Target {
130 self.get_ref()
131 }
132}
133
134impl<T> std::ops::DerefMut for ThreadGuard<T> {
135 fn deref_mut(&mut self) -> &mut Self::Target {
136 self.get_mut()
137 }
138}