Skip to content

Merge the concurrency-5.5-abi-2 branch into release/5.5 #37474

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8534aa6
[Concurrency] Store child record when async let child task spawned
ktoso Mar 30, 2021
1e161f6
concurrency: make the startAsyncLet closure no-escaping
eeckstein Apr 20, 2021
70c7309
ClosureLifetimeFixup: support the startAsyncLet builtin.
eeckstein Apr 20, 2021
808cf13
[Concurrency] Harden async_task_async_let_child_cancel so it does not…
ktoso Apr 28, 2021
d46730c
Add a utility to clone an `@available` attribute.
rjmccall Apr 14, 2021
51b420e
Introduce basic support for custom executors.
rjmccall Apr 13, 2021
64a2084
Remove the implicit nil inhabitant of Builtin.Executor,
rjmccall Apr 15, 2021
48cb386
Default actors carry a null witness-table pointer in Builtin.Executor.
rjmccall Apr 16, 2021
a6c3482
Merge remote-tracking branch 'origin/release/5.5' into concurrency-5.…
fredriss May 12, 2021
7eefefa
Concurrency: allocate an async-let task with its parent's stack alloc…
eeckstein Apr 22, 2021
84d6dd9
[Concurrency] Add a unique Task ID to AsyncTask
fredriss May 7, 2021
a7a552b
Add scheme for concurrency-5.5-abi-2
shahmishal May 12, 2021
7853a47
Merge pull request #37392 from apple/shahmishal/concurrency-5.5-abi-2…
shahmishal May 12, 2021
e822f94
Merge remote-tracking branch 'origin/concurrency-5.5-abi-2' into rele…
rjmccall May 18, 2021
1a31fba
Additional merge fixes
rjmccall May 18, 2021
27bf2c9
[Concurrency] Remove Task.current because it prevents task-local allo…
ktoso Apr 22, 2021
46a75d9
Disable this test on Windows; it seems to not progress. (#37200)
rjmccall May 1, 2021
b598965
Fix some concurrency tests not to run on os stdlib bots
aschwaighofer May 5, 2021
dac80f6
Test fix to use the SwiftStdlib 5.5 check.
rjmccall May 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions include/swift/ABI/AsyncLet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===--- AsyncLet.h - ABI structures for async let -00-----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Swift ABI describing task groups.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_ABI_TASK_ASYNC_LET_H
#define SWIFT_ABI_TASK_ASYNC_LET_H

#include "swift/ABI/Task.h"
#include "swift/ABI/HeapObject.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/Config.h"
#include "swift/Basic/RelativePointer.h"
#include "swift/Basic/STLExtras.h"

namespace swift {

/// Represents an in-flight `async let`, i.e. the Task that is computing the
/// result of the async let, along with the awaited status and other metadata.
class alignas(Alignment_AsyncLet) AsyncLet {
public:
// These constructors do not initialize the AsyncLet instance, and the
// destructor does not destroy the AsyncLet instance; you must call
// swift_asyncLet_{start,end} yourself.
constexpr AsyncLet()
: PrivateData{} {}

// FIXME: not sure how many words we should reserve
void *PrivateData[NumWords_AsyncLet];

// TODO: we could offer a "was awaited on" check here

/// Returns the child task that is associated with this async let.
/// The tasks completion is used to fulfil the value represented by this async let.
AsyncTask *getTask() const;

};

} // end namespace swift

#endif // SWIFT_ABI_TASK_ASYNC_LET_H
79 changes: 38 additions & 41 deletions include/swift/ABI/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,42 @@ class AsyncContext;
class AsyncTask;
class DefaultActor;
class Job;

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
SWIFT_EXPORT_FROM(swift_Concurrency)
Metadata* MainActorMetadata;
class SerialExecutorWitnessTable;

/// An unmanaged reference to an executor.
///
/// The representation is two words: identity and implementation.
/// The identity word is a reference to the executor object; for
/// default actors, this is the actor object. The implementation
/// word describes how the executor works; it carries a witness table
/// as well as a small number of bits indicating various special
/// implementation properties. As an exception to both of these
/// rules, a null identity represents a generic executor and
/// implies a null implementation word.
/// This type corresponds to the type Optional<Builtin.Executor> in
/// Swift. The representation of nil in Optional<Builtin.Executor>
/// aligns with what this type calls the generic executor, so the
/// notional subtype of this type which is never generic corresponds
/// to the type Builtin.Executor.
///
/// An executor reference is divided into two pieces:
///
/// - The identity, which is just a (potentially ObjC) object
/// reference; when this is null, the reference is generic.
/// Equality of executor references is based solely on equality
/// of identity.
///
/// - The implementation, which is an optional reference to a
/// witness table for the SerialExecutor protocol. When this
/// is null, but the identity is non-null, the reference is to
/// a default actor. The low bits of the implementation pointer
/// are reserved for the use of marking interesting properties
/// about the executor's implementation. The runtime masks these
/// bits off before accessing the witness table, so setting them
/// in the future should back-deploy as long as the witness table
/// reference is still present.
class ExecutorRef {
HeapObject *Identity; // Not necessarily Swift reference-countable
uintptr_t Implementation;

// We future-proof the ABI here by masking the low bits off the
// implementation pointer before using it as a witness table.
enum: uintptr_t {
WitnessTableMask = ~uintptr_t(alignof(void*) - 1)
};

constexpr ExecutorRef(HeapObject *identity, uintptr_t implementation)
: Identity(identity), Implementation(implementation) {}

Expand All @@ -58,23 +75,11 @@ class ExecutorRef {
return ExecutorRef(nullptr, 0);
}

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
/// NOTE: I didn't go with Executor::forMainActor(DefaultActor*) because
/// __swift_run_job_main_executor can't take more than one argument.
static ExecutorRef mainExecutor() {
auto identity = getMainActorIdentity();
return ExecutorRef(identity, 0);
}
static HeapObject *getMainActorIdentity() {
return reinterpret_cast<HeapObject*>(
ExecutorRefFlags::MainActorIdentity);
}

/// Given a pointer to a default actor, return an executor reference
/// for it.
static ExecutorRef forDefaultActor(DefaultActor *actor) {
assert(actor);
return ExecutorRef(actor, unsigned(ExecutorRefFlags::DefaultActor));
return ExecutorRef(actor, 0);
}

HeapObject *getIdentity() const {
Expand All @@ -86,37 +91,29 @@ class ExecutorRef {
return Identity == 0;
}

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
bool isMainExecutor() const {
if (Identity == getMainActorIdentity())
return true;

if (Identity == nullptr || MainActorMetadata == nullptr)
return false;

Metadata const* metadata = swift_getObjectType(Identity);
return metadata == MainActorMetadata;
}

/// Is this a default-actor executor reference?
bool isDefaultActor() const {
return Implementation & unsigned(ExecutorRefFlags::DefaultActor);
return !isGeneric() && Implementation == 0;
}
DefaultActor *getDefaultActor() const {
assert(isDefaultActor());
return reinterpret_cast<DefaultActor*>(Identity);
}

const SerialExecutorWitnessTable *getSerialExecutorWitnessTable() const {
assert(!isGeneric() && !isDefaultActor());
auto table = Implementation & WitnessTableMask;
return reinterpret_cast<const SerialExecutorWitnessTable*>(table);
}

/// Do we have to do any work to start running as the requested
/// executor?
bool mustSwitchToRun(ExecutorRef newExecutor) const {
return Identity != newExecutor.Identity;
}

bool operator==(ExecutorRef other) const {
return Identity == other.Identity
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
|| (isMainExecutor() && other.isMainExecutor());
return Identity == other.Identity;
}
bool operator!=(ExecutorRef other) const {
return !(*this == other);
Expand Down
24 changes: 8 additions & 16 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ enum {

/// The number of words in a task group.
NumWords_TaskGroup = 32,

/// The number of words in an AsyncLet (flags + task pointer)
NumWords_AsyncLet = 8, // TODO: not sure how much is enough, these likely could be pretty small
};

struct InProcess;
Expand Down Expand Up @@ -127,6 +130,9 @@ const size_t Alignment_DefaultActor = MaximumAlignment;
/// The alignment of a TaskGroup.
const size_t Alignment_TaskGroup = MaximumAlignment;

/// The alignment of an AsyncLet.
const size_t Alignment_AsyncLet = MaximumAlignment;

/// Flags stored in the value-witness table.
template <typename int_type>
class TargetValueWitnessFlags {
Expand Down Expand Up @@ -1994,7 +2000,7 @@ enum class JobPriority : size_t {
};

/// Flags for schedulable jobs.
class JobFlags : public FlagSet<size_t> {
class JobFlags : public FlagSet<uint32_t> {
public:
enum {
Kind = 0,
Expand All @@ -2013,7 +2019,7 @@ class JobFlags : public FlagSet<size_t> {
Task_IsContinuingAsyncTask = 27,
};

explicit JobFlags(size_t bits) : FlagSet(bits) {}
explicit JobFlags(uint32_t bits) : FlagSet(bits) {}
JobFlags(JobKind kind) { setKind(kind); }
JobFlags(JobKind kind, JobPriority priority) {
setKind(kind);
Expand Down Expand Up @@ -2194,20 +2200,6 @@ enum class ContinuationStatus : size_t {
Resumed = 2
};

/// Flags describing the executor implementation that are stored
/// in the ExecutorRef.
enum class ExecutorRefFlags : size_t {
// The number of bits available here is very limited because it's
// potentially just the alignment bits of a protocol witness table
// pointer

/// The executor is a default actor.
DefaultActor = 0x1,

/// TODO: remove this
MainActorIdentity = 0x2,
};

} // end namespace swift

#endif // SWIFT_ABI_METADATAVALUES_H
57 changes: 52 additions & 5 deletions include/swift/ABI/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ class TaskGroup;
extern FullMetadata<DispatchClassMetadata> jobHeapMetadata;

/// A schedulable job.
class alignas(2 * alignof(void*)) Job : public HeapObject {
class alignas(2 * alignof(void*)) Job :
// For async-let tasks, the refcount bits are initialized as "immortal"
// because such a task is allocated with the parent's stack allocator.
public HeapObject {
public:
// Indices into SchedulerPrivate, for use by the runtime.
enum {
Expand All @@ -65,6 +68,9 @@ class alignas(2 * alignof(void*)) Job : public HeapObject {

JobFlags Flags;

// Derived classes can use this to store a Job Id.
uint32_t Id = 0;

// We use this union to avoid having to do a second indirect branch
// when resuming an asynchronous task, which we expect will be the
// common case.
Expand All @@ -88,6 +94,14 @@ class alignas(2 * alignof(void*)) Job : public HeapObject {
assert(isAsyncTask() && "wrong constructor for a non-task job");
}

/// Create a job with "immortal" reference counts.
/// Used for async let tasks.
Job(JobFlags flags, TaskContinuationFunction *invoke,
const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal)
: HeapObject(metadata, immortal), Flags(flags), ResumeTask(invoke) {
assert(isAsyncTask() && "wrong constructor for a non-task job");
}

bool isAsyncTask() const {
return Flags.isAsyncTask();
}
Expand All @@ -110,8 +124,13 @@ class alignas(2 * alignof(void*)) Job : public HeapObject {
};

// The compiler will eventually assume these.
#if defined(__LP64__) || defined(_WIN64)
static_assert(sizeof(Job) == 6 * sizeof(void*),
"Job size is wrong");
#else
static_assert(sizeof(Job) == 8 * sizeof(void*),
"Job size is wrong");
#endif
static_assert(alignof(Job) == 2 * alignof(void*),
"Job alignment is wrong");

Expand Down Expand Up @@ -218,8 +237,25 @@ class AsyncTask : public Job {
Status(ActiveTaskStatus()),
Local(TaskLocal::Storage()) {
assert(flags.isAsyncTask());
Id = getNextTaskId();
}

/// Create a task with "immortal" reference counts.
/// Used for async let tasks.
AsyncTask(const HeapMetadata *metadata, InlineRefCounts::Immortal_t immortal,
JobFlags flags,
TaskContinuationFunction *run,
AsyncContext *initialContext)
: Job(flags, run, metadata, immortal),
ResumeContext(initialContext),
Status(ActiveTaskStatus()),
Local(TaskLocal::Storage()) {
assert(flags.isAsyncTask());
Id = getNextTaskId();
}

~AsyncTask();

/// Given that we've already fully established the job context
/// in the current thread, start running this task. To establish
/// the job context correctly, call swift_job_run or
Expand Down Expand Up @@ -478,13 +514,24 @@ class AsyncTask : public Job {
return reinterpret_cast<AsyncTask *&>(
SchedulerPrivate[NextWaitingTaskIndex]);
}

/// Get the next non-zero Task ID.
uint32_t getNextTaskId() {
static std::atomic<uint32_t> Id(1);
uint32_t Next = Id.fetch_add(1, std::memory_order_relaxed);
if (Next == 0) Next = Id.fetch_add(1, std::memory_order_relaxed);
return Next;
}
};

// The compiler will eventually assume these.
static_assert(sizeof(AsyncTask) == 14 * sizeof(void*),
"AsyncTask size is wrong");
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
"AsyncTask alignment is wrong");
// Libc hardcodes this offset to extract the TaskID
static_assert(offsetof(AsyncTask, Id) == 4 * sizeof(void *) + 4,
"AsyncTask::Id offset is wrong");

inline void Job::runInFullyEstablishedContext() {
if (auto task = dyn_cast<AsyncTask>(this))
Expand Down Expand Up @@ -604,13 +651,13 @@ class FutureAsyncContext : public AsyncContext {
/// This matches the ABI of a closure `() async throws -> ()`
using AsyncVoidClosureEntryPoint =
SWIFT_CC(swiftasync)
void (SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT HeapObject *);
void (SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT void *);

/// This matches the ABI of a closure `<T>() async throws -> T`
using AsyncGenericClosureEntryPoint =
SWIFT_CC(swiftasync)
void(OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT HeapObject *);
SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT void *);

/// This matches the ABI of the resume function of a closure
/// `() async throws -> ()`.
Expand All @@ -624,7 +671,7 @@ class AsyncContextPrefix {
// passing the closure context instead of via the async context)
AsyncVoidClosureEntryPoint *__ptrauth_swift_task_resume_function
asyncEntryPoint;
HeapObject *closureContext;
void *closureContext;
SwiftError *errorResult;
};

Expand All @@ -637,7 +684,7 @@ class FutureAsyncContextPrefix {
// passing the closure context instead of via the async context)
AsyncGenericClosureEntryPoint *__ptrauth_swift_task_resume_function
asyncEntryPoint;
HeapObject *closureContext;
void *closureContext;
SwiftError *errorResult;
};

Expand Down
6 changes: 3 additions & 3 deletions include/swift/ABI/TaskGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ namespace swift {
/// The task group is responsible for maintaining dynamically created child tasks.
class alignas(Alignment_TaskGroup) TaskGroup {
public:
// These constructors do not initialize the actor instance, and the
// destructor does not destroy the actor instance; you must call
// These constructors do not initialize the group instance, and the
// destructor does not destroy the group instance; you must call
// swift_taskGroup_{initialize,destroy} yourself.
constexpr TaskGroup()
: PrivateData{} {}
Expand All @@ -43,4 +43,4 @@ class alignas(Alignment_TaskGroup) TaskGroup {

} // end namespace swift

#endif
#endif // SWIFT_ABI_TASK_GROUP_H
Loading