@@ -255,6 +255,44 @@ 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+ ///
293+ /// [`park`]: Parker::park
294+ /// [`park_timeout`]: Parker::park_timeout
295+ pub fn unpark_with_result ( & self ) -> UnparkResult {
258296 self . inner . unpark ( )
259297 }
260298
@@ -324,6 +362,21 @@ pub enum UnparkReason {
324362 Timeout ,
325363}
326364
365+ /// An enum that reports the result of an `Unparker::unpark` call. This includes whether the parker
366+ /// was parked when `unpark` was called, and if so, whether it was already notified by a previous
367+ /// `unpark` call.
368+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
369+ pub enum UnparkResult {
370+ /// The parker was not parked when `unpark` was called.
371+ NotParked ,
372+
373+ /// The parker was parked, but was already notified by an earlier `unpark` call.
374+ AlreadyNotified ,
375+
376+ /// The parker was parked and has been successfully notified by this `unpark` call.
377+ Notified ,
378+ }
379+
327380const EMPTY : usize = 0 ;
328381const PARKED : usize = 1 ;
329382const NOTIFIED : usize = 2 ;
@@ -407,15 +460,15 @@ impl Inner {
407460 }
408461 }
409462
410- pub ( crate ) fn unpark ( & self ) {
463+ pub ( crate ) fn unpark ( & self ) -> UnparkResult {
411464 // To ensure the unparked thread will observe any writes we made before this call, we must
412465 // perform a release operation that `park` can synchronize with. To do that we must write
413466 // `NOTIFIED` even if `state` is already `NOTIFIED`. That is why this must be a swap rather
414467 // than a compare-and-swap that returns if it reads `NOTIFIED` on failure.
415468 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
469+ EMPTY => return UnparkResult :: NotParked , // no one was waiting
470+ NOTIFIED => return UnparkResult :: AlreadyNotified , // already unparked
471+ PARKED => { } // gotta go wake someone up
419472 _ => panic ! ( "inconsistent state in unpark" ) ,
420473 }
421474
@@ -429,5 +482,6 @@ impl Inner {
429482 // it doesn't get woken only to have to wait for us to release `lock`.
430483 drop ( self . lock . lock ( ) . unwrap ( ) ) ;
431484 self . cvar . notify_one ( ) ;
485+ UnparkResult :: Notified
432486 }
433487}
0 commit comments