Skip to content

Commit 2012195

Browse files
committed
Alter the runtime interface for awaiting futures and task groups.
First, just call an async -> T function instead of forcing the caller to piece together which case we're in and perform its own copy. This ensures that the task is actually kept alive properly. Second, now that we no longer implicitly depend on the waiting tasks being run synchronously, go ahead and schedule them to run on the global executor. This solves some problems which were blocking the work on TLS-ifying the task/executor state.
1 parent 1c82c71 commit 2012195

File tree

19 files changed

+332
-243
lines changed

19 files changed

+332
-243
lines changed

include/swift/ABI/Task.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,10 @@ class AsyncTask : public HeapObject, public Job {
873873
/// Destroy the storage associated with the future.
874874
void destroy();
875875

876+
const Metadata *getResultType() const {
877+
return resultType;
878+
}
879+
876880
/// Retrieve a pointer to the storage of result.
877881
OpaqueValue *getStoragePtr() {
878882
return reinterpret_cast<OpaqueValue *>(

include/swift/Runtime/Concurrency.h

Lines changed: 19 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

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)