Skip to content

Commit dfcf105

Browse files
authored
[Concurrency] Remove _unsafeInheritExecutor from public APIs, use #isolation (#72578)
1 parent f7ce9a7 commit dfcf105

File tree

11 files changed

+233
-22
lines changed

11 files changed

+233
-22
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/TaskLocal.swift

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,27 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
181181
/// the operation closure.
182182
@inlinable
183183
@discardableResult
184-
@_unsafeInheritExecutor
185-
@backDeployed(before: SwiftStdlib 5.8)
186-
public func withValue<R>(_ valueDuringOperation: Value, operation: () async throws -> R,
184+
@available(SwiftStdlib 5.1, *)
185+
@backDeployed(before: SwiftStdlib 6.0)
186+
public func withValue<R>(_ valueDuringOperation: Value,
187+
operation: () async throws -> R,
188+
isolation: isolated (any Actor)? = #isolation,
187189
file: String = #fileID, line: UInt = #line) async rethrows -> R {
190+
return try await withValueImpl(
191+
valueDuringOperation,
192+
operation: operation,
193+
isolation: isolation,
194+
file: file, line: line)
195+
}
196+
197+
@usableFromInline
198+
@discardableResult
199+
@_unsafeInheritExecutor // ABI compatibility with Swift 5.1
200+
@available(SwiftStdlib 5.1, *)
201+
@_silgen_name("$ss9TaskLocalC9withValue_9operation4file4lineqd__x_qd__yYaKXESSSutYaKlF")
202+
internal func __abi_withValue<R>(_ valueDuringOperation: Value,
203+
operation: () async throws -> R,
204+
file: String = #fileID, line: UInt = #line) async rethrows -> R {
188205
return try await withValueImpl(valueDuringOperation, operation: operation, file: file, line: line)
189206
}
190207

@@ -206,11 +223,30 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
206223
/// to swift_task_de/alloc for the copy as follows:
207224
/// - withValue contains the compiler-emitted calls swift_task_de/alloc.
208225
/// - withValueImpl contains the calls to _taskLocalValuePush/Pop
226+
@inlinable
227+
@discardableResult
228+
@available(SwiftStdlib 5.1, *)
229+
@backDeployed(before: SwiftStdlib 6.0)
230+
internal func withValueImpl<R>(_ valueDuringOperation: __owned Value,
231+
operation: () async throws -> R,
232+
isolation: isolated (any Actor)?,
233+
file: String = #fileID, line: UInt = #line) async rethrows -> R {
234+
// check if we're not trying to bind a value from an illegal context; this may crash
235+
_checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line)
236+
237+
_taskLocalValuePush(key: key, value: consume valueDuringOperation)
238+
defer { _taskLocalValuePop() }
239+
240+
return try await operation()
241+
}
242+
209243
@inlinable
210244
@discardableResult
211245
@_unsafeInheritExecutor
246+
@available(SwiftStdlib 5.1, *)
212247
@backDeployed(before: SwiftStdlib 5.9)
213-
internal func withValueImpl<R>(_ valueDuringOperation: __owned Value, operation: () async throws -> R,
248+
internal func withValueImpl<R>(_ valueDuringOperation: __owned Value,
249+
operation: () async throws -> R,
214250
file: String = #fileID, line: UInt = #line) async rethrows -> R {
215251
// check if we're not trying to bind a value from an illegal context; this may crash
216252
_checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line)
@@ -221,6 +257,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
221257
return try await operation()
222258
}
223259

260+
224261
/// Binds the task-local to the specific value for the duration of the
225262
/// synchronous operation.
226263
///

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/Concurrency/async_task_locals_basic_warnings.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@
77
// REQUIRES: concurrency
88
// REQUIRES: asserts
99

10-
@available(SwiftStdlib 5.1, *)
1110
actor Test {
1211

1312
@TaskLocal static var local: Int?
1413

1514
func run() async {
1615
// This should NOT produce any warnings, the closure withValue uses is @Sendable:
17-
await Test.$local.withValue(42) {
16+
await Self.$local.withValue(42) {
1817
await work()
1918
}
2019
}
2120

2221
func work() async {
23-
print("Hello \(Test.local ?? 0)")
22+
print("Hello \(Self.local ?? 0)")
2423
}
2524
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking
3+
4+
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify
5+
// RUN: %target-swift-frontend -I %t -disable-availability-checking -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify -enable-upcoming-feature RegionBasedIsolation
6+
7+
// REQUIRES: concurrency
8+
// REQUIRES: asserts
9+
10+
// FIXME: rdar://125078448 is resolved
11+
// XFAIL: *
12+
13+
actor Test {
14+
15+
@TaskLocal static var local: Int?
16+
17+
func withTaskLocal(isolation: isolated (any Actor)? = #isolation,
18+
_ body: (consuming NonSendableValue, isolated (any Actor)?) -> Void) async {
19+
Self.$local.withValue(12) {
20+
// Unexpected errors here:
21+
// error: unexpected warning produced: transferring 'body' may cause a race; this is an error in the Swift 6 language mode
22+
// error: unexpected note produced: actor-isolated 'body' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
23+
body(NonSendableValue(), isolation)
24+
}
25+
}
26+
}
27+
28+
class NonSendableValue {}

test/abi/macOS/arm64/concurrency.swift

Lines changed: 16 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
@@ -303,3 +311,11 @@ Added: _$sScfsE13checkIsolatedyyF
303311
Added: _$sScf13checkIsolatedyyFTj
304312
// method descriptor for Swift.SerialExecutor.checkIsolated() -> ()
305313
Added: _$sScf13checkIsolatedyyFTq
314+
315+
// #isolated adoption in TaskLocal.withValue
316+
// Swift.TaskLocal.withValueImpl<A>(_: __owned A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
317+
Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__yYaKXEScA_pSgYiSSSutYaKlF
318+
Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__yYaKXEScA_pSgYiSSSutYaKlFTu
319+
// Swift.TaskLocal.withValue<A>(_: A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
320+
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlF
321+
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlFTu

test/abi/macOS/x86_64/concurrency.swift

Lines changed: 16 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
@@ -303,3 +311,11 @@ Added: _$sScfsE13checkIsolatedyyF
303311
Added: _$sScf13checkIsolatedyyFTj
304312
// method descriptor for Swift.SerialExecutor.checkIsolated() -> ()
305313
Added: _$sScf13checkIsolatedyyFTq
314+
315+
// #isolated adoption in TaskLocal.withValue
316+
// Swift.TaskLocal.withValueImpl<A>(_: __owned A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
317+
Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__yYaKXEScA_pSgYiSSSutYaKlF
318+
Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__yYaKXEScA_pSgYiSSSutYaKlFTu
319+
// Swift.TaskLocal.withValue<A>(_: A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
320+
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlF
321+
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlFTu

0 commit comments

Comments
 (0)