@@ -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 snapshot = self . state ( ) . transition_to_join_handle_dropped ( ) ;
290
+
291
+ if snapshot. is_complete ( ) {
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,25 @@ where
301
303
} ) ) ;
302
304
}
303
305
306
+ if !snapshot. is_join_waker_set ( ) {
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 {
321
+ self . trailer ( ) . set_waker ( None ) ;
322
+ }
323
+ }
324
+
304
325
// Drop the `JoinHandle` reference, possibly deallocating the task
305
326
self . drop_reference ( ) ;
306
327
}
@@ -311,7 +332,6 @@ where
311
332
fn complete ( self ) {
312
333
// The future has completed and its output has been written to the task
313
334
// stage. We transition from running to complete.
314
-
315
335
let snapshot = self . state ( ) . transition_to_complete ( ) ;
316
336
317
337
// We catch panics here in case dropping the future or waking the
@@ -320,13 +340,33 @@ where
320
340
if !snapshot. is_join_interested ( ) {
321
341
// The `JoinHandle` is not interested in the output of
322
342
// this task. It is our responsibility to drop the
323
- // output.
343
+ // output. The join waker was already dropped by the
344
+ // `JoinHandle` before.
324
345
self . core ( ) . drop_future_or_output ( ) ;
325
346
} else if snapshot. is_join_waker_set ( ) {
326
347
// Notify the waker. Reading the waker field is safe per rule 4
327
348
// in task/mod.rs, since the JOIN_WAKER bit is set and the call
328
349
// to transition_to_complete() above set the COMPLETE bit.
329
350
self . trailer ( ) . wake_join ( ) ;
351
+
352
+ // If JOIN_INTEREST is still set at this point the `JoinHandle`
353
+ // was not dropped since setting COMPLETE so we unset JOIN_WAKER
354
+ // to give the responsibility of dropping the join waker back to
355
+ // the `JoinHandle`. `JoinHandle` is able to drop the waker when
356
+ // itself gets dropped.
357
+ if self . state ( ) . unset_waker_if_join_interested ( ) . is_err ( ) {
358
+ // Unsetting JOIN_WAKER flag will fail if JOIN_INTERESTED is
359
+ // not set to indicate that the runtime has the responsibility
360
+ // to drop the join waker here as per rule 7 in task/mod.rs.
361
+ // Safety:
362
+ // If JOIN_INTEREST got unset since setting COMPLETE we are
363
+ // the only ones to have access to the join waker and need
364
+ // to drop it here because the `JoinHandle` of the task
365
+ // already got dropped.
366
+ unsafe {
367
+ self . trailer ( ) . set_waker ( None ) ;
368
+ }
369
+ }
330
370
}
331
371
} ) ) ;
332
372
0 commit comments