Skip to content

Commit dcc1292

Browse files
authored
Merge pull request #36079 from rjmccall/thread-local-task-state
Progress towards storing task/executor state in thread-local storage
2 parents b8329b6 + fd96f41 commit dcc1292

26 files changed

+654
-421
lines changed

include/swift/ABI/Task.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,17 @@ class alignas(2 * alignof(void*)) Job {
8080
return Flags.getPriority();
8181
}
8282

83-
/// Run this job.
84-
void run(ExecutorRef currentExecutor);
83+
/// Given that we've fully established the job context in the current
84+
/// thread, actually start running this job. To establish the context
85+
/// correctly, call swift_job_run or runJobInExecutorContext.
86+
void runInFullyEstablishedContext(ExecutorRef currentExecutor);
87+
88+
/// Given that we've fully established the job context in the
89+
/// current thread, and that the job is a simple (non-task) job,
90+
/// actually start running this job.
91+
void runSimpleInFullyEstablishedContext(ExecutorRef currentExecutor) {
92+
RunJob(this, currentExecutor);
93+
}
8594
};
8695

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

176-
void run(ExecutorRef currentExecutor) {
185+
/// Given that we've already fully established the job context
186+
/// in the current thread, start running this task. To establish
187+
/// the job context correctly, call swift_job_run or
188+
/// runInExecutorContext.
189+
void runInFullyEstablishedContext(ExecutorRef currentExecutor) {
177190
ResumeTask(this, currentExecutor, ResumeContext);
178191
}
179192

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

876+
const Metadata *getResultType() const {
877+
return resultType;
878+
}
879+
863880
/// Retrieve a pointer to the storage of result.
864881
OpaqueValue *getStoragePtr() {
865882
return reinterpret_cast<OpaqueValue *>(
@@ -941,11 +958,11 @@ static_assert(sizeof(AsyncTask) == 12 * sizeof(void*),
941958
static_assert(alignof(AsyncTask) == 2 * alignof(void*),
942959
"AsyncTask alignment is wrong");
943960

944-
inline void Job::run(ExecutorRef currentExecutor) {
961+
inline void Job::runInFullyEstablishedContext(ExecutorRef currentExecutor) {
945962
if (auto task = dyn_cast<AsyncTask>(this))
946-
task->run(currentExecutor);
963+
task->runInFullyEstablishedContext(currentExecutor);
947964
else
948-
RunJob(this, currentExecutor);
965+
runSimpleInFullyEstablishedContext(currentExecutor);
949966
}
950967

951968
/// An asynchronous context within a task. Generally contexts are

include/swift/Runtime/Concurrency.h

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -117,37 +117,36 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
117117
JobPriority
118118
swift_task_escalate(AsyncTask *task, JobPriority newPriority);
119119

120-
/// The result of waiting for a task future.
121-
struct TaskFutureWaitResult {
122-
/// Whether the storage represents the error result vs. the successful
123-
/// result.
124-
bool hadErrorResult;
125-
126-
/// Storage for the result of the future.
127-
///
128-
/// When the future completed normally, this is a pointer to the storage
129-
/// of the result value, which lives inside the future task itself.
130-
///
131-
/// When the future completed by throwing an error, this is the error
132-
/// object itself.
133-
OpaqueValue *storage;
134-
};
135-
136120
using TaskFutureWaitSignature =
137-
AsyncSignature<TaskFutureWaitResult(AsyncTask *), /*throws*/ false>;
121+
AsyncSignature<void(AsyncTask *, OpaqueValue *), /*throws*/ false>;
138122

139-
/// Wait for a future task to complete.
123+
/// Wait for a non-throwing future task to complete.
140124
///
141125
/// This can be called from any thread. Its Swift signature is
142126
///
143127
/// \code
144-
/// func swift_task_future_wait(on task: Builtin.NativeObject) async
145-
/// -> TaskFutureWaitResult
128+
/// func swift_task_future_wait(on task: _owned Builtin.NativeObject) async
129+
/// -> Success
146130
/// \endcode
147131
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
148132
TaskFutureWaitSignature::FunctionType
149133
swift_task_future_wait;
150134

135+
using TaskFutureWaitThrowingSignature =
136+
AsyncSignature<void(AsyncTask *, OpaqueValue *), /*throws*/ true>;
137+
138+
/// Wait for a potentially-throwing future task to complete.
139+
///
140+
/// This can be called from any thread. Its Swift signature is
141+
///
142+
/// \code
143+
/// func swift_task_future_wait_throwing(on task: _owned Builtin.NativeObject)
144+
/// async throws -> Success
145+
/// \endcode
146+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
147+
TaskFutureWaitThrowingSignature::FunctionType
148+
swift_task_future_wait_throwing;
149+
151150
/// Wait for a readyQueue of a Channel to become non empty.
152151
///
153152
/// This can be called from any thread. Its Swift signature is
@@ -422,6 +421,16 @@ void swift_continuation_logFailedCheck(const char *message);
422421
/// Otherwise it uses dispatchMain.
423422
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
424423
void swift_task_asyncMainDrainQueue();
424+
425+
/// Establish that the current thread is running as the given
426+
/// executor, then run a job.
427+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
428+
void swift_job_run(Job *job, ExecutorRef executor);
429+
430+
/// Return the current thread's active task reference.
431+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
432+
AsyncTask *swift_task_getCurrent(void);
433+
425434
}
426435

427436
#endif

include/swift/Runtime/Error.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//===--- Error.h - Swift Runtime ABI for error values -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Swift runtime support for working with error values.
14+
//
15+
// The ABI here is quite different in ObjC and non-ObjC modes.
16+
// In ObjC mode, SwiftError is closely related to the NSError class:
17+
// native errors are boxed as a subclass of NSError, but non-native
18+
// errors may simply be NSError objects directly from Objective-C.
19+
// In non-ObjC mode, SwiftError boxes are always native.
20+
//
21+
//===----------------------------------------------------------------------===//
22+
23+
#ifndef SWIFT_RUNTIME_ERROR_H
24+
#define SWIFT_RUNTIME_ERROR_H
25+
26+
#include "swift/Runtime/HeapObject.h"
27+
#include "swift/Runtime/Metadata.h"
28+
29+
namespace swift {
30+
31+
struct SwiftError;
32+
33+
/// Allocate a catchable error object.
34+
///
35+
/// If value is nonnull, it should point to a value of \c type, which will be
36+
/// copied (or taken if \c isTake is true) into the newly-allocated error box.
37+
/// If value is null, the box's contents will be left uninitialized, and
38+
/// \c isTake should be false.
39+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
40+
BoxPair swift_allocError(const Metadata *type,
41+
const WitnessTable *errorConformance,
42+
OpaqueValue *value, bool isTake);
43+
44+
/// Deallocate an error object whose contained object has already been
45+
/// destroyed.
46+
SWIFT_RUNTIME_STDLIB_API
47+
void swift_deallocError(SwiftError *error, const Metadata *type);
48+
49+
struct ErrorValueResult {
50+
const OpaqueValue *value;
51+
const Metadata *type;
52+
const WitnessTable *errorConformance;
53+
};
54+
55+
/// Extract a pointer to the value, the type metadata, and the Error
56+
/// protocol witness from an error object.
57+
///
58+
/// The "scratch" pointer should point to an uninitialized word-sized
59+
/// temporary buffer. The implementation may write a reference to itself to
60+
/// that buffer if the error object is a toll-free-bridged NSError instead of
61+
/// a native Swift error, in which case the object itself is the "boxed" value.
62+
SWIFT_RUNTIME_STDLIB_API
63+
void swift_getErrorValue(const SwiftError *errorObject,
64+
void **scratch,
65+
ErrorValueResult *out);
66+
67+
/// Called when throwing an error. Serves as a breakpoint hook
68+
/// for debuggers.
69+
SWIFT_CC(swift)
70+
SWIFT_RUNTIME_STDLIB_API void
71+
swift_willThrow(SWIFT_CONTEXT void *unused,
72+
SWIFT_ERROR_RESULT SwiftError **object);
73+
74+
/// Called when an error is thrown out of the top level of a script.
75+
SWIFT_CC(swift)
76+
SWIFT_RUNTIME_STDLIB_API SWIFT_NORETURN void
77+
swift_errorInMain(SwiftError *object);
78+
79+
/// Called when the try! operator fails.
80+
SWIFT_CC(swift)
81+
SWIFT_RUNTIME_STDLIB_API SWIFT_NORETURN void
82+
swift_unexpectedError(SwiftError *object, OpaqueValue *filenameStart,
83+
long filenameLength, bool isAscii, unsigned long line);
84+
85+
/// Retain an error box.
86+
SWIFT_RUNTIME_STDLIB_API
87+
SwiftError *swift_errorRetain(SwiftError *object);
88+
89+
/// Release an error box.
90+
SWIFT_RUNTIME_STDLIB_API
91+
void swift_errorRelease(SwiftError *object);
92+
93+
} // end namespace swift
94+
95+
#endif

lib/IRGen/Callee.h

Lines changed: 81 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -125,28 +125,76 @@ namespace irgen {
125125
/// A function pointer value.
126126
class FunctionPointer {
127127
public:
128-
struct KindTy {
129-
enum class Value {
130-
Function,
131-
AsyncFunctionPointer,
132-
};
133-
static const Value Function = Value::Function;
134-
static const Value AsyncFunctionPointer = Value::AsyncFunctionPointer;
135-
Value value;
136-
KindTy(Value value) : value(value) {}
137-
KindTy(CanSILFunctionType fnType)
138-
: value(fnType->isAsync() ? Value::AsyncFunctionPointer
139-
: Value::Function) {}
140-
friend bool operator==(const KindTy &lhs, const KindTy &rhs) {
128+
enum class BasicKind {
129+
Function,
130+
AsyncFunctionPointer
131+
};
132+
133+
enum class SpecialKind {
134+
TaskFutureWait,
135+
TaskFutureWaitThrowing,
136+
TaskGroupWaitNext,
137+
};
138+
139+
class Kind {
140+
static constexpr unsigned SpecialOffset = 2;
141+
unsigned value;
142+
public:
143+
static constexpr BasicKind Function =
144+
BasicKind::Function;
145+
static constexpr BasicKind AsyncFunctionPointer =
146+
BasicKind::AsyncFunctionPointer;
147+
148+
Kind(BasicKind kind) : value(unsigned(kind)) {}
149+
Kind(SpecialKind kind) : value(unsigned(kind) + SpecialOffset) {}
150+
Kind(CanSILFunctionType fnType)
151+
: Kind(fnType->isAsync() ? BasicKind::AsyncFunctionPointer
152+
: BasicKind::Function) {}
153+
154+
BasicKind getBasicKind() const {
155+
return value < SpecialOffset ? BasicKind(value) : BasicKind::Function;
156+
}
157+
bool isAsyncFunctionPointer() const {
158+
return value == unsigned(BasicKind::AsyncFunctionPointer);
159+
}
160+
161+
bool isSpecial() const {
162+
return value >= SpecialOffset;
163+
}
164+
SpecialKind getSpecialKind() const {
165+
assert(isSpecial());
166+
return SpecialKind(value - SpecialOffset);
167+
}
168+
169+
/// Should we suppress the generic signature from the given function?
170+
///
171+
/// This is a micro-optimization we apply to certain special functions
172+
/// that we know don't need generics.
173+
bool suppressGenerics() const {
174+
if (!isSpecial()) return false;
175+
176+
switch (getSpecialKind()) {
177+
case SpecialKind::TaskFutureWait:
178+
case SpecialKind::TaskFutureWaitThrowing:
179+
// We suppress generics from these as a code-size optimization
180+
// because the runtime can recover the success type from the
181+
// future.
182+
return true;
183+
case SpecialKind::TaskGroupWaitNext:
184+
return false;
185+
}
186+
}
187+
188+
friend bool operator==(Kind lhs, Kind rhs) {
141189
return lhs.value == rhs.value;
142190
}
143-
friend bool operator!=(const KindTy &lhs, const KindTy &rhs) {
191+
friend bool operator!=(Kind lhs, Kind rhs) {
144192
return !(lhs == rhs);
145193
}
146194
};
147195

148196
private:
149-
KindTy Kind;
197+
Kind kind;
150198

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

156204
Signature Sig;
157205

158-
bool isFunctionPointerWithoutContext = false;
159-
160206
public:
161207
/// Construct a FunctionPointer for an arbitrary pointer value.
162208
/// We may add more arguments to this; try to use the other
163209
/// constructors/factories if possible.
164-
explicit FunctionPointer(KindTy kind, llvm::Value *value,
210+
explicit FunctionPointer(Kind kind, llvm::Value *value,
165211
PointerAuthInfo authInfo,
166-
const Signature &signature,
167-
bool isWithoutCtxt = false)
168-
: Kind(kind), Value(value), AuthInfo(authInfo), Sig(signature),
169-
isFunctionPointerWithoutContext(isWithoutCtxt) {
212+
const Signature &signature)
213+
: kind(kind), Value(value), AuthInfo(authInfo), Sig(signature) {
170214
// The function pointer should have function type.
171215
assert(value->getType()->getPointerElementType()->isFunctionTy());
172216
// TODO: maybe assert similarity to signature.getType()?
173217
}
174218

175219
// Temporary only!
176-
explicit FunctionPointer(KindTy kind, llvm::Value *value,
177-
const Signature &signature,
178-
bool isWithoutCtxt = false)
179-
: FunctionPointer(kind, value, PointerAuthInfo(), signature, isWithoutCtxt) {}
220+
explicit FunctionPointer(Kind kind, llvm::Value *value,
221+
const Signature &signature)
222+
: FunctionPointer(kind, value, PointerAuthInfo(), signature) {}
180223

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

185-
static FunctionPointer forDirect(KindTy kind, llvm::Constant *value,
228+
static FunctionPointer forDirect(Kind kind, llvm::Constant *value,
186229
const Signature &signature) {
187230
return FunctionPointer(kind, value, PointerAuthInfo(), signature);
188231
}
@@ -197,7 +240,8 @@ namespace irgen {
197240
return (isa<llvm::Constant>(Value) && AuthInfo.isConstant());
198241
}
199242

200-
KindTy getKind() const { return Kind; }
243+
Kind getKind() const { return kind; }
244+
BasicKind getBasicKind() const { return kind.getBasicKind(); }
201245

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

252296
bool useStaticContextSize() const {
253-
return isFunctionPointerWithoutContext;
297+
return !kind.isAsyncFunctionPointer();
298+
}
299+
300+
bool suppressGenerics() const {
301+
return kind.suppressGenerics();
254302
}
255303
};
256304

@@ -315,6 +363,10 @@ namespace irgen {
315363
return Fn.getSignature();
316364
}
317365

366+
bool suppressGenerics() const {
367+
return Fn.suppressGenerics();
368+
}
369+
318370
/// If this callee has a value for the Swift context slot, return
319371
/// it; otherwise return non-null.
320372
llvm::Value *getSwiftContext() const;

0 commit comments

Comments
 (0)