Skip to content

Commit f3f1e1e

Browse files
committed
[Concurrency] Implement isIsolatingCurrentContext requirement and mode
1 parent b57f8a9 commit f3f1e1e

14 files changed

+323
-29
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -773,8 +773,8 @@ void swift_task_enqueue(Job *job, SerialExecutorRef executor);
773773
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
774774
void swift_task_enqueueGlobal(Job *job);
775775

776-
/// Invoke an executor's `checkIsolated` or otherwise equivalent API,
777-
/// that will crash if the current executor is NOT the passed executor.
776+
/// Invoke an executor's `checkIsolated` implementation;
777+
/// It will crash if the current executor is NOT the passed executor.
778778
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
779779
void swift_task_checkIsolated(SerialExecutorRef executor);
780780

@@ -785,6 +785,15 @@ void swift_task_checkIsolated(SerialExecutorRef executor);
785785
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
786786
bool swift_task_invokeSwiftCheckIsolated(SerialExecutorRef executor);
787787

788+
/// Invoke an executor's `isIsolatingCurrentContext` implementation;
789+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
790+
bool swift_task_isIsolatingCurrentContext(SerialExecutorRef executor);
791+
792+
/// Invoke a Swift executor's `isIsolatingCurrentContext` implementation; returns
793+
/// `true` if it invoked the Swift implementation, `false` otherwise.
794+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
795+
bool swift_task_invokeSwiftIsIsolatingCurrentContext(SerialExecutorRef executor);
796+
788797
/// A count in nanoseconds.
789798
using JobDelay = unsigned long long;
790799

@@ -1037,6 +1046,10 @@ enum swift_task_is_current_executor_flag : uint64_t {
10371046

10381047
/// The routine should assert on failure.
10391048
Assert = 0x8,
1049+
1050+
/// The routine should use 'isIsolatingCurrentContext' function on the
1051+
/// 'expected' executor instead of `checkIsolated`.
1052+
HasIsIsolatingCurrentContext = 0x10,
10401053
};
10411054

10421055
SWIFT_EXPORT_FROM(swift_Concurrency)

include/swift/Runtime/ConcurrencyHooks.def

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ SWIFT_CONCURRENCY_HOOK(void, swift_task_enqueueGlobalWithDeadline,
4444
long long tnsec,
4545
int clock, Job *job);
4646

47-
SWIFT_CONCURRENCY_HOOK(void, swift_task_checkIsolated, SerialExecutorRef executor);
47+
SWIFT_CONCURRENCY_HOOK(void, swift_task_checkIsolated,
48+
SerialExecutorRef executor);
49+
50+
SWIFT_CONCURRENCY_HOOK(bool, swift_task_isIsolatingCurrentContext,
51+
SerialExecutorRef executor);
4852

4953
SWIFT_CONCURRENCY_HOOK(bool, swift_task_isOnExecutor,
5054
HeapObject *executor,

stdlib/public/Concurrency/Actor.cpp

Lines changed: 107 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,15 @@ enum IsCurrentExecutorCheckMode : unsigned {
353353

354354
// Shimming call to Swift runtime because Swift Embedded does not have
355355
// these symbols defined.
356-
bool __swift_bincompat_useLegacyNonCrashingExecutorChecks() {
356+
swift_task_is_current_executor_flag
357+
__swift_bincompat_useLegacyNonCrashingExecutorChecks() {
357358
#if !SWIFT_CONCURRENCY_EMBEDDED
358-
return swift::runtime::bincompat::
359-
swift_bincompat_useLegacyNonCrashingExecutorChecks();
360-
#else
361-
return false;
359+
if (swift::runtime::bincompat::
360+
swift_bincompat_useLegacyNonCrashingExecutorChecks()) {
361+
return swift_task_is_current_executor_flag::None;
362+
}
362363
#endif
364+
return swift_task_is_current_executor_flag::Assert;
363365
}
364366

365367
// Shimming call to Swift runtime because Swift Embedded does not have
@@ -376,22 +378,40 @@ const char *__swift_runtime_env_useLegacyNonCrashingExecutorChecks() {
376378

377379
// Done this way because of the interaction with the initial value of
378380
// 'unexpectedExecutorLogLevel'
379-
bool swift_bincompat_useLegacyNonCrashingExecutorChecks() {
380-
bool legacyMode = __swift_bincompat_useLegacyNonCrashingExecutorChecks();
381+
swift_task_is_current_executor_flag swift_bincompat_useLegacyNonCrashingExecutorChecks() {
382+
swift_task_is_current_executor_flag options =
383+
__swift_bincompat_useLegacyNonCrashingExecutorChecks();
381384

382385
// Potentially, override the platform detected mode, primarily used in tests.
383386
if (const char *modeStr =
384387
__swift_runtime_env_useLegacyNonCrashingExecutorChecks()) {
388+
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = '%s'", modeStr);
385389
if (strcmp(modeStr, "nocrash") == 0 ||
386390
strcmp(modeStr, "legacy") == 0) {
387-
return true;
391+
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = %s => NONE; options = %d",
392+
modeStr, options);
393+
options = swift_task_is_current_executor_flag(
394+
swift_task_is_current_executor_flag::None);
395+
}
396+
397+
if (strcmp(modeStr, "swift62") == 0) {
398+
options = swift_task_is_current_executor_flag(
399+
options | swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext);
400+
// When we're using the isIsolatingCurrentContext we don't want to use crashing APIs,
401+
// so disable it explicitly.
402+
options = swift_task_is_current_executor_flag(
403+
options & ~swift_task_is_current_executor_flag::Assert);
404+
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = %s => 6.2; options = %d", modeStr, options);
388405
} else if (strcmp(modeStr, "crash") == 0 ||
389406
strcmp(modeStr, "swift6") == 0) {
390-
return false; // don't use the legacy mode
407+
options = swift_task_is_current_executor_flag(
408+
options | swift_task_is_current_executor_flag::Assert);
409+
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = %s => 6.9; options = %d", modeStr, options);
391410
} // else, just use the platform detected mode
392411
} // no override, use the default mode
393412

394-
return legacyMode;
413+
SWIFT_TASK_DEBUG_LOG("executor checking: options = %d", options);
414+
return options;
395415
}
396416

397417
// Implemented in Swift to avoid some annoying hard-coding about
@@ -419,6 +439,13 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
419439
swift_task_is_current_executor_flag flags) {
420440
auto options = SwiftTaskIsCurrentExecutorOptions(flags);
421441
auto current = ExecutorTrackingInfo::current();
442+
SWIFT_TASK_DEBUG_LOG("executor checking: current task %p", current);
443+
#ifndef NDEBUG
444+
if (options.contains(swift_task_is_current_executor_flag::Assert))
445+
SWIFT_TASK_DEBUG_LOG("executor checking: active option = Assert (%d)", flags);
446+
if (options.contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext))
447+
SWIFT_TASK_DEBUG_LOG("executor checking: active option = HasIsIsolatingCurrentContext (%d)", flags);
448+
#endif
422449

423450
if (!current) {
424451
// We have no current executor, i.e. we are running "outside" of Swift
@@ -428,15 +455,30 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
428455

429456
// Special handling the main executor by detecting the main thread.
430457
if (expectedExecutor.isMainExecutor() && isExecutingOnMainThread()) {
458+
SWIFT_TASK_DEBUG_LOG("executor checking: expected is main executor & current thread is main thread => pass", nullptr);
431459
return true;
432460
}
433461

434462
// We cannot use 'complexEquality' as it requires two executor instances,
435463
// and we do not have a 'current' executor here.
436464

465+
if (options.contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext)) {
466+
SWIFT_TASK_DEBUG_LOG("executor checking mode option: HasIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext",
467+
expectedExecutor.getIdentity());
468+
// The executor has the most recent 'isIsolatingCurrentContext' API
469+
// so available so we prefer calling that to 'checkIsolated'.
470+
auto result = swift_task_isIsolatingCurrentContext(expectedExecutor);
471+
472+
SWIFT_TASK_DEBUG_LOG("executor checking mode option: HasIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext => %s",
473+
expectedExecutor.getIdentity(), result ? "pass" : "fail");
474+
return result;
475+
}
476+
437477
// Otherwise, as last resort, let the expected executor check using
438478
// external means, as it may "know" this thread is managed by it etc.
439479
if (options.contains(swift_task_is_current_executor_flag::Assert)) {
480+
SWIFT_TASK_DEBUG_LOG("executor checking mode option: Assert; invoke (%p).expectedExecutor",
481+
expectedExecutor.getIdentity());
440482
swift_task_checkIsolated(expectedExecutor); // will crash if not same context
441483

442484
// checkIsolated did not crash, so we are on the right executor, after all!
@@ -448,16 +490,23 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
448490
}
449491

450492
SerialExecutorRef currentExecutor = current->getActiveExecutor();
493+
SWIFT_TASK_DEBUG_LOG("executor checking: current executor %p %s",
494+
currentExecutor.getIdentity(), currentExecutor.getIdentityDebugName());
451495

452496
// Fast-path: the executor is exactly the same memory address;
453497
// We assume executors do not come-and-go appearing under the same address,
454498
// and treat pointer equality of executors as good enough to assume the executor.
455499
if (currentExecutor == expectedExecutor) {
500+
SWIFT_TASK_DEBUG_LOG("executor checking: current executor %p, equal to expected executor => pass",
501+
currentExecutor.getIdentity());
456502
return true;
457503
}
458504

459505
// Fast-path, specialize the common case of comparing two main executors.
460506
if (currentExecutor.isMainExecutor() && expectedExecutor.isMainExecutor()) {
507+
SWIFT_TASK_DEBUG_LOG("executor checking: current executor %p is main executor, and expected executor (%p) is main executor => pass",
508+
currentExecutor.getIdentity(),
509+
expectedExecutor.getIdentity());
461510
return true;
462511
}
463512

@@ -475,8 +524,16 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
475524
// or confirm we actually are on the main queue; or the custom expected
476525
// executor has a chance to implement a similar queue check.
477526
if (!options.contains(swift_task_is_current_executor_flag::Assert)) {
478-
if ((expectedExecutor.isMainExecutor() && !currentExecutor.isMainExecutor()) ||
479-
(!expectedExecutor.isMainExecutor() && currentExecutor.isMainExecutor())) {
527+
if ((expectedExecutor.isMainExecutor() && !currentExecutor.isMainExecutor())) {
528+
SWIFT_TASK_DEBUG_LOG("executor checking: expected executor %p%s is main executor, and current executor %p%s is NOT => fail",
529+
expectedExecutor.getIdentity(), expectedExecutor.getIdentityDebugName(),
530+
currentExecutor.getIdentity(), currentExecutor.getIdentityDebugName());
531+
return false;
532+
}
533+
if ((!expectedExecutor.isMainExecutor() && currentExecutor.isMainExecutor())) {
534+
SWIFT_TASK_DEBUG_LOG("executor checking: expected executor %p%s is NOT main executor, and current executor %p%s is => fail",
535+
expectedExecutor.getIdentity(), expectedExecutor.getIdentityDebugName(),
536+
currentExecutor.getIdentity(), currentExecutor.getIdentityDebugName());
480537
return false;
481538
}
482539
}
@@ -499,6 +556,8 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
499556
currentExecutor.getSerialExecutorWitnessTable()),
500557
reinterpret_cast<const WitnessTable *>(
501558
expectedExecutor.getSerialExecutorWitnessTable()))) {
559+
SWIFT_TASK_DEBUG_LOG("executor checking: can check isComplexEquality (%p, and %p)",
560+
expectedExecutor.getIdentity(), currentExecutor.getIdentity());
502561

503562
auto isSameExclusiveExecutionContextResult =
504563
_task_serialExecutor_isSameExclusiveExecutionContext(
@@ -510,11 +569,25 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
510569
// it and return; if it was false, we need to give checkIsolated another
511570
// chance to check.
512571
if (isSameExclusiveExecutionContextResult) {
572+
SWIFT_TASK_DEBUG_LOG("executor checking: isComplexEquality (%p, and %p) is true => pass",
573+
expectedExecutor.getIdentity(), currentExecutor.getIdentity());
513574
return true;
514575
} // else, we must give 'checkIsolated' a last chance to verify isolation
515576
}
516577
}
517578

579+
if (options.contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext)) {
580+
// We can invoke the 'isIsolatingCurrentContext' function.
581+
SWIFT_TASK_DEBUG_LOG("executor checking: can call (%p).isIsolatingCurrentContext",
582+
expectedExecutor.getIdentity());
583+
584+
bool checkResult = swift_task_isIsolatingCurrentContext(expectedExecutor);
585+
586+
SWIFT_TASK_DEBUG_LOG("executor checking: can call (%p).isIsolatingCurrentContext => %p",
587+
expectedExecutor.getIdentity(), checkResult ? "pass" : "fail");
588+
return checkResult;
589+
}
590+
518591
// This provides a last-resort check by giving the expected SerialExecutor the
519592
// chance to perform a check using some external knowledge if perhaps we are,
520593
// after all, on this executor, but the Swift concurrency runtime was just not
@@ -536,9 +609,13 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
536609
// synchronous, and will not cause suspensions, as that would require the
537610
// presence of a Task.
538611
if (options.contains(swift_task_is_current_executor_flag::Assert)) {
612+
SWIFT_TASK_DEBUG_LOG("executor checking: call (%p).checkIsolated",
613+
expectedExecutor.getIdentity());
539614
swift_task_checkIsolated(expectedExecutor); // will crash if not same context
540615

541616
// The checkIsolated call did not crash, so we are on the right executor.
617+
SWIFT_TASK_DEBUG_LOG("executor checking: call (%p).checkIsolated passed => pass",
618+
expectedExecutor.getIdentity());
542619
return true;
543620
}
544621

@@ -550,12 +627,23 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
550627

551628
// Check override of executor checking mode.
552629
static void swift_task_setDefaultExecutorCheckingFlags(void *context) {
553-
bool useLegacyMode = swift_bincompat_useLegacyNonCrashingExecutorChecks();
554-
auto checkMode = static_cast<swift_task_is_current_executor_flag *>(context);
555-
if (!useLegacyMode) {
556-
*checkMode = swift_task_is_current_executor_flag(
557-
*checkMode | swift_task_is_current_executor_flag::Assert);
558-
}
630+
SWIFT_TASK_DEBUG_LOG("executor checking: swift_task_setDefaultExecutorCheckingFlags = %d", nullptr);
631+
auto useLegacyMode = swift_bincompat_useLegacyNonCrashingExecutorChecks();
632+
SWIFT_TASK_DEBUG_LOG("executor checking: use legacy mode = %d", useLegacyMode);
633+
auto *options = static_cast<swift_task_is_current_executor_flag *>(context);
634+
SWIFT_TASK_DEBUG_LOG("executor checking: options = %d", *options);
635+
// if (!useLegacyMode) {
636+
// *options = swift_task_is_current_executor_flag(
637+
// *options | swift_task_is_current_executor_flag::Assert);
638+
// }
639+
*options = useLegacyMode;
640+
SWIFT_TASK_DEBUG_LOG("executor checking: resulting options = %d", *options);
641+
SWIFT_TASK_DEBUG_LOG("executor checking: option Assert = %d",
642+
SwiftTaskIsCurrentExecutorOptions(*options).
643+
contains(swift_task_is_current_executor_flag::Assert));
644+
SWIFT_TASK_DEBUG_LOG("executor checking: option HasIsIsolatingCurrentContext = %d",
645+
SwiftTaskIsCurrentExecutorOptions(*options).
646+
contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext));
559647
}
560648

561649
SWIFT_CC(swift)
@@ -2306,8 +2394,7 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
23062394
currentTaskExecutor.getIdentity(),
23072395
currentTaskExecutor.isDefined() ? "" : " (undefined)",
23082396
newTaskExecutor.getIdentity(),
2309-
newTaskExecutor.isDefined() ? "" : " (undefined)",
2310-
trackingInfo->isSynchronousStart() ? "[synchronous start]" : "");
2397+
newTaskExecutor.isDefined() ? "" : " (undefined)");
23112398

23122399
// If the current executor is compatible with running the new executor,
23132400
// we can just immediately continue running with the resume function

stdlib/public/Concurrency/ConcurrencyHooks.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ swift::swift_task_enqueueGlobalWithDeadline(
100100

101101
SWIFT_CC(swift) static void
102102
swift_task_checkIsolatedOrig(SerialExecutorRef executor) {
103-
swift_task_checkIsolatedImpl(*reinterpret_cast<SwiftExecutorRef *>(&executor));}
103+
swift_task_checkIsolatedImpl(*reinterpret_cast<SwiftExecutorRef *>(&executor));
104+
}
104105

105106
void
106107
swift::swift_task_checkIsolated(SerialExecutorRef executor) {
@@ -110,6 +111,20 @@ swift::swift_task_checkIsolated(SerialExecutorRef executor) {
110111
swift_task_checkIsolatedOrig(executor);
111112
}
112113

114+
SWIFT_CC(swift) static bool
115+
swift_task_isIsolatingCurrentContextOrig(SerialExecutorRef executor) {
116+
return swift_task_isIsolatingCurrentContextImpl(
117+
*reinterpret_cast<SwiftExecutorRef *>(&executor));
118+
}
119+
120+
bool
121+
swift::swift_task_isIsolatingCurrentContext(SerialExecutorRef executor) {
122+
if (SWIFT_UNLIKELY(swift_task_isIsolatingCurrentContext_hook))
123+
return swift_task_isIsolatingCurrentContext_hook(executor, swift_task_isIsolatingCurrentContextOrig);
124+
else
125+
return swift_task_isIsolatingCurrentContextOrig(executor);
126+
}
127+
113128
// Implemented in Swift because we need to obtain the user-defined flags on the executor ref.
114129
//
115130
// We could inline this with effort, though.

stdlib/public/Concurrency/CooperativeGlobalExecutor.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) {
154154
swift_executor_invokeSwiftCheckIsolated(executor);
155155
}
156156

157+
SWIFT_CC(swift)
158+
bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) {
159+
return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor);
160+
}
161+
157162
/// Insert a job into the cooperative global queue with a delay.
158163
SWIFT_CC(swift)
159164
void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay,

stdlib/public/Concurrency/DispatchGlobalExecutor.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,12 @@ void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) {
448448
swift_Concurrency_fatalError(0, "Incorrect actor executor assumption");
449449
}
450450

451+
SWIFT_CC(swift)
452+
bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) {
453+
return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor);
454+
}
455+
456+
451457
SWIFT_CC(swift)
452458
SwiftExecutorRef swift_task_getMainExecutorImpl() {
453459
return swift_executor_ordinary(

stdlib/public/Concurrency/Executor.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ public protocol SerialExecutor: Executor {
195195
@available(SwiftStdlib 6.0, *)
196196
func checkIsolated()
197197

198+
@available(SwiftStdlib 6.2, *)
199+
func isIsolatingCurrentContext() -> Bool
200+
198201
}
199202

200203
@available(SwiftStdlib 6.0, *)
@@ -210,6 +213,16 @@ extension SerialExecutor {
210213
}
211214
}
212215

216+
@available(SwiftStdlib 6.2, *)
217+
extension SerialExecutor {
218+
219+
@available(SwiftStdlib 6.2, *)
220+
public func isIsolatingCurrentContext() -> Bool {
221+
self.checkIsolated()
222+
return true
223+
}
224+
}
225+
213226
/// An executor that may be used as preferred executor by a task.
214227
///
215228
/// ### Impact of setting a task executor preference
@@ -467,6 +480,13 @@ internal func _task_serialExecutor_checkIsolated<E>(executor: E)
467480
executor.checkIsolated()
468481
}
469482

483+
@available(SwiftStdlib 6.2, *)
484+
@_silgen_name("_task_serialExecutor_isIsolatingCurrentContext")
485+
internal func _task_serialExecutor_isIsolatingCurrentContext<E>(executor: E) -> Bool
486+
where E: SerialExecutor {
487+
return executor.isIsolatingCurrentContext()
488+
}
489+
470490
/// Obtain the executor ref by calling the executor's `asUnownedSerialExecutor()`.
471491
/// The obtained executor ref will have all the user-defined flags set on the executor.
472492
@available(SwiftStdlib 5.9, *)

0 commit comments

Comments
 (0)