50
50
51
51
using namespace swift ;
52
52
53
- #if 1
53
+ #if 0
54
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(), \
@@ -114,7 +114,7 @@ class NaiveTaskGroupQueue {
114
114
queue = std::move (other.queue );
115
115
}
116
116
117
- virtual ~NaiveTaskGroupQueue () {}
117
+ ~NaiveTaskGroupQueue () {}
118
118
119
119
bool dequeue (T &output) {
120
120
if (queue.empty ()) {
@@ -367,8 +367,10 @@ class TaskGroupBase : public TaskGroupTaskStatusRecord {
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
369
// / \param rawContext used to resume the waiting task
370
- // / \return how the waiting task should be handled, e.g. must wait or can be completed immediately
371
- PollResult waitAll (SwiftError* bodyError, AsyncTask *waitingTask, AsyncContext* rawContext);
370
+ void waitAll (SwiftError* bodyError, AsyncTask *waitingTask,
371
+ OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
372
+ ThrowingTaskFutureWaitContinuationFunction *resumeFunction,
373
+ AsyncContext *rawContext);
372
374
373
375
// Enqueue the completed task onto ready queue if there are no waiting tasks yet
374
376
virtual void enqueueCompletedTask (AsyncTask *completedTask, bool hadErrorResult) = 0;
@@ -1035,7 +1037,7 @@ static void _enqueueRawError(DiscardingTaskGroup* _Nonnull group,
1035
1037
// TaskGroup is locked upon entry and exit
1036
1038
void AccumulatingTaskGroup::enqueueCompletedTask (AsyncTask *completedTask, bool hadErrorResult) {
1037
1039
// Retain the task while it is in the queue; it must remain alive until
1038
- // it is found by poll. This retain will balanced by the release in poll.
1040
+ // it is found by poll. This retain will be balanced by the release in poll.
1039
1041
swift_retain (completedTask);
1040
1042
1041
1043
_enqueueCompletedTask (&readyQueue, completedTask, hadErrorResult);
@@ -1181,9 +1183,11 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1181
1183
if (readyQueue.dequeue (readyErrorItem)) {
1182
1184
switch (readyErrorItem.getStatus ()) {
1183
1185
case ReadyStatus::RawError:
1186
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, complete, resume with raw error:%p" , readyErrorItem.getRawError (this ));
1184
1187
resumeWaitingTaskWithError (readyErrorItem.getRawError (this ), assumed, alreadyDecrementedStatus);
1185
1188
break ;
1186
1189
case ReadyStatus::Error:
1190
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, complete, resume with errorItem.task:%p" , readyErrorItem.getTask ());
1187
1191
resumeWaitingTask (readyErrorItem.getTask (), assumed, /* hadErrorResult=*/ true , alreadyDecrementedStatus);
1188
1192
break ;
1189
1193
default :
@@ -1197,16 +1201,18 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1197
1201
} else if (readyQueue.isEmpty ()) {
1198
1202
// There was no waiting task, or other tasks are still pending, so we cannot
1199
1203
// it is the first error we encountered, thus we need to store it for future throwing
1204
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, enqueue child task:%p" , completedTask);
1200
1205
enqueueCompletedTask (completedTask, hadErrorResult);
1201
1206
} else {
1207
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, complete, discard child task:%p" , completedTask);
1202
1208
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1203
1209
}
1204
1210
1205
1211
unlock ();
1206
1212
return ;
1207
1213
}
1208
1214
1209
- assert (!hadErrorResult);
1215
+ assert (!hadErrorResult && " only successfully completed tasks can reach here " );
1210
1216
if (afterComplete.hasWaitingTask () && afterComplete.pendingTasks (this ) == 0 ) {
1211
1217
SWIFT_TASK_GROUP_DEBUG_LOG (this ,
1212
1218
" offer, last pending task completed successfully, resume waitingTask with completedTask:%p" ,
@@ -1230,7 +1236,7 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1230
1236
} else {
1231
1237
// This is the last task, we have a waiting task and there was no error stored previously;
1232
1238
// We must resume the waiting task with a success, so let us return here.
1233
- resumeWaitingTask (completedTask, assumed, hadErrorResult, alreadyDecrementedStatus);
1239
+ resumeWaitingTask (completedTask, assumed, /* hadErrorResult= */ false , alreadyDecrementedStatus);
1234
1240
}
1235
1241
} else {
1236
1242
// it wasn't the last pending task, and there is no-one to resume;
@@ -1292,8 +1298,14 @@ void TaskGroupBase::resumeWaitingTask(
1292
1298
// Run the task.
1293
1299
auto result = PollResult::get (completedTask, hadErrorResult);
1294
1300
SWIFT_TASK_GROUP_DEBUG_LOG (this ,
1295
- " resume waiting DONE, task = %p, backup = %p, complete with = %p, status = %s" ,
1296
- waitingTask, backup, completedTask, statusString ().c_str ());
1301
+ " resume waiting DONE, task = %p, backup = %p, error:%d, complete with = %p, status = %s" ,
1302
+ waitingTask, backup, hadErrorResult, completedTask, statusString ().c_str ());
1303
+
1304
+ auto waitingContext =
1305
+ static_cast <TaskFutureWaitAsyncContext *>(
1306
+ waitingTask->ResumeContext );
1307
+
1308
+ fillGroupNextResult (waitingContext, result);
1297
1309
1298
1310
// Remove the child from the task group's running tasks list.
1299
1311
// The parent task isn't currently running (we're about to wake
@@ -1305,12 +1317,6 @@ void TaskGroupBase::resumeWaitingTask(
1305
1317
// we can't be holding its locks ourselves.
1306
1318
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1307
1319
1308
- auto waitingContext =
1309
- static_cast <TaskFutureWaitAsyncContext *>(
1310
- waitingTask->ResumeContext );
1311
-
1312
- fillGroupNextResult (waitingContext, result);
1313
-
1314
1320
_swift_tsan_acquire (static_cast <Job *>(waitingTask));
1315
1321
// TODO: allow the caller to suggest an executor
1316
1322
waitingTask->flagAsAndEnqueueOnExecutor (ExecutorRef::generic ());
@@ -1651,80 +1657,29 @@ static void swift_taskGroup_waitAllImpl(
1651
1657
auto waitingTask = swift_task_getCurrent ();
1652
1658
1653
1659
auto group = asBaseImpl (_group);
1654
- PollResult polled = group->waitAll (bodyError, waitingTask, rawContext);
1655
-
1656
- auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
1657
- context->ResumeParent =
1658
- reinterpret_cast <TaskContinuationFunction *>(resumeFunction);
1659
- context->Parent = callerContext;
1660
- context->errorResult = nullptr ;
1661
- context->successResultPointer = resultPointer;
1662
-
1663
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl, waiting task = %p, bodyError = %p, status:%s, polled.status = %s, status.NOW=%s" ,
1664
- waitingTask, bodyError, group->statusString ().c_str (), to_string (polled.status ).c_str (), group->statusString ().c_str ());
1665
-
1666
- switch (polled.status ) {
1667
- case PollStatus::MustWait: {
1668
- // The waiting task has been queued on the channel,
1669
- // there were pending tasks so it will be woken up eventually.
1670
- #ifdef __ARM_ARCH_7K__
1671
- workaround_function_swift_taskGroup_waitAllImpl (
1672
- resultPointer, callerContext, _group, bodyError, resumeFunction, rawContext);
1673
- #endif /* __ARM_ARCH_7K__ */
1674
- return ;
1675
- }
1676
-
1677
- case PollStatus::Error: {
1678
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl Error, waiting task = %p, body error = %p, status:%s" ,
1679
- waitingTask, bodyError, group->statusString ().c_str ());
1680
- #if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1681
- if (bodyError) {
1682
- fillGroupNextErrorResult (context, bodyError);
1683
- } else {
1684
- fillGroupNextResult (context, polled);
1685
- }
1686
- #else // so, not SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1687
- fillGroupNextResult (context, polled);
1688
- #endif // SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1689
- if (auto completedTask = polled.retainedTask ) {
1690
- // Remove the child from the task group's running tasks list.
1691
- _swift_taskGroup_detachChild (asAbstract (group), completedTask);
1692
-
1693
- // Balance the retain done by enqueueCompletedTask.
1694
- swift_release (completedTask);
1695
- }
1696
-
1697
- return waitingTask->runInFullyEstablishedContext ();
1698
- }
1699
-
1700
- case PollStatus::Empty:
1701
- case PollStatus::Success: {
1702
- // / Anything else than a "MustWait" can be treated as a successful poll.
1703
- // / Only if there are in flight pending tasks do we need to wait after all.
1704
- SWIFT_TASK_GROUP_DEBUG_LOG (group, " waitAllImpl %s, waiting task = %p, status:%s" ,
1705
- polled.status == TaskGroupBase::PollStatus::Empty ? " empty" : " success" ,
1706
- waitingTask, group->statusString ().c_str ());
1707
-
1708
- if (bodyError) {
1709
- // None of the inner tasks have thrown, so we have to "re throw" the body error:
1710
- fillGroupNextErrorResult (context, bodyError);
1711
- } else {
1712
- fillGroupNextNilResult (context, polled);
1713
- }
1714
-
1715
- return waitingTask->runInFullyEstablishedContext ();
1716
- }
1717
- }
1660
+ return group->waitAll (
1661
+ bodyError, waitingTask,
1662
+ resultPointer, callerContext, resumeFunction, rawContext);
1718
1663
}
1719
1664
1720
- PollResult TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask, AsyncContext *rawContext) {
1665
+ void TaskGroupBase::waitAll (SwiftError* bodyError, AsyncTask *waitingTask,
1666
+ OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
1667
+ ThrowingTaskFutureWaitContinuationFunction *resumeFunction,
1668
+ AsyncContext *rawContext) {
1721
1669
lock ();
1722
1670
1723
1671
// must mutate the waiting task while holding the group lock,
1724
1672
// so we don't get an offer concurrently trying to do so
1725
1673
waitingTask->ResumeTask = task_group_wait_resume_adapter;
1726
1674
waitingTask->ResumeContext = rawContext;
1727
1675
1676
+ auto context = static_cast <TaskFutureWaitAsyncContext *>(rawContext);
1677
+ context->ResumeParent =
1678
+ reinterpret_cast <TaskContinuationFunction *>(resumeFunction);
1679
+ context->Parent = callerContext;
1680
+ context->errorResult = nullptr ;
1681
+ context->successResultPointer = resultPointer;
1682
+
1728
1683
SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, bodyError = %p, status = %s" , bodyError, statusString ().c_str ());
1729
1684
PollResult result = PollResult::getEmpty (this ->successType );
1730
1685
result.status = PollStatus::Empty;
@@ -1748,6 +1703,7 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask,
1748
1703
// / out of waitAll - providing the "the first error gets thrown" semantics of the group.
1749
1704
// / The readyQueue is allowed to have exactly one error element in this case.
1750
1705
if (isDiscardingResults ()) {
1706
+ // ---- 1.1) A discarding group needs to check if there is a stored error to throw
1751
1707
auto discardingGroup = asDiscardingImpl (this );
1752
1708
ReadyQueueItem firstErrorItem;
1753
1709
if (readyQueue.dequeue (firstErrorItem)) {
@@ -1758,25 +1714,41 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask,
1758
1714
result.status = PollStatus::Error;
1759
1715
}
1760
1716
} // else, we're definitely Empty
1761
- unlock ();
1762
- return result;
1763
- }
1717
+ } // else (in an accumulating group), a waitAll can bail out early Empty
1764
1718
1765
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " group is empty, no pending tasks, status = %s" , assumed.to_string (this ).c_str ());
1719
+ SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, early return, no pending tasks, bodyError:%p, status = %s" ,
1720
+ bodyError, assumed.to_string (this ).c_str ());
1766
1721
// No tasks in flight, we know no tasks were submitted before this poll
1767
1722
// was issued, and if we parked here we'd potentially never be woken up.
1768
- // Bail out and return `nil` from `group.next()`.
1723
+
1724
+ #if SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1725
+ if (bodyError) {
1726
+ fillGroupNextErrorResult (context, bodyError);
1727
+ } else {
1728
+ fillGroupNextResult (context, result);
1729
+ }
1730
+ #else // so, not SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1731
+ fillGroupNextResult (context, polled);
1732
+ #endif // SWIFT_TASK_GROUP_BODY_THROWN_ERROR_WINS
1733
+ if (auto completedTask = result.retainedTask ) {
1734
+ // Remove the child from the task group's running tasks list.
1735
+ _swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1736
+
1737
+ // Balance the retain done by enqueueCompletedTask.
1738
+ swift_release (completedTask);
1739
+ }
1740
+
1741
+ waitingTask->runInFullyEstablishedContext ();
1742
+
1769
1743
unlock ();
1770
- return result ;
1744
+ return ;
1771
1745
}
1772
1746
1773
1747
// ==== 2) Add to wait queue -------------------------------------------------
1774
1748
1775
1749
// ---- 2.1) Discarding task group may need to story the bodyError before we park
1776
- if (bodyError && isDiscardingResults ()) {
1750
+ if (bodyError && isDiscardingResults () && readyQueue. isEmpty () ) {
1777
1751
auto discardingGroup = asDiscardingImpl (this );
1778
- assert (readyQueue.isEmpty () &&
1779
- " only a single error may be stored in discarding task group, but something was enqueued already" );
1780
1752
auto readyItem = ReadyQueueItem::getRawError (discardingGroup, bodyError);
1781
1753
readyQueue.enqueue (readyItem);
1782
1754
}
@@ -1789,16 +1761,13 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask,
1789
1761
waitingTask->flagAsSuspended ();
1790
1762
}
1791
1763
// Put the waiting task at the beginning of the wait queue.
1792
- SWIFT_TASK_GROUP_DEBUG_LOG (this , " WATCH OUT, set waiter onto... waitQueue.head = %p" , waitQueue.load (std::memory_order_relaxed));
1793
-
1794
1764
if (waitQueue.compare_exchange_strong (
1795
1765
waitHead, waitingTask,
1796
1766
/* success*/ std::memory_order_release,
1797
1767
/* failure*/ std::memory_order_acquire)) {
1798
1768
statusMarkWaitingAssumeRelease ();
1799
1769
SWIFT_TASK_GROUP_DEBUG_LOG (this , " waitAll, marked waiting status = %s" , statusString ().c_str ());
1800
- unlock ();
1801
-
1770
+
1802
1771
#if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
1803
1772
// The logic here is paired with the logic in TaskGroupBase::offer. Once
1804
1773
// we run the
@@ -1821,10 +1790,16 @@ PollResult TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask,
1821
1790
_swift_task_setCurrent (oldTask);
1822
1791
goto reevaluate_if_TaskGroup_has_results;
1823
1792
#endif
1824
- // no ready tasks, so we must wait.
1825
- result.status = PollStatus::MustWait;
1793
+ // The waiting task has been queued on the channel,
1794
+ // there were pending tasks so it will be woken up eventually.
1795
+ #ifdef __ARM_ARCH_7K__
1796
+ workaround_function_swift_taskGroup_waitAllImpl (
1797
+ resultPointer, callerContext, _group, bodyError, resumeFunction, rawContext);
1798
+ #endif /* __ARM_ARCH_7K__ */
1799
+
1826
1800
_swift_task_clearCurrent ();
1827
- return result;
1801
+ unlock ();
1802
+ return ;
1828
1803
} // else, try again
1829
1804
}
1830
1805
}
0 commit comments