Skip to content

Commit efed449

Browse files
committed
[Concurrency] Further test cleanups, making sure modes interop properly
1 parent e559620 commit efed449

10 files changed

+275
-72
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,14 @@ static IsCurrentExecutorCheckMode isCurrentExecutorMode =
337337

338338

339339
// Shimming call to Swift runtime because Swift Embedded does not have
340-
// these symbols defined
340+
// these symbols defined.
341341
bool swift_bincompat_useLegacyNonCrashingExecutorChecks() {
342342
#if !SWIFT_CONCURRENCY_EMBEDDED
343343
return swift::runtime::bincompat::
344344
swift_bincompat_useLegacyNonCrashingExecutorChecks();
345-
#endif
345+
#else
346346
return false;
347+
#endif
347348
}
348349

349350
// Check override of executor checking mode.
@@ -353,17 +354,15 @@ static void checkIsCurrentExecutorMode(void *context) {
353354

354355
// Potentially, override the platform detected mode, primarily used in tests.
355356
#if SWIFT_STDLIB_HAS_ENVIRON
356-
if (const char *modeStr =
357-
runtime::environment::concurrencyIsCurrentExecutorLegacyModeOverride()) {
358-
if (modeStr) {
359-
if (strcmp(modeStr, "nocrash") == 0) {
360-
useLegacyMode = true;
361-
} else if (strcmp(modeStr, "crash") == 0) {
362-
useLegacyMode = false;
363-
} // else, just use the platform detected mode
364-
}
357+
if (const char *modeStr = runtime::environment::concurrencyIsCurrentExecutorLegacyModeOverride()) {
358+
if (strcmp(modeStr, "nocrash") == 0 || strcmp(modeStr, "legacy") == 0) {
359+
useLegacyMode = true;
360+
} else if (strcmp(modeStr, "crash") == 0 || strcmp(modeStr, "swift6") == 0) {
361+
useLegacyMode = false;
362+
} // else, just use the platform detected mode
365363
}
366364
#endif // SWIFT_STDLIB_HAS_ENVIRON
365+
367366
isCurrentExecutorMode = useLegacyMode ? Legacy_NoCheckIsolated_NonCrashing
368367
: Default_UseCheckIsolated_AllowCrash;
369368
}
@@ -425,32 +424,6 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
425424
return true;
426425
}
427426

428-
// If the expected executor is "default" then we should have matched
429-
// by pointer equality already with the current executor.
430-
if (expectedExecutor.isDefaultActor()) {
431-
// If the expected executor is a default actor, it makes no sense to try
432-
// the 'checkIsolated' call, it must be equal to the other actor, or it is
433-
// not the same isolation domain.
434-
if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
435-
swift_Concurrency_fatalError(0, "Incorrect actor executor assumption");
436-
}
437-
return false;
438-
}
439-
440-
if (expectedExecutor.isMainExecutor() && !currentExecutor.isMainExecutor()) {
441-
if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
442-
// TODO: Invoke checkIsolated() on "main" SerialQueue once it implements `checkIsolated`, for potentially better/more consistent error messages
443-
swift_Concurrency_fatalError(0, "Incorrect actor executor assumption; Expected MainActor executor");
444-
}
445-
return false;
446-
} else if (!expectedExecutor.isMainExecutor() && currentExecutor.isMainExecutor()) {
447-
if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
448-
// TODO: Invoke checkIsolated() on "main" SerialQueue once it implements `checkIsolated`, for potentially better/more consistent error messages
449-
swift_Concurrency_fatalError(0, "Incorrect actor executor assumption; Expected not-MainActor executor");
450-
}
451-
return false;
452-
}
453-
454427
if (expectedExecutor.isComplexEquality()) {
455428
if (currentExecutor.getIdentity() && expectedExecutor.getIdentity() &&
456429
swift_compareWitnessTables(
@@ -470,7 +443,7 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
470443
// chance to check.
471444
if (isSameExclusiveExecutionContextResult) {
472445
return true;
473-
}
446+
} // else, we must give 'checkIsolated' a last chance to verify isolation
474447
}
475448
}
476449

@@ -497,17 +470,17 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
497470
// compat_invoke_swift_task_checkIsolated(expectedExecutor);
498471
if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
499472
swift_task_checkIsolated(expectedExecutor);
473+
500474
// The checkIsolated call did not crash, so we are on the right executor.
501475
return true;
502476
}
503477

504-
assert(isCurrentExecutorMode == Legacy_NoCheckIsolated_NonCrashing);
505478
return false;
506479
}
507480

508481
/// Logging level for unexpected executors:
509-
/// 0 - no logging
510-
/// 1 - warn on each instance
482+
/// 0 - no logging -- will be IGNORED when Swift6 mode of isCurrentExecutor is used
483+
/// 1 - warn on each instance -- will be IGNORED when Swift6 mode of isCurrentExecutor is used
511484
/// 2 - fatal error
512485
///
513486
/// NOTE: The default behavior on Apple platforms depends on the SDK version
@@ -524,9 +497,25 @@ static void checkUnexpectedExecutorLogLevel(void *context) {
524497
if (!levelStr)
525498
return;
526499

500+
auto isCurrentExecutorLegacyMode =
501+
swift_bincompat_useLegacyNonCrashingExecutorChecks();
502+
527503
long level = strtol(levelStr, nullptr, 0);
528-
if (level >= 0 && level < 3)
529-
unexpectedExecutorLogLevel = level;
504+
if (level >= 0 && level < 3) {
505+
if (isCurrentExecutorLegacyMode) {
506+
// legacy mode permits doing nothing or just logging, since the method
507+
// used to perform the check itself is not going to crash:
508+
unexpectedExecutorLogLevel = level;
509+
} else {
510+
// We are in swift6/crash mode of isCurrentExecutor which means that
511+
// rather than returning false, that method will always CRASH when an
512+
// executor mismatch is discovered. This means that it is not possible
513+
// to support logging or warning 'unexpected executor log level' in this mode.
514+
//
515+
// Thus, the only possible mode to support is the 'fatal error' mode.
516+
unexpectedExecutorLogLevel = 2;
517+
}
518+
}
530519
#endif // SWIFT_STDLIB_HAS_ENVIRON
531520
}
532521

stdlib/public/runtime/Bincompat.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,19 @@ bool useLegacySwiftObjCHashing() {
255255
#endif
256256
}
257257

258+
// Controls legacy mode for the 'swift_task_isCurrentExecutorImpl' runtime function.
259+
//
260+
// In "legacy" / "no crash" mode:
261+
// * The `swift_task_isCurrentExecutorImpl` cannot crash
262+
// * This means cases where no "current" executor is present cannot be diagnosed correctly
263+
// * The runtime can NOT use 'SerialExecutor/checkIsolated'
264+
// * The runtime can NOT use 'dispatch_precondition' which is able ot handle some dispatch and main actor edge cases
265+
//
266+
// New behavior in "swift6" "crash" mode:
267+
// * The 'swift_task_isCurrentExecutorImpl' will CRASH rather than return 'false'
268+
// * This allows the method to invoke 'SerialExecutor/checkIsolated'
269+
// * Which is allowed to call 'dispatch_precondition' and handle "on dispatch queue but not on Swift executor" cases
270+
//
258271
// FIXME(concurrency): Once the release is announced, adjust the logic detecting the SDKs
259272
bool swift_bincompat_useLegacyNonCrashingExecutorChecks() {
260273
#if BINARY_COMPATIBILITY_APPLE

test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_bincompat.swift renamed to test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_bincompat_crash_swift_6_mode.swift

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking) | %FileCheck %s
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %env-SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 %target-run %t/a.out
25

36
// REQUIRES: executable_test
47
// REQUIRES: concurrency
@@ -11,6 +14,8 @@
1114
// UNSUPPORTED: use_os_stdlib
1215
// UNSUPPORTED: freestanding
1316

17+
import StdlibUnittest
18+
1419
final class NaiveQueueExecutor: SerialExecutor {
1520
init() {}
1621

@@ -38,32 +43,26 @@ actor ActorOnNaiveQueueExecutor {
3843
self.executor.asUnownedSerialExecutor()
3944
}
4045

46+
// Executes on global pool, but our `checkIsolated` impl pretends
47+
// that it is the same executor by never crashing.
4148
nonisolated func checkPreconditionIsolated() async {
4249
print("Before preconditionIsolated")
4350
self.preconditionIsolated()
4451
print("After preconditionIsolated")
45-
46-
print("Before assumeIsolated")
47-
self.assumeIsolated { iso in
48-
print("Inside assumeIsolated")
49-
}
50-
print("After assumeIsolated")
5152
}
5253
}
5354

5455
@main struct Main {
5556
static func main() async {
56-
if #available(SwiftStdlib 6.0, *) {
57-
let actor = ActorOnNaiveQueueExecutor()
58-
await actor.checkPreconditionIsolated()
59-
// CHECK: Before preconditionIsolated
60-
// CHECK-NEXT: checkIsolated: pretend it is ok!
61-
// CHECK-NEXT: After preconditionIsolated
57+
let tests = TestSuite("AssertPreconditionIsolationTests")
6258

63-
// CHECK-NEXT: Before assumeIsolated
64-
// CHECK-NEXT: checkIsolated: pretend it is ok!
65-
// CHECK-NEXT: Inside assumeIsolated
66-
// CHECK-NEXT: After assumeIsolated
59+
if #available(SwiftStdlib 6.0, *) {
60+
tests.test("[swift6+checkIsolated] Isolation assured by invoking 'checkIsolated'") {
61+
let actor = ActorOnNaiveQueueExecutor()
62+
await actor.checkPreconditionIsolated()
63+
}
6764
}
65+
66+
await runAllTestsAsync()
6867
}
6968
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %env-SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=legacy %target-run %t/a.out
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: concurrency
8+
// REQUIRES: concurrency_runtime
9+
10+
// REQUIRES: libdispatch
11+
12+
// UNSUPPORTED: back_deployment_runtime
13+
// UNSUPPORTED: back_deploy_concurrency
14+
// UNSUPPORTED: use_os_stdlib
15+
// UNSUPPORTED: freestanding
16+
17+
import StdlibUnittest
18+
19+
final class NaiveQueueExecutor: SerialExecutor {
20+
init() {}
21+
22+
public func enqueue(_ job: consuming ExecutorJob) {
23+
job.runSynchronously(on: self.asUnownedSerialExecutor())
24+
}
25+
26+
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
27+
UnownedSerialExecutor(ordinary: self)
28+
}
29+
30+
func checkIsolated() {
31+
print("checkIsolated: pretend it is ok!")
32+
}
33+
}
34+
35+
actor ActorOnNaiveQueueExecutor {
36+
let executor: NaiveQueueExecutor
37+
38+
init() {
39+
self.executor = NaiveQueueExecutor()
40+
}
41+
42+
nonisolated var unownedExecutor: UnownedSerialExecutor {
43+
self.executor.asUnownedSerialExecutor()
44+
}
45+
46+
// Executes on global pool, but our `checkIsolated` impl pretends
47+
// that it is the same executor by never crashing.
48+
nonisolated func checkPreconditionIsolated() async {
49+
print("Before preconditionIsolated")
50+
self.preconditionIsolated()
51+
print("After preconditionIsolated")
52+
}
53+
}
54+
55+
@main struct Main {
56+
static func main() async {
57+
let tests = TestSuite("AssertPreconditionIsolationTests")
58+
59+
if #available(SwiftStdlib 6.0, *) {
60+
tests.test("[legacy mode] expect crash since unable to invoke 'checkIsolated'") {
61+
expectCrashLater() // In legacy mode we do NOT invoke 'checkIsolated' and therefore will crash
62+
63+
let actor = ActorOnNaiveQueueExecutor()
64+
await actor.checkPreconditionIsolated()
65+
}
66+
}
67+
68+
await runAllTestsAsync()
69+
}
70+
}

test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_dispatch_dispatchMain.swift renamed to test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_dispatch_dispatchMain_swift_6_mode.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-build-swift -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library %s -o %t/a.out
33
// RUN: %target-codesign %t/a.out
4-
// RUN: %target-run %t/a.out
4+
// RUN: %env-SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 %target-run %t/a.out
55

66
// REQUIRES: executable_test
77
// REQUIRES: concurrency
@@ -33,6 +33,10 @@ import Musl
3333
Swift.print("DispatchQueue.main.async { MainActor.precondition }")
3434

3535
DispatchQueue.main.async {
36+
// In Swift 6 mode we're allowed to notice that we're asking for main
37+
// queue and use dispatch's assertions directly.
38+
//
39+
// The same code would be crashing without swift6 mode where we're allowed to use 'checkIsolated'
3640
MainActor.preconditionIsolated("I know I'm on the main queue")
3741

3842
Swift.print("OK")

test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_dispatch.swift renamed to test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_dispatch_swift6_mode.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-build-swift -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library %s -o %t/a.out
33
// RUN: %target-codesign %t/a.out
4-
// RUN: %target-run %t/a.out
4+
// RUN: %env-SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 %target-run %t/a.out
55

66
// REQUIRES: executable_test
77
// REQUIRES: concurrency

test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated.swift renamed to test/Concurrency/Runtime/actor_assert_precondition_executor_checkIsolated_swift6_mode.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking) | %FileCheck %s
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %env-SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 %target-run %t/a.out
25

36
// REQUIRES: executable_test
47
// REQUIRES: concurrency

test/Concurrency/Runtime/actor_assert_precondition_executor.swift renamed to test/Concurrency/Runtime/actor_assert_precondition_executor_default_mode.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-as-library %s -o %t/a.out
33
// RUN: %target-codesign %t/a.out
4-
// RUN: %target-run %t/a.out
4+
// RUN: %target-run %t/a.out
55

66
// REQUIRES: executable_test
77
// REQUIRES: concurrency

test/Interpreter/preconcurrency_conformances.swift

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@
1616

1717
// RUN: %target-build-swift -Xfrontend -enable-upcoming-feature -Xfrontend DynamicActorIsolation -I %t -L %t -l Types %t/src/Crash1.swift -o %t/crash1.out
1818
// RUN: %target-codesign %t/crash1.out
19-
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash1.out 2>&1 | %FileCheck %t/src/Crash1.swift
19+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=legacy SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash1.out 2>&1 | %FileCheck %t/src/Crash1.swift --check-prefix=LEGACY_CHECK
20+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash1.out 2>&1 | %FileCheck %t/src/Crash1.swift --check-prefix=SWIFT6_CHECK --dump-input=always
2021

2122
// RUN: %target-build-swift -Xfrontend -enable-upcoming-feature -Xfrontend DynamicActorIsolation -I %t -L %t -l Types %t/src/Crash2.swift -o %t/crash2.out
2223
// RUN: %target-codesign %t/crash2.out
23-
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash2.out 2>&1 | %FileCheck %t/src/Crash2.swift
24+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=legacy SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash2.out 2>&1 | %FileCheck %t/src/Crash2.swift --check-prefix=LEGACY_CHECK
25+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash2.out 2>&1 | %FileCheck %t/src/Crash2.swift --check-prefix=SWIFT6_CHECK --dump-input=always
2426

2527
// RUN: %target-build-swift -Xfrontend -enable-upcoming-feature -Xfrontend DynamicActorIsolation -I %t -L %t -l Types %t/src/Crash3.swift -o %t/crash3.out
2628
// RUN: %target-codesign %t/crash3.out
27-
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash3.out 2>&1 | %FileCheck %t/src/Crash3.swift
29+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=legacy SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash3.out 2>&1 | %FileCheck %t/src/Crash3.swift --check-prefix=LEGACY_CHECK
30+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash3.out 2>&1 | %FileCheck %t/src/Crash3.swift --check-prefix=SWIFT6_CHECK --dump-input=always
2831

2932
// RUN: %target-build-swift -Xfrontend -enable-upcoming-feature -Xfrontend DynamicActorIsolation -I %t -L %t -l Types %t/src/Crash4.swift -o %t/crash4.out
3033
// RUN: %target-codesign %t/crash4.out
31-
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash4.out 2>&1 | %FileCheck %t/src/Crash4.swift
34+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=legacy SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash4.out 2>&1 | %FileCheck %t/src/Crash4.swift --check-prefix=LEGACY_CHECK
35+
// RUN: not --crash env SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6 SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash4.out 2>&1 | %FileCheck %t/src/Crash4.swift --check-prefix=SWIFT6_CHECK --dump-input=always
3236

3337
// REQUIRES: asserts
3438
// REQUIRES: concurrency
@@ -86,19 +90,35 @@ extension ActorTest : @preconcurrency P {
8690
//--- Crash1.swift
8791
import Types
8892
print(await runTest(Test.self))
89-
// CHECK: Incorrect actor executor assumption; Expected MainActor executor
93+
print("OK")
94+
// LEGACY_CHECK: @MainActor function at Types/Types.swift:16 was not called on the main thread
95+
96+
// Crash without good message, since via 'dispatch_assert_queue'
97+
// SWIFT6_CHECK-NOT: OK
9098

9199
//--- Crash2.swift
92100
import Types
93101
print(await runAccessors(Test.self))
94-
// CHECK: Incorrect actor executor assumption; Expected MainActor executor
102+
print("OK")
103+
// LEGACY_CHECK: data race detected: @MainActor function at Types/Types.swift:15 was not called on the main thread
104+
105+
// Crash without good message, since via 'dispatch_assert_queue'
106+
// SWIFT6_CHECK-NOT: OK
95107

96108
//--- Crash3.swift
97109
import Types
98110
print(await runTest(ActorTest.self))
99-
// CHECK: Incorrect actor executor assumption
111+
print("OK")
112+
// LEGACY_CHECK: data race detected: actor-isolated function at Types/Types.swift:33 was not called on the same actor
113+
114+
// SWIFT6_CHECK: Incorrect actor executor assumption
115+
// SWIFT6_CHECK-NOT: OK
100116

101117
//--- Crash4.swift
102118
import Types
103119
print(await runAccessors(ActorTest.self))
104-
// CHECK: Incorrect actor executor assumption
120+
print("OK")
121+
// LEGACY_CHECK: data race detected: actor-isolated function at Types/Types.swift:30 was not called on the same actor
122+
123+
// SWIFT6_CHECK: Incorrect actor executor assumption
124+
// SWIFT6_CHECK-NOT: OK

0 commit comments

Comments
 (0)