1
1
// Take a look at the license at the top of the repository in the LICENSE file.
2
2
3
3
use std:: {
4
- mem, ptr ,
4
+ mem:: ManuallyDrop ,
5
5
sync:: atomic:: { AtomicUsize , Ordering } ,
6
6
} ;
7
7
fn next_thread_id ( ) -> usize {
@@ -23,7 +23,9 @@ pub fn thread_id() -> usize {
23
23
/// Thread guard that only gives access to the contained value on the thread it was created on.
24
24
pub struct ThreadGuard < T > {
25
25
thread_id : usize ,
26
- value : T ,
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 > ,
27
29
}
28
30
29
31
impl < T > ThreadGuard < T > {
@@ -38,7 +40,7 @@ impl<T> ThreadGuard<T> {
38
40
pub fn new ( value : T ) -> Self {
39
41
Self {
40
42
thread_id : thread_id ( ) ,
41
- value,
43
+ value : ManuallyDrop :: new ( value ) ,
42
44
}
43
45
}
44
46
@@ -85,12 +87,16 @@ impl<T> ThreadGuard<T> {
85
87
/// created.
86
88
#[ inline]
87
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
+
88
93
assert ! (
89
- self . thread_id == thread_id( ) ,
94
+ this . thread_id == thread_id( ) ,
90
95
"Value accessed from different thread than where it was created"
91
96
) ;
92
97
93
- unsafe { ptr:: read ( & mem:: ManuallyDrop :: new ( self ) . value ) }
98
+ // SAFETY: We are on the right thread, and this.value will not be touched after this
99
+ unsafe { ManuallyDrop :: take ( & mut this. value ) }
94
100
}
95
101
96
102
// rustdoc-stripper-ignore-next
@@ -108,6 +114,9 @@ impl<T> Drop for ThreadGuard<T> {
108
114
self . thread_id == thread_id( ) ,
109
115
"Value dropped on a different thread than where it was created"
110
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 ) }
111
120
}
112
121
}
113
122
0 commit comments