@@ -9,44 +9,21 @@ use crate::pin::Pin;
9
9
use crate :: sync:: Arc ;
10
10
use super :: { current, park, Inner , Thread } ;
11
11
12
- /// Carries a flag that is used to wakeup the executor.
13
- /// A pointer to this struct is passed to the thread-local waker.
14
- struct LocalWakeState {
15
- is_woken : bool ,
16
- waker_was_cloned : bool ,
17
- }
18
-
19
- /// Returns the vtable that is used for waking up the executor
20
- /// from another thread.
21
- fn threadsafe_waker_vtable ( ) -> & ' static RawWakerVTable {
22
- & RawWakerVTable :: new (
23
- clone_threadsafe_waker,
24
- wake_threadsafe_waker,
25
- wake_threadsafe_waker_by_ref,
26
- drop_threadsafe_waker,
27
- )
28
- }
29
-
30
12
/// Returns the vtable that is used for waking up the executor
31
- /// from inside it's execution on the current thread.
32
- fn current_thread_waker_vtable ( ) -> & ' static RawWakerVTable {
13
+ /// from any thread.
14
+ fn waker_vtable ( ) -> & ' static RawWakerVTable {
33
15
& RawWakerVTable :: new (
34
- create_threadsafe_waker ,
35
- wake_current_thread ,
36
- wake_current_thread_by_ref ,
37
- |_| { } ,
16
+ clone_waker ,
17
+ wake_waker ,
18
+ wake_waker_by_ref ,
19
+ drop_waker ,
38
20
)
39
21
}
40
22
41
- /// This method will be called when the waker reference gets cloned,
42
- /// which makes it possible to transfer it to another thread. In this
43
- /// case we have to create a threadsafe `Waker`. In order to to this
44
- /// we retain the thread handle and store it in the new `RawWaker`s
45
- /// data pointer.
46
- unsafe fn create_threadsafe_waker ( data : * const ( ) ) -> RawWaker {
47
- let wake_state = data as * mut LocalWakeState ;
48
- ( * wake_state) . waker_was_cloned = true ;
49
-
23
+ /// Creates a [`RawWaker`] which captures the current thread handle
24
+ /// and allows to wake up the [`block_on_future`] executor from any
25
+ /// thread by calling [`Thread::unpark()`].
26
+ fn create_threadsafe_raw_waker ( ) -> RawWaker {
50
27
// Get the `Arc<Inner>` of a current thread handle and store into in
51
28
// the type erased pointer.
52
29
//
@@ -61,32 +38,23 @@ unsafe fn create_threadsafe_waker(data: *const()) -> RawWaker {
61
38
// `let arc_thread = Arc::new(current());`
62
39
let arc_thread_inner = current ( ) . inner ;
63
40
let ptr = Arc :: into_raw ( arc_thread_inner) as * const ( ) ;
64
- RawWaker :: new ( ptr, threadsafe_waker_vtable ( ) )
41
+ RawWaker :: new ( ptr, waker_vtable ( ) )
65
42
}
66
43
67
- unsafe fn clone_threadsafe_waker ( data : * const ( ) ) -> RawWaker {
44
+ unsafe fn clone_waker ( data : * const ( ) ) -> RawWaker {
68
45
increase_refcount ( data) ;
69
- RawWaker :: new ( data, threadsafe_waker_vtable ( ) )
70
- }
71
-
72
- fn wake_current_thread ( _data : * const ( ) ) {
73
- unreachable ! ( "A current thread waker can only be woken by reference" ) ;
46
+ RawWaker :: new ( data, waker_vtable ( ) )
74
47
}
75
48
76
- unsafe fn wake_current_thread_by_ref ( data : * const ( ) ) {
77
- let wake_state = data as * mut LocalWakeState ;
78
- ( * wake_state) . is_woken = true ;
79
- }
80
-
81
- unsafe fn wake_threadsafe_waker ( data : * const ( ) ) {
49
+ unsafe fn wake_waker ( data : * const ( ) ) {
82
50
let arc_thread_inner = Arc :: from_raw ( data as * const Inner ) ;
83
51
let thread = Thread {
84
52
inner : arc_thread_inner,
85
53
} ;
86
54
thread. unpark ( ) ;
87
55
}
88
56
89
- unsafe fn wake_threadsafe_waker_by_ref ( data : * const ( ) ) {
57
+ unsafe fn wake_waker_by_ref ( data : * const ( ) ) {
90
58
// Retain `Arc`, but don't touch refcount by wrapping in `ManuallyDrop`
91
59
let arc_thread_inner = Arc :: from_raw ( data as * const Inner ) ;
92
60
let thread = mem:: ManuallyDrop :: new ( Thread {
@@ -95,7 +63,7 @@ unsafe fn wake_threadsafe_waker_by_ref(data: *const ()) {
95
63
thread. unpark ( ) ;
96
64
}
97
65
98
- unsafe fn drop_threadsafe_waker ( data : * const ( ) ) {
66
+ unsafe fn drop_waker ( data : * const ( ) ) {
99
67
drop ( Thread {
100
68
inner : Arc :: from_raw ( data as * const Inner ) ,
101
69
} )
@@ -138,52 +106,23 @@ pub fn block_on_future<F: Future>(mut future: F) -> F::Output {
138
106
// out of this function again.
139
107
let mut future = unsafe { Pin :: new_unchecked ( & mut future) } ;
140
108
141
- let mut waker_state = LocalWakeState {
142
- is_woken : true ,
143
- waker_was_cloned : false ,
109
+ // Safety: The `Waker` that we create upholds all guarantees that are expected
110
+ // from a `Waker`
111
+ let waker = unsafe {
112
+ Waker :: from_raw ( create_threadsafe_raw_waker ( ) )
144
113
} ;
145
114
146
- // Safety: The `Waker` that we create here is references data on the current
147
- // callstack. This is safe, since the polled `Future` only gets a reference
148
- // to this `Waker`. When it tries to clone the `Waker`, a threadsafe and owned
149
- // version is created instead.
150
- unsafe {
151
- let waker = Waker :: from_raw ( RawWaker :: new (
152
- & waker_state as * const LocalWakeState as * const ( ) ,
153
- current_thread_waker_vtable ( ) ) ) ;
154
-
155
- let mut cx = Context :: from_waker ( & waker) ;
156
- loop {
157
- while waker_state. is_woken {
158
- // Reset is_woken, so that we do not spin if the poll does not
159
- // directly wake us up.
160
- waker_state. is_woken = false ;
161
- if let Poll :: Ready ( task_result) = future. as_mut ( ) . poll ( & mut cx) {
162
- return task_result;
163
- }
164
- }
165
-
166
- // The task is not ready, and the `Waker` had not been woken from the
167
- // current thread. In order for us to proceed we wait until the
168
- // thread gets unparked by another thread. If the `Waker` has not been
169
- // cloned this will never happen and represents a deadlock, which
170
- // gets reported here.
171
- if !waker_state. waker_was_cloned {
172
- panic ! ( "Deadlock: Task is not ready, but the Waker had not been cloned" ) ;
173
- // Note: This flag is never reset, since a `Waker` that had been cloned
174
- // once can be cloned more often to wakeup this executor. We don't
175
- // have knowledge on how many clones are around - therefore the
176
- // deadlock detection only works for the case the `Waker` never
177
- // gets cloned.
178
- }
179
- park ( ) ;
180
- // If thread::park has returned, we have been notified by another
181
- // thread. Therefore we are woken.
182
- // Remark: This flag can not be set by the other thread directly,
183
- // because it may no longer be alive at the point of time when
184
- // wake() is called.
185
- waker_state. is_woken = true ;
115
+ let mut cx = Context :: from_waker ( & waker) ;
116
+ loop {
117
+ if let Poll :: Ready ( task_result) = future. as_mut ( ) . poll ( & mut cx) {
118
+ return task_result;
186
119
}
120
+
121
+ // The task is not ready. In order for us to proceed we wait until the
122
+ // thread gets unparked. If the `Waker` had been woken inside `.poll()`,
123
+ // then `park()` will immediately return, and we will call `.poll()`
124
+ // again without any wait period.
125
+ park ( ) ;
187
126
}
188
127
}
189
128
@@ -201,31 +140,28 @@ mod tests {
201
140
fn check_refcounts ( ) {
202
141
let original = current_thread_refcount ( ) ;
203
142
204
- let waker_state = LocalWakeState {
205
- is_woken : true ,
206
- waker_was_cloned : false ,
207
- } ;
208
-
209
- let waker = unsafe { Waker :: from_raw ( RawWaker :: new (
210
- & waker_state as * const LocalWakeState as * const ( ) ,
211
- current_thread_waker_vtable ( ) ) ) } ;
143
+ let waker = unsafe { Waker :: from_raw ( create_threadsafe_raw_waker ( ) ) } ;
144
+ assert_eq ! ( original + 1 , current_thread_refcount( ) ) ;
212
145
213
146
waker. wake_by_ref ( ) ;
214
- assert_eq ! ( original, current_thread_refcount( ) ) ;
147
+ assert_eq ! ( original + 1 , current_thread_refcount( ) ) ;
215
148
216
149
let clone1 = waker. clone ( ) ;
217
- assert_eq ! ( original + 1 , current_thread_refcount( ) ) ;
218
- let clone2 = waker. clone ( ) ;
219
150
assert_eq ! ( original + 2 , current_thread_refcount( ) ) ;
220
- let clone3 = clone1 . clone ( ) ;
151
+ let clone2 = waker . clone ( ) ;
221
152
assert_eq ! ( original + 3 , current_thread_refcount( ) ) ;
153
+ let clone3 = clone1. clone ( ) ;
154
+ assert_eq ! ( original + 4 , current_thread_refcount( ) ) ;
222
155
223
156
drop ( clone1) ;
224
- assert_eq ! ( original + 2 , current_thread_refcount( ) ) ;
157
+ assert_eq ! ( original + 3 , current_thread_refcount( ) ) ;
225
158
226
159
clone2. wake_by_ref ( ) ;
227
- assert_eq ! ( original + 2 , current_thread_refcount( ) ) ;
160
+ assert_eq ! ( original + 3 , current_thread_refcount( ) ) ;
228
161
clone2. wake ( ) ;
162
+ assert_eq ! ( original + 2 , current_thread_refcount( ) ) ;
163
+
164
+ drop ( waker) ;
229
165
assert_eq ! ( original + 1 , current_thread_refcount( ) ) ;
230
166
231
167
clone3. wake_by_ref ( ) ;
0 commit comments