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(val:1, order: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: usize| 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> {}
115