Skip to content

Commit 1947102

Browse files
committed
Change the logic for adding new task status records to a task
This change has two parts to it: 1. Add in a new interface (addStatusRecordWithChecks) for adding task status records that also takes in a function ref. This function ref will be used to evaluate if current state of the parent task has any changes that need to be propagated to the child task that has been created. This is necessary to prevent the following race between task creation and concurrent cancellation and escalation: a. Parent task create child task. It does lazy relaxed loads on its own state while doing so and propagates this state to the child. b. Child task is created but has not been attached to the parent task/task group. c. Parent task gets cancelled by another thread. d. Child task gets linked into the parent’s task status records but no reevaluation has happened to account for changes that might have happened to the parent after (a). 2. Move status record management functions from the Runtime/Concurrency.h to TaskPrivate.h. Remove any corresponding overrides that are no longer needed. Remove unused tryAddStatusRecord method whose functionality is provided by addStatusRecordWithChecks. Radar-Id: rdar://problem/86347801
1 parent d1bb98b commit 1947102

File tree

11 files changed

+154
-271
lines changed

11 files changed

+154
-271
lines changed

Diff for: include/swift/ABI/TaskStatus.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace swift {
3535
/// access by a cancelling thread. In particular, the chain of
3636
/// status records must not be disturbed. When the task leaves
3737
/// the scope that requires the status record, the record can
38-
/// be unregistered from the task with `swift_task_removeStatusRecord`,
38+
/// be unregistered from the task with `removeStatusRecord`,
3939
/// at which point the memory can be returned to the system.
4040
class TaskStatusRecord {
4141
public:
@@ -256,7 +256,7 @@ class TaskGroupTaskStatusRecord : public TaskStatusRecord {
256256
///
257257
/// The end of any call to the function will be ordered before the
258258
/// end of a call to unregister this record from the task. That is,
259-
/// code may call `swift_task_removeStatusRecord` and freely
259+
/// code may call `removeStatusRecord` and freely
260260
/// assume after it returns that this function will not be
261261
/// subsequently used.
262262
class CancellationNotificationStatusRecord : public TaskStatusRecord {
@@ -284,7 +284,7 @@ class CancellationNotificationStatusRecord : public TaskStatusRecord {
284284
///
285285
/// The end of any call to the function will be ordered before the
286286
/// end of a call to unregister this record from the task. That is,
287-
/// code may call `swift_task_removeStatusRecord` and freely
287+
/// code may call `removeStatusRecord` and freely
288288
/// assume after it returns that this function will not be
289289
/// subsequently used.
290290
class EscalationNotificationStatusRecord : public TaskStatusRecord {

Diff for: include/swift/Runtime/Concurrency.h

+1-35
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
#ifndef SWIFT_RUNTIME_CONCURRENCY_H
1818
#define SWIFT_RUNTIME_CONCURRENCY_H
1919

20+
#include "swift/ABI/AsyncLet.h"
2021
#include "swift/ABI/Task.h"
2122
#include "swift/ABI/TaskGroup.h"
22-
#include "swift/ABI/AsyncLet.h"
2323
#include "swift/ABI/TaskStatus.h"
2424

2525
#pragma clang diagnostic push
@@ -466,40 +466,6 @@ void swift_asyncLet_consume_throwing(SWIFT_ASYNC_CONTEXT AsyncContext *,
466466
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
467467
bool swift_taskGroup_hasTaskGroupRecord();
468468

469-
/// Add a status record to a task. The record should not be
470-
/// modified while it is registered with a task.
471-
///
472-
/// This must be called synchronously with the task.
473-
///
474-
/// If the task is already cancelled, returns `false` but still adds
475-
/// the status record.
476-
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
477-
bool swift_task_addStatusRecord(TaskStatusRecord *record);
478-
479-
/// Add a status record to a task if the task has not already
480-
/// been cancelled. The record should not be modified while it is
481-
/// registered with a task.
482-
///
483-
/// This must be called synchronously with the task.
484-
///
485-
/// If the task is already cancelled, returns `false` and does not
486-
/// add the status record.
487-
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
488-
bool swift_task_tryAddStatusRecord(TaskStatusRecord *record);
489-
490-
/// Remove a status record from a task. After this call returns,
491-
/// the record's memory can be freely modified or deallocated.
492-
///
493-
/// This must be called synchronously with the task. The record must
494-
/// be registered with the task or else this may crash.
495-
///
496-
/// The given record need not be the last record added to
497-
/// the task, but the operation may be less efficient if not.
498-
///
499-
/// Returns false if the task has been cancelled.
500-
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
501-
bool swift_task_removeStatusRecord(TaskStatusRecord *record);
502-
503469
/// Signifies whether the current task is in the middle of executing the
504470
/// operation block of a `with(Throwing)TaskGroup(...) { <operation> }`.
505471
///

Diff for: stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

-12
Original file line numberDiff line numberDiff line change
@@ -308,18 +308,6 @@ OVERRIDE_TASK_LOCAL(task_localsCopyTo, void,
308308
(AsyncTask *target),
309309
(target))
310310

311-
OVERRIDE_TASK_STATUS(task_addStatusRecord, bool,
312-
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
313-
swift::, (TaskStatusRecord *newRecord), (newRecord))
314-
315-
OVERRIDE_TASK_STATUS(task_tryAddStatusRecord, bool,
316-
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
317-
swift::, (TaskStatusRecord *newRecord), (newRecord))
318-
319-
OVERRIDE_TASK_STATUS(task_removeStatusRecord, bool,
320-
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
321-
swift::, (TaskStatusRecord *record), (record))
322-
323311
OVERRIDE_TASK_STATUS(task_hasTaskGroupStatusRecord, bool,
324312
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
325313
swift::, , )

Diff for: stdlib/public/Concurrency/AsyncLet.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,14 @@ void swift::asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet,
142142
auto record = impl->getTaskRecord();
143143
assert(impl == record && "the async-let IS the task record");
144144

145-
// ok, now that the group actually is initialized: attach it to the task
146-
swift_task_addStatusRecord(record);
145+
// ok, now that the async let task actually is initialized: attach it to the
146+
// current task
147+
bool addedRecord =
148+
addStatusRecord(record, [&](ActiveTaskStatus parentStatus) {
149+
updateNewChildWithParentAndGroupState(task, parentStatus, NULL);
150+
return true;
151+
});
152+
assert(addedRecord);
147153
}
148154

149155
// =============================================================================
@@ -309,7 +315,7 @@ static void swift_asyncLet_endImpl(AsyncLet *alet) {
309315

310316
// Remove the child record from the parent task
311317
auto record = asImpl(alet)->getTaskRecord();
312-
swift_task_removeStatusRecord(record);
318+
removeStatusRecord(record);
313319

314320
// TODO: we need to implicitly await either before the end or here somehow.
315321

@@ -337,7 +343,7 @@ static void asyncLet_finish_after_task_completion(SWIFT_ASYNC_CONTEXT AsyncConte
337343

338344
// Remove the child record from the parent task
339345
auto record = asImpl(alet)->getTaskRecord();
340-
swift_task_removeStatusRecord(record);
346+
removeStatusRecord(record);
341347

342348
// and finally, release the task and destroy the async-let
343349
assert(swift_task_getCurrent() && "async-let must have a parent task");

Diff for: stdlib/public/Concurrency/Task.cpp

+14-6
Original file line numberDiff line numberDiff line change
@@ -1056,19 +1056,27 @@ swift_task_addCancellationHandlerImpl(
10561056
auto *record = new (allocation)
10571057
CancellationNotificationStatusRecord(unsigned_handler, context);
10581058

1059-
if (swift_task_addStatusRecord(record))
1060-
return record;
1059+
bool fireHandlerNow = false;
10611060

1062-
// else, the task was already cancelled, so while the record was added,
1063-
// we must run it immediately here since no other task will trigger it.
1064-
record->run();
1061+
addStatusRecord(record, [&](ActiveTaskStatus parentStatus) {
1062+
if (parentStatus.isCancelled()) {
1063+
fireHandlerNow = true;
1064+
// We don't fire the cancellation handler here since this function needs
1065+
// to be idempotent
1066+
}
1067+
return true;
1068+
});
1069+
1070+
if (fireHandlerNow) {
1071+
record->run();
1072+
}
10651073
return record;
10661074
}
10671075

10681076
SWIFT_CC(swift)
10691077
static void swift_task_removeCancellationHandlerImpl(
10701078
CancellationNotificationStatusRecord *record) {
1071-
swift_task_removeStatusRecord(record);
1079+
removeStatusRecord(record);
10721080
swift_task_dealloc(record);
10731081
}
10741082

Diff for: stdlib/public/Concurrency/TaskGroup.cpp

+9-6
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,14 @@ static void swift_taskGroup_initializeImpl(TaskGroup *group, const Metadata *T)
474474
assert(impl == record && "the group IS the task record");
475475

476476
// ok, now that the group actually is initialized: attach it to the task
477-
bool notCancelled = swift_task_addStatusRecord(record);
478-
479-
// If the task has already been cancelled, reflect that immediately in
480-
// the group status.
481-
if (!notCancelled) impl->statusCancel();
477+
addStatusRecord(record, [&](ActiveTaskStatus parentStatus) {
478+
// If the task has already been cancelled, reflect that immediately in
479+
// the group's status.
480+
if (parentStatus.isCancelled()) {
481+
impl->statusCancel();
482+
}
483+
return true;
484+
});
482485
}
483486

484487
// =============================================================================
@@ -505,7 +508,7 @@ void TaskGroupImpl::destroy() {
505508
SWIFT_TASK_DEBUG_LOG("destroying task group = %p", this);
506509

507510
// First, remove the group from the task and deallocate the record
508-
swift_task_removeStatusRecord(getTaskRecord());
511+
removeStatusRecord(getTaskRecord());
509512

510513
// No need to drain our queue here, as by the time we call destroy,
511514
// all tasks inside the group must have been awaited on already.

Diff for: stdlib/public/Concurrency/TaskPrivate.h

+44
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,23 @@ class alignas(sizeof(void*) * 2) ActiveTaskStatus {
238238
bool isStoredPriorityEscalated() const {
239239
return Flags & IsEscalated;
240240
}
241+
242+
/// Creates a new active task status for a task with the specified priority
243+
/// and masks away any existing priority related flags on the task status. All
244+
/// other flags about the task are unmodified. This is only safe to use to
245+
/// generate an initial task status for a new task that is not yet running.
246+
ActiveTaskStatus withNewPriority(JobPriority priority) const {
247+
return ActiveTaskStatus(Record,
248+
(Flags & ~PriorityMask) | uintptr_t(priority));
249+
}
250+
241251
ActiveTaskStatus withEscalatedPriority(JobPriority priority) const {
242252
assert(priority > getStoredPriority());
243253
return ActiveTaskStatus(Record,
244254
(Flags & ~PriorityMask)
245255
| IsEscalated | uintptr_t(priority));
246256
}
257+
247258
ActiveTaskStatus withoutStoredPriorityEscalation() const {
248259
assert(isStoredPriorityEscalated());
249260
return ActiveTaskStatus(Record, Flags & ~IsEscalated);
@@ -438,6 +449,39 @@ inline bool AsyncTask::localValuePop() {
438449
return _private().Local.popValue(this);
439450
}
440451

452+
/*************** Methods for Status records manipulation ******************/
453+
454+
/// Remove a status record from a task. After this call returns,
455+
/// the record's memory can be freely modified or deallocated.
456+
///
457+
/// This must be called synchronously with the task. The record must
458+
/// be registered with the task or else this may crash.
459+
///
460+
/// The given record need not be the last record added to
461+
/// the task, but the operation may be less efficient if not.
462+
///
463+
/// Returns false if the task has been cancelled.
464+
SWIFT_CC(swift)
465+
bool removeStatusRecord(TaskStatusRecord *record);
466+
467+
/// Add a status record to a task. This must be called synchronously with the
468+
/// task.
469+
///
470+
/// This function also takes in a function_ref which is given the task status of
471+
/// the task we're adding the record to, to determine if the current status of
472+
/// the task permits adding the status record. This function_ref may be called
473+
/// multiple times and must be idempotent.
474+
SWIFT_CC(swift)
475+
bool addStatusRecord(TaskStatusRecord *record,
476+
llvm::function_ref<bool(ActiveTaskStatus)> testAddRecord);
477+
478+
/// A helper function for updating a new child task that is created with
479+
/// information from the parent or the group that it was going to be added to.
480+
SWIFT_CC(swift)
481+
void updateNewChildWithParentAndGroupState(AsyncTask *child,
482+
ActiveTaskStatus parentStatus,
483+
TaskGroup *group);
484+
441485
} // end namespace swift
442486

443487
#endif

0 commit comments

Comments
 (0)