Skip to content

Commit 6f967bd

Browse files
committed
[Concurrency] Downgrade reportUnexpectedExecutor to error on old SDKs by default
1 parent 7f74e7a commit 6f967bd

File tree

4 files changed

+103
-2
lines changed

4 files changed

+103
-2
lines changed

include/swift/Runtime/Bincompat.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -47,6 +47,17 @@ bool useLegacySwiftValueUnboxingInCasting();
4747
/// if present.
4848
bool useLegacySwiftObjCHashing();
4949

50+
/// Legacy semantics allowed for the `swift_task_reportUnexpectedExecutor` to
51+
/// only log a warning. This changes in future releases and this function
52+
/// will fatal error always.
53+
///
54+
/// Old behavior:
55+
/// - logging a warning on concurrency violation is allowed
56+
/// New behavior:
57+
/// - always fatal error in `swift_task_reportUnexpectedExecutor`
58+
SWIFT_RUNTIME_STDLIB_SPI
59+
bool swift_bincompat_useLegacyWarningModeReportUnexpectedExecutor();
60+
5061
} // namespace bincompat
5162

5263
} // namespace runtime

stdlib/public/Concurrency/Actor.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/Concurrency/Actor.h"
2727
#include "swift/Runtime/AccessibleFunction.h"
2828
#include "swift/Runtime/Atomic.h"
29+
#include "swift/Runtime/Bincompat.h"
2930
#include "swift/Runtime/Casting.h"
3031
#include "swift/Runtime/DispatchShims.h"
3132
#include "swift/Threading/Mutex.h"
@@ -76,6 +77,7 @@ extern "C" void objc_autoreleasePoolPop(void *);
7677
#endif
7778

7879
using namespace swift;
80+
using namespace swift::runtime::bincompat;
7981

8082
/// Should we yield the thread?
8183
static bool shouldYieldThread() {
@@ -353,7 +355,14 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef executor) {
353355
/// 0 - no logging
354356
/// 1 - warn on each instance
355357
/// 2 - fatal error
356-
static unsigned unexpectedExecutorLogLevel = 2;
358+
///
359+
/// NOTE: The default behavior on Apple platforms depends on the SDK version
360+
/// an application was linked to. Since Swift 6 the default is to crash,
361+
/// and the logging behavior is no longer available.
362+
static unsigned unexpectedExecutorLogLevel =
363+
swift_bincompat_useLegacyWarningModeReportUnexpectedExecutor()
364+
? 1 // legacy apps default to the logging mode, and cannot use `checkIsolated`
365+
: 2; // new apps will only crash upon concurrency violations, and will call into `checkIsolated`
357366

358367
static void checkUnexpectedExecutorLogLevel(void *context) {
359368
#if SWIFT_STDLIB_HAS_ENVIRON

stdlib/public/runtime/Bincompat.cpp

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

258+
// FIXME(concurrency): Once the release is announced, adjust the logic detecting the SDKs
259+
bool swift_bincompat_useLegacyWarningModeReportUnexpectedExecutor() {
260+
#if BINARY_COMPATIBILITY_APPLE
261+
return true; // For now, legacy behavior on Apple OSes
262+
#elif SWIFT_TARGET_OS_DARWIN
263+
return true; // For now, use legacy behavior on open-source builds for Apple platforms
264+
#else
265+
return false; // Always use the new behavior on non-Apple OSes
266+
#endif
267+
}
268+
269+
258270
} // namespace bincompat
259271

260272
} // namespace runtime
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library -Xfrontend -disable-availability-checking) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: concurrency_runtime
6+
7+
// REQUIRES: libdispatch
8+
9+
// UNSUPPORTED: back_deployment_runtime
10+
// UNSUPPORTED: back_deploy_concurrency
11+
// UNSUPPORTED: use_os_stdlib
12+
// UNSUPPORTED: freestanding
13+
14+
final class NaiveQueueExecutor: SerialExecutor {
15+
init() {}
16+
17+
public func enqueue(_ job: consuming ExecutorJob) {
18+
job.runSynchronously(on: self.asUnownedSerialExecutor())
19+
}
20+
21+
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
22+
UnownedSerialExecutor(ordinary: self)
23+
}
24+
25+
func checkIsolated() {
26+
print("checkIsolated: pretend it is ok!")
27+
}
28+
}
29+
30+
actor ActorOnNaiveQueueExecutor {
31+
let executor: NaiveQueueExecutor
32+
33+
init() {
34+
self.executor = NaiveQueueExecutor()
35+
}
36+
37+
nonisolated var unownedExecutor: UnownedSerialExecutor {
38+
self.executor.asUnownedSerialExecutor()
39+
}
40+
41+
nonisolated func checkPreconditionIsolated() async {
42+
print("Before preconditionIsolated")
43+
self.preconditionIsolated()
44+
print("After preconditionIsolated")
45+
46+
print("Before assumeIsolated")
47+
self.assumeIsolated { iso in
48+
print("Inside assumeIsolated")
49+
}
50+
print("After assumeIsolated")
51+
}
52+
}
53+
54+
@main struct Main {
55+
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
62+
63+
// CHECK-NEXT: Before assumeIsolated
64+
// CHECK-NEXT: checkIsolated: pretend it is ok!
65+
// CHECK-NEXT: Inside assumeIsolated
66+
// CHECK-NEXT: After assumeIsolated
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)