Skip to content

Commit 3557350

Browse files
committed
[Concurrency] Correct withContinuation APIs isolation handling
rdar://125307764 abi cleanup
1 parent a6be5e0 commit 3557350

File tree

8 files changed

+130
-14
lines changed

8 files changed

+130
-14
lines changed

stdlib/public/Concurrency/CheckedContinuation.swift

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,19 +281,37 @@ extension CheckedContinuation {
281281
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
282282
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
283283
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
284-
@available(SwiftStdlib 5.1, *)
285-
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
286284
@inlinable
287285
@_unavailableInEmbedded
286+
@available(SwiftStdlib 5.1, *)
287+
#if !$Embedded
288+
@backDeployed(before: SwiftStdlib 6.0)
289+
#endif
288290
public func withCheckedContinuation<T>(
289-
function: String = #function,
290-
_ body: (CheckedContinuation<T, Never>) -> Void
291+
isolation: isolated (any Actor)? = #isolation,
292+
function: String = #function,
293+
_ body: (CheckedContinuation<T, Never>) -> Void
294+
) async -> T {
295+
return await withUnsafeContinuation {
296+
body(CheckedContinuation(continuation: $0, function: function))
297+
}
298+
}
299+
300+
@available(SwiftStdlib 5.1, *)
301+
@usableFromInline
302+
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
303+
@_unavailableInEmbedded
304+
@_silgen_name("$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF")
305+
internal func __abi_withCheckedContinuation<T>(
306+
function: String = #function,
307+
_ body: (CheckedContinuation<T, Never>) -> Void
291308
) async -> T {
292309
return await withUnsafeContinuation {
293310
body(CheckedContinuation(continuation: $0, function: function))
294311
}
295312
}
296313

314+
297315
/// Invokes the passed in closure with a checked continuation for the current task.
298316
///
299317
/// The body of the closure executes synchronously on the calling task, and once it returns
@@ -322,13 +340,30 @@ public func withCheckedContinuation<T>(
322340
/// - SeeAlso: `withCheckedContinuation(function:_:)`
323341
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
324342
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
325-
@available(SwiftStdlib 5.1, *)
326-
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
327343
@inlinable
328344
@_unavailableInEmbedded
345+
@available(SwiftStdlib 5.1, *)
346+
#if !$Embedded
347+
@backDeployed(before: SwiftStdlib 6.0)
348+
#endif
329349
public func withCheckedThrowingContinuation<T>(
330-
function: String = #function,
331-
_ body: (CheckedContinuation<T, Error>) -> Void
350+
isolation: isolated (any Actor)? = #isolation,
351+
function: String = #function,
352+
_ body: (CheckedContinuation<T, Error>) -> Void
353+
) async throws -> T {
354+
return try await withUnsafeThrowingContinuation {
355+
body(CheckedContinuation(continuation: $0, function: function))
356+
}
357+
}
358+
359+
@available(SwiftStdlib 5.1, *)
360+
@usableFromInline
361+
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
362+
@_unavailableInEmbedded
363+
@_silgen_name("$ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF")
364+
internal func __abi_withCheckedThrowingContinuation<T>(
365+
function: String = #function,
366+
_ body: (CheckedContinuation<T, Error>) -> Void
332367
) async throws -> T {
333368
return try await withUnsafeThrowingContinuation {
334369
body(CheckedContinuation(continuation: $0, function: function))

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,9 +598,9 @@ internal func _resumeUnsafeThrowingContinuationWithError<T>(
598598
/// - SeeAlso: `withCheckedContinuation(function:_:)`
599599
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
600600
@available(SwiftStdlib 5.1, *)
601-
@_unsafeInheritExecutor
602601
@_alwaysEmitIntoClient
603602
public func withUnsafeContinuation<T>(
603+
isolation: isolated (any Actor)? = #isolation,
604604
_ fn: (UnsafeContinuation<T, Never>) -> Void
605605
) async -> T {
606606
return await Builtin.withUnsafeContinuation {
@@ -634,9 +634,9 @@ public func withUnsafeContinuation<T>(
634634
/// - SeeAlso: `withCheckedContinuation(function:_:)`
635635
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
636636
@available(SwiftStdlib 5.1, *)
637-
@_unsafeInheritExecutor
638637
@_alwaysEmitIntoClient
639638
public func withUnsafeThrowingContinuation<T>(
639+
isolation: isolated (any Actor)? = #isolation,
640640
_ fn: (UnsafeContinuation<T, Error>) -> Void
641641
) async throws -> T {
642642
return try await Builtin.withUnsafeThrowingContinuation {

stdlib/public/Concurrency/Task.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,9 @@ static SerialExecutorRef executorForEnqueuedJob(Job *job) {
346346
return SerialExecutorRef::generic();
347347
#else
348348
void *jobQueue = job->SchedulerPrivate[Job::DispatchQueueIndex];
349-
if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR)
349+
if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR) {
350350
return SerialExecutorRef::generic();
351-
else
351+
} else
352352
return SerialExecutorRef::forOrdinary(reinterpret_cast<HeapObject*>(jobQueue),
353353
_swift_task_getDispatchQueueSerialExecutorWitnessTable());
354354
#endif

stdlib/public/Concurrency/TaskGroup.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,6 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
593593

594594
@usableFromInline
595595
@available(SwiftStdlib 5.1, *)
596-
@_silgen_name("$sScG22awaitAllRemainingTasksyyYaF")
597596
internal mutating func awaitAllRemainingTasks() async {
598597
while let _ = await next(isolation: nil) {}
599598
}

stdlib/toolchain/Compatibility56/Concurrency/Actor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class ExecutorTrackingInfo {
6767

6868
/// Unconditionally initialize a fresh tracking state on the
6969
/// current state, shadowing any previous tracking state.
70-
/// leave() must be called beforet the object goes out of scope.
70+
/// leave() must be called before the object goes out of scope.
7171
void enterAndShadow(ExecutorRef currentExecutor) {
7272
ActiveExecutor = currentExecutor;
7373
SavedInfo = ActiveInfoInThread.get();

test/Concurrency/Runtime/continuation_validation.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,72 @@
1616

1717
import StdlibUnittest
1818

19+
@MainActor
20+
@available(SwiftStdlib 5.1, *)
21+
func test_isolation_withUnsafeContinuation() async {
22+
// This test specifically should have only one suspension point,
23+
// as it would trigger a problem with the previous @_unsafeInheritExecutor
24+
// implementation, where we optimize away a switch accidentally, causing
25+
// wrong isolation.
26+
await withUnsafeContinuation { continuation in
27+
MainActor.shared.assertIsolated() // OK
28+
continuation.resume(returning: ())
29+
}
30+
}
31+
@MainActor
32+
@available(SwiftStdlib 5.1, *)
33+
func test_isolation_withUnsafeThrowingContinuation() async {
34+
// See comment in `test_isolation_withUnsafeContinuation` about exact test case shape
35+
try! await withUnsafeThrowingContinuation { continuation in
36+
MainActor.shared.assertIsolated() // OK
37+
continuation.resume(returning: ())
38+
}
39+
}
40+
@MainActor
41+
@available(SwiftStdlib 5.1, *)
42+
func test_isolation_withCheckedContinuation() async {
43+
// See comment in `test_isolation_withUnsafeContinuation` about exact test case shape
44+
await withCheckedContinuation { continuation in
45+
MainActor.shared.assertIsolated() // OK
46+
continuation.resume(returning: ())
47+
}
48+
}
49+
@MainActor
50+
@available(SwiftStdlib 5.1, *)
51+
func test_isolation_withCheckedThrowingContinuation() async {
52+
// See comment in `test_isolation_withUnsafeContinuation` about exact test case shape
53+
try! await withCheckedThrowingContinuation { continuation in
54+
MainActor.shared.assertIsolated() // OK
55+
continuation.resume(returning: ())
56+
}
57+
}
58+
1959
@main struct Main {
2060
static func main() async {
2161
let tests = TestSuite("ContinuationValidation")
2262

2363
if #available(SwiftStdlib 5.1, *) {
64+
tests.test("withUnsafeThrowingContinuation: continuation should be on calling isolation") {
65+
await Task.detached {
66+
await test_isolation_withUnsafeThrowingContinuation()
67+
}.value
68+
}
69+
tests.test("withUnsafeContinuation: continuation should be on calling isolation") {
70+
await Task.detached {
71+
await test_isolation_withUnsafeContinuation()
72+
}.value
73+
}
74+
tests.test("withCheckedContinuation: continuation should be on calling isolation") {
75+
await Task.detached {
76+
await test_isolation_withCheckedContinuation()
77+
}.value
78+
}
79+
tests.test("withCheckedThrowingContinuation: continuation should be on calling isolation") {
80+
await Task.detached {
81+
await test_isolation_withCheckedThrowingContinuation()
82+
}.value
83+
}
84+
2485
tests.test("trap on double resume of unchecked continuation") {
2586
expectCrashLater(withMessage: "may have already been resumed")
2687

test/abi/macOS/arm64/concurrency.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@ Added: _swift_task_getPreferredTaskExecutor
270270
Added: _swift_task_popTaskExecutorPreference
271271
Added: _swift_task_pushTaskExecutorPreference
272272

273+
// Adopt #isolation in with...Continuation APIs
274+
// Swift.withCheckedThrowingContinuation<A>(isolation: isolated Swift.Actor?, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A
275+
Added: _$ss31withCheckedThrowingContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5Error_pGXEtYaKlF
276+
Added: _$ss31withCheckedThrowingContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5Error_pGXEtYaKlFTu
277+
// Swift.withCheckedContinuation<A>(isolation: isolated Swift.Actor?, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) async -> A
278+
Added: _$ss23withCheckedContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5NeverOGXEtYalF
279+
Added: _$ss23withCheckedContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5NeverOGXEtYalFTu
280+
273281
// Updated signature for withTaskExecutorPreference to used typed throws and #isolation
274282
// Swift.withTaskExecutorPreference<A, B where B: Swift.Error>(_: Swift.TaskExecutor?, isolation: isolated Swift.Actor?, operation: () async throws(B) -> A) async throws(B) -> A
275283
Added: _$ss26withTaskExecutorPreference_9isolation9operationxSch_pSg_ScA_pSgYixyYaq_YKXEtYaq_YKs5ErrorR_r0_lF

test/api-digester/stability-concurrency-abi.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ Protocol SerialExecutor has added inherited protocol Copyable
7878
Protocol SerialExecutor has added inherited protocol Escapable
7979
Protocol AsyncSequence is now without @rethrows
8080

81+
// #isolated adoption in with...Continuation
82+
// api-digester is not aware of silgen_name trickery we do to keep this ABI compatible
83+
Func withCheckedContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)?
84+
Func withCheckedContinuation(function:_:) has parameter 1 type change from (_Concurrency.CheckedContinuation<τ_0_0, Swift.Never>) -> () to Swift.String
85+
Func withCheckedThrowingContinuation(function:_:) has been renamed to Func withCheckedThrowingContinuation(isolation:function:_:)
86+
Func withCheckedThrowingContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedThrowingContinuation<A>(function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A' to '_Concurrency.withCheckedThrowingContinuation<A>(isolation: isolated Swift.Optional<Swift.Actor>, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A'
87+
Func withCheckedThrowingContinuation(function:_:) has parameter 0 type change from Swift.String to (any _Concurrency.Actor)?
88+
Func withCheckedThrowingContinuation(function:_:) has parameter 1 type change from (_Concurrency.CheckedContinuation<τ_0_0, any Swift.Error>) -> () to Swift.String
89+
90+
// #isolated was adopted and the old methods kept: $ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF
91+
Func withCheckedContinuation(function:_:) has been renamed to Func withCheckedContinuation(isolation:function:_:)
92+
Func withCheckedContinuation(function:_:) has mangled name changing from '_Concurrency.withCheckedContinuation<A>(function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) async -> A' to '_Concurrency.withCheckedContinuation<A>(isolation: isolated Swift.Optional<Swift.Actor>, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Never>) -> ()) async -> A'
93+
8194
// SerialExecutor gained `enqueue(_: __owned Job)`, protocol requirements got default implementations
8295
Func SerialExecutor.enqueue(_:) has been added as a protocol requirement
8396

0 commit comments

Comments
 (0)