@@ -273,7 +273,6 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
273
273
}
274
274
};
275
275
276
- protected:
277
276
#if SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY || SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
278
277
// Synchronization is simple here. In a single threaded mode, all swift tasks
279
278
// run on a single thread so no coordination is needed. In a task-to-thread
@@ -294,6 +293,7 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
294
293
void unlock () const { mutex_.unlock (); }
295
294
#endif
296
295
296
+ protected:
297
297
// / Used for queue management, counting number of waiting and ready tasks
298
298
std::atomic<uint64_t > status;
299
299
@@ -351,6 +351,8 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
351
351
352
352
// / Attempt to park the `waitingTask` in the waiting queue.
353
353
// /
354
+ // / Must be called while holding `group.lock`.
355
+ // /
354
356
// / If unable to complete the waiting task immediately (with an readily
355
357
// / available completed task), either returns an `PollStatus::Empty`
356
358
// / result if it is known that there are no pending tasks in the group,
@@ -526,7 +528,7 @@ struct TaskGroupStatus {
526
528
// / TaskGroupStatus{ C:{cancelled} W:{waiting task} R:{ready tasks} P:{pending tasks} {binary repr} }
527
529
// / If discarding results:
528
530
// / TaskGroupStatus{ C:{cancelled} W:{waiting task} P:{pending tasks} {binary repr} }
529
- std::string to_string (const TaskGroupBase* _Nonnull group) {
531
+ std::string to_string (const TaskGroupBase* group) {
530
532
std::string str;
531
533
str.append (" TaskGroupStatus{ " );
532
534
str.append (" C:" ); // cancelled
@@ -553,7 +555,7 @@ struct TaskGroupStatus {
553
555
bool TaskGroupBase::statusCompletePendingReadyWaiting (TaskGroupStatus &old) {
554
556
return status.compare_exchange_strong (
555
557
old.status , old.completingPendingReadyWaiting (this ).status ,
556
- /* success*/ std::memory_order_relaxed ,
558
+ /* success*/ std::memory_order_release ,
557
559
/* failure*/ std::memory_order_relaxed);
558
560
}
559
561
@@ -1148,7 +1150,7 @@ void AccumulatingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *contex
1148
1150
hadErrorResult = true ;
1149
1151
}
1150
1152
1151
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " ready: %d, pending: %u " ,
1153
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " ready: %d, pending: %llu " ,
1152
1154
assumed.readyTasks (this ), assumed.pendingTasks (this ));
1153
1155
1154
1156
// ==== a) has waiting task, so let us complete it right away
@@ -1201,7 +1203,8 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1201
1203
1202
1204
// / If we're the last task we've been waiting for, and there is a waiting task on the group
1203
1205
bool lastPendingTaskAndWaitingTask =
1204
- assumed.pendingTasks (this ) == 1 && assumed.hasWaitingTask ();
1206
+ assumed.pendingTasks (this ) == 1 &&
1207
+ assumed.hasWaitingTask ();
1205
1208
1206
1209
// Immediately decrement the pending count.
1207
1210
// We can do this, since in this mode there is no ready count to keep track of,
@@ -1297,6 +1300,8 @@ void TaskGroupBase::resumeWaitingTask(
1297
1300
if (statusCompletePendingReadyWaiting (assumed)) {
1298
1301
// Run the task.
1299
1302
auto result = PollResult::get (completedTask, hadErrorResult);
1303
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " resume waiting DONE, task = %p, complete with = %p, status = %s" ,
1304
+ waitingTask, completedTask, statusString ().c_str ());
1300
1305
1301
1306
// Remove the child from the task group's running tasks list.
1302
1307
// The parent task isn't currently running (we're about to wake
@@ -1652,6 +1657,7 @@ static void swift_taskGroup_waitAllImpl(
1652
1657
waitingTask->ResumeContext = rawContext;
1653
1658
1654
1659
auto group = asBaseImpl (_group);
1660
+ group->lock ();
1655
1661
PollResult polled = group->waitAll (bodyError, waitingTask);
1656
1662
1657
1663
auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
@@ -1666,8 +1672,8 @@ static void swift_taskGroup_waitAllImpl(
1666
1672
1667
1673
switch (polled.status ) {
1668
1674
case PollStatus::MustWait: {
1669
- SWIFT_TASK_GROUP_DEBUG_LOG ( group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p " ,
1670
- waitingTask);
1675
+ group-> unlock ();
1676
+
1671
1677
// The waiting task has been queued on the channel,
1672
1678
// there were pending tasks so it will be woken up eventually.
1673
1679
#ifdef __ARM_ARCH_7K__
@@ -1679,6 +1685,8 @@ static void swift_taskGroup_waitAllImpl(
1679
1685
}
1680
1686
1681
1687
case PollStatus::Error: {
1688
+ group->unlock ();
1689
+
1682
1690
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error, waiting task = %p, body error = %p, status:%s" ,
1683
1691
waitingTask, bodyError, group->statusString ().c_str ());
1684
1692
#if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
@@ -1703,6 +1711,8 @@ static void swift_taskGroup_waitAllImpl(
1703
1711
1704
1712
case PollStatus::Empty:
1705
1713
case PollStatus::Success: {
1714
+ group->unlock ();
1715
+
1706
1716
// / Anything else than a "MustWait" can be treated as a successful poll.
1707
1717
// / Only if there are in flight pending tasks do we need to wait after all.
1708
1718
SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
@@ -1721,9 +1731,8 @@ static void swift_taskGroup_waitAllImpl(
1721
1731
}
1722
1732
}
1723
1733
1734
+ // / Must be called while holding `group.lock`.
1724
1735
PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask) {
1725
- lock (); // TODO: remove group lock, and use status for synchronization
1726
-
1727
1736
SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, bodyError = %p, status = %s" , bodyError, statusString ().c_str ());
1728
1737
PollResult result = PollResult::getEmpty (this ->successType );
1729
1738
result.status = PollStatus::Empty;
@@ -1739,8 +1748,7 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1739
1748
// otherwise we don't modify the status
1740
1749
auto assumed = statusLoadAcquire ();
1741
1750
1742
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, LOAD STATUS, status = %s" ,
1743
- assumed.to_string (this ).c_str ());
1751
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, status = %s" , assumed.to_string (this ).c_str ());
1744
1752
1745
1753
// ==== 1) may be able to bail out early if no tasks are pending -------------
1746
1754
if (assumed.isEmpty (this )) {
@@ -1758,16 +1766,13 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1758
1766
result.status = PollStatus::Error;
1759
1767
}
1760
1768
} // else, we're definitely Empty
1761
-
1762
- unlock ();
1763
1769
return result;
1764
1770
}
1765
1771
1766
1772
SWIFT_TASK_GROUP_DEBUG_LOG (this , " group is empty, no pending tasks, status = %s" , assumed.to_string (this ).c_str ());
1767
1773
// No tasks in flight, we know no tasks were submitted before this poll
1768
1774
// was issued, and if we parked here we'd potentially never be woken up.
1769
1775
// Bail out and return `nil` from `group.next()`.
1770
- unlock ();
1771
1776
return result;
1772
1777
}
1773
1778
@@ -1795,7 +1800,6 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask)
1795
1800
/* success*/ std::memory_order_release,
1796
1801
/* failure*/ std::memory_order_acquire)) {
1797
1802
statusMarkWaitingAssumeRelease ();
1798
- unlock (); // TODO: remove fragment lock, and use status for synchronization
1799
1803
#if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
1800
1804
// The logic here is paired with the logic in TaskGroupBase::offer. Once
1801
1805
// we run the
0 commit comments