@@ -3,18 +3,17 @@ use std::marker::PhantomData;
3
3
use std:: sync:: atomic:: AtomicUsize ;
4
4
use std:: sync:: atomic:: Ordering :: SeqCst ;
5
5
use std:: sync:: { Arc , Condvar , Mutex } ;
6
- use std:: time:: Duration ;
6
+ use std:: time:: { Duration , Instant } ;
7
7
8
8
/// A thread parking primitive.
9
9
///
10
10
/// Conceptually, each `Parker` has an associated token which is initially not present:
11
11
///
12
12
/// * The [`park`] method blocks the current thread unless or until the token is available, at
13
- /// which point it automatically consumes the token. It may also return *spuriously*, without
14
- /// consuming the token.
13
+ /// which point it automatically consumes the token.
15
14
///
16
- /// * The [`park_timeout`] method works the same as [`park`], but blocks for a specified maximum
17
- /// time.
15
+ /// * The [`park_timeout`] and [`park_deadline`] methods work the same as [`park`], but block for
16
+ /// a specified maximum time.
18
17
///
19
18
/// * The [`unpark`] method atomically makes the token available if it wasn't already. Because the
20
19
/// token is initially absent, [`unpark`] followed by [`park`] will result in the second call
@@ -43,13 +42,13 @@ use std::time::Duration;
43
42
/// u.unpark();
44
43
/// });
45
44
///
46
- /// // Wakes up when `u.unpark()` provides the token, but may also wake up
47
- /// // spuriously before that without consuming the token.
45
+ /// // Wakes up when `u.unpark()` provides the token.
48
46
/// p.park();
49
47
/// ```
50
48
///
51
49
/// [`park`]: Parker::park
52
50
/// [`park_timeout`]: Parker::park_timeout
51
+ /// [`park_deadline`]: Parker::park_deadline
53
52
/// [`unpark`]: Unparker::unpark
54
53
pub struct Parker {
55
54
unparker : Unparker ,
@@ -90,9 +89,6 @@ impl Parker {
90
89
91
90
/// Blocks the current thread until the token is made available.
92
91
///
93
- /// A call to `park` may wake up spuriously without consuming the token, and callers should be
94
- /// prepared for this possibility.
95
- ///
96
92
/// # Examples
97
93
///
98
94
/// ```
@@ -113,9 +109,6 @@ impl Parker {
113
109
114
110
/// Blocks the current thread until the token is made available, but only for a limited time.
115
111
///
116
- /// A call to `park_timeout` may wake up spuriously without consuming the token, and callers
117
- /// should be prepared for this possibility.
118
- ///
119
112
/// # Examples
120
113
///
121
114
/// ```
@@ -128,7 +121,25 @@ impl Parker {
128
121
/// p.park_timeout(Duration::from_millis(500));
129
122
/// ```
130
123
pub fn park_timeout ( & self , timeout : Duration ) {
131
- self . unparker . inner . park ( Some ( timeout) ) ;
124
+ self . park_deadline ( Instant :: now ( ) + timeout)
125
+ }
126
+
127
+ /// Blocks the current thread until the token is made available, or until a certain deadline.
128
+ ///
129
+ /// # Examples
130
+ ///
131
+ /// ```
132
+ /// use std::time::{Duration, Instant};
133
+ /// use crossbeam_utils::sync::Parker;
134
+ ///
135
+ /// let p = Parker::new();
136
+ /// let deadline = Instant::now() + Duration::from_millis(500);
137
+ ///
138
+ /// // Waits for the token to become available, but will not wait longer than 500 ms.
139
+ /// p.park_deadline(deadline);
140
+ /// ```
141
+ pub fn park_deadline ( & self , deadline : Instant ) {
142
+ self . unparker . inner . park ( Some ( deadline) )
132
143
}
133
144
134
145
/// Returns a reference to an associated [`Unparker`].
@@ -227,8 +238,7 @@ impl Unparker {
227
238
/// u.unpark();
228
239
/// });
229
240
///
230
- /// // Wakes up when `u.unpark()` provides the token, but may also wake up
231
- /// // spuriously before that without consuming the token.
241
+ /// // Wakes up when `u.unpark()` provides the token.
232
242
/// p.park();
233
243
/// ```
234
244
///
@@ -302,7 +312,7 @@ struct Inner {
302
312
}
303
313
304
314
impl Inner {
305
- fn park ( & self , timeout : Option < Duration > ) {
315
+ fn park ( & self , deadline : Option < Instant > ) {
306
316
// If we were previously notified then we consume this notification and return quickly.
307
317
if self
308
318
. state
@@ -313,8 +323,8 @@ impl Inner {
313
323
}
314
324
315
325
// If the timeout is zero, then there is no need to actually block.
316
- if let Some ( ref dur ) = timeout {
317
- if * dur == Duration :: from_millis ( 0 ) {
326
+ if let Some ( deadline ) = deadline {
327
+ if deadline <= Instant :: now ( ) {
318
328
return ;
319
329
}
320
330
}
@@ -338,36 +348,38 @@ impl Inner {
338
348
Err ( n) => panic ! ( "inconsistent park_timeout state: {}" , n) ,
339
349
}
340
350
341
- match timeout {
342
- None => {
343
- loop {
344
- // Block the current thread on the conditional variable.
345
- m = self . cvar . wait ( m) . unwrap ( ) ;
346
-
347
- if self
348
- . state
349
- . compare_exchange ( NOTIFIED , EMPTY , SeqCst , SeqCst )
350
- . is_ok ( )
351
- {
352
- // got a notification
353
- return ;
351
+ loop {
352
+ // Block the current thread on the conditional variable.
353
+ m = match deadline {
354
+ None => self . cvar . wait ( m) . unwrap ( ) ,
355
+ Some ( deadline) => {
356
+ let now = Instant :: now ( ) ;
357
+ if now < deadline {
358
+ // We could check for a timeout here, in the return value of wait_timeout,
359
+ // but in the case that a timeout and an unpark arrive simultaneously, we
360
+ // prefer to report the former.
361
+ self . cvar . wait_timeout ( m, deadline - now) . unwrap ( ) . 0
362
+ } else {
363
+ // We've timed out; swap out the state back to empty on our way out
364
+ match self . state . swap ( EMPTY , SeqCst ) {
365
+ NOTIFIED | PARKED => return ,
366
+ n => panic ! ( "inconsistent park_timeout state: {}" , n) ,
367
+ } ;
354
368
}
355
-
356
- // spurious wakeup, go back to sleep
357
369
}
358
- }
359
- Some ( timeout) => {
360
- // Wait with a timeout, and if we spuriously wake up or otherwise wake up from a
361
- // notification we just want to unconditionally set `state` back to `EMPTY`, either
362
- // consuming a notification or un-flagging ourselves as parked.
363
- let ( _m, _result) = self . cvar . wait_timeout ( m, timeout) . unwrap ( ) ;
370
+ } ;
364
371
365
- match self . state . swap ( EMPTY , SeqCst ) {
366
- NOTIFIED => { } // got a notification
367
- PARKED => { } // no notification
368
- n => panic ! ( "inconsistent park_timeout state: {}" , n) ,
369
- }
372
+ if self
373
+ . state
374
+ . compare_exchange ( NOTIFIED , EMPTY , SeqCst , SeqCst )
375
+ . is_ok ( )
376
+ {
377
+ // got a notification
378
+ return ;
370
379
}
380
+
381
+ // Spurious wakeup, go back to sleep. Alternatively, if we timed out, it will be caught
382
+ // in the branch above, when we discover the deadline is in the past
371
383
}
372
384
}
373
385
0 commit comments