@@ -256,11 +256,14 @@ static bool withStatusRecordLock(AsyncTask *task,
256
256
/* ************************** RECORD MANAGEMENT ****************************/
257
257
/* *************************************************************************/
258
258
259
+ SWIFT_EXPORT_FROM (swift_Concurrency)
259
260
SWIFT_CC(swift)
260
- static bool swift_task_addStatusRecordImpl(TaskStatusRecord *newRecord) {
261
- auto task = swift_task_getCurrent ();
261
+ bool swift_task_addStatusRecordWithChecks(
262
+ TaskStatusRecord *newRecord,
263
+ llvm::function_ref<bool (ActiveTaskStatus status)> shouldAddRecord) {
262
264
263
- // Load the current state. We can use a relaxed load because we're
265
+ auto task = swift_task_getCurrent ();
266
+ // Load the current state. We can use a relaxed load because we're
264
267
// synchronous with the task.
265
268
auto oldStatus = task->_private ().Status .load (std::memory_order_relaxed);
266
269
@@ -273,53 +276,28 @@ static bool swift_task_addStatusRecordImpl(TaskStatusRecord *newRecord) {
273
276
newRecord->resetParent (oldStatus.getInnermostRecord ());
274
277
275
278
// Set the record as the new innermost record.
276
- // We have to use a release on success to make the initialization of
277
- // the new record visible to the cancelling thread.
278
279
ActiveTaskStatus newStatus = oldStatus.withInnermostRecord (newRecord);
279
- if (task->_private ().Status .compare_exchange_weak (oldStatus, newStatus,
280
- /* success*/ std::memory_order_release,
281
- /* failure*/ std::memory_order_relaxed))
282
- return !oldStatus.isCancelled ();
283
- }
284
- }
285
-
286
- SWIFT_CC (swift)
287
- static bool swift_task_tryAddStatusRecordImpl(TaskStatusRecord *newRecord) {
288
- auto task = swift_task_getCurrent ();
289
-
290
- // Load the current state. We can use a relaxed load because we're
291
- // synchronous with the task.
292
- auto oldStatus = task->_private ().Status .load (std::memory_order_relaxed);
293
280
294
- while (true ) {
295
- // If the old info is already cancelled, do nothing.
296
- if (oldStatus.isCancelled ())
281
+ if (shouldAddRecord (newStatus)) {
282
+ // We have to use a release on success to make the initialization of
283
+ // the new record visible to the cancelling thread.
284
+ if (task->_private ().Status .compare_exchange_weak (
285
+ oldStatus, newStatus,
286
+ /* success*/ std::memory_order_release,
287
+ /* failure*/ std::memory_order_relaxed)) {
288
+ return true ;
289
+ } else {
290
+ /* Retry */
291
+ }
292
+ } else {
297
293
return false ;
298
-
299
- // Wait for any active lock to be released.
300
- if (oldStatus.isLocked ()) {
301
- waitForStatusRecordUnlock (task, oldStatus);
302
-
303
- if (oldStatus.isCancelled ())
304
- return false ;
305
294
}
306
-
307
- // Reset the parent of the new record.
308
- newRecord->resetParent (oldStatus.getInnermostRecord ());
309
-
310
- // Set the record as the new innermost record.
311
- // We have to use a release on success to make the initialization of
312
- // the new record visible to the cancelling thread.
313
- ActiveTaskStatus newStatus = oldStatus.withInnermostRecord (newRecord);
314
- if (task->_private ().Status .compare_exchange_weak (oldStatus, newStatus,
315
- /* success*/ std::memory_order_release,
316
- /* failure*/ std::memory_order_relaxed))
317
- return true ;
318
295
}
319
296
}
320
297
298
+ SWIFT_EXPORT_FROM (swift_Concurrency)
321
299
SWIFT_CC(swift)
322
- static bool swift_task_removeStatusRecordImpl (TaskStatusRecord *record) {
300
+ bool swift_task_removeStatusRecord (TaskStatusRecord *record) {
323
301
auto task = swift_task_getCurrent ();
324
302
SWIFT_TASK_DEBUG_LOG (" remove status record = %p, from current task = %p" ,
325
303
record, task);
@@ -411,7 +389,14 @@ swift_task_attachChildImpl(AsyncTask *child) {
411
389
auto record = new (allocation) swift::ChildTaskStatusRecord (child);
412
390
SWIFT_TASK_DEBUG_LOG (" attach child task = %p, record = %p, to current task = %p" ,
413
391
child, record, swift_task_getCurrent ());
414
- swift_task_addStatusRecord (record);
392
+
393
+ bool added_record = swift_task_addStatusRecordWithChecks (
394
+ record, [&](ActiveTaskStatus parentStatus) {
395
+ swift_task_updateNewChildWithParentAndGroupState (child, parentStatus,
396
+ NULL );
397
+ return true ;
398
+ });
399
+ assert (added_record);
415
400
return record;
416
401
}
417
402
@@ -421,6 +406,43 @@ swift_task_detachChildImpl(ChildTaskStatusRecord *record) {
421
406
swift_task_removeStatusRecord (record);
422
407
}
423
408
409
+ /* Called in the path of linking a child into a parent/group synchronously with
410
+ * the parent task.
411
+ *
412
+ * When called to link a child into a parent directly, this does not hold the
413
+ * parent's task status record lock. When called to link a child into a task
414
+ * group, this holds the parent's task status record lock.
415
+ */
416
+ SWIFT_EXPORT_FROM (swift_Concurrency)
417
+ SWIFT_CC(swift)
418
+ void swift_task_updateNewChildWithParentAndGroupState(
419
+ AsyncTask *child, ActiveTaskStatus parentStatus, TaskGroup *group) {
420
+ /*
421
+ * We can take the fast path of just modifying the ActiveTaskStatus in the
422
+ * child task since we know that it won't have any task status records and
423
+ * cannot be accessed by anyone else since it hasn't been linked in yet.
424
+ * Avoids the extra logic in `swift_task_cancel` and `swift_task_escalate`
425
+ */
426
+ auto oldChildTaskStatus =
427
+ child->_private ().Status .load (std::memory_order_relaxed);
428
+ assert (oldChildTaskStatus.getInnermostRecord () == NULL );
429
+
430
+ auto newChildTaskStatus = oldChildTaskStatus;
431
+
432
+ /* Parent task is cancelled or group the child task is part of (if any) is
433
+ * cancelled */
434
+ if (parentStatus.isCancelled () || (group && group->isCancelled ())) {
435
+ newChildTaskStatus = newChildTaskStatus.withCancelled ();
436
+ }
437
+
438
+ /* Parent task got escalated, make sure to propagate it to child. */
439
+ if (parentStatus.isStoredPriorityEscalated ()) {
440
+ newChildTaskStatus = newChildTaskStatus.withEscalatedPriority (
441
+ parentStatus.getStoredPriority ());
442
+ }
443
+ child->_private ().Status .store (newChildTaskStatus, std::memory_order_relaxed);
444
+ }
445
+
424
446
SWIFT_CC (swift)
425
447
static void swift_taskGroup_attachChildImpl(TaskGroup *group,
426
448
AsyncTask *child) {
@@ -430,12 +452,25 @@ static void swift_taskGroup_attachChildImpl(TaskGroup *group,
430
452
// Acquire the status record lock of parent - we want to synchronize with
431
453
// concurrent cancellation or escalation as we're adding new tasks to the
432
454
// group.
433
-
434
455
auto parent = swift_task_getCurrent ();
435
456
withStatusRecordLock (parent, LockContext::OnTask,
436
- [&](ActiveTaskStatus &status) {
437
- group->addChildTask (child);
438
- });
457
+ [&](ActiveTaskStatus &parentStatus) {
458
+ group->addChildTask (child);
459
+ /*
460
+ * After getting parent's status record lock, do some
461
+ * sanity checks to see if parent task or group has
462
+ * state changes that need to be propagated to the
463
+ * child.
464
+ *
465
+ * This is the same logic that we would do if we were
466
+ * adding a child task status record - see also
467
+ * asyncLet_addImpl. Since we attach a child task to a
468
+ * TaskGroupRecord instead, we synchronize on the
469
+ * parent's task status and then update the child.
470
+ */
471
+ swift_task_updateNewChildWithParentAndGroupState (
472
+ child, parentStatus, group);
473
+ });
439
474
}
440
475
441
476
/* ***************************** CANCELLATION ******************************/
0 commit comments