1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
// REQUIRES: rdar80824152
// RUN: %target-run-simple-swift( -plugin-path %swift-plugin-dir -Xfrontend -disable-availability-checking -parse-as-library %import-libdispatch) | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: libdispatch
// rdar://76038845
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime
@available(SwiftStdlib 5.1, *)
enum TL {
@TaskLocal
static var number: Int = 0
@TaskLocal
static var other: Int = 0
}
@available(SwiftStdlib 5.1, *)
@discardableResult
func printTaskLocal<V>(
_ key: TaskLocal<V>,
_ expected: V? = nil,
file: String = #file, line: UInt = #line
) -> V? {
let value = key.get()
print("\(key) (\(value)) at \(file):\(line)")
if let expected = expected {
assert("\(expected)" == "\(value)",
"Expected [\(expected)] but found: \(value), at \(file):\(line)")
}
return expected
}
// ==== ------------------------------------------------------------------------
@available(SwiftStdlib 5.1, *)
func copyTo_async() async {
await TL.$number.withValue(1111) {
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (1111)
await TL.$number.withValue(2222) {
await TL.$other.withValue(9999) {
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (2222)
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
let handle = Task {
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (2222)
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
TL.$number.withValue(3333) {
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (3333)
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
}
}
_ = await handle.value
}
}
}
}
@available(SwiftStdlib 5.1, *)
func copyTo_async_noWait() async {
print(#function)
TL.$number.withValue(1111) {
TL.$number.withValue(2222) {
TL.$other.withValue(9999) {
Task {
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (2222)
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
TL.$number.withValue(3333) {
printTaskLocal(TL.$number) // CHECK: TaskLocal<Int>(defaultValue: 0) (3333)
printTaskLocal(TL.$other) // CHECK: TaskLocal<Int>(defaultValue: 0) (9999)
}
}
}
}
}
let second = UInt64(100_000_000) // ns
await Task.sleep(2 * second)
}
@available(SwiftStdlib 5.1, *)
class CustomClass {
@TaskLocal
static var current: CustomClass?
init() {
print("init \(ObjectIdentifier(self))")
}
deinit {
print("deinit \(ObjectIdentifier(self))")
}
}
@available(SwiftStdlib 5.1, *)
func test_unstructured_retains() async {
let instance = CustomClass()
CustomClass.$current.withValue(instance) {
print("BEFORE send: \(String(reflecting: CustomClass.current))")
// don't await on the un-structured tasks on purpose, we want to see that the tasks
// themselves keep the object alive even if we don't hold onto them
Task {
print("in async task: \(String(reflecting: CustomClass.current))")
}
Task {
print("in async task: \(String(reflecting: CustomClass.current))")
}
print("AFTER send: \(String(reflecting: CustomClass.current))")
}
// CHECK: init
// CHECK: BEFORE send: Optional(main.CustomClass)
// CHECK: in async task: Optional(main.CustomClass)
// CHECK: in async task: Optional(main.CustomClass)
// the deinit MUST NOT happen before the async tasks runs
// CHECK: deinit
await Task.sleep(2 * 1_000_000_000)
}
@available(SwiftStdlib 5.1, *)
func test_unstructured_noValues() async {
await Task {
// no values to copy
}.value
}
@available(SwiftStdlib 5.1, *)
func downloadImage(from url: String) async throws -> String {
await Task.sleep(10_000)
return ""
}
@available(SwiftStdlib 5.1, *)
func test_unstructured_noValues_childTasks() async {
@Sendable func work() async throws {
let handle = Task {
try await downloadImage(from: "")
}
}
// these child tasks have a parent pointer in their task local storage.
// we must not copy it when performing the copyTo for a new unstructured task.
async let one = work()
async let two = work()
async let three = work()
try! await one
try! await two
try! await three
}
@available(SwiftStdlib 5.1, *)
@main struct Main {
static func main() async {
await copyTo_async()
await copyTo_async_noWait()
await test_unstructured_retains()
await test_unstructured_noValues()
await test_unstructured_noValues_childTasks()
}
}
|