File tree Expand file tree Collapse file tree 3 files changed +50
-4
lines changed
stdlib/public/Concurrency Expand file tree Collapse file tree 3 files changed +50
-4
lines changed Original file line number Diff line number Diff line change @@ -829,11 +829,12 @@ struct AsyncTask::PrivateStorage {
829829 // result of the task
830830 auto oldStatus = task->_private ()._status ().load (std::memory_order_relaxed);
831831 while (true ) {
832- // Task is completing
833832 assert (oldStatus.getInnermostRecord () == NULL &&
834833 " Status records should have been removed by this time!" );
835- assert (!oldStatus.isStatusRecordLocked () &&
836- " Task is completing, cannot be locked anymore!" );
834+
835+ // Don't assert !isStatusRecordLocked(). It can be legitimately true here,
836+ // for example if another thread is canceling this task right as it
837+ // completes.
837838
838839 assert (oldStatus.isRunning ());
839840
Original file line number Diff line number Diff line change @@ -67,7 +67,19 @@ static void withStatusRecordLock(
6767 // see that the is-locked bit is now set, and then wait for the lock.
6868 task->_private ().statusLock .lock ();
6969
70- bool alreadyLocked = status.isStatusRecordLocked ();
70+ bool alreadyLocked = false ;
71+
72+ // `status` was loaded before we acquired the lock. If its is-locked bit is
73+ // not set, then we know that this thread doesn't already hold the lock.
74+ // However, if the is-locked bit is set, then we don't know if this thread
75+ // held the lock or another thread did. In that case, we reload the status
76+ // after acquiring the lock. If the reloaded status still has the is-locked
77+ // bit set, then we know it's this thread. If it doesn't, then we know it was
78+ // a different thread.
79+ if (status.isStatusRecordLocked ()) {
80+ status = task->_private ()._status ().load (std::memory_order_relaxed);
81+ alreadyLocked = status.isStatusRecordLocked ();
82+ }
7183
7284 // If it's already locked then this thread is the thread that locked it, and
7385 // we can leave that bit alone here.
Original file line number Diff line number Diff line change 1+ // RUN: %target-run-simple-swift( -target %target-swift-5.1-abi-triple %import-libdispatch)
2+ // REQUIRES: concurrency
3+ // REQUIRES: executable_test
4+
5+ // REQUIRES: concurrency_runtime
6+ // UNSUPPORTED: freestanding
7+
8+ func recurseABunch( _ call: ( ) async throws -> Void , n: Int = 100 ) async throws {
9+ try await withTaskCancellationHandler {
10+ if n == 0 {
11+ try await call ( )
12+ return
13+ }
14+
15+ try await recurseABunch ( call, n: n - 1 )
16+ } onCancel: {
17+ }
18+ }
19+
20+ for _ in 0 ..< 100_000 {
21+ let task : Task < Void , any Error > = Task {
22+ try await Task . sleep ( nanoseconds: UInt64 . random ( in: 0 ..< 1_000 ) )
23+ try await recurseABunch ( ) {
24+ await withTaskCancellationHandler {
25+ } onCancel: {
26+ }
27+ }
28+ }
29+ Task {
30+ try await Task . sleep ( nanoseconds: UInt64 . random ( in: 0 ..< 1_000 ) )
31+ task. cancel ( )
32+ }
33+ }
You can’t perform that action at this time.
0 commit comments