51
51
using namespace swift ;
52
52
53
53
#if 0
54
- #define SWIFT_TASK_GROUP_DEBUG_LOG(group, fmt, ...) \
54
+ #define SWIFT_TASK_GROUP_DEBUG_LOG(group, fmt, ...) \
55
55
fprintf(stderr, "[%#lx] [%s:%d][group(%p%s)] (%s) " fmt "\n", \
56
56
(unsigned long)Thread::current().platformThreadId(), \
57
57
__FILE__, __LINE__, \
58
58
group, group->isDiscardingResults() ? ",discardResults" : "", \
59
59
__FUNCTION__, \
60
60
__VA_ARGS__)
61
+
62
+ #define SWIFT_TASK_GROUP_DEBUG_LOG_0(group, fmt, ...) \
63
+ fprintf(stderr, "[%#lx] [%s:%d][group(%p)] (%s) " fmt "\n", \
64
+ (unsigned long)Thread::current().platformThreadId(), \
65
+ __FILE__, __LINE__, \
66
+ group, \
67
+ __FUNCTION__, \
68
+ __VA_ARGS__)
61
69
#else
62
70
#define SWIFT_TASK_GROUP_DEBUG_LOG (group, fmt, ...) (void )0
71
+ #define SWIFT_TASK_GROUP_DEBUG_LOG_0 (group, fmt, ...) (void )0
63
72
#endif
64
73
65
74
using FutureFragment = AsyncTask::FutureFragment;
@@ -354,7 +363,11 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
354
363
// / There can be only at-most-one waiting task on a group at any given time,
355
364
// / and the waiting task is expected to be the parent task in which the group
356
365
// / body is running.
357
- PollResult waitAll (AsyncTask *waitingTask);
366
+ // /
367
+ // / \param bodyError error thrown by the body of a with...TaskGroup method
368
+ // / \param waitingTask the task waiting on the group
369
+ // / \return how the waiting task should be handled, e.g. must wait or can be completed immediately
370
+ PollResult waitAll (SwiftError* bodyError, AsyncTask *waitingTask);
358
371
359
372
// Enqueue the completed task onto ready queue if there are no waiting tasks yet
360
373
virtual void enqueueCompletedTask (AsyncTask *completedTask, bool hadErrorResult) = 0;
@@ -411,6 +424,16 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
411
424
virtual TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) = 0;
412
425
};
413
426
427
+ [[maybe_unused]]
428
+ static std::string to_string (TaskGroupBase::PollStatus status) {
429
+ switch (status) {
430
+ case TaskGroupBase::PollStatus::Empty: return " Empty" ;
431
+ case TaskGroupBase::PollStatus::MustWait: return " MustWait" ;
432
+ case TaskGroupBase::PollStatus::Success: return " Success" ;
433
+ case TaskGroupBase::PollStatus::Error: return " Error" ;
434
+ }
435
+ }
436
+
414
437
// / The status of a task group.
415
438
// /
416
439
// / Its exact structure depends on the type of group, and therefore a group must be passed to operations
@@ -623,7 +646,7 @@ class AccumulatingTaskGroup: public TaskGroupBase {
623
646
// / so unconditionally.
624
647
// /
625
648
// / Returns *assumed* new status, including the just performed +1.
626
- TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) {
649
+ TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) override {
627
650
auto old = status.fetch_add (TaskGroupStatus::onePendingTask,
628
651
std::memory_order_relaxed);
629
652
auto s = TaskGroupStatus{old + TaskGroupStatus::onePendingTask};
@@ -720,7 +743,7 @@ class DiscardingTaskGroup: public TaskGroupBase {
720
743
// / so unconditionally.
721
744
// /
722
745
// / Returns *assumed* new status, including the just performed +1.
723
- TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) {
746
+ TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) override {
724
747
auto old = status.fetch_add (TaskGroupStatus::onePendingTask,
725
748
std::memory_order_relaxed);
726
749
auto s = TaskGroupStatus{old + TaskGroupStatus::onePendingTask};
@@ -779,8 +802,6 @@ class DiscardingTaskGroup: public TaskGroupBase {
779
802
// / and the waitingTask eventually be woken up by a completion.
780
803
PollResult poll (AsyncTask *waitingTask);
781
804
782
- bool offerBodyError (SwiftError* _Nonnull bodyError);
783
-
784
805
private:
785
806
// / Resume waiting task with specified error
786
807
void resumeWaitingTaskWithError (SwiftError *error, TaskGroupStatus &assumed);
@@ -862,8 +883,8 @@ static void swift_taskGroup_initializeWithFlagsImpl(size_t rawGroupFlags,
862
883
TaskGroup *group, const Metadata *T) {
863
884
864
885
TaskGroupFlags groupFlags (rawGroupFlags);
865
- SWIFT_TASK_DEBUG_LOG ( " group(%p) create; flags: isDiscardingResults=%d" ,
866
- group, groupFlags.isDiscardResults ());
886
+ SWIFT_TASK_GROUP_DEBUG_LOG_0 ( group, " create group ; flags: isDiscardingResults=%d" ,
887
+ groupFlags.isDiscardResults ());
867
888
868
889
TaskGroupBase *impl;
869
890
if (groupFlags.isDiscardResults ()) {
@@ -1158,7 +1179,7 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1158
1179
assert (completedTask->hasChildFragment ());
1159
1180
assert (completedTask->hasGroupChildFragment ());
1160
1181
assert (completedTask->groupChildFragment ()->getGroup () == asAbstract (this ));
1161
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, completedTask:%p , status:%s" , completedTask, statusString ().c_str ());
1182
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, completedTask:%p, status:%s" , completedTask, statusString ().c_str ());
1162
1183
1163
1184
// The current ownership convention is that we are *not* given ownership
1164
1185
// of a retain on completedTask; we're called from the task completion
@@ -1224,7 +1245,7 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1224
1245
}
1225
1246
1226
1247
auto afterComplete = statusCompletePendingAssumeRelease ();
1227
- (void )afterComplete; // silence "not used" warning
1248
+ (void ) afterComplete;
1228
1249
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, either more pending tasks, or no waiting task, status:%s" ,
1229
1250
afterComplete.to_string (this ).c_str ());
1230
1251
}
@@ -1635,7 +1656,7 @@ static void swift_taskGroup_waitAllImpl(
1635
1656
waitingTask->ResumeContext = rawContext;
1636
1657
1637
1658
auto group = asBaseImpl (_group);
1638
- PollResult polled = group->waitAll (waitingTask);
1659
+ PollResult polled = group->waitAll (bodyError, waitingTask);
1639
1660
1640
1661
auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
1641
1662
context->ResumeParent =
@@ -1644,23 +1665,13 @@ static void swift_taskGroup_waitAllImpl(
1644
1665
context->errorResult = nullptr ;
1645
1666
context->successResultPointer = resultPointer;
1646
1667
1647
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl, waiting task = %p, bodyError = %p, status:%s" ,
1648
- waitingTask, bodyError, group->statusString ().c_str ());
1668
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl, waiting task = %p, bodyError = %p, status:%s, polled.status = %s " ,
1669
+ waitingTask, bodyError, group->statusString ().c_str (), to_string (polled. status ). c_str () );
1649
1670
1650
1671
switch (polled.status ) {
1651
1672
case PollStatus::MustWait:
1652
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll MustWait, pending tasks exist, waiting task = %p" ,
1673
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1653
1674
waitingTask);
1654
- if (bodyError && group->isDiscardingResults ()) {
1655
- auto discardingGroup = asDiscardingImpl (_group);
1656
- bool storedBodyError = discardingGroup->offerBodyError (bodyError);
1657
- if (storedBodyError) {
1658
- SWIFT_TASK_GROUP_DEBUG_LOG (
1659
- group, " waitAll, stored error thrown by with...Group body, error = %p" ,
1660
- bodyError);
1661
- }
1662
- }
1663
-
1664
1675
// The waiting task has been queued on the channel,
1665
1676
// there were pending tasks so it will be woken up eventually.
1666
1677
#ifdef __ARM_ARCH_7K__
@@ -1671,7 +1682,7 @@ static void swift_taskGroup_waitAllImpl(
1671
1682
#endif /* __ARM_ARCH_7K__ */
1672
1683
1673
1684
case PollStatus::Error:
1674
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll found error , waiting task = %p, body error = %p, status:%s" ,
1685
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error , waiting task = %p, body error = %p, status:%s" ,
1675
1686
waitingTask, bodyError, group->statusString ().c_str ());
1676
1687
#if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1677
1688
if (bodyError) {
@@ -1693,17 +1704,10 @@ static void swift_taskGroup_waitAllImpl(
1693
1704
return waitingTask->runInFullyEstablishedContext ();
1694
1705
1695
1706
case PollStatus::Empty:
1696
- // / Anything else than a "MustWait" can be treated as a successful poll.
1697
- // / Only if there are in flight pending tasks do we need to wait after all.
1698
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll %s, waiting task = %p, status:%s" ,
1699
- polled.status == TaskGroupBase::PollStatus::Empty ? " empty" : " success" ,
1700
- waitingTask, group->statusString ().c_str ());
1701
-
1702
-
1703
1707
case PollStatus::Success:
1704
1708
// / Anything else than a "MustWait" can be treated as a successful poll.
1705
1709
// / Only if there are in flight pending tasks do we need to wait after all.
1706
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll %s, waiting task = %p, status:%s" ,
1710
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
1707
1711
polled.status == TaskGroupBase::PollStatus::Empty ? " empty" : " success" ,
1708
1712
waitingTask, group->statusString ().c_str ());
1709
1713
@@ -1718,26 +1722,13 @@ static void swift_taskGroup_waitAllImpl(
1718
1722
}
1719
1723
}
1720
1724
1721
- bool DiscardingTaskGroup::offerBodyError (SwiftError* _Nonnull bodyError) {
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) {
1722
1729
lock (); // TODO: remove group lock, and use status for synchronization
1723
1730
1724
- if (!readyQueue.isEmpty ()) {
1725
- // already other error stored, discard this one
1726
- unlock ();
1727
- return false ;
1728
- }
1729
-
1730
- auto readyItem = ReadyQueueItem::getRawError (this , bodyError);
1731
- readyQueue.enqueue (readyItem);
1732
- unlock ();
1733
-
1734
- return true ;
1735
- }
1736
-
1737
- PollResult TaskGroupBase::waitAll (AsyncTask *waitingTask) {
1738
- lock (); // TODO: remove group lock, and use status for synchronization
1739
-
1740
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, status = %s" , statusString ().c_str ());
1731
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, bodyError = %p, status = %s" , bodyError, statusString ().c_str ());
1741
1732
PollResult result = PollResult::getEmpty (this ->successType );
1742
1733
result.status = PollStatus::Empty;
1743
1734
result.storage = nullptr ;
@@ -1781,6 +1772,16 @@ PollResult TaskGroupBase::waitAll(AsyncTask *waitingTask) {
1781
1772
}
1782
1773
1783
1774
// ==== 2) Add to wait queue -------------------------------------------------
1775
+
1776
+ // ---- 2.1) Discarding task group may need to story the bodyError before we park
1777
+ if (bodyError && isDiscardingResults ()) {
1778
+ auto discardingGroup = asDiscardingImpl (this );
1779
+ assert (readyQueue.isEmpty () &&
1780
+ " only a single error may be stored in discarding task group, but something was enqueued already" );
1781
+ auto readyItem = ReadyQueueItem::getRawError (discardingGroup, bodyError);
1782
+ readyQueue.enqueue (readyItem);
1783
+ }
1784
+
1784
1785
auto waitHead = waitQueue.load (std::memory_order_acquire);
1785
1786
_swift_tsan_release (static_cast <Job *>(waitingTask));
1786
1787
while (true ) {
0 commit comments