File tree 3 files changed +50
-4
lines changed
stdlib/public/Concurrency
3 files changed +50
-4
lines changed Original file line number Diff line number Diff line change @@ -829,11 +829,12 @@ struct AsyncTask::PrivateStorage {
829
829
// result of the task
830
830
auto oldStatus = task->_private ()._status ().load (std::memory_order_relaxed);
831
831
while (true ) {
832
- // Task is completing
833
832
assert (oldStatus.getInnermostRecord () == NULL &&
834
833
" 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.
837
838
838
839
assert (oldStatus.isRunning ());
839
840
Original file line number Diff line number Diff line change @@ -67,7 +67,19 @@ static void withStatusRecordLock(
67
67
// see that the is-locked bit is now set, and then wait for the lock.
68
68
task->_private ().statusLock .lock ();
69
69
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
+ }
71
83
72
84
// If it's already locked then this thread is the thread that locked it, and
73
85
// 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