1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{ |
4 | mem, ptr, |
5 | sync::atomic::{AtomicUsize, Ordering}, |
6 | }; |
7 | fn 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. |
17 | pub 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. |
24 | pub struct ThreadGuard<T> { |
25 | thread_id: usize, |
26 | value: T, |
27 | } |
28 | |
29 | impl<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 | |
104 | impl<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 | |
114 | unsafe impl<T> Send for ThreadGuard<T> {} |
115 | |