@@ -366,8 +366,9 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
366
366
// /
367
367
// / \param bodyError error thrown by the body of a with...TaskGroup method
368
368
// / \param waitingTask the task waiting on the group
369
+ // / \param rawContext used to resume the waiting task
369
370
// / \return how the waiting task should be handled, e.g. must wait or can be completed immediately
370
- PollResult waitAll (SwiftError* bodyError, AsyncTask *waitingTask);
371
+ PollResult waitAll (SwiftError* bodyError, AsyncTask *waitingTask, AsyncContext* rawContext );
371
372
372
373
// Enqueue the completed task onto ready queue if there are no waiting tasks yet
373
374
virtual void enqueueCompletedTask (AsyncTask *completedTask, bool hadErrorResult) = 0;
@@ -378,6 +379,7 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
378
379
// ==== Status manipulation -------------------------------------------------
379
380
380
381
TaskGroupStatus statusLoadRelaxed () const ;
382
+ TaskGroupStatus statusLoadAcquire () const ;
381
383
382
384
std::string statusString () const ;
383
385
@@ -409,6 +411,10 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
409
411
// / Remove waiting status bit.
410
412
TaskGroupStatus statusRemoveWaitingRelease ();
411
413
414
+ // / Mark the waiting status bit.
415
+ // / A waiting task MUST have been already enqueued in the `waitQueue`.
416
+ TaskGroupStatus statusMarkWaitingAssumeRelease ();
417
+
412
418
// / Cancels the group and returns true if was already cancelled before.
413
419
// / After this function returns, the group is guaranteed to be cancelled.
414
420
// /
@@ -521,7 +527,7 @@ struct TaskGroupStatus {
521
527
// / TaskGroupStatus{ C:{cancelled} W:{waiting task} R:{ready tasks} P:{pending tasks} {binary repr} }
522
528
// / If discarding results:
523
529
// / TaskGroupStatus{ C:{cancelled} W:{waiting task} P:{pending tasks} {binary repr} }
524
- std::string to_string (const TaskGroupBase* _Nonnull group) {
530
+ std::string to_string (const TaskGroupBase* group) {
525
531
std::string str;
526
532
str.append (" TaskGroupStatus{ " );
527
533
str.append (" C:" ); // cancelled
@@ -548,7 +554,7 @@ struct TaskGroupStatus {
548
554
bool TaskGroupBase::statusCompletePendingReadyWaiting (TaskGroupStatus &old) {
549
555
return status.compare_exchange_strong (
550
556
old.status , old.completingPendingReadyWaiting (this ).status ,
551
- /* success*/ std::memory_order_relaxed ,
557
+ /* success*/ std::memory_order_release ,
552
558
/* failure*/ std::memory_order_relaxed);
553
559
}
554
560
@@ -561,6 +567,10 @@ TaskGroupStatus TaskGroupBase::statusLoadRelaxed() const {
561
567
return TaskGroupStatus{status.load (std::memory_order_relaxed)};
562
568
}
563
569
570
+ TaskGroupStatus TaskGroupBase::statusLoadAcquire () const {
571
+ return TaskGroupStatus{status.load (std::memory_order_acquire)};
572
+ }
573
+
564
574
std::string TaskGroupBase::statusString () const {
565
575
return statusLoadRelaxed ().to_string (this );
566
576
}
@@ -580,6 +590,12 @@ TaskGroupStatus TaskGroupBase::statusMarkWaitingAssumeAcquire() {
580
590
return TaskGroupStatus{old | TaskGroupStatus::waiting};
581
591
}
582
592
593
+ TaskGroupStatus TaskGroupBase::statusMarkWaitingAssumeRelease () {
594
+ auto old = status.fetch_or (TaskGroupStatus::waiting,
595
+ std::memory_order_release);
596
+ return TaskGroupStatus{old | TaskGroupStatus::waiting};
597
+ }
598
+
583
599
TaskGroupStatus TaskGroupBase::statusRemoveWaitingRelease () {
584
600
auto old = status.fetch_and (~TaskGroupStatus::waiting,
585
601
std::memory_order_release);
@@ -709,18 +725,6 @@ class DiscardingTaskGroup: public TaskGroupBase {
709
725
return true ;
710
726
}
711
727
712
- // / Returns *assumed* new status, including the just performed +1.
713
- TaskGroupStatus statusMarkWaitingAssumeAcquire () {
714
- auto old = status.fetch_or (TaskGroupStatus::waiting, std::memory_order_acquire);
715
- return TaskGroupStatus{old | TaskGroupStatus::waiting};
716
- }
717
-
718
- TaskGroupStatus statusRemoveWaitingRelease () {
719
- auto old = status.fetch_and (~TaskGroupStatus::waiting,
720
- std::memory_order_release);
721
- return TaskGroupStatus{old};
722
- }
723
-
724
728
// / Returns *assumed* new status.
725
729
TaskGroupStatus statusAddReadyAssumeAcquire (const DiscardingTaskGroup *group) {
726
730
assert (group->isDiscardingResults ());
@@ -1152,7 +1156,7 @@ void AccumulatingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *contex
1152
1156
hadErrorResult = true ;
1153
1157
}
1154
1158
1155
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " ready: %d, pending: %u " ,
1159
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " ready: %d, pending: %llu " ,
1156
1160
assumed.readyTasks (this ), assumed.pendingTasks (this ));
1157
1161
1158
1162
// ==== a) has waiting task, so let us complete it right away
@@ -1205,13 +1209,14 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1205
1209
1206
1210
// / If we're the last task we've been waiting for, and there is a waiting task on the group
1207
1211
bool lastPendingTaskAndWaitingTask =
1208
- assumed.pendingTasks (this ) == 1 && assumed.hasWaitingTask ();
1212
+ assumed.pendingTasks (this ) == 1 &&
1213
+ assumed.hasWaitingTask ();
1209
1214
1210
1215
// Immediately decrement the pending count.
1211
1216
// We can do this, since in this mode there is no ready count to keep track of,
1212
1217
// and we immediately discard the result.
1213
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " discard result, hadError:%d, was pending:%llu" ,
1214
- hadErrorResult, assumed.pendingTasks (this ));
1218
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " discard result, hadError:%d, was pending:%llu, status = %s " ,
1219
+ hadErrorResult, assumed.pendingTasks (this ), assumed. to_string ( this ). c_str () );
1215
1220
// If this was the last pending task, and there is a waiting task (from waitAll),
1216
1221
// we must resume the task; but not otherwise. There cannot be any waiters on next()
1217
1222
// while we're discarding results.
@@ -1301,6 +1306,8 @@ void TaskGroupBase::resumeWaitingTask(
1301
1306
if (statusCompletePendingReadyWaiting (assumed)) {
1302
1307
// Run the task.
1303
1308
auto result = PollResult::get (completedTask, hadErrorResult);
1309
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " resume waiting DONE, task = %p, complete with = %p, status = %s" ,
1310
+ waitingTask, completedTask, statusString ().c_str ());
1304
1311
1305
1312
// Remove the child from the task group's running tasks list.
1306
1313
// The parent task isn't currently running (we're about to wake
@@ -1652,11 +1659,9 @@ static void swift_taskGroup_waitAllImpl(
1652
1659
ThrowingTaskFutureWaitContinuationFunction *resumeFunction,
1653
1660
AsyncContext *rawContext) {
1654
1661
auto waitingTask = swift_task_getCurrent ();
1655
- waitingTask->ResumeTask = task_group_wait_resume_adapter;
1656
- waitingTask->ResumeContext = rawContext;
1657
1662
1658
1663
auto group = asBaseImpl (_group);
1659
- PollResult polled = group->waitAll (bodyError, waitingTask);
1664
+ PollResult polled = group->waitAll (bodyError, waitingTask, rawContext );
1660
1665
1661
1666
auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
1662
1667
context->ResumeParent =
@@ -1669,19 +1674,17 @@ static void swift_taskGroup_waitAllImpl(
1669
1674
waitingTask, bodyError, group->statusString ().c_str (), to_string (polled.status ).c_str ());
1670
1675
1671
1676
switch (polled.status ) {
1672
- case PollStatus::MustWait:
1673
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1674
- waitingTask);
1677
+ case PollStatus::MustWait: {
1675
1678
// The waiting task has been queued on the channel,
1676
1679
// there were pending tasks so it will be woken up eventually.
1677
1680
#ifdef __ARM_ARCH_7K__
1678
- return workaround_function_swift_taskGroup_waitAllImpl (
1681
+ workaround_function_swift_taskGroup_waitAllImpl (
1679
1682
resultPointer, callerContext, _group, bodyError, resumeFunction, rawContext);
1680
- #else /* __ARM_ARCH_7K__ */
1681
- return ;
1682
1683
#endif /* __ARM_ARCH_7K__ */
1684
+ return ;
1685
+ }
1683
1686
1684
- case PollStatus::Error:
1687
+ case PollStatus::Error: {
1685
1688
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error, waiting task = %p, body error = %p, status:%s" ,
1686
1689
waitingTask, bodyError, group->statusString ().c_str ());
1687
1690
#if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
@@ -1702,9 +1705,10 @@ static void swift_taskGroup_waitAllImpl(
1702
1705
}
1703
1706
1704
1707
return waitingTask->runInFullyEstablishedContext ();
1708
+ }
1705
1709
1706
1710
case PollStatus::Empty:
1707
- case PollStatus::Success:
1711
+ case PollStatus::Success: {
1708
1712
// / Anything else than a "MustWait" can be treated as a successful poll.
1709
1713
// / Only if there are in flight pending tasks do we need to wait after all.
1710
1714
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
@@ -1719,14 +1723,17 @@ static void swift_taskGroup_waitAllImpl(
1719
1723
}
1720
1724
1721
1725
return waitingTask->runInFullyEstablishedContext ();
1726
+ }
1722
1727
}
1723
1728
}
1724
1729
1725
- // / Must be called while holding the `taskGroup.lock`!
1726
- // / This is because the discarding task group still has some follow-up operations that must
1727
- // / be performed atomically after this operation sometimes, so we cannot unlock inside `waitAll` itself.
1728
- PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask) {
1729
- lock (); // TODO: remove group lock, and use status for synchronization
1730
+ PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask, AsyncContext *rawContext) {
1731
+ lock ();
1732
+
1733
+ // must mutate the waiting task while holding the group lock,
1734
+ // so we don't get an offer concurrently trying to do so
1735
+ waitingTask->ResumeTask = task_group_wait_resume_adapter;
1736
+ waitingTask->ResumeContext = rawContext;
1730
1737
1731
1738
SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, bodyError = %p, status = %s" , bodyError, statusString ().c_str ());
1732
1739
PollResult result = PollResult::getEmpty (this ->successType );
@@ -1739,7 +1746,11 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1739
1746
bool haveRunOneChildTaskInline = false ;
1740
1747
1741
1748
reevaluate_if_TaskGroup_has_results:;
1742
- auto assumed = statusMarkWaitingAssumeAcquire ();
1749
+ // Paired with a release when marking Waiting,
1750
+ // otherwise we don't modify the status
1751
+ auto assumed = statusLoadAcquire ();
1752
+
1753
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, status = %s" , assumed.to_string (this ).c_str ());
1743
1754
1744
1755
// ==== 1) may be able to bail out early if no tasks are pending -------------
1745
1756
if (assumed.isEmpty (this )) {
@@ -1757,7 +1768,6 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1757
1768
result.status = PollStatus::Error;
1758
1769
}
1759
1770
} // else, we're definitely Empty
1760
-
1761
1771
unlock ();
1762
1772
return result;
1763
1773
}
@@ -1766,7 +1776,6 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1766
1776
// No tasks in flight, we know no tasks were submitted before this poll
1767
1777
// was issued, and if we parked here we'd potentially never be woken up.
1768
1778
// Bail out and return `nil` from `group.next()`.
1769
- statusRemoveWaitingRelease ();
1770
1779
unlock ();
1771
1780
return result;
1772
1781
}
@@ -1794,7 +1803,9 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1794
1803
waitHead, waitingTask,
1795
1804
/* success*/ std::memory_order_release,
1796
1805
/* failure*/ std::memory_order_acquire)) {
1797
- unlock (); // TODO: remove fragment lock, and use status for synchronization
1806
+ statusMarkWaitingAssumeRelease ();
1807
+ unlock ();
1808
+
1798
1809
#if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
1799
1810
// The logic here is paired with the logic in TaskGroupBase::offer. Once
1800
1811
// we run the
0 commit comments