Skip to content

Commit 144581c

Browse files
committed
PoC Allow a SerialExecutor to check its own tracking for isolation checks
1 parent aa89306 commit 144581c

File tree

3 files changed

+126
-2
lines changed

3 files changed

+126
-2
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@ bool _task_serialExecutor_isSameExclusiveExecutionContext(
313313
const Metadata *selfType,
314314
const SerialExecutorWitnessTable *wtable);
315315

316+
extern "C" SWIFT_CC(swift)
317+
bool _task_serialExecutor_checkIsolated(
318+
HeapObject *executor,
319+
const Metadata *selfType,
320+
const SerialExecutorWitnessTable *wtable);
321+
316322
SWIFT_CC(swift)
317323
static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef executor) {
318324
auto current = ExecutorTrackingInfo::current();
@@ -342,11 +348,16 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef executor) {
342348
return _task_serialExecutor_isSameExclusiveExecutionContext(
343349
currentExecutor.getIdentity(),
344350
executor.getIdentity(),
345-
swift_getObjectType(currentExecutor.getIdentity()),
351+
currentExecutor.getIdentity() ? swift_getObjectType(currentExecutor.getIdentity()) : 0,
346352
executor.getSerialExecutorWitnessTable());
347353
}
348354

349-
return false;
355+
_task_serialExecutor_checkIsolated(
356+
executor.getIdentity(),
357+
swift_getObjectType(executor.getIdentity()),
358+
executor.getSerialExecutorWitnessTable());
359+
360+
return true;
350361
}
351362

352363
/// Logging level for unexpected executors:

stdlib/public/Concurrency/Executor.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ public protocol SerialExecutor: Executor {
100100
/// perspective–to execute code assuming one on the other.
101101
@available(SwiftStdlib 5.9, *)
102102
func isSameExclusiveExecutionContext(other: Self) -> Bool
103+
104+
/// Must crash if unable to confirm that the calling thread belongs to this executor.
105+
@available(SwiftStdlib 9999, *)
106+
func checkIsolated()
107+
108+
}
109+
110+
@available(SwiftStdlib 9999, *)
111+
extension SerialExecutor {
112+
@available(SwiftStdlib 9999, *)
113+
@_unavailableInEmbedded
114+
public func checkIsolated() {
115+
fatalError("Cannot confirm to be executing on \(Self.self)")
116+
}
103117
}
104118

105119
/// An executor that may be used as preferred executor by a task.
@@ -361,6 +375,13 @@ internal func _task_serialExecutor_isSameExclusiveExecutionContext<E>(current cu
361375
currentExecutor.isSameExclusiveExecutionContext(other: executor)
362376
}
363377

378+
@available(SwiftStdlib 9999, *)
379+
@_silgen_name("_task_serialExecutor_checkIsolated")
380+
internal func _task_serialExecutor_checkIsolated<E>(executor: E)
381+
where E: SerialExecutor {
382+
executor.checkIsolated()
383+
}
384+
364385
/// Obtain the executor ref by calling the executor's `asUnownedSerialExecutor()`.
365386
/// The obtained executor ref will have all the user-defined flags set on the executor.
366387
@available(SwiftStdlib 5.9, *)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -parse-as-library %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: concurrency
8+
// REQUIRES: concurrency_runtime
9+
10+
// UNSUPPORTED: back_deployment_runtime
11+
// UNSUPPORTED: back_deploy_concurrency
12+
// UNSUPPORTED: use_os_stdlib
13+
// UNSUPPORTED: freestanding
14+
15+
// rdar://119743909 fails in optimize tests.
16+
// UNSUPPORTED: swift_test_mode_optimize
17+
// UNSUPPORTED: swift_test_mode_optimize_size
18+
19+
//import StdlibUnittest
20+
import Dispatch
21+
22+
extension DispatchSerialQueue {
23+
func checkIsolated() {
24+
print("CHECK \(self)")
25+
dispatchPrecondition(condition: .onQueue(self))
26+
}
27+
}
28+
29+
/// We only do the executor dance because missing 'asUnownedSerialExecutor'
30+
/// on DispatchSerialQueue on some platforms.
31+
final class NaiveQueueExecutor: SerialExecutor {
32+
let queue: DispatchSerialQueue
33+
34+
init(queue: DispatchSerialQueue) {
35+
self.queue = queue
36+
}
37+
38+
public func enqueue(_ unowned: UnownedJob) {
39+
queue.sync {
40+
unowned.runSynchronously(on: self.asUnownedSerialExecutor())
41+
}
42+
}
43+
44+
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
45+
UnownedSerialExecutor(ordinary: self)
46+
}
47+
48+
func checkIsolated() {
49+
dispatchPrecondition(condition: .onQueue(self.queue))
50+
}
51+
}
52+
53+
54+
actor FamousActor {
55+
let queue: DispatchSerialQueue
56+
let executor: NaiveQueueExecutor
57+
58+
init() {
59+
self.queue = DispatchSerialQueue(label: "MyQueue")
60+
self.executor = NaiveQueueExecutor(queue: queue)
61+
}
62+
63+
nonisolated var unownedExecutor: UnownedSerialExecutor {
64+
self.executor.asUnownedSerialExecutor()
65+
}
66+
67+
nonisolated func callCheck() async {
68+
print("Before queue.sync {}")
69+
self.queue.sync {
70+
print("Before preconditionIsolated")
71+
self.preconditionIsolated()
72+
print("After preconditionIsolated")
73+
}
74+
}
75+
}
76+
77+
@main struct Main {
78+
static func main() async {
79+
// let tests = TestSuite("AssertPreconditionActorExecutor")
80+
81+
if #available(SwiftStdlib 9999, *) {
82+
// === MainActor --------------------------------------------------------
83+
84+
// tests.test("queue.async { preconditionIsolated() } ") {
85+
await FamousActor().callCheck()
86+
// }
87+
88+
}
89+
90+
// await runAllTestsAsync()
91+
}
92+
}

0 commit comments

Comments
 (0)