Skip to content

Commit 7dd16da

Browse files
authored
Merge pull request #63916 from apple/rokhinip/105930505-updateStatusRecord-and-friends
Improve functionality available for manipulating status records.
2 parents 7ca8241 + d0aa556 commit 7dd16da

File tree

6 files changed

+115
-64
lines changed

6 files changed

+115
-64
lines changed

include/swift/ABI/TaskStatus.h

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,10 @@ class TaskStatusRecord {
6161

6262
TaskStatusRecord *getParent() const { return Parent; }
6363

64-
/// Change the parent of this unregistered status record to the
65-
/// given record.
66-
///
67-
/// This should be used when the record has been previously initialized
68-
/// without knowing what the true parent is. If we decide to cache
69-
/// important information (e.g. the earliest timeout) in the innermost
70-
/// status record, this is the method that should fill that in
71-
/// from the parent.
64+
/// Change the parent of this status record to the given record.
7265
void resetParent(TaskStatusRecord *newParent) {
7366
Parent = newParent;
74-
// TODO: cache
7567
}
76-
77-
/// Splice a record out of the status-record chain.
78-
///
79-
/// Unlike resetParent, this assumes that it's just removing one or
80-
/// more records from the chain and that there's no need to do any
81-
/// extra cache manipulation.
82-
void spliceParent(TaskStatusRecord *newParent) { Parent = newParent; }
8368
};
8469

8570
/// A status record which states that a task has one or

stdlib/public/Concurrency/AsyncLet.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ static void swift_asyncLet_endImpl(AsyncLet *alet) {
334334

335335
// Remove the child record from the parent task
336336
auto record = asImpl(alet)->getTaskRecord();
337-
removeStatusRecord(record);
337+
removeStatusRecordFromSelf(record);
338338

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

@@ -362,7 +362,7 @@ static void asyncLet_finish_after_task_completion(SWIFT_ASYNC_CONTEXT AsyncConte
362362

363363
// Remove the child record from the parent task
364364
auto record = asImpl(alet)->getTaskRecord();
365-
removeStatusRecord(record);
365+
removeStatusRecordFromSelf(record);
366366

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

stdlib/public/Concurrency/Task.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ swift_task_addCancellationHandlerImpl(
15201520
SWIFT_CC(swift)
15211521
static void swift_task_removeCancellationHandlerImpl(
15221522
CancellationNotificationStatusRecord *record) {
1523-
removeStatusRecord(record);
1523+
removeStatusRecordFromSelf(record);
15241524
swift_task_dealloc(record);
15251525
}
15261526

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ void AccumulatingTaskGroup::destroy() {
985985
assert(this->isEmpty() && "Attempted to destroy non-empty task group!");
986986

987987
// First, remove the group from the task and deallocate the record
988-
removeStatusRecord(getTaskRecord());
988+
removeStatusRecordFromSelf(getTaskRecord());
989989

990990
// No need to drain our queue here, as by the time we call destroy,
991991
// all tasks inside the group must have been awaited on already.
@@ -1008,7 +1008,7 @@ void DiscardingTaskGroup::destroy() {
10081008
assert(this->isEmpty() && "Attempted to destroy non-empty task group!");
10091009

10101010
// First, remove the group from the task and deallocate the record
1011-
removeStatusRecord(getTaskRecord());
1011+
removeStatusRecordFromSelf(getTaskRecord());
10121012

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

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -124,34 +124,6 @@ _swift_task_getDispatchQueueSerialExecutorWitnessTable() {
124124

125125
/*************** Methods for Status records manipulation ******************/
126126

127-
/// Remove the status record from input task which may not be the current task.
128-
/// This may be called asynchronously from the current task. After this call
129-
/// returns, the record's memory can be freely modified or deallocated. The
130-
/// record must be registered with the task. If it isn't, this function will
131-
/// crash.
132-
///
133-
/// This function also takes in a function_ref which is given the old
134-
/// ActiveTaskStatus on the task and a reference to the new ActiveTaskStatus
135-
/// that is to be set on the task that we are removing the record from. It may
136-
/// modify the new ActiveTaskStatus that is to be set on the task. This function
137-
/// may be called multiple times inside a RMW loop and must be therefore be
138-
/// idempotent. The new status passed to `fn` is freshly derived from the
139-
/// current status and does not include modifications made by previous runs
140-
/// through the loop
141-
SWIFT_CC(swift)
142-
void removeStatusRecord(AsyncTask *task, TaskStatusRecord *record,
143-
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn);
144-
145-
/// Remove a status record from the current task. After this call returns,
146-
/// the record's memory can be freely modified or deallocated.
147-
///
148-
/// This must be called synchronously with the task.
149-
///
150-
/// The given record need not be the last record added to
151-
/// the task, but the operation may be less efficient if not.
152-
SWIFT_CC(swift)
153-
void removeStatusRecord(TaskStatusRecord *record);
154-
155127
/// Add a status record to the input task.
156128
///
157129
/// Clients can optionally pass in the status of the task if they have already
@@ -160,6 +132,12 @@ void removeStatusRecord(TaskStatusRecord *record);
160132
/// perform. This status will be updated with the last status on the task prior
161133
/// to updating it with the new status if the input function_ref allows so.
162134
///
135+
/// Clients can optionally pass in the status of the task if they have already
136+
/// done the load on it or if they require the oldStatus on the task prior to
137+
/// the atomic ActiveTaskStatus update that addStatusRecord will do. This status
138+
/// will be updated with the last status on the task prior to updating it with
139+
/// the new status if the input function_ref allows so.
140+
///
163141
/// This function also takes in a function_ref which is given the old
164142
/// ActiveTaskStatus on the task and a reference to the new ActiveTaskStatus
165143
/// that is to be set on the task that we are adding the record to.
@@ -195,6 +173,59 @@ SWIFT_CC(swift)
195173
bool addStatusRecordToSelf(TaskStatusRecord *record, ActiveTaskStatus& taskStatus,
196174
llvm::function_ref<bool(ActiveTaskStatus, ActiveTaskStatus&)> testAddRecord);
197175

176+
/// Remove the status record from input task which may not be the current task.
177+
/// This may be called asynchronously from the current task. After this call
178+
/// returns, the record's memory can be freely modified or deallocated. The
179+
/// record must be registered with the task. If it isn't, this function will
180+
/// crash.
181+
///
182+
/// The given record need not be the last record added to
183+
/// the task, but the operation may be less efficient if not.
184+
///
185+
/// This function also takes in a function_ref which is given the old
186+
/// ActiveTaskStatus on the task and a reference to the new ActiveTaskStatus
187+
/// that is to be set on the task that we are removing the record from. It may
188+
/// modify the new ActiveTaskStatus that is to be set on the task. This function
189+
/// may be called multiple times inside a RMW loop and must be therefore be
190+
/// idempotent. The new status passed to `fn` is freshly derived from the
191+
/// current status and does not include modifications made by previous runs
192+
/// through the loop
193+
SWIFT_CC(swift)
194+
void removeStatusRecord(AsyncTask *task, TaskStatusRecord *record, ActiveTaskStatus& status,
195+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn = nullptr);
196+
197+
SWIFT_CC(swift)
198+
void removeStatusRecord(AsyncTask *task, TaskStatusRecord *record,
199+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn = nullptr);
200+
201+
/// Remove a status record from the current task. This must be called
202+
/// synchronously with the task.
203+
SWIFT_CC(swift)
204+
void removeStatusRecordFromSelf(TaskStatusRecord *record, ActiveTaskStatus &status,
205+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn = nullptr);
206+
207+
SWIFT_CC(swift)
208+
void removeStatusRecordFromSelf(TaskStatusRecord *record,
209+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn = nullptr);
210+
211+
/// Update the specified input status record while holding the status record
212+
/// lock of the task. The status record must already be registered with the
213+
/// task - if it isn't, this API provides no additional protections.
214+
///
215+
/// This function also takes in a function_ref which is given the old
216+
/// ActiveTaskStatus on the task and a reference to the new ActiveTaskStatus
217+
/// that is to be set on the task when we are unlocking the task status record
218+
/// lock. It may modify the new ActiveTaskStatus that is to be set on the task.
219+
/// This function may be called multiple times inside a RMW loop and must be
220+
/// therefore be idempotent. The new status passed to `fn` is freshly derived
221+
/// from the current status and does not include modifications made by previous
222+
/// runs through the loop
223+
SWIFT_CC(swift)
224+
void updateStatusRecord(AsyncTask *task, TaskStatusRecord *record,
225+
llvm::function_ref<void()>updateRecord,
226+
ActiveTaskStatus& status,
227+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn = nullptr);
228+
198229
/// A helper function for updating a new child task that is created with
199230
/// information from the parent or the group that it was going to be added to.
200231
SWIFT_CC(swift)
@@ -842,7 +873,7 @@ inline void AsyncTask::flagAsRunning() {
842873
SWIFT_TASK_DEBUG_LOG("[Dependency] %p->flagAsRunning() and remove dependencyRecord %p",
843874
this, dependencyRecord);
844875

845-
removeStatusRecord(this, dependencyRecord, [&](ActiveTaskStatus oldStatus,
876+
removeStatusRecord(this, dependencyRecord, oldStatus, [&](ActiveTaskStatus unused,
846877
ActiveTaskStatus& newStatus) {
847878

848879
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
@@ -863,7 +894,6 @@ inline void AsyncTask::flagAsRunning() {
863894
newStatus = newStatus.withoutEnqueued();
864895
newStatus = newStatus.withoutTaskDependency();
865896
});
866-
867897
this->destroyTaskDependency(dependencyRecord);
868898

869899
adoptTaskVoucher(this);

stdlib/public/Concurrency/TaskStatus.cpp

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ bool swift::addStatusRecord(AsyncTask *task, TaskStatusRecord *newRecord,
299299
ActiveTaskStatus& oldStatus,
300300
llvm::function_ref<bool(ActiveTaskStatus, ActiveTaskStatus&)> shouldAddRecord) {
301301

302+
SWIFT_TASK_DEBUG_LOG("Adding %p record to task %p", newRecord, task);
302303
while (true) {
303304
// Wait for any active lock to be released.
304305
if (oldStatus.isStatusRecordLocked()) {
@@ -343,16 +344,22 @@ bool swift::addStatusRecordToSelf(TaskStatusRecord *record,
343344
return addStatusRecord(swift_task_getCurrent(), record, testAddRecord);
344345
}
345346

347+
SWIFT_CC(swift)
348+
bool swift::addStatusRecordToSelf(TaskStatusRecord *record, ActiveTaskStatus &status,
349+
llvm::function_ref<bool(ActiveTaskStatus, ActiveTaskStatus&)> testAddRecord) {
350+
return addStatusRecord(swift_task_getCurrent(), record, status, testAddRecord);
351+
}
352+
346353
static void removeStatusRecordLocked(ActiveTaskStatus status, TaskStatusRecord *record) {
347354
bool removedRecord = false;
348355
auto cur = status.getInnermostRecord();
349356
assert(cur->getKind() == TaskStatusRecordKind::Private_RecordLock);
350357

351-
// Splice the record out.
358+
// Cut the record out.
352359
while (cur != nullptr) {
353360
auto next = cur->getParent();
354361
if (next == record) {
355-
cur->spliceParent(record->getParent());
362+
cur->resetParent(record->getParent());
356363
removedRecord = true;
357364
break;
358365
}
@@ -365,13 +372,12 @@ static void removeStatusRecordLocked(ActiveTaskStatus status, TaskStatusRecord *
365372
// modify some flags in the ActiveTaskStatus at the same time.
366373
SWIFT_CC(swift)
367374
void swift::removeStatusRecord(AsyncTask *task, TaskStatusRecord *record,
368-
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn = nullptr) {
375+
ActiveTaskStatus& oldStatus,
376+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn) {
369377

370378
SWIFT_TASK_DEBUG_LOG("remove status record = %p, from task = %p",
371379
record, task);
372380

373-
auto oldStatus = task->_private()._status().load(std::memory_order_relaxed);
374-
375381
if (oldStatus.isStatusRecordLocked() &&
376382
waitForStatusRecordUnlockIfNotSelfLocked(task, oldStatus)) {
377383
SWIFT_TASK_DEBUG_LOG("[StatusRecordLock] Lock for task %p is already owned by thread", task);
@@ -446,16 +452,46 @@ void swift::removeStatusRecord(AsyncTask *task, TaskStatusRecord *record,
446452

447453
}
448454

449-
// Convenience wrapper for when we don't care to make any modifications to the
450-
// flags fields of the active task status when we are removing a task status
451-
// record
455+
// Convenience wrapper for when client hasnt already done the load of the status
452456
SWIFT_CC(swift)
453-
void swift::removeStatusRecord(TaskStatusRecord *record) {
457+
void swift::removeStatusRecord(AsyncTask *task, TaskStatusRecord *record,
458+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn) {
459+
auto oldStatus = task->_private()._status().load(std::memory_order_relaxed);
460+
return removeStatusRecord(task, record, oldStatus, fn);
461+
}
454462

455-
auto task = swift_task_getCurrent();
456-
SWIFT_TASK_DEBUG_LOG("remove status record = %p, from current task = %p",
457-
record, task);
458-
return removeStatusRecord(task, record);
463+
// Convenience wrapper for modifications on current task
464+
SWIFT_CC(swift)
465+
void swift::removeStatusRecordFromSelf(TaskStatusRecord *record, ActiveTaskStatus &status,
466+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn) {
467+
return removeStatusRecord(swift_task_getCurrent(), record, status, fn);
468+
}
469+
470+
SWIFT_CC(swift)
471+
void swift::removeStatusRecordFromSelf(TaskStatusRecord *record,
472+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn) {
473+
return removeStatusRecord(swift_task_getCurrent(), record, fn);
474+
}
475+
476+
SWIFT_CC(swift)
477+
void swift::updateStatusRecord(AsyncTask *task, TaskStatusRecord *record,
478+
llvm::function_ref<void()>updateRecord,
479+
ActiveTaskStatus& status,
480+
llvm::function_ref<void(ActiveTaskStatus, ActiveTaskStatus&)>fn) {
481+
482+
withStatusRecordLock(task, status, [&](ActiveTaskStatus lockedStatus) {
483+
#if NDEBUG
484+
bool foundRecord = false;
485+
for (auto cur: lockedStatus.records()) {
486+
if (cur == record) {
487+
foundRecord = true;
488+
break;
489+
}
490+
}
491+
assert(foundRecord);
492+
#endif
493+
updateRecord();
494+
}, fn);
459495
}
460496

461497
SWIFT_CC(swift)

0 commit comments

Comments
 (0)