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
@@ -619,7 +642,7 @@ class AccumulatingTaskGroup: public TaskGroupBase {
619
642
// / so unconditionally.
620
643
// /
621
644
// / Returns *assumed* new status, including the just performed +1.
622
- TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) {
645
+ TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) override {
623
646
auto old = status.fetch_add (TaskGroupStatus::onePendingTask,
624
647
std::memory_order_relaxed);
625
648
auto s = TaskGroupStatus{old + TaskGroupStatus::onePendingTask};
@@ -713,7 +736,7 @@ class DiscardingTaskGroup: public TaskGroupBase {
713
736
// / so unconditionally.
714
737
// /
715
738
// / Returns *assumed* new status, including the just performed +1.
716
- TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) {
739
+ TaskGroupStatus statusAddPendingTaskRelaxed (bool unconditionally) override {
717
740
auto old = status.fetch_add (TaskGroupStatus::onePendingTask,
718
741
std::memory_order_relaxed);
719
742
auto s = TaskGroupStatus{old + TaskGroupStatus::onePendingTask};
@@ -772,8 +795,6 @@ class DiscardingTaskGroup: public TaskGroupBase {
772
795
// / and the waitingTask eventually be woken up by a completion.
773
796
PollResult poll (AsyncTask *waitingTask);
774
797
775
- bool offerBodyError (SwiftError* _Nonnull bodyError);
776
-
777
798
private:
778
799
// / Resume waiting task with specified error
779
800
void resumeWaitingTaskWithError (SwiftError *error, TaskGroupStatus &assumed);
@@ -855,8 +876,8 @@ static void swift_taskGroup_initializeWithFlagsImpl(size_t rawGroupFlags,
855
876
TaskGroup *group, const Metadata *T) {
856
877
857
878
TaskGroupFlags groupFlags (rawGroupFlags);
858
- SWIFT_TASK_DEBUG_LOG ( " group(%p) create; flags: isDiscardingResults=%d" ,
859
- group, groupFlags.isDiscardResults ());
879
+ SWIFT_TASK_GROUP_DEBUG_LOG_0 ( group, " create group ; flags: isDiscardingResults=%d" ,
880
+ groupFlags.isDiscardResults ());
860
881
861
882
TaskGroupBase *impl;
862
883
if (groupFlags.isDiscardResults ()) {
@@ -1151,7 +1172,7 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1151
1172
assert (completedTask->hasChildFragment ());
1152
1173
assert (completedTask->hasGroupChildFragment ());
1153
1174
assert (completedTask->groupChildFragment ()->getGroup () == asAbstract (this ));
1154
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, completedTask:%p , status:%s" , completedTask, statusString ().c_str ());
1175
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, completedTask:%p, status:%s" , completedTask, statusString ().c_str ());
1155
1176
1156
1177
// The current ownership convention is that we are *not* given ownership
1157
1178
// of a retain on completedTask; we're called from the task completion
@@ -1217,7 +1238,7 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1217
1238
}
1218
1239
1219
1240
auto afterComplete = statusCompletePendingAssumeRelease ();
1220
- (void )afterComplete; // silence "not used" warning
1241
+ (void ) afterComplete;
1221
1242
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, either more pending tasks, or no waiting task, status:%s" ,
1222
1243
afterComplete.to_string (this ).c_str ());
1223
1244
}
@@ -1628,7 +1649,7 @@ static void swift_taskGroup_waitAllImpl(
1628
1649
waitingTask->ResumeContext = rawContext;
1629
1650
1630
1651
auto group = asBaseImpl (_group);
1631
- PollResult polled = group->waitAll (waitingTask);
1652
+ PollResult polled = group->waitAll (bodyError, waitingTask);
1632
1653
1633
1654
auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
1634
1655
context->ResumeParent =
@@ -1637,23 +1658,13 @@ static void swift_taskGroup_waitAllImpl(
1637
1658
context->errorResult = nullptr ;
1638
1659
context->successResultPointer = resultPointer;
1639
1660
1640
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl, waiting task = %p, bodyError = %p, status:%s" ,
1641
- waitingTask, bodyError, group->statusString ().c_str ());
1661
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl, waiting task = %p, bodyError = %p, status:%s, polled.status = %s " ,
1662
+ waitingTask, bodyError, group->statusString ().c_str (), to_string (polled. status ). c_str () );
1642
1663
1643
1664
switch (polled.status ) {
1644
1665
case PollStatus::MustWait:
1645
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll MustWait, pending tasks exist, waiting task = %p" ,
1666
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl MustWait, pending tasks exist, waiting task = %p" ,
1646
1667
waitingTask);
1647
- if (bodyError && group->isDiscardingResults ()) {
1648
- auto discardingGroup = asDiscardingImpl (_group);
1649
- bool storedBodyError = discardingGroup->offerBodyError (bodyError);
1650
- if (storedBodyError) {
1651
- SWIFT_TASK_GROUP_DEBUG_LOG (
1652
- group, " waitAll, stored error thrown by with...Group body, error = %p" ,
1653
- bodyError);
1654
- }
1655
- }
1656
-
1657
1668
// The waiting task has been queued on the channel,
1658
1669
// there were pending tasks so it will be woken up eventually.
1659
1670
#ifdef __ARM_ARCH_7K__
@@ -1664,7 +1675,7 @@ static void swift_taskGroup_waitAllImpl(
1664
1675
#endif /* __ARM_ARCH_7K__ */
1665
1676
1666
1677
case PollStatus::Error:
1667
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll found error , waiting task = %p, body error = %p, status:%s" ,
1678
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error , waiting task = %p, body error = %p, status:%s" ,
1668
1679
waitingTask, bodyError, group->statusString ().c_str ());
1669
1680
#if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1670
1681
if (bodyError) {
@@ -1686,17 +1697,10 @@ static void swift_taskGroup_waitAllImpl(
1686
1697
return waitingTask->runInFullyEstablishedContext ();
1687
1698
1688
1699
case PollStatus::Empty:
1689
- // / Anything else than a "MustWait" can be treated as a successful poll.
1690
- // / Only if there are in flight pending tasks do we need to wait after all.
1691
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll %s, waiting task = %p, status:%s" ,
1692
- polled.status == TaskGroupBase::PollStatus::Empty ? " empty" : " success" ,
1693
- waitingTask, group->statusString ().c_str ());
1694
-
1695
-
1696
1700
case PollStatus::Success:
1697
1701
// / Anything else than a "MustWait" can be treated as a successful poll.
1698
1702
// / Only if there are in flight pending tasks do we need to wait after all.
1699
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAll %s, waiting task = %p, status:%s" ,
1703
+ SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
1700
1704
polled.status == TaskGroupBase::PollStatus::Empty ? " empty" : " success" ,
1701
1705
waitingTask, group->statusString ().c_str ());
1702
1706
@@ -1711,26 +1715,13 @@ static void swift_taskGroup_waitAllImpl(
1711
1715
}
1712
1716
}
1713
1717
1714
- bool DiscardingTaskGroup::offerBodyError (SwiftError* _Nonnull bodyError) {
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.
1721
+ PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask) {
1715
1722
lock (); // TODO: remove group lock, and use status for synchronization
1716
1723
1717
- if (!readyQueue.isEmpty ()) {
1718
- // already other error stored, discard this one
1719
- unlock ();
1720
- return false ;
1721
- }
1722
-
1723
- auto readyItem = ReadyQueueItem::getRawError (this , bodyError);
1724
- readyQueue.enqueue (readyItem);
1725
- unlock ();
1726
-
1727
- return true ;
1728
- }
1729
-
1730
- PollResult TaskGroupBase::waitAll (AsyncTask *waitingTask) {
1731
- lock (); // TODO: remove group lock, and use status for synchronization
1732
-
1733
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, status = %s" , statusString ().c_str ());
1724
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, bodyError = %p, status = %s" , bodyError, statusString ().c_str ());
1734
1725
PollResult result = PollResult::getEmpty (this ->successType );
1735
1726
result.status = PollStatus::Empty;
1736
1727
result.storage = nullptr ;
@@ -1774,6 +1765,16 @@ PollResult TaskGroupBase::waitAll(AsyncTask *waitingTask) {
1774
1765
}
1775
1766
1776
1767
// ==== 2) Add to wait queue -------------------------------------------------
1768
+
1769
+ // ---- 2.1) Discarding task group may need to story the bodyError before we park
1770
+ if (bodyError && isDiscardingResults ()) {
1771
+ auto discardingGroup = asDiscardingImpl (this );
1772
+ assert (readyQueue.isEmpty () &&
1773
+ " only a single error may be stored in discarding task group, but something was enqueued already" );
1774
+ auto readyItem = ReadyQueueItem::getRawError (discardingGroup, bodyError);
1775
+ readyQueue.enqueue (readyItem);
1776
+ }
1777
+
1777
1778
auto waitHead = waitQueue.load (std::memory_order_acquire);
1778
1779
_swift_tsan_release (static_cast <Job *>(waitingTask));
1779
1780
while (true ) {
0 commit comments