Skip to content

[Concurrency][ABI additive] add options to wait operations #38156

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 136 additions & 26 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,30 +96,67 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
JobPriority
swift_task_escalate(AsyncTask *task, JobPriority newPriority);

// TODO: "async let wait" and "async let destroy" would be expressed
// similar to like TaskFutureWait;
/// Wait for a non-throwing future task to complete, passing options.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_task_future_wait_with_options(
/// on task: _owned Builtin.NativeObject,
/// options: Builtin.RawPointer?
/// ) async -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_task_future_wait_with_options(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncTask *,
TaskOptionRecord *,
TaskContinuationFunction *, AsyncContext *);

/// Wait for a non-throwing future task to complete, passing task options.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_task_future_wait(
/// on task: _owned Builtin.NativeObject
/// ) async -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_task_future_wait(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncTask *,
TaskContinuationFunction *, AsyncContext *);

/// Wait for a non-throwing future task to complete.
/// Wait for a potentially-throwing future task to complete, passing options.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_task_future_wait(on task: _owned Builtin.NativeObject) async
/// -> Success
/// func swift_task_future_wait_throwing_with_options(
/// on task: _owned Builtin.NativeObject,
/// options: Builtin.RawPointer?
/// ) async throws -> Success,
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_task_future_wait(OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *,
TaskContinuationFunction *,
AsyncContext *);
void swift_task_future_wait_throwing_with_options(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncTask *,
TaskOptionRecord *,
ThrowingTaskFutureWaitContinuationFunction *,
AsyncContext *);

/// 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
/// func swift_task_future_wait_throwing(
/// on task: _owned Builtin.NativeObject
/// ) async throws -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_task_future_wait_throwing(
Expand All @@ -129,21 +166,44 @@ void swift_task_future_wait_throwing(
ThrowingTaskFutureWaitContinuationFunction *,
AsyncContext *);

/// Wait for a readyQueue of a Channel to become non empty.
/// Wait for a readyQueue of a Channel to become non empty, with task options.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_taskGroup_wait_next_throwing(
/// waitingTask: Builtin.NativeObject, // current task
/// group: Builtin.RawPointer
/// group: Builtin.RawPointer,
/// options: Builtin.RawPointer?
/// ) async -> T
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swiftasync)
void swift_taskGroup_wait_next_throwing_with_options(
OpaqueValue *resultPointer,
SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
TaskGroup *group,
TaskOptionRecord *options,
ThrowingTaskFutureWaitContinuationFunction *resumeFn,
AsyncContext *callContext);

/// Wait for a readyQueue of a Channel to become non empty.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_taskGroup_wait_next_throwing(
/// waitingTask: Builtin.NativeObject, // current task
/// group: Builtin.RawPointer
/// ) async -> T
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency)
SWIFT_CC(swiftasync)
void swift_taskGroup_wait_next_throwing(
OpaqueValue *resultPointer, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
TaskGroup *group, ThrowingTaskFutureWaitContinuationFunction *resumeFn,
OpaqueValue *resultPointer,
SWIFT_ASYNC_CONTEXT AsyncContext *callerContext,
TaskGroup *group,
ThrowingTaskFutureWaitContinuationFunction *resumeFn,
AsyncContext *callContext);

/// Initialize a `TaskGroup` in the passed `group` memory location.
Expand Down Expand Up @@ -261,6 +321,24 @@ using AsyncLetWaitSignature =
void(OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *, AsyncTask *, Metadata *);

/// Wait for a non-throwing async-let to complete.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_asyncLet_wait(
/// _ asyncLet: _owned Builtin.RawPointer,
///// _ options: Builtin.RawPointer?
/// ) async -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_asyncLet_wait_with_options(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncLet *,
TaskOptionRecord *,
TaskContinuationFunction *, AsyncContext *);

/// Wait for a non-throwing async-let to complete.
///
/// This can be called from any thread. Its Swift signature is
Expand All @@ -271,10 +349,29 @@ using AsyncLetWaitSignature =
/// ) async -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_asyncLet_wait(OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncLet *, TaskContinuationFunction *,
AsyncContext *);
void swift_asyncLet_wait(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncLet *,
TaskContinuationFunction *, AsyncContext *);

/// Wait for a potentially-throwing async-let to complete.
///
/// This can be called from any thread. Its Swift signature is
///
/// \code
/// func swift_asyncLet_wait_throwing(
/// _ asyncLet: _owned Builtin.RawPointer,
/// _ options: Builtin.RawPointer?
/// ) async throws -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_asyncLet_wait_throwing_with_options(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncLet *,
TaskOptionRecord *,
ThrowingTaskFutureWaitContinuationFunction *, AsyncContext *);

/// Wait for a potentially-throwing async-let to complete.
///
Expand All @@ -286,19 +383,32 @@ void swift_asyncLet_wait(OpaqueValue *,
/// ) async throws -> Success
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swiftasync)
void swift_asyncLet_wait_throwing(OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncLet *,
ThrowingTaskFutureWaitContinuationFunction *,
AsyncContext *);
void swift_asyncLet_wait_throwing(
OpaqueValue *,
SWIFT_ASYNC_CONTEXT AsyncContext *,
AsyncLet *,
ThrowingTaskFutureWaitContinuationFunction *, AsyncContext *);

/// Its Swift signature is
///
/// \code
/// func swift_asyncLet_end(_ alet: Builtin.RawPointer)
/// func swift_asyncLet_end(
/// _ alet: Builtin.RawPointer,
/// _ options: Builtin.RawPointer?
/// )
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_asyncLet_end_with_options(AsyncLet *, TaskOptionRecord *);

/// Its Swift signature is
///
/// \code
/// func swift_asyncLet_end(
/// _ alet: Builtin.RawPointer
/// )
/// \endcode
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_asyncLet_end(AsyncLet *alet);
void swift_asyncLet_end(AsyncLet *);

/// Returns true if the currently executing AsyncTask has a
/// 'TaskGroupTaskStatusRecord' present.
Expand Down
9 changes: 7 additions & 2 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -1690,12 +1690,17 @@ FUNCTION(AsyncLetStart,
),
ATTRS(NoUnwind, ArgMemOnly))

// void swift_asyncLet_end(AsyncLet *alet);
// void swift_asyncLet_end(
// AsyncLet *alet,
// TaskOptionRecord *options
// );
FUNCTION(EndAsyncLet,
swift_asyncLet_end, SwiftCC,
ConcurrencyAvailability,
RETURNS(VoidTy),
ARGS(SwiftAsyncLetPtrTy),
ARGS(SwiftAsyncLetPtrTy, // AsyncLet*
SwiftTaskOptionRecordPtrTy // options
),
ATTRS(NoUnwind))

// void swift_taskGroup_initialize(TaskGroup *group);
Expand Down
22 changes: 19 additions & 3 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1511,9 +1511,25 @@ static ValueDecl *getStartAsyncLet(ASTContext &ctx, Identifier id) {
}

static ValueDecl *getEndAsyncLet(ASTContext &ctx, Identifier id) {
return getBuiltinFunction(ctx, id, _thin,
_parameters(_rawPointer),
_void);
ModuleDecl *M = ctx.TheBuiltinModule;
DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);
SynthesisContext SC(ctx, DC);

BuiltinFunctionBuilder builder(ctx);

// AsyncLet*
builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType)));

// TaskOptionRecord*
builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType)));

// -> Void
builder.setResult(makeConcrete(synthesizeType(SC, _void)));
return builder.build(id);
// return getBuiltinFunction(ctx, id, _thin,
// _parameters(_rawPointer,
// makeConcrete(OptionalType::get(ctx.TheRawPointerType))),
// _void);
}

static ValueDecl *getCreateTaskGroup(ASTContext &ctx, Identifier id) {
Expand Down
4 changes: 3 additions & 1 deletion lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
}

if (Builtin.ID == BuiltinValueKind::EndAsyncLet) {
emitEndAsyncLet(IGF, args.claimNext());
auto alet = args.claimNext();
auto taskOptions = args.claimNext();
emitEndAsyncLet(IGF, alet, taskOptions);
// Ignore a second operand which is inserted by ClosureLifetimeFixup and
// only used for dependency tracking.
(void)args.claimAll();
Expand Down
8 changes: 6 additions & 2 deletions lib/IRGen/GenConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,13 @@ llvm::Value *irgen::emitBuiltinStartAsyncLet(IRGenFunction &IGF,
return alet;
}

void irgen::emitEndAsyncLet(IRGenFunction &IGF, llvm::Value *alet) {
void irgen::emitEndAsyncLet(IRGenFunction &IGF,
llvm::Value *alet,
llvm::Value *taskOptions) {
IGF.IGM.getEndAsyncLetFn()->dump();

auto *call = IGF.Builder.CreateCall(IGF.IGM.getEndAsyncLetFn(),
{alet});
{alet, taskOptions});
call->setDoesNotThrow();
call->setCallingConv(IGF.IGM.SwiftCC);

Expand Down
4 changes: 3 additions & 1 deletion lib/IRGen/GenConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ llvm::Value *emitBuiltinStartAsyncLet(IRGenFunction &IGF,
SubstitutionMap subs);

/// Emit the endAsyncLet builtin.
void emitEndAsyncLet(IRGenFunction &IGF, llvm::Value *alet);
void emitEndAsyncLet(IRGenFunction &IGF,
llvm::Value *alet,
llvm::Value *taskOptions);

/// Emit the createTaskGroup builtin.
llvm::Value *emitCreateTaskGroup(IRGenFunction &IGF, SubstitutionMap subs);
Expand Down
15 changes: 10 additions & 5 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2511,17 +2511,22 @@ FunctionPointer::Kind irgen::classifyFunctionPointerKind(SILFunction *fn) {
// Check for some special cases, which are currently all async:
if (fn->isAsync()) {
auto name = fn->getName();
if (name.equals("swift_task_future_wait"))
if (name.equals("swift_task_future_wait") ||
name.equals("swift_task_future_wait_with_options"))
return SpecialKind::TaskFutureWait;
if (name.equals("swift_task_future_wait_throwing"))
if (name.equals("swift_task_future_wait_throwing") ||
name.equals("swift_task_future_wait_throwing_with_options"))
return SpecialKind::TaskFutureWaitThrowing;

if (name.equals("swift_asyncLet_wait"))
if (name.equals("swift_asyncLet_wait") ||
name.equals("swift_asyncLet_wait_with_options"))
return SpecialKind::AsyncLetWait;
if (name.equals("swift_asyncLet_wait_throwing"))
if (name.equals("swift_asyncLet_wait_throwing") ||
name.equals("swift_asyncLet_wait_throwing_with_options"))
return SpecialKind::AsyncLetWaitThrowing;

if (name.equals("swift_taskGroup_wait_next_throwing"))
if (name.equals("swift_taskGroup_wait_next_throwing") ||
name.equals("swift_taskGroup_wait_next_throwing_with_options"))
return SpecialKind::TaskGroupWaitNext;
}

Expand Down
25 changes: 20 additions & 5 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5834,13 +5834,25 @@ ManagedValue SILGenFunction::emitCancelAsyncTask(
}

void SILGenFunction::completeAsyncLetChildTask(
PatternBindingDecl *patternBinding, unsigned index) {
PatternBindingDecl *patternBinding, unsigned index
// ,
// SILValue taskOptions
) {
auto &C = patternBinding->getASTContext();

SILValue asyncLet;
bool isThrowing;
std::tie(asyncLet, isThrowing)= AsyncLetChildTasks[{patternBinding, index}];

// TODO: if (options) { ... } else { make the null option }
// Create a "none" options
SILLocation loc(patternBinding);
auto taskOptions = B.createManagedOptionalNone(
loc, SILType::getOptionalType(SILType::getRawPointerType(C)));

Type childResultType = patternBinding->getPattern(index)->getType();

// TODO: call this Wait rather than Get for consitency
auto asyncLetGet = isThrowing
? SGM.getAsyncLetGetThrowing()
: SGM.getAsyncLetGet();
Expand All @@ -5852,7 +5864,9 @@ void SILGenFunction::completeAsyncLetChildTask(
ArrayRef<ProtocolConformanceRef>{});
RValue childResult = emitApplyOfLibraryIntrinsic(
SILLocation(patternBinding), asyncLetGet, subs,
{ ManagedValue::forTrivialObjectRValue(asyncLet) },
{ ManagedValue::forTrivialObjectRValue(asyncLet),
taskOptions
},
SGFContext());

// Write the child result into the pattern variables.
Expand All @@ -5861,14 +5875,15 @@ void SILGenFunction::completeAsyncLetChildTask(
std::move(childResult));
}

ManagedValue SILGenFunction::emitEndAsyncLet(
SILLocation loc, SILValue asyncLet) {
ManagedValue SILGenFunction::emitEndAsyncLet(SILLocation loc,
SILValue asyncLet,
SILValue taskOptions) {
ASTContext &ctx = getASTContext();
auto apply = B.createBuiltin(
loc,
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::EndAsyncLet)),
getLoweredType(ctx.TheEmptyTupleType), SubstitutionMap(),
{ asyncLet });
{ asyncLet, taskOptions });
return ManagedValue::forUnmanaged(apply);
}

Expand Down
Loading