Skip to content

Commit 2943425

Browse files
committed
Allow swift_continuation_await to be forced to honor an executor.
1 parent 3aa04db commit 2943425

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
@@ -948,6 +948,8 @@ static AsyncTask *swift_continuation_initImpl(ContinuationAsyncContext *context,
948948
AsyncContinuationFlags flags) {
949949
context->Flags = AsyncContextKind::Continuation;
950950
if (flags.canThrow()) context->Flags.setCanThrow(true);
951+
if (flags.isExecutorSwitchForced())
952+
context->Flags.continuation_setIsExecutorSwitchForced(true);
951953
context->ErrorResult = nullptr;
952954

953955
// Set the current executor as the target executor unless there's
@@ -993,9 +995,12 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) {
993995

994996
// If the status is already Resumed, we can resume immediately.
995997
// Comparing against Pending may be very slightly more compact.
996-
if (oldStatus != ContinuationStatus::Pending)
997-
// This is fine given how swift_continuation_init sets it up.
998+
if (oldStatus != ContinuationStatus::Pending) {
999+
if (context->isExecutorSwitchForced())
1000+
return swift_task_switch(context, context->ResumeParent,
1001+
context->ResumeToExecutor);
9981002
return context->ResumeParent(context);
1003+
}
9991004

10001005
// Load the current task (we alreaady did this in assertions builds).
10011006
#ifdef NDEBUG
@@ -1021,7 +1026,10 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) {
10211026

10221027
// Restore the running state of the task and resume it.
10231028
task->flagAsRunning();
1024-
return task->runInFullyEstablishedContext();
1029+
if (context->isExecutorSwitchForced())
1030+
return swift_task_switch(context, context->ResumeParent,
1031+
context->ResumeToExecutor);
1032+
return context->ResumeParent(context);
10251033
}
10261034

10271035
static void resumeTaskAfterContinuation(AsyncTask *task,

0 commit comments

Comments
 (0)