Skip to content

Commit bc6ea39

Browse files
authored
[TaskLocal] copyTo must initializeWithCopy the task local value (swiftlang#37999)
1 parent d725391 commit bc6ea39

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

stdlib/public/Concurrency/TaskLocal.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ void TaskLocal::Item::copyTo(AsyncTask *target) {
223223
assert(target && "TaskLocal item attempt to copy to null target task!");
224224

225225
auto item = Item::createLink(target, this->key, this->valueType);
226-
valueType->vw_initializeWithTake(item->getStoragePtr(), this->getStoragePtr());
226+
valueType->vw_initializeWithCopy(item->getStoragePtr(), this->getStoragePtr());
227227

228228
/// A `copyTo` may ONLY be invoked BEFORE the task is actually scheduled,
229229
/// so right now we can safely copy the value into the task without additional

test/Concurrency/Runtime/async_task_locals_copy_to_async.swift

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func copyTo_async() async {
4343
await TL.$other.withValue(9999) {
4444
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (2222)
4545
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
46-
let handle = async {
46+
let handle = Task {
4747
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (2222)
4848
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
4949
TL.$number.withValue(3333) {
@@ -52,7 +52,7 @@ func copyTo_async() async {
5252
}
5353
}
5454

55-
_ = await handle.get()
55+
_ = await handle.value
5656
}
5757
}
5858
}
@@ -64,7 +64,7 @@ func copyTo_async_noWait() async {
6464
TL.$number.withValue(1111) {
6565
TL.$number.withValue(2222) {
6666
TL.$other.withValue(9999) {
67-
async {
67+
Task {
6868
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (2222)
6969
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
7070
TL.$number.withValue(3333) {
@@ -80,11 +80,51 @@ func copyTo_async_noWait() async {
8080
await Task.sleep(2 * second)
8181
}
8282

83+
@available(SwiftStdlib 5.5, *)
84+
class CustomClass {
85+
@TaskLocal
86+
static var current: CustomClass?
87+
88+
init() {
89+
print("init \(ObjectIdentifier(self))")
90+
}
91+
92+
deinit {
93+
print("deinit \(ObjectIdentifier(self))")
94+
}
95+
}
96+
97+
@available(SwiftStdlib 5.5, *)
98+
func test_async_retains() async {
99+
let instance = CustomClass()
100+
CustomClass.$current.withValue(instance) {
101+
print("BEFORE send: \(String(reflecting: CustomClass.current))")
102+
// don't await on the un-structured tasks on purpose, we want to see that the tasks
103+
// themselves keep the object alive even if we don't hold onto them
104+
Task {
105+
print("in async task: \(String(reflecting: CustomClass.current))")
106+
}
107+
Task {
108+
print("in async task: \(String(reflecting: CustomClass.current))")
109+
}
110+
print("AFTER send: \(String(reflecting: CustomClass.current))")
111+
}
112+
113+
// CHECK: init
114+
// CHECK: BEFORE send: Optional(main.CustomClass)
115+
// CHECK: in async task: Optional(main.CustomClass)
116+
// CHECK: in async task: Optional(main.CustomClass)
117+
// the deinit MUST NOT happen before the async tasks runs
118+
// CHECK: deinit
119+
await Task.sleep(2 * 1_000_000_000)
120+
}
121+
83122
@available(SwiftStdlib 5.5, *)
84123
@main struct Main {
85124
static func main() async {
86125
await copyTo_async()
87126
await copyTo_async()
88127
await copyTo_async_noWait()
128+
await test_async_retains()
89129
}
90130
}

0 commit comments

Comments
 (0)