@@ -284,9 +284,11 @@ where
284
284
}
285
285
286
286
pub ( super ) fn drop_join_handle_slow ( self ) {
287
- // Try to unset `JOIN_INTEREST`. This must be done as a first step in
287
+ // Try to unset `JOIN_INTEREST` and `JOIN_WAKER` . This must be done as a first step in
288
288
// case the task concurrently completed.
289
- if self . state ( ) . unset_join_interested ( ) . is_err ( ) {
289
+ let transition = self . state ( ) . transition_to_join_handle_dropped ( ) ;
290
+
291
+ if transition. drop_output {
290
292
// It is our responsibility to drop the output. This is critical as
291
293
// the task output may not be `Send` and as such must remain with
292
294
// the scheduler or `JoinHandle`. i.e. if the output remains in the
@@ -301,6 +303,23 @@ where
301
303
} ) ) ;
302
304
}
303
305
306
+ if transition. drop_waker {
307
+ // If the JOIN_WAKER flag is unset at this point, the task is either
308
+ // already terminal or not complete so the `JoinHandle` is responsible
309
+ // for dropping the waker.
310
+ // Safety:
311
+ // If the JOIN_WAKER bit is not set the join handle has exclusive
312
+ // access to the waker as per rule 2 in task/mod.rs.
313
+ // This can only be the case at this point in two scenarios:
314
+ // 1. The task completed and the runtime unset `JOIN_WAKER` flag
315
+ // after accessing the waker during task completion. So the
316
+ // `JoinHandle` is the only one to access the join waker here.
317
+ // 2. The task is not completed so the `JoinHandle` was able to unset
318
+ // `JOIN_WAKER` bit itself to get mutable access to the waker.
319
+ // The runtime will not access the waker when this flag is unset.
320
+ unsafe { self . trailer ( ) . set_waker ( None ) } ;
321
+ }
322
+
304
323
// Drop the `JoinHandle` reference, possibly deallocating the task
305
324
self . drop_reference ( ) ;
306
325
}
@@ -311,7 +330,6 @@ where
311
330
fn complete ( self ) {
312
331
// The future has completed and its output has been written to the task
313
332
// stage. We transition from running to complete.
314
-
315
333
let snapshot = self . state ( ) . transition_to_complete ( ) ;
316
334
317
335
// We catch panics here in case dropping the future or waking the
@@ -320,13 +338,28 @@ where
320
338
if !snapshot. is_join_interested ( ) {
321
339
// The `JoinHandle` is not interested in the output of
322
340
// this task. It is our responsibility to drop the
323
- // output.
341
+ // output. The join waker was already dropped by the
342
+ // `JoinHandle` before.
324
343
self . core ( ) . drop_future_or_output ( ) ;
325
344
} else if snapshot. is_join_waker_set ( ) {
326
345
// Notify the waker. Reading the waker field is safe per rule 4
327
346
// in task/mod.rs, since the JOIN_WAKER bit is set and the call
328
347
// to transition_to_complete() above set the COMPLETE bit.
329
348
self . trailer ( ) . wake_join ( ) ;
349
+
350
+ // Inform the `JoinHandle` that we are done waking the waker by
351
+ // unsetting the `JOIN_WAKER` bit. If the `JoinHandle` has
352
+ // already been dropped and `JOIN_INTEREST` is unset, then we must
353
+ // drop the waker ourselves.
354
+ if !self
355
+ . state ( )
356
+ . unset_waker_after_complete ( )
357
+ . is_join_interested ( )
358
+ {
359
+ // SAFETY: We have COMPLETE=1 and JOIN_INTEREST=0, so
360
+ // we have exclusive access to the waker.
361
+ unsafe { self . trailer ( ) . set_waker ( None ) } ;
362
+ }
330
363
}
331
364
} ) ) ;
332
365
0 commit comments