Skip to content

Commit 305b988

Browse files
committed
done
1 parent e4c8760 commit 305b988

File tree

1 file changed

+60
-30
lines changed

1 file changed

+60
-30
lines changed

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,55 +1164,85 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
11641164
// Immediately decrement the pending count.
11651165
// We can do this, since in this mode there is no ready count to keep track of,
11661166
// and we immediately discard the result.
1167-
SWIFT_TASK_GROUP_DEBUG_LOG(this, "discard result, hadError:%d, was pending:%llu, status = %s",
1168-
hadErrorResult, assumed.pendingTasks(this), assumed.to_string(this).c_str());
1167+
auto afterComplete = statusCompletePendingAssumeRelease();
1168+
(void) afterComplete;
1169+
const bool alreadyDecrementedStatus = true;
1170+
SWIFT_TASK_GROUP_DEBUG_LOG(this, "offer, complete, status afterComplete:%s", afterComplete.to_string(this).c_str());
11691171

1170-
if (hadErrorResult && readyQueue.isEmpty()) {
1171-
// a discardResults throwing task group must retain the FIRST error it encounters.
1172-
SWIFT_TASK_GROUP_DEBUG_LOG(this, "offer error, error:%d, completedTask:%p", hadErrorResult, completedTask);
1173-
enqueueCompletedTask(completedTask, /*hadErrorResult=*/hadErrorResult);
1172+
// Errors need special treatment
1173+
if (hadErrorResult) {
1174+
// Discarding results mode immediately treats a child failure as group cancellation.
1175+
// "All for one, one for all!" - any task failing must cause the group and all sibling tasks to be cancelled,
1176+
// such that the discarding group can exit as soon as possible.
1177+
cancelAll();
1178+
1179+
if (afterComplete.hasWaitingTask() && afterComplete.pendingTasks(this) == 0) {
1180+
// This is the last pending task, and we must resume the waiting task.
1181+
// - if there already was a previous error stored, we resume using it,
1182+
// - otherwise, we resume using this current (failed) completedTask
1183+
ReadyQueueItem readyErrorItem;
1184+
if (readyQueue.dequeue(readyErrorItem)) {
1185+
switch (readyErrorItem.getStatus()) {
1186+
case ReadyStatus::RawError:
1187+
resumeWaitingTaskWithError(readyErrorItem.getRawError(this), assumed, alreadyDecrementedStatus);
1188+
break;
1189+
case ReadyStatus::Error:
1190+
resumeWaitingTask(readyErrorItem.getTask(), assumed, /*hadErrorResult=*/true, alreadyDecrementedStatus);
1191+
break;
1192+
default:
1193+
swift_Concurrency_fatalError(0,
1194+
"only errors can be stored by a discarding task group, yet it wasn't an error! 1");
1195+
}
1196+
} else {
1197+
// There was no prior failed task stored, so we should resume the waitingTask with this (failed) completedTask
1198+
resumeWaitingTask(completedTask, assumed, hadErrorResult, alreadyDecrementedStatus);
1199+
}
1200+
} else if (readyQueue.isEmpty()) {
1201+
// There was no waiting task, or other tasks are still pending, so we cannot
1202+
// it is the first error we encountered, thus we need to store it for future throwing
1203+
enqueueCompletedTask(completedTask, hadErrorResult);
11741204
} else {
1175-
// we just are going to discard it.
1176-
SWIFT_TASK_GROUP_DEBUG_LOG(this, "offer discard the completedTask:%p, status=%s", completedTask, statusString().c_str());
11771205
_swift_taskGroup_detachChild(asAbstract(this), completedTask);
11781206
}
11791207

1180-
auto afterComplete = statusCompletePendingAssumeRelease();
1181-
(void) afterComplete;
1182-
bool alreadyDecrementedStatus = true;
1183-
SWIFT_TASK_GROUP_DEBUG_LOG(this, "offer, complete, status:%s",
1184-
afterComplete.to_string(this).c_str());
1185-
1186-
// Discarding results mode, immediately treats a child failure as group cancellation.
1187-
// "All for one, one for all!" - any task failing must cause the group and all sibling tasks to be cancelled,
1188-
// such that the discarding group can exit as soon as possible.
1189-
if (hadErrorResult) {
1190-
cancelAll();
1208+
unlock();
1209+
return;
11911210
}
11921211

1212+
assert(!hadErrorResult);
11931213
if (afterComplete.hasWaitingTask() && afterComplete.pendingTasks(this) == 0) {
1194-
ReadyQueueItem priorErrorItem;
1195-
if (readyQueue.dequeue(priorErrorItem)) {
1196-
SWIFT_TASK_GROUP_DEBUG_LOG(this, "offer, last pending task, prior error found, fail waitingTask:%p",
1197-
waitQueue.load(std::memory_order_relaxed));
1198-
switch (priorErrorItem.getStatus()) {
1214+
SWIFT_TASK_GROUP_DEBUG_LOG(this,
1215+
"offer, last pending task completed successfully, resume waitingTask with completedTask:%p",
1216+
completedTask);
1217+
1218+
/// If there was an error previously stored, we must resume the waitingTask using that error.
1219+
ReadyQueueItem readyErrorItem;
1220+
if (readyQueue.dequeue(readyErrorItem)) {
1221+
_swift_taskGroup_detachChild(asAbstract(this), completedTask);
1222+
switch (readyErrorItem.getStatus()) {
11991223
case ReadyStatus::RawError:
1200-
resumeWaitingTaskWithError(priorErrorItem.getRawError(this), assumed, alreadyDecrementedStatus);
1224+
resumeWaitingTaskWithError(readyErrorItem.getRawError(this), assumed, alreadyDecrementedStatus);
12011225
break;
12021226
case ReadyStatus::Error:
1203-
resumeWaitingTask(priorErrorItem.getTask(), assumed, /*hadErrorResult=*/true, alreadyDecrementedStatus);
1227+
resumeWaitingTask(readyErrorItem.getTask(), assumed, /*hadErrorResult=*/true, alreadyDecrementedStatus);
12041228
break;
12051229
default:
1206-
swift_Concurrency_fatalError(0, "only errors can be stored by a discarding task group, yet it wasn't an error!");
1230+
swift_Concurrency_fatalError(0,
1231+
"only errors can be stored by a discarding task group, yet it wasn't an error! 2");
12071232
}
12081233
} else {
1209-
SWIFT_TASK_GROUP_DEBUG_LOG(this, "offer, last pending task, completing with completedTask:%p, completedTask.error:%d, waitingTask:%p",
1210-
completedTask, hadErrorResult, waitQueue.load(std::memory_order_relaxed));
1211-
resumeWaitingTask(completedTask, assumed, /*hadErrorResult=*/hadErrorResult, alreadyDecrementedStatus);
1234+
// This is the last task, we have a waiting task and there was no error stored previously;
1235+
// We must resume the waiting task with a success, so let us return here.
1236+
resumeWaitingTask(completedTask, assumed, hadErrorResult, alreadyDecrementedStatus);
12121237
}
1238+
} else {
1239+
// it wasn't the last pending task, and there is no-one to resume;
1240+
// Since this is a successful result, and we're a discarding task group -- always just ignore this task.
1241+
_swift_taskGroup_detachChild(asAbstract(this), completedTask);
12131242
}
12141243

12151244
unlock();
1245+
return;
12161246
}
12171247

12181248
/// Must be called while holding the TaskGroup lock.

0 commit comments

Comments
 (0)