Skip to content

Commit 93367ed

Browse files
committed
concurrency: make the startAsyncLet closure no-escaping
The closure does not escape the startAsyncLet - endAsyncLet scope. Even though it's (potentially) running on a different thread. The substantial change in the runtime is to not call swift_release on the closure context if it's a non-escaping closure.
1 parent 48ca690 commit 93367ed

File tree

10 files changed

+52
-21
lines changed

10 files changed

+52
-21
lines changed

include/swift/ABI/Task.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,13 +582,13 @@ class FutureAsyncContext : public AsyncContext {
582582
/// This matches the ABI of a closure `() async throws -> ()`
583583
using AsyncVoidClosureEntryPoint =
584584
SWIFT_CC(swiftasync)
585-
void (SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT HeapObject *);
585+
void (SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_CONTEXT void *);
586586

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

593593
/// This matches the ABI of the resume function of a closure
594594
/// `() async throws -> ()`.
@@ -602,7 +602,7 @@ class AsyncContextPrefix {
602602
// passing the closure context instead of via the async context)
603603
AsyncVoidClosureEntryPoint *__ptrauth_swift_task_resume_function
604604
asyncEntryPoint;
605-
HeapObject *closureContext;
605+
void *closureContext;
606606
SwiftError *errorResult;
607607
};
608608

@@ -615,7 +615,7 @@ class FutureAsyncContextPrefix {
615615
// passing the closure context instead of via the async context)
616616
AsyncGenericClosureEntryPoint *__ptrauth_swift_task_resume_function
617617
asyncEntryPoint;
618-
HeapObject *closureContext;
618+
void *closureContext;
619619
SwiftError *errorResult;
620620
};
621621

include/swift/Runtime/Concurrency.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,15 +281,15 @@ bool swift_taskGroup_isEmpty(TaskGroup *group);
281281
/// \code
282282
/// func swift_asyncLet_start<T>(
283283
/// _ alet: Builtin.RawPointer,
284-
/// operation: __owned @Sendable @escaping () async throws -> T
284+
/// operation: __owned @Sendable () async throws -> T
285285
/// )
286286
/// \endcode
287287
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
288288
void swift_asyncLet_start(
289289
AsyncLet *alet,
290290
const Metadata *futureResultType,
291291
void *closureEntryPoint,
292-
HeapObject * /* +1 */ closureContext);
292+
void *closureContext);
293293

294294
/// This matches the ABI of a closure `<T>(Builtin.RawPointer) async -> T`
295295
using AsyncLetWaitSignature =

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,7 +1682,7 @@ FUNCTION(DefaultActorDeallocateResilient,
16821682
/// AsyncLet *alet,
16831683
/// const Metadata *futureResultType,
16841684
/// void *closureEntryPoint,
1685-
/// HeapObject *closureContext
1685+
/// OpaqueValue *closureContext
16861686
/// );
16871687
FUNCTION(AsyncLetStart,
16881688
swift_asyncLet_start, SwiftCC,
@@ -1691,7 +1691,7 @@ FUNCTION(AsyncLetStart,
16911691
ARGS(SwiftAsyncLetPtrTy, // AsyncLet*, alias for Int8PtrTy
16921692
TypeMetadataPtrTy, // futureResultType
16931693
Int8PtrTy, // closureEntry
1694-
RefCountedPtrTy, // closureContext
1694+
OpaquePtrTy, // closureContext
16951695
),
16961696
ATTRS(NoUnwind, ArgMemOnly))
16971697

lib/AST/Builtins.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,7 @@ static ValueDecl *getStartAsyncLet(ASTContext &ctx, Identifier id) {
14961496
builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType)));
14971497

14981498
// operation async function pointer: () async throws -> T
1499-
auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().build();
1499+
auto extInfo = ASTExtInfoBuilder().withAsync().withThrows().withNoEscape().build();
15001500
builder.addParameter(
15011501
makeConcrete(FunctionType::get({ }, genericParam, extInfo)));
15021502

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@ OVERRIDE_TASK(task_create_group_future_common, AsyncTaskAndContext, , , ,
100100
(JobFlags flags, TaskGroup *group,
101101
const Metadata *futureResultType,
102102
FutureAsyncSignature::FunctionType *function,
103-
HeapObject */* +1 */ closureContext, size_t initialContextSize),
103+
void *closureContext, bool owningClosureContext,
104+
size_t initialContextSize),
104105
(flags, group, futureResultType, function, closureContext,
105-
initialContextSize))
106+
owningClosureContext, initialContextSize))
106107

107108
OVERRIDE_TASK(task_future_wait, void, SWIFT_EXPORT_FROM(swift_Concurrency),
108109
SWIFT_CC(swiftasync), swift::,
@@ -155,7 +156,7 @@ OVERRIDE_ASYNC_LET(asyncLet_start, void,
155156
(AsyncLet *alet,
156157
const Metadata *futureResultType,
157158
void *closureEntryPoint,
158-
HeapObject */* +1 */ closureContext
159+
void *closureContext
159160
),
160161
(alet, futureResultType,
161162
closureEntryPoint, closureContext))

stdlib/public/Concurrency/AsyncLet.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,15 @@ SWIFT_CC(swift)
9797
static void swift_asyncLet_startImpl(AsyncLet *alet,
9898
const Metadata *futureResultType,
9999
void *closureEntryPoint,
100-
HeapObject * /* +1 */ closureContext) {
100+
void *closureContext) {
101101
AsyncTask *parent = swift_task_getCurrent();
102102
assert(parent && "async-let cannot be created without parent task");
103103

104104
auto flags = JobFlags(JobKind::Task, parent->getPriority());
105105
flags.task_setIsFuture(true);
106106
flags.task_setIsChildTask(true);
107107

108-
auto childTaskAndContext = swift_task_create_future(
108+
auto childTaskAndContext = swift_task_create_future_no_escaping(
109109
flags,
110110
futureResultType,
111111
closureEntryPoint,

stdlib/public/Concurrency/AsyncLet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import Swift
2020
@_silgen_name("swift_asyncLet_start")
2121
public func _asyncLetStart<T>(
2222
asyncLet: Builtin.RawPointer,
23-
operation: __owned @Sendable @escaping () async throws -> T
23+
operation: @Sendable () async throws -> T
2424
)
2525

2626
/// Similar to _taskFutureGet but for AsyncLet

stdlib/public/Concurrency/Task.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ static void completeTaskWithClosure(SWIFT_ASYNC_CONTEXT AsyncContext *context,
301301
auto asyncContextPrefix = reinterpret_cast<AsyncContextPrefix *>(
302302
reinterpret_cast<char *>(context) - sizeof(AsyncContextPrefix));
303303

304-
swift_release(asyncContextPrefix->closureContext);
304+
swift_release((HeapObject *)asyncContextPrefix->closureContext);
305305

306306
// Clean up the rest of the task.
307307
return completeTask(context, error);
@@ -336,7 +336,7 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
336336
JobFlags flags, TaskGroup *group,
337337
const Metadata *futureResultType,
338338
FutureAsyncSignature::FunctionType *function,
339-
HeapObject * /* +1 */ closureContext,
339+
void *closureContext, bool owningClosureContext,
340340
size_t initialContextSize) {
341341
assert((futureResultType != nullptr) == flags.task_isFuture());
342342
assert(!flags.task_isFuture() ||
@@ -472,7 +472,8 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
472472
// be is the final hop. Store a signed null instead.
473473
initialContext->Parent = nullptr;
474474
initialContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(
475-
closureContext ? &completeTaskWithClosure : &completeTask);
475+
(closureContext && owningClosureContext) ? &completeTaskWithClosure :
476+
&completeTask);
476477
initialContext->Flags = AsyncContextKind::Ordinary;
477478
initialContext->Flags.setShouldNotDeallocateInCallee(true);
478479

@@ -493,7 +494,8 @@ static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
493494
static AsyncTaskAndContext swift_task_create_group_future_common(
494495
JobFlags flags, TaskGroup *group, const Metadata *futureResultType,
495496
FutureAsyncSignature::FunctionType *function,
496-
HeapObject * /* +1 */ closureContext, size_t initialContextSize);
497+
void *closureContext, bool owningClosureContext,
498+
size_t initialContextSize);
497499

498500
AsyncTaskAndContext
499501
swift::swift_task_create_f(JobFlags flags,
@@ -522,6 +524,7 @@ AsyncTaskAndContext swift::swift_task_create_group_future_f(
522524
return swift_task_create_group_future_common(flags, group,
523525
futureResultType,
524526
function, nullptr,
527+
/*owningClosureContext*/ false,
525528
initialContextSize);
526529
}
527530

@@ -557,6 +560,26 @@ AsyncTaskAndContext swift::swift_task_create_future(JobFlags flags,
557560
return swift_task_create_group_future_common(
558561
flags, nullptr, futureResultType,
559562
taskEntry, closureContext,
563+
/*owningClosureContext*/ true,
564+
initialContextSize);
565+
}
566+
567+
AsyncTaskAndContext swift::swift_task_create_future_no_escaping(JobFlags flags,
568+
const Metadata *futureResultType,
569+
void *closureEntry,
570+
void *closureContext) {
571+
FutureAsyncSignature::FunctionType *taskEntry;
572+
size_t initialContextSize;
573+
std::tie(taskEntry, initialContextSize)
574+
= getAsyncClosureEntryPointAndContextSize<
575+
FutureAsyncSignature,
576+
SpecialPointerAuthDiscriminators::AsyncFutureFunction
577+
>(closureEntry, (HeapObject *)closureContext);
578+
579+
return swift_task_create_group_future_common(
580+
flags, nullptr, futureResultType,
581+
taskEntry, closureContext,
582+
/*owningClosureContext*/ false,
560583
initialContextSize);
561584
}
562585

@@ -576,6 +599,7 @@ swift::swift_task_create_group_future(
576599
return swift_task_create_group_future_common(
577600
flags, group, futureResultType,
578601
taskEntry, closureContext,
602+
/*owningClosureContext*/ true,
579603
initialContextSize);
580604
}
581605

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ void runJobInEstablishedExecutorContext(Job *job);
5555
/// Clear the active task reference for the current thread.
5656
AsyncTask *_swift_task_clearCurrent();
5757

58+
AsyncTaskAndContext swift_task_create_future_no_escaping(JobFlags flags,
59+
const Metadata *futureResultType,
60+
void *closureEntry,
61+
void *closureContext);
62+
5863
#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME)
5964
#define SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR 1
6065
#else

test/SILGen/async_let.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ func testAsyncLetInt() async -> Int {
2121
// CHECK: [[THICK_CLOSURE:%.*]] = thin_to_thick_function [[CLOSURE]] : $@convention(thin) @Sendable @async () -> Int to $@Sendable @async @callee_guaranteed () -> Int
2222
// CHECK: [[REABSTRACT_THUNK:%.*]] = function_ref @$sSiIeghHd_Sis5Error_pIegHrzo_TR : $@convention(thin) @async (@guaranteed @Sendable @async @callee_guaranteed () -> Int) -> (@out Int, @error Error)
2323
// CHECK: [[REABSTRACT_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[REABSTRACT_THUNK]]([[THICK_CLOSURE]]) : $@convention(thin) @async (@guaranteed @Sendable @async @callee_guaranteed () -> Int) -> (@out Int, @error Error)
24-
// CHECK: [[CLOSURE_ARG:%.*]] = convert_function [[REABSTRACT_CLOSURE]] : $@async @callee_guaranteed () -> (@out Int, @error Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <Int>
25-
// CHECK: [[ASYNC_LET_START:%.*]] = builtin "startAsyncLet"<Int>([[CLOSURE_ARG]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <Int>) : $Builtin.RawPointer
24+
// CHECK: [[ESCAPING_CLOSURE:%.*]] = convert_function [[REABSTRACT_CLOSURE]] : $@async @callee_guaranteed () -> (@out Int, @error Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <Int>
25+
// CHECK: [[CLOSURE_ARG:%.*]] = convert_escape_to_noescape [not_guaranteed] [[ESCAPING_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <Int> to $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <Int>
26+
// CHECK: [[ASYNC_LET_START:%.*]] = builtin "startAsyncLet"<Int>([[CLOSURE_ARG]] : $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <Int>) : $Builtin.RawPointer
2627
async let i = await getInt()
2728

2829
// CHECK: [[ASYNC_LET_GET:%.*]] = function_ref @swift_asyncLet_wait : $@convention(thin) @async <τ_0_0> (Builtin.RawPointer) -> @out τ_0_0

0 commit comments

Comments
 (0)