@@ -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 ());
@@ -748,6 +751,8 @@ class DiscardingTaskGroup: public TaskGroupBase {
748
751
s = TaskGroupStatus{o - TaskGroupStatus::onePendingTask};
749
752
}
750
753
754
+ // TODO: guard overflow
755
+
751
756
return s;
752
757
}
753
758
@@ -1203,8 +1208,8 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1203
1208
// Immediately decrement the pending count.
1204
1209
// We can do this, since in this mode there is no ready count to keep track of,
1205
1210
// 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 ));
1211
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " discard result, hadError:%d, was pending:%llu, status = %s " ,
1212
+ hadErrorResult, assumed.pendingTasks (this ), assumed. to_string ( this ). c_str () );
1208
1213
// If this was the last pending task, and there is a waiting task (from waitAll),
1209
1214
// we must resume the task; but not otherwise. There cannot be any waiters on next()
1210
1215
// while we're discarding results.
@@ -1662,9 +1667,12 @@ static void swift_taskGroup_waitAllImpl(
1662
1667
waitingTask, bodyError, group->statusString ().c_str (), to_string (polled.status ).c_str ());
1663
1668
1664
1669
switch (polled.status ) {
1665
- case PollStatus::MustWait:
1666
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1667
- waitingTask);
1670
+ case PollStatus::MustWait: {
1671
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1672
+ waitingTask);
1673
+
1674
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, status = %s" ,
1675
+ group->statusString ().c_str ());
1668
1676
// The waiting task has been queued on the channel,
1669
1677
// there were pending tasks so it will be woken up eventually.
1670
1678
#ifdef __ARM_ARCH_7K__
@@ -1673,8 +1681,9 @@ static void swift_taskGroup_waitAllImpl(
1673
1681
#else /* __ARM_ARCH_7K__ */
1674
1682
return ;
1675
1683
#endif /* __ARM_ARCH_7K__ */
1684
+ }
1676
1685
1677
- case PollStatus::Error:
1686
+ case PollStatus::Error: {
1678
1687
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error, waiting task = %p, body error = %p, status:%s" ,
1679
1688
waitingTask, bodyError, group->statusString ().c_str ());
1680
1689
#if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
@@ -1695,9 +1704,10 @@ static void swift_taskGroup_waitAllImpl(
1695
1704
}
1696
1705
1697
1706
return waitingTask->runInFullyEstablishedContext ();
1707
+ }
1698
1708
1699
1709
case PollStatus::Empty:
1700
- case PollStatus::Success:
1710
+ case PollStatus::Success: {
1701
1711
// / Anything else than a "MustWait" can be treated as a successful poll.
1702
1712
// / Only if there are in flight pending tasks do we need to wait after all.
1703
1713
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
@@ -1712,12 +1722,11 @@ static void swift_taskGroup_waitAllImpl(
1712
1722
}
1713
1723
1714
1724
return waitingTask->runInFullyEstablishedContext ();
1725
+ }
1715
1726
}
1716
1727
}
1717
1728
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.
1729
+ // / Caller must mark the `waiting` status bit when MustWait is returned.
1721
1730
PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask) {
1722
1731
lock (); // TODO: remove group lock, and use status for synchronization
1723
1732
@@ -1732,7 +1741,13 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1732
1741
bool haveRunOneChildTaskInline = false ;
1733
1742
1734
1743
reevaluate_if_TaskGroup_has_results:;
1735
- auto assumed = statusMarkWaitingAssumeAcquire ();
1744
+ // auto assumed = statusMarkWaitingAssumeAcquire();
1745
+ // Paired with a release when marking Waiting,
1746
+ // otherwise we don't modify the status
1747
+ auto assumed = statusLoadAcquire ();
1748
+
1749
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, LOAD STATUS, status = %s" ,
1750
+ assumed.to_string (this ).c_str ());
1736
1751
1737
1752
// ==== 1) may be able to bail out early if no tasks are pending -------------
1738
1753
if (assumed.isEmpty (this )) {
@@ -1759,7 +1774,6 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1759
1774
// No tasks in flight, we know no tasks were submitted before this poll
1760
1775
// was issued, and if we parked here we'd potentially never be woken up.
1761
1776
// Bail out and return `nil` from `group.next()`.
1762
- statusRemoveWaitingRelease ();
1763
1777
unlock ();
1764
1778
return result;
1765
1779
}
@@ -1787,6 +1801,7 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1787
1801
waitHead, waitingTask,
1788
1802
/* success*/ std::memory_order_release,
1789
1803
/* failure*/ std::memory_order_acquire)) {
1804
+ statusMarkWaitingAssumeRelease ();
1790
1805
unlock (); // TODO: remove fragment lock, and use status for synchronization
1791
1806
#if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
1792
1807
// The logic here is paired with the logic in TaskGroupBase::offer. Once
0 commit comments