Skip to content

Commit

Permalink
Change the async ABI to not pass the active task and executor.
Browse files Browse the repository at this point in the history
Most of the async runtime functions have been changed to not
expect the task and executor to be passed in.  When knowing the
task and executor is necessary, there are runtime functions
available to recover them.

The biggest change I had to make to a runtime function signature
was to swift_task_switch, which has been altered to expect to be
passed the context and resumption function instead of requiring
the caller to park the task.  This has the pleasant consequence
of allowing the implementation to very quickly turn around when
it recognizes that the current executor is satisfactory.  It does
mean that on arm64e we have to sign the continuation function
pointer as an argument and then potentially resign it when
assigning into the task's resume slot.

rdar://70546948
  • Loading branch information
rjmccall committed Mar 17, 2021
1 parent 5499235 commit 6c879d6
Show file tree
Hide file tree
Showing 88 changed files with 624 additions and 684 deletions.
4 changes: 2 additions & 2 deletions include/swift/ABI/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ class ExecutorRef {

using JobInvokeFunction =
SWIFT_CC(swiftasync)
void (Job *, ExecutorRef);
void (Job *);

using TaskContinuationFunction =
SWIFT_CC(swiftasync)
void (AsyncTask *, ExecutorRef, SWIFT_ASYNC_CONTEXT AsyncContext *);
void (SWIFT_ASYNC_CONTEXT AsyncContext *);

template <class AsyncSignature>
class AsyncFunctionPointer;
Expand Down
29 changes: 14 additions & 15 deletions include/swift/ABI/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ class alignas(2 * alignof(void*)) Job : public HeapObject {
/// Given that we've fully established the job context in the current
/// thread, actually start running this job. To establish the context
/// correctly, call swift_job_run or runJobInExecutorContext.
void runInFullyEstablishedContext(ExecutorRef currentExecutor);
void runInFullyEstablishedContext();

/// Given that we've fully established the job context in the
/// current thread, and that the job is a simple (non-task) job,
/// actually start running this job.
void runSimpleInFullyEstablishedContext(ExecutorRef currentExecutor) {
RunJob(this, currentExecutor);
void runSimpleInFullyEstablishedContext() {
RunJob(this);
}
};

Expand Down Expand Up @@ -193,8 +193,8 @@ class AsyncTask : public Job {
/// in the current thread, start running this task. To establish
/// the job context correctly, call swift_job_run or
/// runInExecutorContext.
void runInFullyEstablishedContext(ExecutorRef currentExecutor) {
ResumeTask(this, currentExecutor, ResumeContext);
void runInFullyEstablishedContext() {
ResumeTask(ResumeContext);
}

/// Check whether this task has been cancelled.
Expand Down Expand Up @@ -433,7 +433,7 @@ class AsyncTask : public Job {
///
/// Upon completion, any waiting tasks will be scheduled on the given
/// executor.
void completeFuture(AsyncContext *context, ExecutorRef executor);
void completeFuture(AsyncContext *context);

// ==== ----------------------------------------------------------------------

Expand All @@ -456,11 +456,11 @@ static_assert(sizeof(AsyncTask) == 14 * sizeof(void*),
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
"AsyncTask alignment is wrong");

inline void Job::runInFullyEstablishedContext(ExecutorRef currentExecutor) {
inline void Job::runInFullyEstablishedContext() {
if (auto task = dyn_cast<AsyncTask>(this))
task->runInFullyEstablishedContext(currentExecutor);
task->runInFullyEstablishedContext();
else
runSimpleInFullyEstablishedContext(currentExecutor);
runSimpleInFullyEstablishedContext();
}

/// An asynchronous context within a task. Generally contexts are
Expand All @@ -484,6 +484,7 @@ class alignas(MaximumAlignment) AsyncContext {
ResumeParent;

/// The executor that the parent needs to be resumed on.
/// FIXME: remove this
ExecutorRef ResumeParentExecutor;

/// Flags describing this context.
Expand All @@ -496,10 +497,9 @@ class alignas(MaximumAlignment) AsyncContext {

AsyncContext(AsyncContextFlags flags,
TaskContinuationFunction *resumeParent,
ExecutorRef resumeParentExecutor,
AsyncContext *parent)
: Parent(parent), ResumeParent(resumeParent),
ResumeParentExecutor(resumeParentExecutor),
ResumeParentExecutor(ExecutorRef::generic()),
Flags(flags) {}

AsyncContext(const AsyncContext &) = delete;
Expand All @@ -509,10 +509,10 @@ class alignas(MaximumAlignment) AsyncContext {
///
/// Generally this should be tail-called.
SWIFT_CC(swiftasync)
void resumeParent(AsyncTask *task, ExecutorRef executor) {
void resumeParent() {
// TODO: destroy context before returning?
// FIXME: force tail call
return ResumeParent(task, executor, Parent);
return ResumeParent(Parent);
}
};

Expand All @@ -529,11 +529,10 @@ class YieldingAsyncContext : public AsyncContext {

YieldingAsyncContext(AsyncContextFlags flags,
TaskContinuationFunction *resumeParent,
ExecutorRef resumeParentExecutor,
TaskContinuationFunction *yieldToParent,
ExecutorRef yieldToParentExecutor,
AsyncContext *parent)
: AsyncContext(flags, resumeParent, resumeParentExecutor, parent),
: AsyncContext(flags, resumeParent, parent),
YieldToParent(yieldToParent),
YieldToParentExecutor(yieldToParentExecutor) {}

Expand Down
2 changes: 1 addition & 1 deletion include/swift/ABI/TaskGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class alignas(Alignment_TaskGroup) TaskGroup {
void *PrivateData[NumWords_TaskGroup];

/// Upon a future task's completion, offer it to the task group it belongs to.
void offer(AsyncTask *completed, AsyncContext *context, ExecutorRef executor);
void offer(AsyncTask *completed, AsyncContext *context);
};

} // end namespace swift
Expand Down
81 changes: 36 additions & 45 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#include "swift/ABI/TaskGroup.h"
#include "swift/ABI/TaskStatus.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"

namespace swift {
class DefaultActor;

Expand All @@ -34,7 +37,6 @@ struct AsyncTaskAndContext {
/// function.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTaskAndContext swift_task_create_f(JobFlags flags,
AsyncTask *parent,
ThinNullaryAsyncSignature::FunctionType *function,
size_t initialContextSize);

Expand All @@ -47,24 +49,23 @@ using FutureAsyncSignature =
/// closure.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTaskAndContext swift_task_create_future(
JobFlags flags, AsyncTask *parent, const Metadata *futureResultType,
JobFlags flags, const Metadata *futureResultType,
void *closureEntryPoint,
HeapObject * /* +1 */ closureContext);

/// Create a task object with a future which will run the given
/// function.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTaskAndContext swift_task_create_future_f(
JobFlags flags, AsyncTask *parent, const Metadata *futureResultType,
JobFlags flags, const Metadata *futureResultType,
FutureAsyncSignature::FunctionType *function,
size_t initialContextSize);

/// Create a task object with a future which will run the given
/// closure, and offer its result to the task group
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTaskAndContext swift_task_create_group_future(
JobFlags flags,
AsyncTask *parent, TaskGroup *group,
JobFlags flags, TaskGroup *group,
const Metadata *futureResultType,
void *closureEntryPoint,
HeapObject * /* +1 */ closureContext);
Expand All @@ -73,8 +74,7 @@ AsyncTaskAndContext swift_task_create_group_future(
/// function, and offer its result to the task group
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTaskAndContext swift_task_create_group_future_f(
JobFlags flags,
AsyncTask *parent, TaskGroup *group,
JobFlags flags, TaskGroup *group,
const Metadata *futureResultType,
FutureAsyncSignature::FunctionType *function,
size_t initialContextSize);
Expand All @@ -85,15 +85,15 @@ AsyncTaskAndContext swift_task_create_group_future_f(
///
/// All allocations will be rounded to a multiple of MAX_ALIGNMENT.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void *swift_task_alloc(AsyncTask *task, size_t size);
void *swift_task_alloc(size_t size);

/// Deallocate memory in a task.
///
/// The pointer provided must be the last pointer allocated on
/// this task that has not yet been deallocated; that is, memory
/// must be allocated and deallocated in a strict stack discipline.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_dealloc(AsyncTask *task, void *ptr);
void swift_task_dealloc(void *ptr);

/// Cancel a task and all of its child tasks.
///
Expand All @@ -103,9 +103,9 @@ void swift_task_dealloc(AsyncTask *task, void *ptr);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_cancel(AsyncTask *task);

/// Cancel all child tasks of `parent` that belong to the `group`.
/// Cancel all the child tasks that belong to the `group`.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_cancel_group_child_tasks(AsyncTask *task, TaskGroup *group);
void swift_task_cancel_group_child_tasks(TaskGroup *group);

/// Escalate the priority of a task and all of its child tasks.
///
Expand Down Expand Up @@ -170,26 +170,22 @@ swift_taskGroup_wait_next_throwing;
/// Its Swift signature is
///
/// \code
/// func swift_taskGroup_create(
/// _ task: Builtin.NativeObject
/// ) -> Builtin.RawPointer
/// func swift_taskGroup_create() -> Builtin.RawPointer
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
TaskGroup* swift_taskGroup_create(AsyncTask *task); // TODO: probably remove this call, and just use the initialize always
TaskGroup* swift_taskGroup_create(); // TODO: probably remove this call, and just use the initialize always

/// Initialize a `TaskGroup` in the passed `group` memory location.
/// The caller is responsible for retaining and managing the group's lifecycle.
///
/// Its Swift signature is
///
/// \code
/// func swift_taskGroup_initialize(
/// _ task: Builtin.NativeObject,
/// group: Builtin.RawPointer,
/// func swift_taskGroup_initialize(group: Builtin.RawPointer
/// )
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_taskGroup_initialize(AsyncTask *task, TaskGroup *group);
void swift_taskGroup_initialize(TaskGroup *group);

/// Attach a child task to the parent task's task group record.
///
Expand All @@ -215,13 +211,10 @@ void swift_taskGroup_attachChild(TaskGroup *group, AsyncTask *child);
/// This function MUST be called from the AsyncTask running the task group.
///
/// \code
/// func swift_taskGroup_destroy(
/// _ task: Builtin.NativeObject,
/// _ group: UnsafeRawPointer
/// )
/// func swift_taskGroup_destroy(_ group: UnsafeRawPointer)
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_taskGroup_destroy(AsyncTask *task, TaskGroup *group);
void swift_taskGroup_destroy(TaskGroup *group);

/// Before starting a task group child task, inform the group that there is one
/// more 'pending' child to account for.
Expand All @@ -247,13 +240,10 @@ bool swift_taskGroup_addPending(TaskGroup *group);
/// Its Swift signature is
///
/// \code
/// func swift_taskGroup_cancelAll(
/// task: Builtin.NativeObject,
/// group: UnsafeRawPointer
/// )
/// func swift_taskGroup_cancelAll(group: UnsafeRawPointer)
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_taskGroup_cancelAll(AsyncTask *task, TaskGroup *group);
void swift_taskGroup_cancelAll(TaskGroup *group);

/// Check ONLY if the group was explicitly cancelled, e.g. by `cancelAll`.
///
Expand All @@ -263,13 +253,10 @@ void swift_taskGroup_cancelAll(AsyncTask *task, TaskGroup *group);
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_taskGroup_isCancelled(
/// task: Builtin.NativeObject,
/// group: UnsafeRawPointer
/// )
/// func swift_taskGroup_isCancelled(group: UnsafeRawPointer)
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
bool swift_taskGroup_isCancelled(AsyncTask *task, TaskGroup *group);
bool swift_taskGroup_isCancelled(TaskGroup *group);

/// Check the readyQueue of a task group, return true if it has no pending tasks.
///
Expand All @@ -291,8 +278,7 @@ bool swift_taskGroup_isEmpty(TaskGroup *group);
/// If the task is already cancelled, returns `false` but still adds
/// the status record.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
bool swift_task_addStatusRecord(AsyncTask *task,
TaskStatusRecord *record);
bool swift_task_addStatusRecord(TaskStatusRecord *record);

/// Add a status record to a task if the task has not already
/// been cancelled. The record should not be modified while it is
Expand All @@ -303,8 +289,7 @@ bool swift_task_addStatusRecord(AsyncTask *task,
/// If the task is already cancelled, returns `false` and does not
/// add the status record.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
bool swift_task_tryAddStatusRecord(AsyncTask *task,
TaskStatusRecord *record);
bool swift_task_tryAddStatusRecord(TaskStatusRecord *record);

/// Remove a status record from a task. After this call returns,
/// the record's memory can be freely modified or deallocated.
Expand All @@ -317,7 +302,7 @@ bool swift_task_tryAddStatusRecord(AsyncTask *task,
///s
/// Returns false if the task has been cancelled.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
bool swift_task_removeStatusRecord(AsyncTask *task, TaskStatusRecord *record);
bool swift_task_removeStatusRecord(TaskStatusRecord *record);

/// Attach a child task to its parent task and return the newly created
/// `ChildTaskStatusRecord`.
Expand All @@ -326,11 +311,11 @@ bool swift_task_removeStatusRecord(AsyncTask *task, TaskStatusRecord *record);
/// `swift_task_detachChild` when the child has completed.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
ChildTaskStatusRecord*
swift_task_attachChild(AsyncTask *parent, AsyncTask *child);
swift_task_attachChild(AsyncTask *child);

/// Remove a child task from the parent tracking it.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_detachChild(AsyncTask *parent, ChildTaskStatusRecord *record);
void swift_task_detachChild(ChildTaskStatusRecord *record);

SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
size_t swift_task_getJobFlags(AsyncTask* task);
Expand All @@ -342,12 +327,12 @@ bool swift_task_isCancelled(AsyncTask* task);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
CancellationNotificationStatusRecord*
swift_task_addCancellationHandler(
AsyncTask *task, CancellationNotificationStatusRecord::FunctionType handler);
CancellationNotificationStatusRecord::FunctionType handler);

/// Remove the passed cancellation record from the task.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_removeCancellationHandler(
AsyncTask *task, CancellationNotificationStatusRecord *record);
CancellationNotificationStatusRecord *record);

/// Get a task local value from the passed in task. Its Swift signature is
///
Expand Down Expand Up @@ -446,8 +431,8 @@ void swift_task_runAndBlockThread(const void *function,
/// generally be tail-called, as it may continue executing the task
/// synchronously if possible.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_task_switch(AsyncTask *task,
ExecutorRef currentExecutor,
void swift_task_switch(SWIFT_ASYNC_CONTEXT AsyncContext *resumeToContext,
TaskContinuationFunction *resumeFunction,
ExecutorRef newExecutor);

/// Enqueue the given job to run asynchronously on the given executor.
Expand Down Expand Up @@ -552,6 +537,12 @@ void swift_job_run(Job *job, ExecutorRef executor);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTask *swift_task_getCurrent(void);

/// Return the current thread's active executor reference.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
ExecutorRef swift_task_getCurrentExecutor(void);

}

#pragma clang diagnostic pop

#endif
Loading

0 comments on commit 6c879d6

Please sign in to comment.