@@ -1164,55 +1164,85 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1164
1164
// Immediately decrement the pending count.
1165
1165
// We can do this, since in this mode there is no ready count to keep track of,
1166
1166
// 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 ());
1169
1171
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);
1174
1204
} 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 ());
1177
1205
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1178
1206
}
1179
1207
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 ;
1191
1210
}
1192
1211
1212
+ assert (!hadErrorResult);
1193
1213
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 ()) {
1199
1223
case ReadyStatus::RawError:
1200
- resumeWaitingTaskWithError (priorErrorItem .getRawError (this ), assumed, alreadyDecrementedStatus);
1224
+ resumeWaitingTaskWithError (readyErrorItem .getRawError (this ), assumed, alreadyDecrementedStatus);
1201
1225
break ;
1202
1226
case ReadyStatus::Error:
1203
- resumeWaitingTask (priorErrorItem .getTask (), assumed, /* hadErrorResult=*/ true , alreadyDecrementedStatus);
1227
+ resumeWaitingTask (readyErrorItem .getTask (), assumed, /* hadErrorResult=*/ true , alreadyDecrementedStatus);
1204
1228
break ;
1205
1229
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" );
1207
1232
}
1208
1233
} 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);
1212
1237
}
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);
1213
1242
}
1214
1243
1215
1244
unlock ();
1245
+ return ;
1216
1246
}
1217
1247
1218
1248
// / Must be called while holding the TaskGroup lock.
0 commit comments