Skip to content

Commit d773067

Browse files
committed
Improve TaskDependencyStatusRecord so that it can now track the executor
you are enqueued on Radar-Id: rdar://problem/101864092
1 parent d0aa556 commit d773067

File tree

4 files changed

+60
-26
lines changed

4 files changed

+60
-26
lines changed

include/swift/ABI/Executor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class ExecutorRef {
6767
: Identity(identity), Implementation(implementation) {}
6868

6969
public:
70+
7071
/// A generic execution environment. When running in a generic
7172
/// environment, it's presumed to be okay to switch synchronously
7273
/// to an actor. As an executor request, this represents a request

include/swift/ABI/TaskStatus.h

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

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

2728
namespace swift {
@@ -278,22 +279,23 @@ class EscalationNotificationStatusRecord : public TaskStatusRecord {
278279
// This record is allocated for a task to record what it is dependent on before
279280
// the task can make progress again.
280281
class TaskDependencyStatusRecord : public TaskStatusRecord {
281-
// A word sized storage which references what this task is suspended waiting
282-
// for. Note that this is different from the waitQueue in the future fragment
283-
// of a task since that denotes all the tasks which this specific task, will
284-
// unblock.
282+
// A word sized storage which references what this task is waiting for. Note
283+
// that this is different from the waitQueue in the future fragment of a task
284+
// since that denotes all the tasks which this specific task, will unblock.
285285
//
286286
// This field is only really pointing to something valid when the
287-
// ActiveTaskStatus specifies that the task is suspended. It can be accessed
288-
// asynchronous to the task during escalation which will therefore require the
289-
// task status record lock for synchronization.
287+
// ActiveTaskStatus specifies that the task is suspended or enqueued. It can
288+
// be accessed asynchronous to the task during escalation which will therefore
289+
// require the task status record lock for synchronization.
290290
//
291291
// When a task has TaskDependencyStatusRecord in the status record list, it
292292
// must be the innermost status record, barring the status record lock which
293293
// could be taken while this record is present.
294294
//
295295
// The type of thing we are waiting on, is specified in the enum below
296-
union {
296+
union Dependent {
297+
constexpr Dependent() {}
298+
297299
// This task is suspended waiting on another task. This could be an async
298300
// let child task or it could be another unstructured task.
299301
AsyncTask *Task;
@@ -310,35 +312,58 @@ class TaskDependencyStatusRecord : public TaskStatusRecord {
310312
// the duration of the wait. We do not need to take an additional +1 on this
311313
// task group in this dependency record.
312314
TaskGroup *TaskGroup;
313-
} WaitingOn;
315+
316+
// The task is enqueued waiting on an executor. It could be any kind of
317+
// executor - the generic executor, the default actor's executor, or an
318+
// actor with a custom executor.
319+
//
320+
// This information is helpful to know *where* a task is enqueued into
321+
// (potentially intrusively), so that the appropriate escalation effect
322+
// (which may be different for each type of executor) can happen if a task
323+
// is escalated while enqueued.
324+
ExecutorRef Executor;
325+
} DependentOn;
314326

315327
// Enum specifying the type of dependency this task has
316328
enum DependencyKind {
317329
WaitingOnTask = 1,
318330
WaitingOnContinuation,
319331
WaitingOnTaskGroup,
332+
333+
EnqueuedOnExecutor,
320334
} DependencyKind;
321335

336+
// The task that has this task status record - ie a backpointer from the
337+
// record to the task with the record. This is not its own +1, we rely on the
338+
// fact that since this status record is linked into a task, the task is
339+
// already alive and maintained by someone and we can safely borrow the
340+
// reference.
341+
AsyncTask *WaitingTask;
342+
322343
public:
323-
TaskDependencyStatusRecord(AsyncTask *task) :
344+
TaskDependencyStatusRecord(AsyncTask *waitingTask, AsyncTask *task) :
324345
TaskStatusRecord(TaskStatusRecordKind::TaskDependency),
325-
DependencyKind(WaitingOnTask) {
326-
WaitingOn.Task = task;
346+
DependencyKind(WaitingOnTask), WaitingTask(waitingTask) {
347+
DependentOn.Task = task;
327348
}
328349

329-
TaskDependencyStatusRecord(ContinuationAsyncContext *context) :
350+
TaskDependencyStatusRecord(AsyncTask *waitingTask, ContinuationAsyncContext *context) :
330351
TaskStatusRecord(TaskStatusRecordKind::TaskDependency),
331-
DependencyKind(WaitingOnContinuation) {
332-
WaitingOn.Continuation = context;
352+
DependencyKind(WaitingOnContinuation), WaitingTask(waitingTask) {
353+
DependentOn.Continuation = context;
333354
}
334355

335-
TaskDependencyStatusRecord(TaskGroup *taskGroup) :
356+
TaskDependencyStatusRecord(AsyncTask *waitingTask, TaskGroup *taskGroup) :
336357
TaskStatusRecord(TaskStatusRecordKind::TaskDependency),
337-
DependencyKind(WaitingOnTaskGroup) {
338-
WaitingOn.TaskGroup = taskGroup;
358+
DependencyKind(WaitingOnTaskGroup), WaitingTask(waitingTask){
359+
DependentOn.TaskGroup = taskGroup;
339360
}
340361

341-
void destroy() { }
362+
TaskDependencyStatusRecord(AsyncTask *waitingTask, ExecutorRef executor) :
363+
TaskStatusRecord(TaskStatusRecordKind::TaskDependency),
364+
DependencyKind(EnqueuedOnExecutor), WaitingTask(waitingTask) {
365+
DependentOn.Executor = executor;
366+
}
342367

343368
static bool classof(const TaskStatusRecord *record) {
344369
return record->getKind() == TaskStatusRecordKind::TaskDependency;

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,6 @@ void AsyncTask::flagAsSuspended(TaskDependencyStatusRecord *dependencyStatusReco
10631063
inline void AsyncTask::destroyTaskDependency(TaskDependencyStatusRecord *dependencyRecord) {
10641064
assert(_private().dependencyRecord == dependencyRecord);
10651065

1066-
dependencyRecord->destroy();
10671066
_swift_task_dealloc_specific(this, dependencyRecord);
10681067

10691068
_private().dependencyRecord = nullptr;
@@ -1075,7 +1074,7 @@ inline void AsyncTask::flagAsSuspendedOnTask(AsyncTask *task) {
10751074
assert(_private().dependencyRecord == nullptr);
10761075

10771076
void *allocation = _swift_task_alloc_specific(this, sizeof(class TaskDependencyStatusRecord));
1078-
auto record = ::new (allocation) TaskDependencyStatusRecord(task);
1077+
auto record = ::new (allocation) TaskDependencyStatusRecord(this, task);
10791078
SWIFT_TASK_DEBUG_LOG("[Dependency] Create a dependencyRecord %p for dependency on task %p", allocation, task);
10801079
_private().dependencyRecord = record;
10811080

@@ -1086,7 +1085,7 @@ inline void AsyncTask::flagAsSuspendedOnContinuation(ContinuationAsyncContext *c
10861085
assert(_private().dependencyRecord == nullptr);
10871086

10881087
void *allocation = _swift_task_alloc_specific(this, sizeof(class TaskDependencyStatusRecord));
1089-
auto record = ::new (allocation) TaskDependencyStatusRecord(context);
1088+
auto record = ::new (allocation) TaskDependencyStatusRecord(this, context);
10901089
SWIFT_TASK_DEBUG_LOG("[Dependency] Create a dependencyRecord %p for dependency on continuation %p", allocation, context);
10911090
_private().dependencyRecord = record;
10921091

@@ -1097,7 +1096,7 @@ inline void AsyncTask::flagAsSuspendedOnTaskGroup(TaskGroup *taskGroup) {
10971096
assert(_private().dependencyRecord == nullptr);
10981097

10991098
void *allocation = _swift_task_alloc_specific(this, sizeof(class TaskDependencyStatusRecord));
1100-
auto record = ::new (allocation) TaskDependencyStatusRecord(taskGroup);
1099+
auto record = ::new (allocation) TaskDependencyStatusRecord(this, taskGroup);
11011100
SWIFT_TASK_DEBUG_LOG("[Dependency] Create a dependencyRecord %p for dependency on taskGroup %p", allocation, taskGroup);
11021101
_private().dependencyRecord = record;
11031102

stdlib/public/Concurrency/TaskStatus.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -820,19 +820,28 @@ static swift_task_escalateImpl(AsyncTask *task, JobPriority newPriority) {
820820
void TaskDependencyStatusRecord::performEscalationAction(JobPriority newPriority) {
821821
switch (this->DependencyKind) {
822822
case WaitingOnTask:
823-
swift_task_escalate(this->WaitingOn.Task, newPriority);
823+
SWIFT_TASK_DEBUG_LOG("[Dependency] Escalate dependent task %p noted in %p record",
824+
this->DependentOn.Task, this);
825+
swift_task_escalate(this->DependentOn.Task, newPriority);
824826
break;
825827
case WaitingOnContinuation:
826828
// We can't do anything meaningful to escalate this since we don't know
827829
// who will resume the continuation
830+
SWIFT_TASK_DEBUG_LOG("[Dependency] Escalate dependent continuation %p noted in %p record -- do nothing",
831+
this->DependentOn.Continuation, this);
828832
break;
829-
case WaitingOnTaskGroup: {
833+
case WaitingOnTaskGroup:
830834
// If a task is being escalated while waiting on a task group, the task
831835
// should also have a TaskGroupTaskStatusRecord and the escalation
832836
// action on that record should do the needful to propagate the
833837
// escalation to the child tasks. We can short-circuit here.
838+
SWIFT_TASK_DEBUG_LOG("[Dependency] Escalate dependent taskgroup %p noted in %p record -- do nothing",
839+
this->DependentOn.TaskGroup, this);
840+
break;
841+
case EnqueuedOnExecutor:
842+
SWIFT_TASK_DEBUG_LOG("[Dependency] Escalate dependent executor %p noted in %p record",
843+
this->DependentOn.Executor, this);
834844
break;
835-
}
836845
}
837846
}
838847

0 commit comments

Comments
 (0)