22 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44
5- use std:: mem;
5+ use std:: { mem, sync :: Mutex } ;
66
77use super :: { RustFutureContinuationCallback , RustFuturePoll } ;
88
@@ -20,7 +20,12 @@ use super::{RustFutureContinuationCallback, RustFuturePoll};
2020/// state, invoking any future callbacks as soon as they're stored.
2121
2222#[ derive( Debug ) ]
23- pub ( super ) enum Scheduler {
23+ pub ( super ) struct Scheduler {
24+ state : Mutex < SchedulerState > ,
25+ }
26+
27+ #[ derive( Debug ) ]
28+ pub ( super ) enum SchedulerState {
2429 /// No continuations set, neither wake() nor cancel() called.
2530 Empty ,
2631 /// `wake()` was called when there was no continuation set. The next time `store` is called,
@@ -33,58 +38,138 @@ pub(super) enum Scheduler {
3338 Set ( RustFutureContinuationCallback , * const ( ) ) ,
3439}
3540
36- impl Scheduler {
37- pub ( super ) fn new ( ) -> Self {
38- Self :: Empty
41+ /// Encapsulates a call to a RustFutureContinuationCallback
42+ struct CallbackCall {
43+ callback : RustFutureContinuationCallback ,
44+ data : * const ( ) ,
45+ poll_data : RustFuturePoll ,
46+ }
47+
48+ impl CallbackCall {
49+ fn new (
50+ callback : RustFutureContinuationCallback ,
51+ data : * const ( ) ,
52+ poll_data : RustFuturePoll ,
53+ ) -> Self {
54+ Self {
55+ callback,
56+ data,
57+ poll_data,
58+ }
3959 }
4060
41- /// Store new continuation data if we are in the `Empty` state. If we are in the `Waked` or
42- /// `Cancelled` state, call the continuation immediately with the data.
43- pub ( super ) fn store ( & mut self , callback : RustFutureContinuationCallback , data : * const ( ) ) {
61+ fn invoke ( self ) {
62+ ( self . callback ) ( self . data , self . poll_data )
63+ }
64+ }
65+
66+ /// The SchedulerState impl contains all the ways to mutate the inner state field.
67+ ///
68+ /// All methods return an `Option<CallbackCall>` rather than invoking the callback directly.
69+ /// This is important, since the Mutex is locked while inside these methods. If we called the
70+ /// callback directly, the foreign code could poll the future again, which would try to lock the
71+ /// mutex again and lead to a deadlock.
72+ impl SchedulerState {
73+ fn store (
74+ & mut self ,
75+ callback : RustFutureContinuationCallback ,
76+ data : * const ( ) ,
77+ ) -> Option < CallbackCall > {
4478 match self {
45- Self :: Empty => * self = Self :: Set ( callback, data) ,
79+ Self :: Empty => {
80+ * self = Self :: Set ( callback, data) ;
81+ None
82+ }
4683 Self :: Set ( old_callback, old_data) => {
4784 log:: error!(
48- "store: observed `Self ::Set` state. Is poll() being called from multiple threads at once?"
85+ "store: observed `SchedulerState ::Set` state. Is poll() being called from multiple threads at once?"
4986 ) ;
50- old_callback ( * old_data, RustFuturePoll :: Ready ) ;
87+ let call = CallbackCall :: new ( * old_callback , * old_data, RustFuturePoll :: Ready ) ;
5188 * self = Self :: Set ( callback, data) ;
89+ Some ( call)
5290 }
5391 Self :: Waked => {
5492 * self = Self :: Empty ;
55- callback ( data, RustFuturePoll :: MaybeReady ) ;
56- }
57- Self :: Cancelled => {
58- callback ( data, RustFuturePoll :: Ready ) ;
93+ Some ( CallbackCall :: new (
94+ callback,
95+ data,
96+ RustFuturePoll :: MaybeReady ,
97+ ) )
5998 }
99+ Self :: Cancelled => Some ( CallbackCall :: new ( callback, data, RustFuturePoll :: Ready ) ) ,
60100 }
61101 }
62102
63- pub ( super ) fn wake ( & mut self ) {
103+ fn wake ( & mut self ) -> Option < CallbackCall > {
64104 match self {
65105 // If we had a continuation set, then call it and transition to the `Empty` state.
66- Self :: Set ( callback, old_data) => {
106+ SchedulerState :: Set ( callback, old_data) => {
67107 let old_data = * old_data;
68108 let callback = * callback;
69- * self = Self :: Empty ;
70- callback ( old_data, RustFuturePoll :: MaybeReady ) ;
109+ * self = SchedulerState :: Empty ;
110+ Some ( CallbackCall :: new (
111+ callback,
112+ old_data,
113+ RustFuturePoll :: MaybeReady ,
114+ ) )
71115 }
72116 // If we were in the `Empty` state, then transition to `Waked`. The next time `store`
73117 // is called, we will immediately call the continuation.
74- Self :: Empty => * self = Self :: Waked ,
118+ SchedulerState :: Empty => {
119+ * self = SchedulerState :: Waked ;
120+ None
121+ }
75122 // This is a no-op if we were in the `Cancelled` or `Waked` state.
76- _ => ( ) ,
123+ _ => None ,
124+ }
125+ }
126+
127+ fn cancel ( & mut self ) -> Option < CallbackCall > {
128+ if let SchedulerState :: Set ( callback, old_data) =
129+ mem:: replace ( self , SchedulerState :: Cancelled )
130+ {
131+ Some ( CallbackCall :: new ( callback, old_data, RustFuturePoll :: Ready ) )
132+ } else {
133+ None
134+ }
135+ }
136+ }
137+
138+ impl Scheduler {
139+ pub ( super ) fn new ( ) -> Self {
140+ Self {
141+ state : Mutex :: new ( SchedulerState :: Empty ) ,
77142 }
78143 }
79144
80- pub ( super ) fn cancel ( & mut self ) {
81- if let Self :: Set ( callback, old_data) = mem:: replace ( self , Self :: Cancelled ) {
82- callback ( old_data, RustFuturePoll :: Ready ) ;
145+ /// Call a method on the inner state field
146+ ///
147+ /// If it returns a callback to invoke, then make the call after releasing the mutex.
148+ fn call_state_method ( & self , f : impl Fn ( & mut SchedulerState ) -> Option < CallbackCall > ) {
149+ let mut state = self . state . lock ( ) . unwrap ( ) ;
150+ let callback_call = f ( & mut state) ;
151+ drop ( state) ;
152+ if let Some ( callback_call) = callback_call {
153+ callback_call. invoke ( )
83154 }
84155 }
85156
157+ /// Store new continuation data if we are in the `Empty` state. If we are in the `Waked` or
158+ /// `Cancelled` state, call the continuation immediately with the data.
159+ pub ( super ) fn store ( & self , callback : RustFutureContinuationCallback , data : * const ( ) ) {
160+ self . call_state_method ( |state| state. store ( callback, data) )
161+ }
162+
163+ pub ( super ) fn wake ( & self ) {
164+ self . call_state_method ( SchedulerState :: wake)
165+ }
166+
167+ pub ( super ) fn cancel ( & self ) {
168+ self . call_state_method ( SchedulerState :: cancel)
169+ }
170+
86171 pub ( super ) fn is_cancelled ( & self ) -> bool {
87- matches ! ( self , Self :: Cancelled )
172+ matches ! ( * self . state . lock ( ) . unwrap ( ) , SchedulerState :: Cancelled )
88173 }
89174}
90175
0 commit comments