Skip to content

Commit

Permalink
Merge pull request swiftlang#36079 from rjmccall/thread-local-task-state
Browse files Browse the repository at this point in the history
Progress towards storing task/executor state in thread-local storage
  • Loading branch information
rjmccall authored Feb 22, 2021
2 parents b8329b6 + fd96f41 commit dcc1292
Show file tree
Hide file tree
Showing 26 changed files with 654 additions and 421 deletions.
29 changes: 23 additions & 6 deletions include/swift/ABI/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,17 @@ class alignas(2 * alignof(void*)) Job {
return Flags.getPriority();
}

/// Run this job.
void run(ExecutorRef currentExecutor);
/// 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);

/// 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);
}
};

// The compiler will eventually assume these.
Expand Down Expand Up @@ -173,7 +182,11 @@ class AsyncTask : public HeapObject, public Job {
assert(flags.isAsyncTask());
}

void run(ExecutorRef currentExecutor) {
/// 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
/// runInExecutorContext.
void runInFullyEstablishedContext(ExecutorRef currentExecutor) {
ResumeTask(this, currentExecutor, ResumeContext);
}

Expand Down Expand Up @@ -860,6 +873,10 @@ class AsyncTask : public HeapObject, public Job {
/// Destroy the storage associated with the future.
void destroy();

const Metadata *getResultType() const {
return resultType;
}

/// Retrieve a pointer to the storage of result.
OpaqueValue *getStoragePtr() {
return reinterpret_cast<OpaqueValue *>(
Expand Down Expand Up @@ -941,11 +958,11 @@ static_assert(sizeof(AsyncTask) == 12 * sizeof(void*),
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
"AsyncTask alignment is wrong");

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

/// An asynchronous context within a task. Generally contexts are
Expand Down
49 changes: 29 additions & 20 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,37 +117,36 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
JobPriority
swift_task_escalate(AsyncTask *task, JobPriority newPriority);

/// The result of waiting for a task future.
struct TaskFutureWaitResult {
/// Whether the storage represents the error result vs. the successful
/// result.
bool hadErrorResult;

/// Storage for the result of the future.
///
/// When the future completed normally, this is a pointer to the storage
/// of the result value, which lives inside the future task itself.
///
/// When the future completed by throwing an error, this is the error
/// object itself.
OpaqueValue *storage;
};

using TaskFutureWaitSignature =
AsyncSignature<TaskFutureWaitResult(AsyncTask *), /*throws*/ false>;
AsyncSignature<void(AsyncTask *, OpaqueValue *), /*throws*/ false>;

/// Wait for a future task to complete.
/// Wait for a non-throwing future task to complete.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_task_future_wait(on task: Builtin.NativeObject) async
/// -> TaskFutureWaitResult
/// func swift_task_future_wait(on task: _owned Builtin.NativeObject) async
/// -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
TaskFutureWaitSignature::FunctionType
swift_task_future_wait;

using TaskFutureWaitThrowingSignature =
AsyncSignature<void(AsyncTask *, OpaqueValue *), /*throws*/ true>;

/// Wait for a potentially-throwing future task to complete.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_task_future_wait_throwing(on task: _owned Builtin.NativeObject)
/// async throws -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
TaskFutureWaitThrowingSignature::FunctionType
swift_task_future_wait_throwing;

/// Wait for a readyQueue of a Channel to become non empty.
///
/// This can be called from any thread. Its Swift signature is
Expand Down Expand Up @@ -422,6 +421,16 @@ void swift_continuation_logFailedCheck(const char *message);
/// Otherwise it uses dispatchMain.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_asyncMainDrainQueue();

/// Establish that the current thread is running as the given
/// executor, then run a job.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_job_run(Job *job, ExecutorRef executor);

/// Return the current thread's active task reference.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTask *swift_task_getCurrent(void);

}

#endif
95 changes: 95 additions & 0 deletions include/swift/Runtime/Error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===--- Error.h - Swift Runtime ABI for error values -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 runtime support for working with error values.
//
// The ABI here is quite different in ObjC and non-ObjC modes.
// In ObjC mode, SwiftError is closely related to the NSError class:
// native errors are boxed as a subclass of NSError, but non-native
// errors may simply be NSError objects directly from Objective-C.
// In non-ObjC mode, SwiftError boxes are always native.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_RUNTIME_ERROR_H
#define SWIFT_RUNTIME_ERROR_H

#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/Metadata.h"

namespace swift {

struct SwiftError;

/// Allocate a catchable error object.
///
/// If value is nonnull, it should point to a value of \c type, which will be
/// copied (or taken if \c isTake is true) into the newly-allocated error box.
/// If value is null, the box's contents will be left uninitialized, and
/// \c isTake should be false.
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
BoxPair swift_allocError(const Metadata *type,
const WitnessTable *errorConformance,
OpaqueValue *value, bool isTake);

/// Deallocate an error object whose contained object has already been
/// destroyed.
SWIFT_RUNTIME_STDLIB_API
void swift_deallocError(SwiftError *error, const Metadata *type);

struct ErrorValueResult {
const OpaqueValue *value;
const Metadata *type;
const WitnessTable *errorConformance;
};

/// Extract a pointer to the value, the type metadata, and the Error
/// protocol witness from an error object.
///
/// The "scratch" pointer should point to an uninitialized word-sized
/// temporary buffer. The implementation may write a reference to itself to
/// that buffer if the error object is a toll-free-bridged NSError instead of
/// a native Swift error, in which case the object itself is the "boxed" value.
SWIFT_RUNTIME_STDLIB_API
void swift_getErrorValue(const SwiftError *errorObject,
void **scratch,
ErrorValueResult *out);

/// Called when throwing an error. Serves as a breakpoint hook
/// for debuggers.
SWIFT_CC(swift)
SWIFT_RUNTIME_STDLIB_API void
swift_willThrow(SWIFT_CONTEXT void *unused,
SWIFT_ERROR_RESULT SwiftError **object);

/// Called when an error is thrown out of the top level of a script.
SWIFT_CC(swift)
SWIFT_RUNTIME_STDLIB_API SWIFT_NORETURN void
swift_errorInMain(SwiftError *object);

/// Called when the try! operator fails.
SWIFT_CC(swift)
SWIFT_RUNTIME_STDLIB_API SWIFT_NORETURN void
swift_unexpectedError(SwiftError *object, OpaqueValue *filenameStart,
long filenameLength, bool isAscii, unsigned long line);

/// Retain an error box.
SWIFT_RUNTIME_STDLIB_API
SwiftError *swift_errorRetain(SwiftError *object);

/// Release an error box.
SWIFT_RUNTIME_STDLIB_API
void swift_errorRelease(SwiftError *object);

} // end namespace swift

#endif
110 changes: 81 additions & 29 deletions lib/IRGen/Callee.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,28 +125,76 @@ namespace irgen {
/// A function pointer value.
class FunctionPointer {
public:
struct KindTy {
enum class Value {
Function,
AsyncFunctionPointer,
};
static const Value Function = Value::Function;
static const Value AsyncFunctionPointer = Value::AsyncFunctionPointer;
Value value;
KindTy(Value value) : value(value) {}
KindTy(CanSILFunctionType fnType)
: value(fnType->isAsync() ? Value::AsyncFunctionPointer
: Value::Function) {}
friend bool operator==(const KindTy &lhs, const KindTy &rhs) {
enum class BasicKind {
Function,
AsyncFunctionPointer
};

enum class SpecialKind {
TaskFutureWait,
TaskFutureWaitThrowing,
TaskGroupWaitNext,
};

class Kind {
static constexpr unsigned SpecialOffset = 2;
unsigned value;
public:
static constexpr BasicKind Function =
BasicKind::Function;
static constexpr BasicKind AsyncFunctionPointer =
BasicKind::AsyncFunctionPointer;

Kind(BasicKind kind) : value(unsigned(kind)) {}
Kind(SpecialKind kind) : value(unsigned(kind) + SpecialOffset) {}
Kind(CanSILFunctionType fnType)
: Kind(fnType->isAsync() ? BasicKind::AsyncFunctionPointer
: BasicKind::Function) {}

BasicKind getBasicKind() const {
return value < SpecialOffset ? BasicKind(value) : BasicKind::Function;
}
bool isAsyncFunctionPointer() const {
return value == unsigned(BasicKind::AsyncFunctionPointer);
}

bool isSpecial() const {
return value >= SpecialOffset;
}
SpecialKind getSpecialKind() const {
assert(isSpecial());
return SpecialKind(value - SpecialOffset);
}

/// Should we suppress the generic signature from the given function?
///
/// This is a micro-optimization we apply to certain special functions
/// that we know don't need generics.
bool suppressGenerics() const {
if (!isSpecial()) return false;

switch (getSpecialKind()) {
case SpecialKind::TaskFutureWait:
case SpecialKind::TaskFutureWaitThrowing:
// We suppress generics from these as a code-size optimization
// because the runtime can recover the success type from the
// future.
return true;
case SpecialKind::TaskGroupWaitNext:
return false;
}
}

friend bool operator==(Kind lhs, Kind rhs) {
return lhs.value == rhs.value;
}
friend bool operator!=(const KindTy &lhs, const KindTy &rhs) {
friend bool operator!=(Kind lhs, Kind rhs) {
return !(lhs == rhs);
}
};

private:
KindTy Kind;
Kind kind;

/// The actual pointer, either to the function or to its descriptor.
llvm::Value *Value;
Expand All @@ -155,34 +203,29 @@ namespace irgen {

Signature Sig;

bool isFunctionPointerWithoutContext = false;

public:
/// Construct a FunctionPointer for an arbitrary pointer value.
/// We may add more arguments to this; try to use the other
/// constructors/factories if possible.
explicit FunctionPointer(KindTy kind, llvm::Value *value,
explicit FunctionPointer(Kind kind, llvm::Value *value,
PointerAuthInfo authInfo,
const Signature &signature,
bool isWithoutCtxt = false)
: Kind(kind), Value(value), AuthInfo(authInfo), Sig(signature),
isFunctionPointerWithoutContext(isWithoutCtxt) {
const Signature &signature)
: kind(kind), Value(value), AuthInfo(authInfo), Sig(signature) {
// The function pointer should have function type.
assert(value->getType()->getPointerElementType()->isFunctionTy());
// TODO: maybe assert similarity to signature.getType()?
}

// Temporary only!
explicit FunctionPointer(KindTy kind, llvm::Value *value,
const Signature &signature,
bool isWithoutCtxt = false)
: FunctionPointer(kind, value, PointerAuthInfo(), signature, isWithoutCtxt) {}
explicit FunctionPointer(Kind kind, llvm::Value *value,
const Signature &signature)
: FunctionPointer(kind, value, PointerAuthInfo(), signature) {}

static FunctionPointer forDirect(IRGenModule &IGM,
llvm::Constant *value,
CanSILFunctionType fnType);

static FunctionPointer forDirect(KindTy kind, llvm::Constant *value,
static FunctionPointer forDirect(Kind kind, llvm::Constant *value,
const Signature &signature) {
return FunctionPointer(kind, value, PointerAuthInfo(), signature);
}
Expand All @@ -197,7 +240,8 @@ namespace irgen {
return (isa<llvm::Constant>(Value) && AuthInfo.isConstant());
}

KindTy getKind() const { return Kind; }
Kind getKind() const { return kind; }
BasicKind getBasicKind() const { return kind.getBasicKind(); }

/// Given that this value is known to have been constructed from a direct
/// function, Return the name of that function.
Expand Down Expand Up @@ -250,7 +294,11 @@ namespace irgen {
FunctionPointer getAsFunction(IRGenFunction &IGF) const;

bool useStaticContextSize() const {
return isFunctionPointerWithoutContext;
return !kind.isAsyncFunctionPointer();
}

bool suppressGenerics() const {
return kind.suppressGenerics();
}
};

Expand Down Expand Up @@ -315,6 +363,10 @@ namespace irgen {
return Fn.getSignature();
}

bool suppressGenerics() const {
return Fn.suppressGenerics();
}

/// If this callee has a value for the Swift context slot, return
/// it; otherwise return non-null.
llvm::Value *getSwiftContext() const;
Expand Down
Loading

0 comments on commit dcc1292

Please sign in to comment.