@@ -378,6 +378,7 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
378
378
// ==== Status manipulation -------------------------------------------------
379
379
380
380
TaskGroupStatus statusLoadRelaxed () const ;
381
+ TaskGroupStatus statusLoadAcquire () const ;
381
382
382
383
std::string statusString () const ;
383
384
@@ -409,6 +410,10 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
409
410
// / Remove waiting status bit.
410
411
TaskGroupStatus statusRemoveWaitingRelease ();
411
412
413
+ // / Mark the waiting status bit.
414
+ // / A waiting task MUST have been already enqueued in the `waitQueue`.
415
+ TaskGroupStatus statusMarkWaitingAssumeRelease ();
416
+
412
417
// / Cancels the group and returns true if was already cancelled before.
413
418
// / After this function returns, the group is guaranteed to be cancelled.
414
419
// /
@@ -561,6 +566,10 @@ TaskGroupStatus TaskGroupBase::statusLoadRelaxed() const {
561
566
return TaskGroupStatus{status.load (std::memory_order_relaxed)};
562
567
}
563
568
569
+ TaskGroupStatus TaskGroupBase::statusLoadAcquire () const {
570
+ return TaskGroupStatus{status.load (std::memory_order_acquire)};
571
+ }
572
+
564
573
std::string TaskGroupBase::statusString () const {
565
574
return statusLoadRelaxed ().to_string (this );
566
575
}
@@ -580,6 +589,12 @@ TaskGroupStatus TaskGroupBase::statusMarkWaitingAssumeAcquire() {
580
589
return TaskGroupStatus{old | TaskGroupStatus::waiting};
581
590
}
582
591
592
+ TaskGroupStatus TaskGroupBase::statusMarkWaitingAssumeRelease () {
593
+ auto old = status.fetch_or (TaskGroupStatus::waiting,
594
+ std::memory_order_release);
595
+ return TaskGroupStatus{old | TaskGroupStatus::waiting};
596
+ }
597
+
583
598
TaskGroupStatus TaskGroupBase::statusRemoveWaitingRelease () {
584
599
auto old = status.fetch_and (~TaskGroupStatus::waiting,
585
600
std::memory_order_release);
@@ -702,18 +717,6 @@ class DiscardingTaskGroup: public TaskGroupBase {
702
717
return true ;
703
718
}
704
719
705
- // / Returns *assumed* new status, including the just performed +1.
706
- TaskGroupStatus statusMarkWaitingAssumeAcquire () {
707
- auto old = status.fetch_or (TaskGroupStatus::waiting, std::memory_order_acquire);
708
- return TaskGroupStatus{old | TaskGroupStatus::waiting};
709
- }
710
-
711
- TaskGroupStatus statusRemoveWaitingRelease () {
712
- auto old = status.fetch_and (~TaskGroupStatus::waiting,
713
- std::memory_order_release);
714
- return TaskGroupStatus{old};
715
- }
716
-
717
720
// / Returns *assumed* new status.
718
721
TaskGroupStatus statusAddReadyAssumeAcquire (const DiscardingTaskGroup *group) {
719
722
assert (group->isDiscardingResults ());
@@ -1203,8 +1206,8 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1203
1206
// Immediately decrement the pending count.
1204
1207
// We can do this, since in this mode there is no ready count to keep track of,
1205
1208
// and we immediately discard the result.
1206
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " discard result, hadError:%d, was pending:%llu" ,
1207
- hadErrorResult, assumed.pendingTasks (this ));
1209
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " discard result, hadError:%d, was pending:%llu, status = %s " ,
1210
+ hadErrorResult, assumed.pendingTasks (this ), assumed. to_string ( this ). c_str () );
1208
1211
// If this was the last pending task, and there is a waiting task (from waitAll),
1209
1212
// we must resume the task; but not otherwise. There cannot be any waiters on next()
1210
1213
// while we're discarding results.
@@ -1662,9 +1665,9 @@ static void swift_taskGroup_waitAllImpl(
1662
1665
waitingTask, bodyError, group->statusString ().c_str (), to_string (polled.status ).c_str ());
1663
1666
1664
1667
switch (polled.status ) {
1665
- case PollStatus::MustWait:
1666
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1667
- waitingTask);
1668
+ case PollStatus::MustWait: {
1669
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1670
+ waitingTask);
1668
1671
// The waiting task has been queued on the channel,
1669
1672
// there were pending tasks so it will be woken up eventually.
1670
1673
#ifdef __ARM_ARCH_7K__
@@ -1673,8 +1676,9 @@ static void swift_taskGroup_waitAllImpl(
1673
1676
#else /* __ARM_ARCH_7K__ */
1674
1677
return ;
1675
1678
#endif /* __ARM_ARCH_7K__ */
1679
+ }
1676
1680
1677
- case PollStatus::Error:
1681
+ case PollStatus::Error: {
1678
1682
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error, waiting task = %p, body error = %p, status:%s" ,
1679
1683
waitingTask, bodyError, group->statusString ().c_str ());
1680
1684
#if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
@@ -1695,9 +1699,10 @@ static void swift_taskGroup_waitAllImpl(
1695
1699
}
1696
1700
1697
1701
return waitingTask->runInFullyEstablishedContext ();
1702
+ }
1698
1703
1699
1704
case PollStatus::Empty:
1700
- case PollStatus::Success:
1705
+ case PollStatus::Success: {
1701
1706
// / Anything else than a "MustWait" can be treated as a successful poll.
1702
1707
// / Only if there are in flight pending tasks do we need to wait after all.
1703
1708
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
@@ -1712,12 +1717,11 @@ static void swift_taskGroup_waitAllImpl(
1712
1717
}
1713
1718
1714
1719
return waitingTask->runInFullyEstablishedContext ();
1720
+ }
1715
1721
}
1716
1722
}
1717
1723
1718
- // / Must be called while holding the `taskGroup.lock`!
1719
- // / This is because the discarding task group still has some follow-up operations that must
1720
- // / be performed atomically after this operation sometimes, so we cannot unlock inside `waitAll` itself.
1724
+ // / Caller must mark the `waiting` status bit when MustWait is returned.
1721
1725
PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask) {
1722
1726
lock (); // TODO: remove group lock, and use status for synchronization
1723
1727
@@ -1732,7 +1736,12 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1732
1736
bool haveRunOneChildTaskInline = false ;
1733
1737
1734
1738
reevaluate_if_TaskGroup_has_results:;
1735
- auto assumed = statusMarkWaitingAssumeAcquire ();
1739
+ // Paired with a release when marking Waiting,
1740
+ // otherwise we don't modify the status
1741
+ auto assumed = statusLoadAcquire ();
1742
+
1743
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, LOAD STATUS, status = %s" ,
1744
+ assumed.to_string (this ).c_str ());
1736
1745
1737
1746
// ==== 1) may be able to bail out early if no tasks are pending -------------
1738
1747
if (assumed.isEmpty (this )) {
@@ -1759,7 +1768,6 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1759
1768
// No tasks in flight, we know no tasks were submitted before this poll
1760
1769
// was issued, and if we parked here we'd potentially never be woken up.
1761
1770
// Bail out and return `nil` from `group.next()`.
1762
- statusRemoveWaitingRelease ();
1763
1771
unlock ();
1764
1772
return result;
1765
1773
}
@@ -1787,6 +1795,7 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1787
1795
waitHead, waitingTask,
1788
1796
/* success*/ std::memory_order_release,
1789
1797
/* failure*/ std::memory_order_acquire)) {
1798
+ statusMarkWaitingAssumeRelease ();
1790
1799
unlock (); // TODO: remove fragment lock, and use status for synchronization
1791
1800
#if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
1792
1801
// The logic here is paired with the logic in TaskGroupBase::offer. Once
0 commit comments