Skip to content

Commit 5fa02d8

Browse files
committed
[concurrency] Add a new entrypoint: swift_task_isCurrentExecutorWithFlags.
This entrypoint is similar to swift_task_isCurrentExecutor except that it provides an ABI level option flag that enables one to configure its behavior in a backwards deployable manner via the option flag. I used this to expose at the ABI level the ability to check the current executor without crashing on failure, while preserving the current behavior of swift_task_isCurrentExecutor (which crashes on failure). I am going to use this to implement swift_task_runOnMainActor.
1 parent 763666e commit 5fa02d8

File tree

6 files changed

+84
-25
lines changed

6 files changed

+84
-25
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,45 @@ TaskExecutorRef swift_task_getPreferredTaskExecutor(void);
963963
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
964964
bool swift_task_isCurrentExecutor(SerialExecutorRef executor);
965965

966+
/// This is an options enum that is used to pass flags to
967+
/// swift_task_isCurrentExecutorWithFlags. It is meant to be a flexible toggle.
968+
///
969+
/// Since this is an options enum, so all values should be powers of 2.
970+
///
971+
/// NOTE: We are purposely leaving this as a uint64_t so that on all platforms
972+
/// this could be a pointer to a different enum instance if we need it to be.
973+
enum swift_task_is_current_executor_flag : uint64_t {
974+
/// We aren't passing any flags.
975+
None = 0x0,
976+
977+
/// This is not used today, but is just future ABI reservation.
978+
///
979+
/// The intention is that we may want the ability to tell future versions of
980+
/// the runtime that this uint64_t is actually a pointer that it should
981+
/// dereference and then have further extended behavior controlled by a
982+
/// different enum. By placing this here, we ensure that we will have a tagged
983+
/// pointer compatible flag for this purpose.
984+
TaggedPointer = 0x1,
985+
986+
/// This is not used today, but is just future ABI reservation.
987+
///
988+
/// \see swift_task_is_current_executor_flag::TaggedPointer
989+
TaggedPointer2 = 0x2,
990+
991+
/// This is not used today, but is just future ABI reservation.
992+
///
993+
/// \see swift_task_is_current_executor_flag::TaggedPointer
994+
TaggedPointer3 = 0x4,
995+
996+
/// The routine should assert on failure.
997+
Assert = 0x8,
998+
};
999+
1000+
SWIFT_EXPORT_FROM(swift_Concurrency)
1001+
SWIFT_CC(swift)
1002+
bool swift_task_isCurrentExecutorWithFlags(
1003+
SerialExecutorRef executor, swift_task_is_current_executor_flag flags);
1004+
9661005
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
9671006
void swift_task_reportUnexpectedExecutor(
9681007
const unsigned char *file, uintptr_t fileLength, bool fileIsASCII,

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ OVERRIDE_ACTOR(task_isCurrentExecutor, bool,
136136
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
137137
swift::, (SerialExecutorRef executor), (executor))
138138

139+
OVERRIDE_ACTOR(task_isCurrentExecutorWithFlags, bool,
140+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
141+
(SerialExecutorRef executor, swift_task_is_current_executor_flag flags),
142+
(executor, flags))
143+
139144
OVERRIDE_ACTOR(task_switch, void,
140145
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swiftasync),
141146
swift::, (SWIFT_ASYNC_CONTEXT AsyncContext *resumeToContext,

stdlib/public/Concurrency/Actor.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -379,15 +379,6 @@ bool swift_bincompat_useLegacyNonCrashingExecutorChecks() {
379379
return legacyMode;
380380
}
381381

382-
// Check override of executor checking mode.
383-
static void checkIsCurrentExecutorMode(void *context) {
384-
bool useLegacyMode =
385-
swift_bincompat_useLegacyNonCrashingExecutorChecks();
386-
auto checkMode = static_cast<IsCurrentExecutorCheckMode *>(context);
387-
*checkMode = useLegacyMode ? Legacy_NoCheckIsolated_NonCrashing
388-
: Swift6_UseCheckIsolated_AllowCrash;
389-
}
390-
391382
// Implemented in Swift to avoid some annoying hard-coding about
392383
// TaskExecutor's protocol witness table. We could inline this
393384
// with effort, though.
@@ -402,9 +393,16 @@ extern "C" SWIFT_CC(swift) void _swift_task_enqueueOnExecutor(
402393
Job *job, HeapObject *executor, const Metadata *executorType,
403394
const SerialExecutorWitnessTable *wtable);
404395

396+
namespace {
397+
using SwiftTaskIsCurrentExecutorOptions =
398+
OptionSet<swift_task_is_current_executor_flag>;
399+
}
400+
405401
SWIFT_CC(swift)
406-
static bool isCurrentExecutor(SerialExecutorRef expectedExecutor,
407-
IsCurrentExecutorCheckMode checkMode) {
402+
static bool swift_task_isCurrentExecutorWithFlagsImpl(
403+
SerialExecutorRef expectedExecutor,
404+
swift_task_is_current_executor_flag flags) {
405+
auto options = SwiftTaskIsCurrentExecutorOptions(flags);
408406
auto current = ExecutorTrackingInfo::current();
409407

410408
if (!current) {
@@ -423,14 +421,14 @@ static bool isCurrentExecutor(SerialExecutorRef expectedExecutor,
423421

424422
// Otherwise, as last resort, let the expected executor check using
425423
// external means, as it may "know" this thread is managed by it etc.
426-
if (checkMode == Swift6_UseCheckIsolated_AllowCrash) {
424+
if (options.contains(swift_task_is_current_executor_flag::Assert)) {
427425
swift_task_checkIsolated(expectedExecutor); // will crash if not same context
428426

429427
// checkIsolated did not crash, so we are on the right executor, after all!
430428
return true;
431429
}
432430

433-
assert(checkMode == Legacy_NoCheckIsolated_NonCrashing);
431+
assert(!options.contains(swift_task_is_current_executor_flag::Assert));
434432
return false;
435433
}
436434

@@ -461,7 +459,7 @@ static bool isCurrentExecutor(SerialExecutorRef expectedExecutor,
461459
// the crashing 'dispatch_assert_queue(main queue)' which will either crash
462460
// or confirm we actually are on the main queue; or the custom expected
463461
// executor has a chance to implement a similar queue check.
464-
if (checkMode == Legacy_NoCheckIsolated_NonCrashing) {
462+
if (!options.contains(swift_task_is_current_executor_flag::Assert)) {
465463
if ((expectedExecutor.isMainExecutor() && !currentExecutor.isMainExecutor()) ||
466464
(!expectedExecutor.isMainExecutor() && currentExecutor.isMainExecutor())) {
467465
return false;
@@ -522,7 +520,7 @@ static bool isCurrentExecutor(SerialExecutorRef expectedExecutor,
522520
// Note that this only works because the closure in assumeIsolated is
523521
// synchronous, and will not cause suspensions, as that would require the
524522
// presence of a Task.
525-
if (checkMode == Swift6_UseCheckIsolated_AllowCrash) {
523+
if (options.contains(swift_task_is_current_executor_flag::Assert)) {
526524
swift_task_checkIsolated(expectedExecutor); // will crash if not same context
527525

528526
// The checkIsolated call did not crash, so we are on the right executor.
@@ -531,10 +529,20 @@ static bool isCurrentExecutor(SerialExecutorRef expectedExecutor,
531529

532530
// In the end, since 'checkIsolated' could not be used, so we must assume
533531
// that the executors are not the same context.
534-
assert(checkMode == Legacy_NoCheckIsolated_NonCrashing);
532+
assert(!options.contains(swift_task_is_current_executor_flag::Assert));
535533
return false;
536534
}
537535

536+
// Check override of executor checking mode.
537+
static void swift_task_setDefaultExecutorCheckingFlags(void *context) {
538+
bool useLegacyMode = swift_bincompat_useLegacyNonCrashingExecutorChecks();
539+
auto checkMode = static_cast<swift_task_is_current_executor_flag *>(context);
540+
if (!useLegacyMode) {
541+
*checkMode = swift_task_is_current_executor_flag(
542+
*checkMode | swift_task_is_current_executor_flag::Assert);
543+
}
544+
}
545+
538546
SWIFT_CC(swift)
539547
static bool
540548
swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor) {
@@ -546,11 +554,14 @@ swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor) {
546554
// instead must call into 'checkIsolated' or crash directly.
547555
//
548556
// Whenever we confirm an executor equality, we can return true, in any mode.
549-
static IsCurrentExecutorCheckMode checkMode;
550-
static swift::once_t checkModeToken;
551-
swift::once(checkModeToken, checkIsCurrentExecutorMode, &checkMode);
557+
static swift_task_is_current_executor_flag isCurrentExecutorFlag;
558+
static swift::once_t isCurrentExecutorFlagToken;
559+
swift::once(isCurrentExecutorFlagToken,
560+
swift_task_setDefaultExecutorCheckingFlags,
561+
&isCurrentExecutorFlag);
552562

553-
return isCurrentExecutor(expectedExecutor, checkMode);
563+
return swift_task_isCurrentExecutorWithFlags(expectedExecutor,
564+
isCurrentExecutorFlag);
554565
}
555566

556567
/// Logging level for unexpected executors:
@@ -2337,11 +2348,8 @@ static void swift_task_deinitOnExecutorImpl(void *object,
23372348
//
23382349
// Note that isCurrentExecutor() returns true for @MainActor
23392350
// when running on the main thread without any executor.
2340-
//
2341-
// We always use "legacy" checking mode here, because that's the desired
2342-
// behaviour for this use case. This does not change with SDK version or
2343-
// language mode.
2344-
if (isCurrentExecutor(newExecutor, Legacy_NoCheckIsolated_NonCrashing)) {
2351+
if (swift_task_isCurrentExecutorWithFlags(
2352+
newExecutor, swift_task_is_current_executor_flag::None)) {
23452353
return work(object); // 'return' forces tail call
23462354
}
23472355

test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,7 @@ _swift_task_getMainExecutor
997997
_swift_task_hasTaskGroupStatusRecord
998998
_swift_task_isCancelled
999999
_swift_task_isCurrentExecutor
1000+
_swift_task_isCurrentExecutorWithFlags
10001001
_swift_task_isOnExecutor
10011002
_swift_task_isOnExecutor_hook
10021003
_swift_task_localValueGet

test/abi/macOS/arm64/concurrency.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,4 @@ Added: _$ss15SuspendingClockV7InstantV3nowADvpZMV
371371
Added: _$ss9TaskLocalC18_enclosingInstance7wrapped7storagexs5NeverO_s24ReferenceWritableKeyPathCyAGxGAIyAgByxGGtcipZMV
372372

373373
Added: _swift_taskGroup_initializeWithOptions
374+
Added: _swift_task_isCurrentExecutorWithFlags

unittests/runtime/CompatibilityOverrideConcurrency.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startOnMainActorImpl) {
330330
swift_task_startOnMainActor(nullptr);
331331
}
332332

333+
TEST_F(CompatibilityOverrideConcurrencyTest,
334+
test_swift_task_isCurrentExecutorWithFlags) {
335+
swift_task_isCurrentExecutorWithFlags(swift_task_getMainExecutor(), 0);
336+
}
337+
333338
#if RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST
334339
TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_asyncMainDrainQueue) {
335340

0 commit comments

Comments
 (0)