Skip to content

Commit 78e45e4

Browse files
committed
Allow swift_continuation_await to be forced to honor an executor.
1 parent e124253 commit 78e45e4

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,7 +2211,11 @@ class AsyncContextFlags : public FlagSet<uint32_t> {
22112211
Kind_width = 8,
22122212

22132213
CanThrow = 8,
2214-
ShouldNotDeallocate = 9
2214+
ShouldNotDeallocate = 9,
2215+
2216+
// Kind-specific flags should grow down from 31.
2217+
2218+
Continuation_IsExecutorSwitchForced = 31,
22152219
};
22162220

22172221
explicit AsyncContextFlags(uint32_t bits) : FlagSet(bits) {}
@@ -2238,6 +2242,11 @@ class AsyncContextFlags : public FlagSet<uint32_t> {
22382242
FLAGSET_DEFINE_FLAG_ACCESSORS(ShouldNotDeallocate,
22392243
shouldNotDeallocateInCallee,
22402244
setShouldNotDeallocateInCallee)
2245+
2246+
/// See AsyncContinuationFlags::isExecutorSwitchForced.
2247+
FLAGSET_DEFINE_FLAG_ACCESSORS(Continuation_IsExecutorSwitchForced,
2248+
continuation_isExecutorSwitchForced,
2249+
continuation_setIsExecutorSwitchForced)
22412250
};
22422251

22432252
/// Flags passed to swift_continuation_init.
@@ -2247,6 +2256,7 @@ class AsyncContinuationFlags : public FlagSet<size_t> {
22472256
CanThrow = 0,
22482257
HasExecutorOverride = 1,
22492258
IsPreawaited = 2,
2259+
IsExecutorSwitchForced = 3,
22502260
};
22512261

22522262
explicit AsyncContinuationFlags(size_t bits) : FlagSet(bits) {}
@@ -2262,10 +2272,27 @@ class AsyncContinuationFlags : public FlagSet<size_t> {
22622272
hasExecutorOverride,
22632273
setHasExecutorOverride)
22642274

2275+
/// Whether the switch to the target executor should be forced
2276+
/// by swift_continuation_await. If this is not set, and
2277+
/// swift_continuation_await finds that the continuation has
2278+
/// already been resumed, then execution will continue on the
2279+
/// current executor. This has no effect in combination with
2280+
/// pre-awaiting.
2281+
///
2282+
/// Setting this flag when you know statically that you're
2283+
/// already on the right executor is suboptimal. In particular,
2284+
/// there's no good reason to set this if you're not also using
2285+
/// an executor override.
2286+
FLAGSET_DEFINE_FLAG_ACCESSORS(IsExecutorSwitchForced,
2287+
isExecutorSwitchForced,
2288+
setIsExecutorSwitchForced)
2289+
22652290
/// Whether the continuation is "pre-awaited". If so, it should
22662291
/// be set up in the already-awaited state, and so resumptions
22672292
/// will immediately schedule the continuation to begin
2268-
/// asynchronously.
2293+
/// asynchronously. The continuation must not be subsequently
2294+
/// awaited if this is set. The task is immediately treated as
2295+
/// suspended.
22692296
FLAGSET_DEFINE_FLAG_ACCESSORS(IsPreawaited,
22702297
isPreawaited,
22712298
setIsPreawaited)

include/swift/ABI/Task.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,10 @@ class ContinuationAsyncContext : public AsyncContext {
650650
ErrorResult = error;
651651
}
652652

653+
bool isExecutorSwitchForced() const {
654+
return Flags.continuation_isExecutorSwitchForced();
655+
}
656+
653657
static bool classof(const AsyncContext *context) {
654658
return context->Flags.getKind() == AsyncContextKind::Continuation;
655659
}

stdlib/public/Concurrency/Task.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,8 @@ static AsyncTask *swift_continuation_initImpl(ContinuationAsyncContext *context,
942942
AsyncContinuationFlags flags) {
943943
context->Flags = AsyncContextKind::Continuation;
944944
if (flags.canThrow()) context->Flags.setCanThrow(true);
945+
if (flags.isExecutorSwitchForced())
946+
context->Flags.continuation_setIsExecutorSwitchForced(true);
945947
context->ErrorResult = nullptr;
946948

947949
// Set the current executor as the target executor unless there's
@@ -987,9 +989,12 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) {
987989

988990
// If the status is already Resumed, we can resume immediately.
989991
// Comparing against Pending may be very slightly more compact.
990-
if (oldStatus != ContinuationStatus::Pending)
991-
// This is fine given how swift_continuation_init sets it up.
992+
if (oldStatus != ContinuationStatus::Pending) {
993+
if (context->isExecutorSwitchForced())
994+
return swift_task_switch(context, context->ResumeParent,
995+
context->ResumeToExecutor);
992996
return context->ResumeParent(context);
997+
}
993998

994999
// Load the current task (we alreaady did this in assertions builds).
9951000
#ifdef NDEBUG
@@ -1015,7 +1020,10 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) {
10151020

10161021
// Restore the running state of the task and resume it.
10171022
task->flagAsRunning();
1018-
return task->runInFullyEstablishedContext();
1023+
if (context->isExecutorSwitchForced())
1024+
return swift_task_switch(context, context->ResumeParent,
1025+
context->ResumeToExecutor);
1026+
return context->ResumeParent(context);
10191027
}
10201028

10211029
static void resumeTaskAfterContinuation(AsyncTask *task,

0 commit comments

Comments
 (0)