58
58
// [4]: Windows Internals, Part 1, ISBN 9780735671300
59
59
60
60
use crate :: pin:: Pin ;
61
- use crate :: ptr;
62
61
use crate :: sync:: atomic:: {
63
- AtomicI8 , AtomicPtr ,
64
- Ordering :: { Acquire , Relaxed , Release } ,
62
+ AtomicI8 ,
63
+ Ordering :: { Acquire , Release } ,
65
64
} ;
66
65
use crate :: sys:: { c, dur2timeout} ;
67
66
use crate :: time:: Duration ;
@@ -111,26 +110,21 @@ impl Parker {
111
110
return ;
112
111
}
113
112
114
- if let Some ( wait_on_address) = c:: WaitOnAddress :: option ( ) {
115
- loop {
116
- // Wait for something to happen, assuming it's still set to PARKED.
117
- wait_on_address ( self . ptr ( ) , & PARKED as * const _ as c:: LPVOID , 1 , c:: INFINITE ) ;
118
- // Change NOTIFIED=>EMPTY but leave PARKED alone.
119
- if self . state . compare_exchange ( NOTIFIED , EMPTY , Acquire , Acquire ) . is_ok ( ) {
120
- // Actually woken up by unpark().
121
- return ;
122
- } else {
123
- // Spurious wake up. We loop to try again.
124
- }
113
+ #[ cfg( target_vendor = "win7" ) ]
114
+ if c:: WaitOnAddress :: option ( ) . is_none ( ) {
115
+ return keyed_events:: park ( self ) ;
116
+ }
117
+
118
+ loop {
119
+ // Wait for something to happen, assuming it's still set to PARKED.
120
+ c:: WaitOnAddress ( self . ptr ( ) , & PARKED as * const _ as c:: LPVOID , 1 , c:: INFINITE ) ;
121
+ // Change NOTIFIED=>EMPTY but leave PARKED alone.
122
+ if self . state . compare_exchange ( NOTIFIED , EMPTY , Acquire , Acquire ) . is_ok ( ) {
123
+ // Actually woken up by unpark().
124
+ return ;
125
+ } else {
126
+ // Spurious wake up. We loop to try again.
125
127
}
126
- } else {
127
- // Wait for unpark() to produce this event.
128
- c:: NtWaitForKeyedEvent ( keyed_event_handle ( ) , self . ptr ( ) , 0 , ptr:: null_mut ( ) ) ;
129
- // Set the state back to EMPTY (from either PARKED or NOTIFIED).
130
- // Note that we don't just write EMPTY, but use swap() to also
131
- // include an acquire-ordered read to synchronize with unpark()'s
132
- // release-ordered write.
133
- self . state . swap ( EMPTY , Acquire ) ;
134
128
}
135
129
}
136
130
@@ -144,47 +138,23 @@ impl Parker {
144
138
return ;
145
139
}
146
140
147
- if let Some ( wait_on_address) = c:: WaitOnAddress :: option ( ) {
148
- // Wait for something to happen, assuming it's still set to PARKED.
149
- wait_on_address ( self . ptr ( ) , & PARKED as * const _ as c:: LPVOID , 1 , dur2timeout ( timeout) ) ;
150
- // Set the state back to EMPTY (from either PARKED or NOTIFIED).
151
- // Note that we don't just write EMPTY, but use swap() to also
152
- // include an acquire-ordered read to synchronize with unpark()'s
153
- // release-ordered write.
154
- if self . state . swap ( EMPTY , Acquire ) == NOTIFIED {
155
- // Actually woken up by unpark().
156
- } else {
157
- // Timeout or spurious wake up.
158
- // We return either way, because we can't easily tell if it was the
159
- // timeout or not.
160
- }
141
+ #[ cfg( target_vendor = "win7" ) ]
142
+ if c:: WaitOnAddress :: option ( ) . is_none ( ) {
143
+ return keyed_events:: park_timeout ( self , timeout) ;
144
+ }
145
+
146
+ // Wait for something to happen, assuming it's still set to PARKED.
147
+ c:: WaitOnAddress ( self . ptr ( ) , & PARKED as * const _ as c:: LPVOID , 1 , dur2timeout ( timeout) ) ;
148
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
149
+ // Note that we don't just write EMPTY, but use swap() to also
150
+ // include an acquire-ordered read to synchronize with unpark()'s
151
+ // release-ordered write.
152
+ if self . state . swap ( EMPTY , Acquire ) == NOTIFIED {
153
+ // Actually woken up by unpark().
161
154
} else {
162
- // Need to wait for unpark() using NtWaitForKeyedEvent.
163
- let handle = keyed_event_handle ( ) ;
164
-
165
- // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
166
- // values to indicate a relative time on the monotonic clock.
167
- // This is documented here for the underlying KeWaitForSingleObject function:
168
- // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
169
- let mut timeout = match i64:: try_from ( ( timeout. as_nanos ( ) + 99 ) / 100 ) {
170
- Ok ( t) => -t,
171
- Err ( _) => i64:: MIN ,
172
- } ;
173
-
174
- // Wait for unpark() to produce this event.
175
- let unparked =
176
- c:: NtWaitForKeyedEvent ( handle, self . ptr ( ) , 0 , & mut timeout) == c:: STATUS_SUCCESS ;
177
-
178
- // Set the state back to EMPTY (from either PARKED or NOTIFIED).
179
- let prev_state = self . state . swap ( EMPTY , Acquire ) ;
180
-
181
- if !unparked && prev_state == NOTIFIED {
182
- // We were awoken by a timeout, not by unpark(), but the state
183
- // was set to NOTIFIED, which means we *just* missed an
184
- // unpark(), which is now blocked on us to wait for it.
185
- // Wait for it to consume the event and unblock that thread.
186
- c:: NtWaitForKeyedEvent ( handle, self . ptr ( ) , 0 , ptr:: null_mut ( ) ) ;
187
- }
155
+ // Timeout or spurious wake up.
156
+ // We return either way, because we can't easily tell if it was the
157
+ // timeout or not.
188
158
}
189
159
}
190
160
@@ -198,18 +168,11 @@ impl Parker {
198
168
// with park().
199
169
if self . state . swap ( NOTIFIED , Release ) == PARKED {
200
170
unsafe {
201
- if let Some ( wake_by_address_single) = c:: WakeByAddressSingle :: option ( ) {
202
- wake_by_address_single ( self . ptr ( ) ) ;
203
- } else {
204
- // If we run NtReleaseKeyedEvent before the waiting thread runs
205
- // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
206
- // If the waiting thread wakes up before we run NtReleaseKeyedEvent
207
- // (e.g. due to a timeout), this blocks until we do wake up a thread.
208
- // To prevent this thread from blocking indefinitely in that case,
209
- // park_impl() will, after seeing the state set to NOTIFIED after
210
- // waking up, call NtWaitForKeyedEvent again to unblock us.
211
- c:: NtReleaseKeyedEvent ( keyed_event_handle ( ) , self . ptr ( ) , 0 , ptr:: null_mut ( ) ) ;
171
+ #[ cfg( target_vendor = "win7" ) ]
172
+ if c:: WakeByAddressSingle :: option ( ) . is_none ( ) {
173
+ return keyed_events:: unpark ( self ) ;
212
174
}
175
+ c:: WakeByAddressSingle ( self . ptr ( ) ) ;
213
176
}
214
177
}
215
178
}
@@ -219,35 +182,97 @@ impl Parker {
219
182
}
220
183
}
221
184
222
- fn keyed_event_handle ( ) -> c:: HANDLE {
223
- const INVALID : c:: HANDLE = ptr:: invalid_mut ( !0 ) ;
224
- static HANDLE : AtomicPtr < crate :: ffi:: c_void > = AtomicPtr :: new ( INVALID ) ;
225
- match HANDLE . load ( Relaxed ) {
226
- INVALID => {
227
- let mut handle = c:: INVALID_HANDLE_VALUE ;
228
- unsafe {
229
- match c:: NtCreateKeyedEvent (
230
- & mut handle,
231
- c:: GENERIC_READ | c:: GENERIC_WRITE ,
232
- ptr:: null_mut ( ) ,
233
- 0 ,
234
- ) {
235
- c:: STATUS_SUCCESS => { }
236
- r => panic ! ( "Unable to create keyed event handle: error {r}" ) ,
185
+ #[ cfg( target_vendor = "win7" ) ]
186
+ mod keyed_events {
187
+ use super :: { Parker , EMPTY , NOTIFIED } ;
188
+ use crate :: sys:: c;
189
+ use core:: pin:: Pin ;
190
+ use core:: ptr;
191
+ use core:: sync:: atomic:: {
192
+ AtomicPtr ,
193
+ Ordering :: { Acquire , Relaxed } ,
194
+ } ;
195
+ use core:: time:: Duration ;
196
+
197
+ pub unsafe fn park ( parker : Pin < & Parker > ) {
198
+ // Wait for unpark() to produce this event.
199
+ c:: NtWaitForKeyedEvent ( keyed_event_handle ( ) , parker. ptr ( ) , 0 , ptr:: null_mut ( ) ) ;
200
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
201
+ // Note that we don't just write EMPTY, but use swap() to also
202
+ // include an acquire-ordered read to synchronize with unpark()'s
203
+ // release-ordered write.
204
+ parker. state . swap ( EMPTY , Acquire ) ;
205
+ return ;
206
+ }
207
+ pub unsafe fn park_timeout ( parker : Pin < & Parker > , timeout : Duration ) {
208
+ // Need to wait for unpark() using NtWaitForKeyedEvent.
209
+ let handle = keyed_event_handle ( ) ;
210
+
211
+ // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
212
+ // values to indicate a relative time on the monotonic clock.
213
+ // This is documented here for the underlying KeWaitForSingleObject function:
214
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
215
+ let mut timeout = match i64:: try_from ( ( timeout. as_nanos ( ) + 99 ) / 100 ) {
216
+ Ok ( t) => -t,
217
+ Err ( _) => i64:: MIN ,
218
+ } ;
219
+
220
+ // Wait for unpark() to produce this event.
221
+ let unparked =
222
+ c:: NtWaitForKeyedEvent ( handle, parker. ptr ( ) , 0 , & mut timeout) == c:: STATUS_SUCCESS ;
223
+
224
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
225
+ let prev_state = parker. state . swap ( EMPTY , Acquire ) ;
226
+
227
+ if !unparked && prev_state == NOTIFIED {
228
+ // We were awoken by a timeout, not by unpark(), but the state
229
+ // was set to NOTIFIED, which means we *just* missed an
230
+ // unpark(), which is now blocked on us to wait for it.
231
+ // Wait for it to consume the event and unblock that thread.
232
+ c:: NtWaitForKeyedEvent ( handle, parker. ptr ( ) , 0 , ptr:: null_mut ( ) ) ;
233
+ }
234
+ }
235
+ pub unsafe fn unpark ( parker : Pin < & Parker > ) {
236
+ // If we run NtReleaseKeyedEvent before the waiting thread runs
237
+ // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
238
+ // If the waiting thread wakes up before we run NtReleaseKeyedEvent
239
+ // (e.g. due to a timeout), this blocks until we do wake up a thread.
240
+ // To prevent this thread from blocking indefinitely in that case,
241
+ // park_impl() will, after seeing the state set to NOTIFIED after
242
+ // waking up, call NtWaitForKeyedEvent again to unblock us.
243
+ c:: NtReleaseKeyedEvent ( keyed_event_handle ( ) , parker. ptr ( ) , 0 , ptr:: null_mut ( ) ) ;
244
+ }
245
+
246
+ fn keyed_event_handle ( ) -> c:: HANDLE {
247
+ const INVALID : c:: HANDLE = ptr:: invalid_mut ( !0 ) ;
248
+ static HANDLE : AtomicPtr < crate :: ffi:: c_void > = AtomicPtr :: new ( INVALID ) ;
249
+ match HANDLE . load ( Relaxed ) {
250
+ INVALID => {
251
+ let mut handle = c:: INVALID_HANDLE_VALUE ;
252
+ unsafe {
253
+ match c:: NtCreateKeyedEvent (
254
+ & mut handle,
255
+ c:: GENERIC_READ | c:: GENERIC_WRITE ,
256
+ ptr:: null_mut ( ) ,
257
+ 0 ,
258
+ ) {
259
+ c:: STATUS_SUCCESS => { }
260
+ r => panic ! ( "Unable to create keyed event handle: error {r}" ) ,
261
+ }
237
262
}
238
- }
239
- match HANDLE . compare_exchange ( INVALID , handle, Relaxed , Relaxed ) {
240
- Ok ( _) => handle,
241
- Err ( h) => {
242
- // Lost the race to another thread initializing HANDLE before we did.
243
- // Closing our handle and using theirs instead.
244
- unsafe {
245
- c:: CloseHandle ( handle) ;
263
+ match HANDLE . compare_exchange ( INVALID , handle, Relaxed , Relaxed ) {
264
+ Ok ( _) => handle,
265
+ Err ( h) => {
266
+ // Lost the race to another thread initializing HANDLE before we did.
267
+ // Closing our handle and using theirs instead.
268
+ unsafe {
269
+ c:: CloseHandle ( handle) ;
270
+ }
271
+ h
246
272
}
247
- h
248
273
}
249
274
}
275
+ handle => handle,
250
276
}
251
- handle => handle,
252
277
}
253
278
}
0 commit comments