11use std:: sync:: Arc ;
2+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
23
34use { Future , Poll } ;
45use slot:: { Slot , Token } ;
5- use task;
6+ use lock:: Lock ;
7+ use task:: { self , TaskHandle } ;
68
79/// A future representing the completion of a computation happening elsewhere in
810/// memory.
@@ -24,6 +26,8 @@ pub struct Complete<T> {
2426
2527struct Inner < T > {
2628 slot : Slot < Option < T > > ,
29+ oneshot_gone : AtomicBool ,
30+ notify_cancel : Lock < Option < TaskHandle > > ,
2731}
2832
2933/// Creates a new in-memory oneshot used to represent completing a computation.
@@ -55,6 +59,8 @@ struct Inner<T> {
5559pub fn oneshot < T > ( ) -> ( Complete < T > , Oneshot < T > ) {
5660 let inner = Arc :: new ( Inner {
5761 slot : Slot :: new ( None ) ,
62+ oneshot_gone : AtomicBool :: new ( false ) ,
63+ notify_cancel : Lock :: new ( None ) ,
5864 } ) ;
5965 let oneshot = Oneshot {
6066 inner : inner. clone ( ) ,
@@ -78,6 +84,57 @@ impl<T> Complete<T> {
7884 self . send ( Some ( t) )
7985 }
8086
87+ /// Polls this `Complete` half to detect whether the `Oneshot` this has
88+ /// paired with has gone away.
89+ ///
90+ /// This function can be used to learn about when the `Oneshot` (consumer)
91+ /// half has gone away and nothing will be able to receive a message sent
92+ /// from `complete`.
93+ ///
94+ /// Like `Future::poll`, this function will panic if it's not called from
95+ /// within the context of a task. In otherwords, this should only ever be
96+ /// called from inside another future.
97+ ///
98+ /// If `Poll::Ok` is returned then it means that the `Oneshot` has
99+ /// disappeared and the result this `Complete` would otherwise produce
100+ /// should no longer be produced.
101+ ///
102+ /// If `Poll::NotReady` is returned then the `Oneshot` is still alive and
103+ /// may be able to receive a message if sent. The current task, however,
104+ /// is scheduled to receive a notification if the corresponding `Oneshot`
105+ /// goes away.
106+ pub fn poll_cancel ( & mut self ) -> Poll < ( ) , ( ) > {
107+ // Fast path up first, just read the flag and see if our other half is
108+ // gone.
109+ if self . inner . oneshot_gone . load ( Ordering :: SeqCst ) {
110+ return Poll :: Ok ( ( ) )
111+ }
112+
113+ // If our other half is not gone then we need to park our current task
114+ // and move it into the `notify_cancel` slot to get notified when it's
115+ // actually gone.
116+ //
117+ // If `try_lock` fails, then the `Oneshot` is in the process of using
118+ // it, so we can deduce that it's now in the process of going away and
119+ // hence we're canceled. If it succeeds then we just store our handle.
120+ //
121+ // Crucially we then check `oneshot_gone` *again* before we return.
122+ // While we were storing our handle inside `notify_cancel` the `Oneshot`
123+ // may have been dropped. The first thing it does is set the flag, and
124+ // if it fails to acquire the lock it assumes that we'll see the flag
125+ // later on. So... we then try to see the flag later on!
126+ let handle = task:: park ( ) ;
127+ match self . inner . notify_cancel . try_lock ( ) {
128+ Some ( mut p) => * p = Some ( handle) ,
129+ None => return Poll :: Ok ( ( ) ) ,
130+ }
131+ if self . inner . oneshot_gone . load ( Ordering :: SeqCst ) {
132+ Poll :: Ok ( ( ) )
133+ } else {
134+ Poll :: NotReady
135+ }
136+ }
137+
81138 fn send ( & mut self , t : Option < T > ) {
82139 if let Err ( e) = self . inner . slot . try_produce ( t) {
83140 self . inner . slot . on_empty ( Some ( e. into_inner ( ) ) , |slot, item| {
@@ -125,8 +182,27 @@ impl<T> Future for Oneshot<T> {
125182
126183impl < T > Drop for Oneshot < T > {
127184 fn drop ( & mut self ) {
185+ // First up, if we squirreled away a task to get notified once the
186+ // oneshot was filled in, we cancel that notification. We'll never end
187+ // up actually receiving data (as we're being dropped) so no need to
188+ // hold onto the task.
128189 if let Some ( cancel_token) = self . cancel_token . take ( ) {
129190 self . inner . slot . cancel ( cancel_token)
130191 }
192+
193+ // Next up, inform the `Complete` half that we're going away. First up
194+ // we flag ourselves as gone, and next we'll attempt to wake up any
195+ // handle that was stored.
196+ //
197+ // If we fail to acquire the lock on the handle, that means that a
198+ // `Complete` is in the process of storing one, and it'll check
199+ // `oneshot_gone` on its way out to see our write here.
200+ self . inner . oneshot_gone . store ( true , Ordering :: SeqCst ) ;
201+ if let Some ( mut handle) = self . inner . notify_cancel . try_lock ( ) {
202+ if let Some ( task) = handle. take ( ) {
203+ drop ( handle) ;
204+ task. unpark ( )
205+ }
206+ }
131207 }
132208}
0 commit comments