File: async_task_locals_copy_to_async.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (165 lines) | stat: -rw-r--r-- 4,773 bytes parent folder | download
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()
  }
}