@@ -280,6 +280,12 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus {
280
280
// / actor. This bit is cleared when a starts running on a thread, suspends
281
281
// / or is completed.
282
282
IsEnqueued = 0x1000 ,
283
+
284
+ #ifndef NDEBUG
285
+ // / Task has been completed. This is purely used to enable an assertion
286
+ // / that the task is completed when we destroy it.
287
+ IsComplete = 0x2000 ,
288
+ #endif
283
289
};
284
290
285
291
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
@@ -393,6 +399,20 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus {
393
399
#endif
394
400
}
395
401
402
+ #ifndef NDEBUG
403
+ bool isComplete () const {
404
+ return Flags & IsComplete;
405
+ }
406
+
407
+ ActiveTaskStatus withComplete () const {
408
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
409
+ return ActiveTaskStatus (Record, Flags | IsComplete, ExecutionLock);
410
+ #else
411
+ return ActiveTaskStatus (Record, Flags | IsComplete);
412
+ #endif
413
+ }
414
+ #endif
415
+
396
416
// / Is there a lock on the linked list of status records?
397
417
bool isStatusRecordLocked () const { return Flags & IsStatusRecordLocked; }
398
418
ActiveTaskStatus withLockingRecord (TaskStatusRecord *lockRecord) const {
@@ -564,6 +584,9 @@ struct AsyncTask::PrivateStorage {
564
584
auto newStatus = oldStatus.withRunning (false );
565
585
newStatus = newStatus.withoutStoredPriorityEscalation ();
566
586
newStatus = newStatus.withoutEnqueued ();
587
+ #ifndef NDEBUG
588
+ newStatus = newStatus.withComplete ();
589
+ #endif
567
590
568
591
// This can fail since the task can still get concurrently cancelled or
569
592
// escalated.
@@ -650,6 +673,7 @@ retry:;
650
673
while (true ) {
651
674
// We can get here from being suspended or being enqueued
652
675
assert (!oldStatus.isRunning ());
676
+ assert (!oldStatus.isComplete ());
653
677
654
678
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
655
679
// Task's priority is greater than the thread's - do a self escalation
0 commit comments