Skip to content

Commit d6ed231

Browse files
committed
[Concurrency] TaskLocal.withValue adopting #isolation
1 parent f751131 commit d6ed231

File tree

5 files changed

+105
-9
lines changed

5 files changed

+105
-9
lines changed

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
///

test/Concurrency/async_task_locals_basic_warnings.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,36 @@
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)")
23+
}
24+
}
25+
26+
class NonSendableValue {}
27+
28+
@MainActor final class MainHelper {
29+
30+
@TaskLocal static var local: Int?
31+
32+
func run() async {
33+
// This should NOT produce any warnings, the closure withValue uses is @Sendable:
34+
await Self.$local.withValue(42) {
35+
await work()
36+
}
37+
}
38+
39+
func work() async {
40+
print("Hello \(Self.local ?? 0)")
2441
}
2542
}
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,11 @@ Added: _$sScfsE13checkIsolatedyyF
290290
Added: _$sScf13checkIsolatedyyFTj
291291
// method descriptor for Swift.SerialExecutor.checkIsolated() -> ()
292292
Added: _$sScf13checkIsolatedyyFTq
293+
294+
// #isolated adoption in TaskLocal.withValue
295+
// Swift.TaskLocal.withValueImpl<A>(_: __owned A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
296+
Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__yYaKXEScA_pSgYiSSSutYaKlF
297+
Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__yYaKXEScA_pSgYiSSSutYaKlFTu
298+
// Swift.TaskLocal.withValue<A>(_: A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
299+
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlF
300+
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlFTu

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,14 @@ Func SerialExecutor.enqueue(_:) has been added as a protocol requirement
8787
// rdar://106833284 (ABI checker confused with overload getting deprecated)
8888
Func Executor.enqueue(_:) is a new API without @available attribute
8989

90-
// This function correctly inherits its availability from the TaskLocal struct.
91-
Func TaskLocal.withValueImpl(_:operation:file:line:) is a new API without @available attribute
90+
// Adopt #isolation in TaskLocal.withValue APIs
91+
Func TaskLocal.withValue(_:operation:file:line:) has been renamed to Func withValue(_:operation:isolation:file:line:)
92+
Func TaskLocal.withValue(_:operation:file:line:) has mangled name changing from '_Concurrency.TaskLocal.withValue<A>(_: A, operation: () async throws -> A1, file: Swift.String, line: Swift.UInt) async throws -> A1' to '_Concurrency.TaskLocal.withValue<A>(_: A, operation: () throws -> A1, file: Swift.String, line: Swift.UInt) throws -> A1'
93+
Func TaskLocal.withValue(_:operation:file:line:) has mangled name changing from '_Concurrency.TaskLocal.withValue<A>(_: A, operation: () throws -> A1, file: Swift.String, line: Swift.UInt) throws -> A1' to '_Concurrency.TaskLocal.withValue<A>(_: A, operation: () async throws -> A1, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> A1'
94+
Func TaskLocal.withValue(_:operation:file:line:) has parameter 1 type change from () async throws -> τ_1_0 to () throws -> τ_1_0
95+
Func TaskLocal.withValue(_:operation:file:line:) has parameter 1 type change from () throws -> τ_1_0 to () async throws -> τ_1_0
96+
Func TaskLocal.withValue(_:operation:file:line:) has parameter 2 type change from Swift.String to (any _Concurrency.Actor)?
97+
Func TaskLocal.withValue(_:operation:file:line:) has parameter 3 type change from Swift.UInt to Swift.String
9298

9399
// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)
94100

0 commit comments

Comments
 (0)