@@ -17,6 +17,7 @@ use std::panic::{RefUnwindSafe, UnwindSafe};
17
17
use std:: pin:: Pin ;
18
18
use std:: ptr:: NonNull ;
19
19
use std:: sync:: atomic:: Ordering :: { self , Acquire , Relaxed , Release , SeqCst } ;
20
+ use std:: sync:: Arc ;
20
21
use std:: task:: { Context , Poll , Waker } ;
21
22
22
23
type WaitList = LinkedList < Waiter , <Waiter as linked_list:: Link >:: Target > ;
@@ -397,6 +398,38 @@ pub struct Notified<'a> {
397
398
unsafe impl < ' a > Send for Notified < ' a > { }
398
399
unsafe impl < ' a > Sync for Notified < ' a > { }
399
400
401
+ /// Future returned from [`Notify::notified_owned()`].
402
+ ///
403
+ /// This future is fused, so once it has completed, any future calls to poll
404
+ /// will immediately return `Poll::Ready`.
405
+ #[ derive( Debug ) ]
406
+ #[ must_use = "futures do nothing unless you `.await` or poll them" ]
407
+ pub struct OwnedNotified {
408
+ /// The `Notify` being received on.
409
+ notify : Arc < Notify > ,
410
+
411
+ /// The current state of the receiving process.
412
+ state : State ,
413
+
414
+ /// Number of calls to `notify_waiters` at the time of creation.
415
+ notify_waiters_calls : usize ,
416
+
417
+ /// Entry in the waiter `LinkedList`.
418
+ waiter : Waiter ,
419
+ }
420
+
421
+ unsafe impl Sync for OwnedNotified { }
422
+
423
+ /// A custom `project` implementation is used in place of `pin-project-lite`
424
+ /// as a custom drop for [`Notified`] and [`OwnedNotified`] implementation
425
+ /// is needed.
426
+ struct NotifiedProject < ' a > {
427
+ notify : & ' a Notify ,
428
+ state : & ' a mut State ,
429
+ notify_waiters_calls : & ' a usize ,
430
+ waiter : & ' a Waiter ,
431
+ }
432
+
400
433
#[ derive( Debug ) ]
401
434
enum State {
402
435
Init ,
@@ -541,6 +574,53 @@ impl Notify {
541
574
}
542
575
}
543
576
577
+ /// Wait for a notification with an owned `Future`.
578
+ ///
579
+ /// Unlike [`Self::notified`] which returns a future tied to the `Notify`'s
580
+ /// lifetime, `notified_owned` creates a self-contained future that owns its
581
+ /// notification state, making it safe to move between threads.
582
+ ///
583
+ /// See [`Self::notified`] for more details.
584
+ ///
585
+ /// # Cancel safety
586
+ ///
587
+ /// This method uses a queue to fairly distribute notifications in the order
588
+ /// they were requested. Cancelling a call to `notified_owned` makes you lose your
589
+ /// place in the queue.
590
+ ///
591
+ /// # Examples
592
+ ///
593
+ /// ```
594
+ /// use std::sync::Arc;
595
+ /// use tokio::sync::Notify;
596
+ ///
597
+ /// #[tokio::main]
598
+ /// async fn main() {
599
+ /// let notify = Arc::new(Notify::new());
600
+ ///
601
+ /// for _ in 0..10 {
602
+ /// let notified = notify.clone().notified_owned();
603
+ /// tokio::spawn(async move {
604
+ /// notified.await;
605
+ /// println!("received notification");
606
+ /// });
607
+ /// }
608
+ ///
609
+ /// println!("sending notification");
610
+ /// notify.notify_waiters();
611
+ /// }
612
+ /// ```
613
+ pub fn notified_owned ( self : Arc < Self > ) -> OwnedNotified {
614
+ // we load the number of times notify_waiters
615
+ // was called and store that in the future.
616
+ let state = self . state . load ( SeqCst ) ;
617
+ OwnedNotified {
618
+ notify : self ,
619
+ state : State :: Init ,
620
+ notify_waiters_calls : get_num_notify_waiters_calls ( state) ,
621
+ waiter : Waiter :: new ( ) ,
622
+ }
623
+ }
544
624
/// Notifies the first waiting task.
545
625
///
546
626
/// If a task is currently waiting, that task is notified. Otherwise, a
@@ -911,9 +991,62 @@ impl Notified<'_> {
911
991
self . poll_notified ( None ) . is_ready ( )
912
992
}
913
993
994
+ fn project ( self : Pin < & mut Self > ) -> NotifiedProject < ' _ > {
995
+ unsafe {
996
+ // Safety: `notify`, `state` and `notify_waiters_calls` are `Unpin`.
997
+
998
+ is_unpin :: < & Notify > ( ) ;
999
+ is_unpin :: < State > ( ) ;
1000
+ is_unpin :: < usize > ( ) ;
1001
+
1002
+ let me = self . get_unchecked_mut ( ) ;
1003
+ NotifiedProject {
1004
+ notify : me. notify ,
1005
+ state : & mut me. state ,
1006
+ notify_waiters_calls : & me. notify_waiters_calls ,
1007
+ waiter : & me. waiter ,
1008
+ }
1009
+ }
1010
+ }
1011
+
1012
+ fn poll_notified ( self : Pin < & mut Self > , waker : Option < & Waker > ) -> Poll < ( ) > {
1013
+ self . project ( ) . poll_notified ( waker)
1014
+ }
1015
+ }
1016
+
1017
+ impl Future for Notified < ' _ > {
1018
+ type Output = ( ) ;
1019
+
1020
+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < ( ) > {
1021
+ self . poll_notified ( Some ( cx. waker ( ) ) )
1022
+ }
1023
+ }
1024
+
1025
+ impl Drop for Notified < ' _ > {
1026
+ fn drop ( & mut self ) {
1027
+ // Safety: The type only transitions to a "Waiting" state when pinned.
1028
+ unsafe { Pin :: new_unchecked ( self ) }
1029
+ . project ( )
1030
+ . drop_notified ( ) ;
1031
+ }
1032
+ }
1033
+
1034
+ // ===== impl OwnedNotified =====
1035
+
1036
+ impl OwnedNotified {
1037
+ /// Adds this future to the list of futures that are ready to receive
1038
+ /// wakeups from calls to [`notify_one`].
1039
+ ///
1040
+ /// See [`Notified::enable`] for more details.
1041
+ ///
1042
+ /// [`notify_one`]: Notify::notify_one()
1043
+ pub fn enable ( self : Pin < & mut Self > ) -> bool {
1044
+ self . poll_notified ( None ) . is_ready ( )
1045
+ }
1046
+
914
1047
/// A custom `project` implementation is used in place of `pin-project-lite`
915
1048
/// as a custom drop implementation is needed.
916
- fn project ( self : Pin < & mut Self > ) -> ( & Notify , & mut State , & usize , & Waiter ) {
1049
+ fn project ( self : Pin < & mut Self > ) -> NotifiedProject < ' _ > {
917
1050
unsafe {
918
1051
// Safety: `notify`, `state` and `notify_waiters_calls` are `Unpin`.
919
1052
@@ -922,17 +1055,47 @@ impl Notified<'_> {
922
1055
is_unpin :: < usize > ( ) ;
923
1056
924
1057
let me = self . get_unchecked_mut ( ) ;
925
- (
926
- me. notify ,
927
- & mut me. state ,
928
- & me. notify_waiters_calls ,
929
- & me. waiter ,
930
- )
1058
+ NotifiedProject {
1059
+ notify : & me. notify ,
1060
+ state : & mut me. state ,
1061
+ notify_waiters_calls : & me. notify_waiters_calls ,
1062
+ waiter : & me. waiter ,
1063
+ }
931
1064
}
932
1065
}
933
1066
934
1067
fn poll_notified ( self : Pin < & mut Self > , waker : Option < & Waker > ) -> Poll < ( ) > {
935
- let ( notify, state, notify_waiters_calls, waiter) = self . project ( ) ;
1068
+ self . project ( ) . poll_notified ( waker)
1069
+ }
1070
+ }
1071
+
1072
+ impl Future for OwnedNotified {
1073
+ type Output = ( ) ;
1074
+
1075
+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < ( ) > {
1076
+ self . poll_notified ( Some ( cx. waker ( ) ) )
1077
+ }
1078
+ }
1079
+
1080
+ impl Drop for OwnedNotified {
1081
+ fn drop ( & mut self ) {
1082
+ // Safety: The type only transitions to a "Waiting" state when pinned.
1083
+ unsafe { Pin :: new_unchecked ( self ) }
1084
+ . project ( )
1085
+ . drop_notified ( ) ;
1086
+ }
1087
+ }
1088
+
1089
+ // ===== impl NotifiedProject =====
1090
+
1091
+ impl NotifiedProject < ' _ > {
1092
+ fn poll_notified ( self , waker : Option < & Waker > ) -> Poll < ( ) > {
1093
+ let NotifiedProject {
1094
+ notify,
1095
+ state,
1096
+ notify_waiters_calls,
1097
+ waiter,
1098
+ } = self ;
936
1099
937
1100
' outer_loop: loop {
938
1101
match * state {
@@ -1143,20 +1306,14 @@ impl Notified<'_> {
1143
1306
}
1144
1307
}
1145
1308
}
1146
- }
1147
-
1148
- impl Future for Notified < ' _ > {
1149
- type Output = ( ) ;
1150
1309
1151
- fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < ( ) > {
1152
- self . poll_notified ( Some ( cx. waker ( ) ) )
1153
- }
1154
- }
1155
-
1156
- impl Drop for Notified < ' _ > {
1157
- fn drop ( & mut self ) {
1158
- // Safety: The type only transitions to a "Waiting" state when pinned.
1159
- let ( notify, state, _, waiter) = unsafe { Pin :: new_unchecked ( self ) . project ( ) } ;
1310
+ fn drop_notified ( self ) {
1311
+ let NotifiedProject {
1312
+ notify,
1313
+ state,
1314
+ waiter,
1315
+ ..
1316
+ } = self ;
1160
1317
1161
1318
// This is where we ensure safety. The `Notified` value is being
1162
1319
// dropped, which means we must ensure that the waiter entry is no
0 commit comments