Skip to content

Commit 2e6baf2

Browse files
committed
Add support to addStatusRecord and removeStatusRecord so that the flags
field of an ActiveTaskStatus can also be modified while the TaskStatusRecord list is being modified. Make the StatusRecordLock reentrant. Radar-Id: rdar://problem/88093007
1 parent 4152834 commit 2e6baf2

File tree

6 files changed

+316
-130
lines changed

6 files changed

+316
-130
lines changed

include/swift/ABI/TaskStatus.h

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,26 @@
2222

2323
#include "swift/ABI/MetadataValues.h"
2424
#include "swift/ABI/Task.h"
25+
#include "swift/Runtime/HeapObject.h"
2526

2627
namespace swift {
2728

2829
/// The abstract base class for all status records.
2930
///
30-
/// TaskStatusRecords are typically allocated on the stack (possibly
31-
/// in the task context), partially initialized, and then atomically
32-
/// added to the task with `swift_task_addTaskStatusRecord`. While
33-
/// registered with the task, a status record should only be
34-
/// modified in ways that respect the possibility of asynchronous
35-
/// access by a cancelling thread. In particular, the chain of
36-
/// status records must not be disturbed. When the task leaves
37-
/// the scope that requires the status record, the record can
38-
/// be unregistered from the task with `removeStatusRecord`,
39-
/// at which point the memory can be returned to the system.
31+
/// TaskStatusRecords are typically allocated on the stack (possibly in the task
32+
/// context), partially initialized, and then atomically added to the task with
33+
/// `addStatusRecord`.
34+
///
35+
/// Status records currently are only added to a task by itself but they may be
36+
/// removed from the task by other threads, or accessed by other threads
37+
/// asynchronous to the task for other purposes.
38+
///
39+
/// As a result, while registered with the task, a status record should only be
40+
/// modified in ways that respect the possibility of asynchronous access by a
41+
/// different thread. In particular, the chain of status records must not be
42+
/// disturbed. When the task leaves the scope that requires the status record,
43+
/// the record can be unregistered from the task with `removeStatusRecord`, at
44+
/// which point the memory can be returned to the system.
4045
class TaskStatusRecord {
4146
public:
4247
TaskStatusRecordFlags Flags;

stdlib/public/Concurrency/AsyncLet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ void swift::asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet,
150150
// ok, now that the async let task actually is initialized: attach it to the
151151
// current task
152152
bool addedRecord =
153-
addStatusRecord(record, [&](ActiveTaskStatus parentStatus) {
153+
addStatusRecord(record, [&](ActiveTaskStatus parentStatus, ActiveTaskStatus& newStatus) {
154154
updateNewChildWithParentAndGroupState(task, parentStatus, NULL);
155155
return true;
156156
});

stdlib/public/Concurrency/Task.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,8 +1533,8 @@ swift_task_addCancellationHandlerImpl(
15331533

15341534
bool fireHandlerNow = false;
15351535

1536-
addStatusRecord(record, [&](ActiveTaskStatus parentStatus) {
1537-
if (parentStatus.isCancelled()) {
1536+
addStatusRecord(record, [&](ActiveTaskStatus oldStatus, ActiveTaskStatus& newStatus) {
1537+
if (oldStatus.isCancelled()) {
15381538
fireHandlerNow = true;
15391539
// We don't fire the cancellation handler here since this function needs
15401540
// to be idempotent

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,10 +888,10 @@ static void swift_taskGroup_initializeWithFlagsImpl(size_t rawGroupFlags,
888888
assert(record->getKind() == swift::TaskStatusRecordKind::TaskGroup);
889889

890890
// ok, now that the group actually is initialized: attach it to the task
891-
addStatusRecord(record, [&](ActiveTaskStatus parentStatus) {
891+
addStatusRecord(record, [&](ActiveTaskStatus oldStatus, ActiveTaskStatus& newStatus) {
892892
// If the task has already been cancelled, reflect that immediately in
893893
// the group's status.
894-
if (parentStatus.isCancelled()) {
894+
if (oldStatus.isCancelled()) {
895895
impl->statusCancel();
896896
}
897897
return true;

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace swift {
5151

5252
class AsyncTask;
5353
class TaskGroup;
54+
class ActiveTaskStatus;
5455

5556
/// Allocate task-local memory on behalf of a specific task,
5657
/// not necessarily the current one. Generally this should only be
@@ -115,6 +116,63 @@ _swift_task_getDispatchQueueSerialExecutorWitnessTable() {
115116
}
116117
#endif
117118

119+
/*************** Methods for Status records manipulation ******************/
120+
121+
/// Remove the status record from input task which may not be the current task.
122+
/// This may be called asynchronously from the current task. After this call
123+
/// returns, the record's memory can be freely modified or deallocated. The
124+
/// record must be registered with the task. If it isn't, this function will
125+
/// crash.
126+
///
127+
/// This function also takes in a function_ref which is given the old
128+
/// ActiveTaskStatus on the task and a reference to the new ActiveTaskStatus
129+
/// that is to be set on the task that we are removing the record from. It may
130+
/// modify the new ActiveTaskStatus that is to be set on the task. This function
131+
/// may be called multiple times inside a RMW loop and must be therefore be
132+
/// idempotent. The new status passed to `fn` is freshly derived from the
133+
/// current status and does not include modifications made by previous runs
134+
/// through the loop
135+
SWIFT_CC(swift)
136+
void removeStatusRecord(AsyncTask *task, TaskStatusRecord *record,
137+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn);
138+
139+
/// Remove a status record from the current task. After this call returns,
140+
/// the record's memory can be freely modified or deallocated.
141+
///
142+
/// This must be called synchronously with the task.
143+
///
144+
/// The given record need not be the last record added to
145+
/// the task, but the operation may be less efficient if not.
146+
SWIFT_CC(swift)
147+
void removeStatusRecord(TaskStatusRecord *record);
148+
149+
/// Add a status record to the current task. This must be called synchronously
150+
/// with the task.
151+
///
152+
/// This function also takes in a function_ref which is given the old
153+
/// ActiveTaskStatus on the task and a reference to the new ActiveTaskStatus
154+
/// that is to be set on the task that we are adding the record to.
155+
///
156+
/// This can be used by the function to
157+
/// (1) to determine if the current status of the task permits adding the status
158+
/// record
159+
/// (2) modify the active task status that is to be set if needed
160+
///
161+
/// If the function_ref returns false, the status record is not added to the
162+
/// task. This function_ref may be called multiple times and must be idempotent.
163+
/// The new status passed to `fn` is freshly derived from the current status and
164+
/// does not include modifications made by previous runs through the loop
165+
SWIFT_CC(swift)
166+
bool addStatusRecord(TaskStatusRecord *record,
167+
llvm::function_ref<bool(ActiveTaskStatus, ActiveTaskStatus&)> testAddRecord);
168+
169+
/// A helper function for updating a new child task that is created with
170+
/// information from the parent or the group that it was going to be added to.
171+
SWIFT_CC(swift)
172+
void updateNewChildWithParentAndGroupState(AsyncTask *child,
173+
ActiveTaskStatus parentStatus,
174+
TaskGroup *group);
175+
118176
// ==== ------------------------------------------------------------------------
119177

120178
namespace {
@@ -851,39 +909,6 @@ inline bool AsyncTask::localValuePop() {
851909
return _private().Local.popValue(this);
852910
}
853911

854-
/*************** Methods for Status records manipulation ******************/
855-
856-
/// Remove a status record from a task. After this call returns,
857-
/// the record's memory can be freely modified or deallocated.
858-
///
859-
/// This must be called synchronously with the task. The record must
860-
/// be registered with the task or else this may crash.
861-
///
862-
/// The given record need not be the last record added to
863-
/// the task, but the operation may be less efficient if not.
864-
///
865-
/// Returns false if the task has been cancelled.
866-
SWIFT_CC(swift)
867-
bool removeStatusRecord(TaskStatusRecord *record);
868-
869-
/// Add a status record to a task. This must be called synchronously with the
870-
/// task.
871-
///
872-
/// This function also takes in a function_ref which is given the task status of
873-
/// the task we're adding the record to, to determine if the current status of
874-
/// the task permits adding the status record. This function_ref may be called
875-
/// multiple times and must be idempotent.
876-
SWIFT_CC(swift)
877-
bool addStatusRecord(TaskStatusRecord *record,
878-
llvm::function_ref<bool(ActiveTaskStatus)> testAddRecord);
879-
880-
/// A helper function for updating a new child task that is created with
881-
/// information from the parent or the group that it was going to be added to.
882-
SWIFT_CC(swift)
883-
void updateNewChildWithParentAndGroupState(AsyncTask *child,
884-
ActiveTaskStatus parentStatus,
885-
TaskGroup *group);
886-
887912
} // end namespace swift
888913

889914
#endif

0 commit comments

Comments
 (0)