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, ptr,
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    value: T,
27}
28
29impl<T> ThreadGuard<T> {
30    // rustdoc-stripper-ignore-next
31    /// Create a new thread guard around `value`.
32    ///
33    /// The thread guard ensures that access to the value is only allowed from the thread it was
34    /// created on, and otherwise panics.
35    ///
36    /// The thread guard implements the `Send` trait even if the contained value does not.
37    #[inline]
38    pub fn new(value: T) -> Self {
39        Self {
40            thread_id: thread_id(),
41            value,
42        }
43    }
44
45    // rustdoc-stripper-ignore-next
46    /// Return a reference to the contained value from the thread guard.
47    ///
48    /// # Panics
49    ///
50    /// This function panics if called from a different thread than where the thread guard was
51    /// created.
52    #[inline]
53    pub fn get_ref(&self) -> &T {
54        assert!(
55            self.thread_id == thread_id(),
56            "Value accessed from different thread than where it was created"
57        );
58
59        &self.value
60    }
61
62    // rustdoc-stripper-ignore-next
63    /// Return a mutable reference to the contained value from the thread guard.
64    ///
65    /// # Panics
66    ///
67    /// This function panics if called from a different thread than where the thread guard was
68    /// created.
69    #[inline]
70    pub fn get_mut(&mut self) -> &mut T {
71        assert!(
72            self.thread_id == thread_id(),
73            "Value accessed from different thread than where it was created"
74        );
75
76        &mut self.value
77    }
78
79    // rustdoc-stripper-ignore-next
80    /// Return the contained value from the thread guard.
81    ///
82    /// # Panics
83    ///
84    /// This function panics if called from a different thread than where the thread guard was
85    /// created.
86    #[inline]
87    pub fn into_inner(self) -> T {
88        assert!(
89            self.thread_id == thread_id(),
90            "Value accessed from different thread than where it was created"
91        );
92
93        unsafe { ptr::read(&mem::ManuallyDrop::new(self).value) }
94    }
95
96    // rustdoc-stripper-ignore-next
97    /// Returns `true` if the current thread owns the value, i.e. it can be accessed safely.
98    #[inline]
99    pub fn is_owner(&self) -> bool {
100        self.thread_id == thread_id()
101    }
102}
103
104impl<T> Drop for ThreadGuard<T> {
105    #[inline]
106    fn drop(&mut self) {
107        assert!(
108            self.thread_id == thread_id(),
109            "Value dropped on a different thread than where it was created"
110        );
111    }
112}
113
114unsafe impl<T> Send for ThreadGuard<T> {}
115unsafe impl<T> Sync for ThreadGuard<T> {}