Skip to content

Commit fb996b8

Browse files
committed
[Concurrency] tasklocal withValue inside actor is safe
1 parent 1f3e159 commit fb996b8

File tree

4 files changed

+45
-2
lines changed

4 files changed

+45
-2
lines changed

stdlib/public/BackDeployConcurrency/TaskLocal.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,9 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
134134
///
135135
/// If the value is a reference type, it will be retained for the duration of
136136
/// the operation closure.
137+
@Sendable
137138
@discardableResult
138-
public func withValue<R>(_ valueDuringOperation: Value, operation: () async throws -> R,
139+
public func withValue<R>(_ valueDuringOperation: Value, operation: @Sendable () async throws -> R,
139140
file: String = #file, line: UInt = #line) async rethrows -> R {
140141
// check if we're not trying to bind a value from an illegal context; this may crash
141142
_checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line)

stdlib/public/Concurrency/TaskLocal.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public final class TaskLocal<Value: Sendable>: Sendable, CustomStringConvertible
135135
/// If the value is a reference type, it will be retained for the duration of
136136
/// the operation closure.
137137
@discardableResult
138-
public func withValue<R>(_ valueDuringOperation: Value, operation: () async throws -> R,
138+
public func withValue<R>(_ valueDuringOperation: Value, operation: @Sendable () async throws -> R,
139139
file: String = #fileID, line: UInt = #line) async rethrows -> R {
140140
// check if we're not trying to bind a value from an illegal context; this may crash
141141
_checkIllegalTaskLocalBindingWithinWithTaskGroup(file: file, line: line)

test/Concurrency/Runtime/async_task_locals_basic.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,24 @@ func withLocal_body_mustNotEscape() async {
199199
_ = something // silence not used warning
200200
}
201201

202+
@available(SwiftStdlib 5.1, *)
203+
actor Worker {
204+
@TaskLocal
205+
static var declaredInActor: String = "<default-value>"
206+
207+
func setAndRead() async {
208+
print("setAndRead") // CHECK: setAndRead
209+
await Worker.$declaredInActor.withValue("value-1") {
210+
await printTaskLocalAsync(Worker.$declaredInActor) // CHECK-NEXT: TaskLocal<String>(defaultValue: <default-value>) (value-1)
211+
}
212+
}
213+
}
214+
215+
@available(SwiftStdlib 5.1, *)
216+
func inside_actor() async {
217+
await Worker().setAndRead()
218+
}
219+
202220
@available(SwiftStdlib 5.1, *)
203221
@main struct Main {
204222
static func main() async {
@@ -210,5 +228,6 @@ func withLocal_body_mustNotEscape() async {
210228
await nested_3_onlyTopContributes()
211229
await nested_3_onlyTopContributesAsync()
212230
await nested_3_onlyTopContributesMixed()
231+
await inside_actor()
213232
}
214233
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency -parse-as-library
4+
// REQUIRES: concurrency
5+
6+
// REQUIRES: concurrency
7+
8+
@available(SwiftStdlib 5.1, *)
9+
actor Test {
10+
11+
@TaskLocal static var local: Int?
12+
13+
func run() async {
14+
// This should NOT produce any warnings, the closure withValue uses is @Sendable:
15+
await Test.$local.withValue(42) {
16+
await work()
17+
}
18+
}
19+
20+
func work() async {
21+
print("Hello \(Test.local ?? 0)")
22+
}
23+
}

0 commit comments

Comments
 (0)