@@ -1158,7 +1158,7 @@ void AccumulatingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *contex
1158
1158
// This is wasteful, and the task completion function should be fixed to
1159
1159
// transfer ownership of a retain into this function, in which case we
1160
1160
// will need to release in the other path.
1161
- lock (); // TODO: remove fragment lock, and use status for synchronization
1161
+ lock ();
1162
1162
1163
1163
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, completedTask:%p, status:%s" ,
1164
1164
completedTask,
@@ -1190,9 +1190,9 @@ void AccumulatingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *contex
1190
1190
1191
1191
// ==== a) has waiting task, so let us complete it right away
1192
1192
if (assumed.hasWaitingTask ()) {
1193
- resumeWaitingTask (completedTask, assumed, hadErrorResult);
1194
- unlock (); // TODO: remove fragment lock, and use status for synchronization
1195
- return ;
1193
+ // Must unlock before we resume the waiting task
1194
+ unlock ();
1195
+ return resumeWaitingTask (completedTask, assumed, hadErrorResult) ;
1196
1196
} else {
1197
1197
// ==== b) enqueue completion ------------------------------------------------
1198
1198
//
@@ -1202,7 +1202,7 @@ void AccumulatingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *contex
1202
1202
assert (!waitQueue.load (std::memory_order_relaxed));
1203
1203
1204
1204
enqueueCompletedTask (completedTask, hadErrorResult);
1205
- unlock (); // TODO: remove fragment lock, and use status for synchronization
1205
+ return unlock ();
1206
1206
}
1207
1207
}
1208
1208
@@ -1253,41 +1253,54 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1253
1253
switch (readyErrorItem.getStatus ()) {
1254
1254
case ReadyStatus::RawError:
1255
1255
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, complete, resume with raw error:%p" , readyErrorItem.getRawError (this ));
1256
- resumeWaitingTaskWithError (readyErrorItem.getRawError (this ), assumed,
1256
+ // The following MUST be done in the following order: detach, unlock, resume waitingTask.
1257
+ // because we do not want to allow another task to run and have the potential to lock or even destroy
1258
+ // the group before we've given up the lock.
1259
+ _swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1260
+ unlock ();
1261
+ return resumeWaitingTaskWithError (readyErrorItem.getRawError (this ), assumed,
1257
1262
alreadyDecrementedStatus);
1258
- break ;
1259
1263
case ReadyStatus::Error:
1260
1264
// The completed task failed, but we already stored a different failed task.
1261
1265
// Thus we discard this error and complete with the previously stored.
1262
1266
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, complete, discard error completedTask %p, resume with errorItem.task:%p" ,
1263
1267
completedTask,
1264
1268
readyErrorItem.getTask ());
1269
+ // The following MUST be done in the following order: detach, unlock, resume waitingTask.
1270
+ // because we do not want to allow another task to run and have the potential to lock or even destroy
1271
+ // the group before we've given up the lock.
1265
1272
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1266
- resumeWaitingTask (readyErrorItem.getTask (), assumed,
1273
+ unlock ();
1274
+ return resumeWaitingTask (readyErrorItem.getTask (), assumed,
1267
1275
/* hadErrorResult=*/ true ,
1268
1276
alreadyDecrementedStatus,
1269
1277
/* taskWasRetained=*/ true );
1270
- break ;
1271
1278
default :
1272
1279
swift_Concurrency_fatalError (0 ,
1273
1280
" only errors can be stored by a discarding task group, yet it wasn't an error! 1" );
1274
1281
}
1275
1282
} else {
1283
+ // The following MUST be done in the following order: detach, unlock, resume waitingTask.
1284
+ // because we do not want to allow another task to run and have the potential to lock or even destroy
1285
+ // the group before we've given up the lock.
1286
+ _swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1287
+ unlock ();
1276
1288
// There was no prior failed task stored, so we should resume the waitingTask with this (failed) completedTask
1277
- resumeWaitingTask (completedTask, assumed, hadErrorResult, alreadyDecrementedStatus);
1289
+ return resumeWaitingTask (completedTask, assumed, hadErrorResult, alreadyDecrementedStatus);
1278
1290
}
1279
1291
} else if (readyQueue.isEmpty ()) {
1280
1292
// There was no waiting task, or other tasks are still pending, so we cannot
1281
1293
// it is the first error we encountered, thus we need to store it for future throwing
1282
1294
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, enqueue child task:%p" , completedTask);
1283
1295
enqueueCompletedTask (completedTask, hadErrorResult);
1296
+ return unlock ();
1284
1297
} else {
1285
1298
SWIFT_TASK_GROUP_DEBUG_LOG (this , " offer, complete, discard child task:%p" , completedTask);
1286
1299
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1300
+ return unlock ();
1287
1301
}
1288
-
1289
- unlock ();
1290
- return ;
1302
+ swift_Concurrency_fatalError (0 , " expected to early return from when "
1303
+ " handling offer of last task in group" );
1291
1304
}
1292
1305
1293
1306
assert (!hadErrorResult && " only successfully completed tasks can reach here" );
@@ -1302,31 +1315,38 @@ void DiscardingTaskGroup::offer(AsyncTask *completedTask, AsyncContext *context)
1302
1315
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1303
1316
switch (readyErrorItem.getStatus ()) {
1304
1317
case ReadyStatus::RawError:
1305
- resumeWaitingTaskWithError (readyErrorItem.getRawError (this ), assumed, alreadyDecrementedStatus);
1306
- break ;
1318
+ // The following MUST be done in the following order: detach, unlock, resume waitingTask.
1319
+ // because we do not want to allow another task to run and have the potential to lock or even destroy
1320
+ // the group before we've given up the lock.
1321
+ _swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1322
+ unlock ();
1323
+ return resumeWaitingTaskWithError (readyErrorItem.getRawError (this ), assumed, alreadyDecrementedStatus);
1307
1324
case ReadyStatus::Error:
1308
- resumeWaitingTask (readyErrorItem.getTask (), assumed,
1325
+ // The following MUST be done in the following order: detach, unlock, resume waitingTask.
1326
+ // because we do not want to allow another task to run and have the potential to lock or even destroy
1327
+ // the group before we've given up the lock.
1328
+ _swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1329
+ unlock ();
1330
+ return resumeWaitingTask (readyErrorItem.getTask (), assumed,
1309
1331
/* hadErrorResult=*/ true ,
1310
1332
alreadyDecrementedStatus,
1311
1333
/* taskWasRetained=*/ true );
1312
- break ;
1313
1334
default :
1314
1335
swift_Concurrency_fatalError (0 ,
1315
1336
" only errors can be stored by a discarding task group, yet it wasn't an error! 2" );
1316
1337
}
1317
1338
} else {
1339
+ unlock ();
1318
1340
// This is the last task, we have a waiting task and there was no error stored previously;
1319
1341
// We must resume the waiting task with a success, so let us return here.
1320
- resumeWaitingTask (completedTask, assumed, /* hadErrorResult=*/ false , alreadyDecrementedStatus);
1342
+ return resumeWaitingTask (completedTask, assumed, /* hadErrorResult=*/ false , alreadyDecrementedStatus);
1321
1343
}
1322
1344
} else {
1323
1345
// it wasn't the last pending task, and there is no-one to resume;
1324
1346
// Since this is a successful result, and we're a discarding task group -- always just ignore this task.
1325
1347
_swift_taskGroup_detachChild (asAbstract (this ), completedTask);
1348
+ return unlock ();
1326
1349
}
1327
-
1328
- unlock ();
1329
- return ;
1330
1350
}
1331
1351
1332
1352
// / Must be called while holding the TaskGroup lock.
0 commit comments