@@ -96,9 +96,18 @@ impl From<tokio::task::JoinError> for JoinError {
96
96
}
97
97
}
98
98
99
+ #[ must_use = "Tasks are canceled when their `SpawnHandle` is dropped." ]
99
100
pub trait SpawnHandle : Send + Sync {
101
+ /// Stops the spawned task "soon". This happens asynchronously.
100
102
fn shutdown ( & mut self ) ;
103
+ /// Wait for the spawned task to finish. Don't use this function directly,
104
+ /// call `.join()` instead.
101
105
fn poll_join ( & mut self , cx : & mut std:: task:: Context < ' _ > ) -> Poll < Result < ( ) , JoinError > > ;
106
+ /// Allows the spawned task to keep running indefinitely. By default, a task
107
+ /// is shut down on drop.
108
+ fn detach ( self : Box < Self > ) ;
109
+ /// Wait for the spawned task to finish. Returns an error if the task was
110
+ /// canceled (using `.shutdown()`) or panicked.
102
111
fn join < ' a > ( mut self ) -> impl Future < Output = Result < ( ) , JoinError > > + ' a
103
112
where
104
113
Self : Sized + ' a ,
@@ -115,6 +124,10 @@ impl<T: SpawnHandle + ?Sized> SpawnHandle for Box<T> {
115
124
fn poll_join ( & mut self , cx : & mut std:: task:: Context < ' _ > ) -> Poll < Result < ( ) , JoinError > > {
116
125
( * * self ) . poll_join ( cx)
117
126
}
127
+
128
+ fn detach ( self : Box < Self > ) {
129
+ ( * self ) . detach ( )
130
+ }
118
131
}
119
132
120
133
impl dyn SpawnHandle {
@@ -123,27 +136,61 @@ impl dyn SpawnHandle {
123
136
pub fn join ( self : Box < Self > ) -> impl Future < Output = Result < ( ) , JoinError > > {
124
137
SpawnHandle :: join ( self )
125
138
}
139
+
140
+ /// Wait for the spawn task to finish, but if the returned future is
141
+ /// canceled, the spawned task continues running as though it were
142
+ /// `detach()`ed.
143
+ pub fn join_or_detach ( self : Box < Self > ) -> impl Future < Output = Result < ( ) , JoinError > > {
144
+ struct DetachOnDrop ( Option < Box < dyn SpawnHandle > > ) ;
145
+ impl Drop for DetachOnDrop {
146
+ fn drop ( & mut self ) {
147
+ self . 0 . take ( ) . expect ( "lost spawn handle?" ) . detach ( ) ;
148
+ }
149
+ }
150
+ let mut handle = DetachOnDrop ( Some ( self ) ) ;
151
+ future:: poll_fn ( move |cx| handle. 0 . as_mut ( ) . expect ( "lost spawn handle?" ) . poll_join ( cx) )
152
+ }
153
+
154
+ pub fn shutdown_and_join ( self : Box < Self > ) -> impl Future < Output = anyhow:: Result < ( ) > > {
155
+ shutdown_and_join ( self )
156
+ }
126
157
}
127
158
128
159
pub struct TokioSpawnHandle {
129
- handle : tokio:: task:: JoinHandle < ( ) > ,
160
+ handle : Option < tokio:: task:: JoinHandle < ( ) > > ,
130
161
}
131
162
132
163
impl From < tokio:: task:: JoinHandle < ( ) > > for TokioSpawnHandle {
133
164
fn from ( handle : tokio:: task:: JoinHandle < ( ) > ) -> Self {
134
- Self { handle }
165
+ Self {
166
+ handle : Some ( handle) ,
167
+ }
135
168
}
136
169
}
137
170
138
171
impl SpawnHandle for TokioSpawnHandle {
139
172
fn shutdown ( & mut self ) {
140
- self . handle . abort ( ) ;
173
+ self . handle . as_ref ( ) . expect ( "shutdown after detach" ) . abort ( ) ;
141
174
}
142
175
143
176
fn poll_join ( & mut self , cx : & mut std:: task:: Context < ' _ > ) -> Poll < Result < ( ) , JoinError > > {
144
- std:: task:: ready!( Pin :: new( & mut self . handle) . poll( cx) ) ?;
177
+ std:: task:: ready!(
178
+ Pin :: new( & mut self . handle. as_mut( ) . expect( "poll after detach" ) ) . poll( cx)
179
+ ) ?;
145
180
Poll :: Ready ( Ok ( ( ) ) )
146
181
}
182
+
183
+ fn detach ( mut self : Box < Self > ) {
184
+ self . handle . take ( ) ;
185
+ }
186
+ }
187
+
188
+ impl Drop for TokioSpawnHandle {
189
+ fn drop ( & mut self ) {
190
+ if let Some ( handle) = & self . handle {
191
+ handle. abort ( ) ;
192
+ }
193
+ }
147
194
}
148
195
149
196
/// Shutdown the associated future, preempting it at its next yield point, and
@@ -230,16 +277,29 @@ pub trait Runtime: Clone + Sync + Send + 'static {
230
277
fn wait ( & self , duration : Duration ) -> Pin < Box < dyn FusedFuture < Output = ( ) > + Send + ' static > > ;
231
278
232
279
/// Spawn a future on the runtime's executor.
280
+ ///
281
+ /// The spawned task will be canceled if the returned `SpawnHandle` is
282
+ /// dropped, unless `detach()` is called on it.
233
283
fn spawn (
234
284
& self ,
235
285
name : & ' static str ,
236
286
f : impl Future < Output = ( ) > + Send + ' static ,
237
287
) -> Box < dyn SpawnHandle > ;
238
288
289
+ /// Shorthand for `spawn().detach()`
290
+ ///
291
+ /// This should only be used for tasks that are best-effort (e.g. cleaning
292
+ /// up partial progress) or that are truly process-global.
293
+ fn spawn_background ( & self , name : & ' static str , f : impl Future < Output = ( ) > + Send + ' static ) {
294
+ self . spawn ( name, f) . detach ( )
295
+ }
296
+
239
297
/// Spawn a future on a reserved OS thread. This is only really necessary
240
298
/// for libraries like `V8` that care about being called from a
241
299
/// particular thread.
242
- #[ must_use = "Threads are canceled when their `SpawnHandle` is dropped." ]
300
+ ///
301
+ /// The spawned task will be canceled if the returned `SpawnHandle` is
302
+ /// dropped, unless `detach()` is called on it.
243
303
fn spawn_thread < Fut : Future < Output = ( ) > , F : FnOnce ( ) -> Fut + Send + ' static > (
244
304
& self ,
245
305
name : & str ,
0 commit comments