You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Use trylock to eliminate the remaining race condition in Test.cancel().
This PR fixes the race condition in `Test.cancel()` that could occur if an
unstructured task, created from within a test's task, called `Test.cancel()` at
just the right moment. The order of events for the race is:
- Unstructured task is created and inherits task-locals including the reference
to the test's unsafe current task;
- Test's task starts tearing down;
- Unstructured task calls `takeUnsafeCurrentTask()` and gets a reference to the
unsafe current task;
- Test's task finishes tearing down;
- Unstructured task calls `UnsafeCurrentTask.cancel()`.
The fix is to use `trylock` semantics when cancelling the unsafe current task.
If the test's task is still alive, the task is cancelled while the lock is held,
which will block the test's task from being torn down as it has a lock-guarded
call to clear the unsafe current task reference. If the test's task is no longer
alive, the reference is already `nil` by the time the unstructured task acquires
the lock and it bails early. If we recursively call `cancel()` (which can happen
via the concurrency-level cancellation handler), the `trylock` means we won't
acquire the lock a second time, so we won't end up deadlocking or aborting
(which is what prevents calling `cancel()` while holding the lock in the current
implementation).
I hope some part of that made sense.
0 commit comments