@@ -255,6 +255,41 @@ impl Unparker {
255255 /// [`park`]: Parker::park
256256 /// [`park_timeout`]: Parker::park_timeout
257257 pub fn unpark ( & self ) {
258+ self . inner . unpark ( ) ;
259+ }
260+
261+ /// Atomically makes the token available if it is not already, returning an [`UnparkResult`]
262+ /// indicating the result of the unpark operation.
263+ ///
264+ /// This method will wake up the thread blocked on [`park`] or [`park_timeout`], if there is
265+ /// any.
266+ ///
267+ /// # Examples
268+ ///
269+ /// ```
270+ /// use std::thread;
271+ /// use std::time::Duration;
272+ /// use crossbeam_utils::sync::{Parker, UnparkResult};
273+ ///
274+ /// let p = Parker::new();
275+ /// let u = p.unparker().clone();
276+ ///
277+ /// let result = u.unpark_with_result();
278+ /// assert_eq!(result, UnparkResult::NotParked);
279+ /// p.park(); // consume the token and immediately return
280+ ///
281+ /// # let t =
282+ /// thread::spawn(move || {
283+ /// thread::sleep(Duration::from_millis(500));
284+ /// let result = u.unpark_with_result();
285+ /// assert_eq!(result, UnparkResult::Notified);
286+ /// });
287+ ///
288+ /// // Wakes up when `u.unpark()` provides the token.
289+ /// p.park();
290+ /// # t.join().unwrap(); // join thread to avoid https://github.com/rust-lang/miri/issues/1371
291+ /// ```
292+ pub fn unpark_with_result ( & self ) -> UnparkResult {
258293 self . inner . unpark ( )
259294 }
260295
@@ -324,6 +359,21 @@ pub enum UnparkReason {
324359 Timeout ,
325360}
326361
362+ /// An enum that reports the result of an `Unparker::unpark` call. This includes whether the parker
363+ /// was parked when `unpark` was called, and if so, whether it was already notified by a previous
364+ /// `unpark` call.
365+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
366+ pub enum UnparkResult {
367+ /// The parker was not parked when `unpark` was called.
368+ NotParked ,
369+
370+ /// The parker was parked, but was already notified by an earlier `unpark` call.
371+ AlreadyNotified ,
372+
373+ /// The parker was parked and has been successfully notified by this `unpark` call.
374+ Notified ,
375+ }
376+
327377const EMPTY : usize = 0 ;
328378const PARKED : usize = 1 ;
329379const NOTIFIED : usize = 2 ;
@@ -407,15 +457,15 @@ impl Inner {
407457 }
408458 }
409459
410- pub ( crate ) fn unpark ( & self ) {
460+ pub ( crate ) fn unpark ( & self ) -> UnparkResult {
411461 // To ensure the unparked thread will observe any writes we made before this call, we must
412462 // perform a release operation that `park` can synchronize with. To do that we must write
413463 // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather
414464 // than a compare-and-swap that returns if it reads `NOTIFIED` on failure.
415465 match self . state . swap ( NOTIFIED , SeqCst ) {
416- EMPTY => return , // no one was waiting
417- NOTIFIED => return , // already unparked
418- PARKED => { } // gotta go wake someone up
466+ EMPTY => return UnparkResult :: NotParked , // no one was waiting
467+ NOTIFIED => return UnparkResult :: AlreadyNotified , // already unparked
468+ PARKED => { } // gotta go wake someone up
419469 _ => panic ! ( "inconsistent state in unpark" ) ,
420470 }
421471
@@ -429,5 +479,6 @@ impl Inner {
429479 // it doesn't get woken only to have to wait for us to release `lock`.
430480 drop ( self . lock . lock ( ) . unwrap ( ) ) ;
431481 self . cvar . notify_one ( ) ;
482+ UnparkResult :: Notified
432483 }
433484}
0 commit comments