File: async_tasks.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 (148 lines) | stat: -rw-r--r-- 5,451 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
// RUN: %target-swift-frontend -strict-concurrency=targeted -disable-availability-checking %s -o /dev/null -verify -emit-sil  -DALLOW_TYPECHECKER_ERRORS -verify-additional-prefix typechecker-
// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking %s -o /dev/null -verify -emit-sil -DALLOW_TYPECHECKER_ERRORS -verify-additional-prefix typechecker-
// RUN: %target-swift-frontend -strict-concurrency=complete -disable-availability-checking %s -o /dev/null -verify -emit-sil -verify-additional-prefix tns-

// REQUIRES: concurrency
// REQUIRES: asserts

@available(SwiftStdlib 5.1, *)
func someAsyncFunc() async -> String { "" }

struct MyError: Error {}
@available(SwiftStdlib 5.1, *)
func someThrowingAsyncFunc() async throws -> String { throw MyError() }

// ==== Unsafe Continuations ---------------------------------------------------

struct Vegetable {}

func buyVegetables(
  shoppingList: [String],
  // a) if all veggies were in store, this is invoked *exactly-once*
  onGotAllVegetables: ([Vegetable]) -> (),

  // b) if not all veggies were in store, invoked one by one (one or more times)
  onGotVegetable: (Vegetable) -> (),
  // b) if at least one onGotVegetable was called *exactly-once*
  // this is invoked once no more veggies will be emitted
  onNoMoreVegetables: () -> (),
  // c) if no veggies _at all_ were available, this is invoked *exactly once*
  onNoVegetablesInStore: (Error) -> ()
) {}

// returns 1 or more vegetables or throws an error
@available(SwiftStdlib 5.1, *)
func buyVegetables(shoppingList: [String]) async throws -> [Vegetable] {
  try await withUnsafeThrowingContinuation { continuation in
    var veggies: [Vegetable] = []

    buyVegetables(
      shoppingList: shoppingList,
      onGotAllVegetables: { veggies in continuation.resume(returning: veggies) },
      onGotVegetable: { v in veggies.append(v) },
      onNoMoreVegetables: { continuation.resume(returning: veggies) },
      onNoVegetablesInStore: { error in continuation.resume(throwing: error) }
      )
  }
}


@available(SwiftStdlib 5.1, *)
func test_unsafeContinuations() async {
  // the closure should not allow async operations;
  // after all: if you have async code, just call it directly, without the unsafe continuation
#if ALLOW_TYPECHECKER_ERRORS
  let _: String = withUnsafeContinuation { continuation in // expected-typechecker-error{{cannot pass function of type '(UnsafeContinuation<String, Never>) async -> Void' to parameter expecting synchronous function type}}
    let s = await someAsyncFunc() // expected-typechecker-note {{'async' inferred from asynchronous operation used here}}
    continuation.resume(returning: s)
  }
#endif

  let _: String = await withUnsafeContinuation { continuation in
    continuation.resume(returning: "")
  }

  // rdar://76475495 - suppress warnings for invalid expressions
#if ALLOW_TYPECHECKER_ERRORS
  func test_invalid_async_no_warnings() async -> Int {
	  return await withUnsafeContinuation {
		  $0.resume(throwing: 1) // expected-typechecker-error {{cannot convert value of type 'Int' to expected argument type 'Never'}}
	  }
  }
#endif
}

@available(SwiftStdlib 5.1, *)
func test_unsafeThrowingContinuations() async throws {
  let _: String = try await withUnsafeThrowingContinuation { continuation in
    continuation.resume(returning: "")
  }

  let _: String = try await withUnsafeThrowingContinuation { continuation in
    continuation.resume(throwing: MyError())
  }

  // using resume(with:)
  let _: String = try await withUnsafeThrowingContinuation { continuation in
    let result : Result<String, MyError> = .success("")
    continuation.resume(with: result)
  }

  let _: String = try await withUnsafeThrowingContinuation { continuation in
    continuation.resume(with: .failure(MyError()))
  }

  // TODO: Potentially could offer some warnings if we know that a continuation was resumed or escaped at all in a closure?
}

// ==== Sendability ------------------------------------------------------------
class NotSendable { }

@available(SwiftStdlib 5.1, *)
func test_nonsendableContinuation() async throws {
  let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
    continuation.resume(returning: NotSendable())
  }

  let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
    Task {
      continuation.resume(returning: NotSendable()) // okay
    }
  }
}

// ==== Detached Tasks ---------------------------------------------------------

@available(SwiftStdlib 5.1, *)
func test_detached() async throws {
  let handle = Task.detached {
    await someAsyncFunc() // able to call async functions
  }

  let result: String = await handle.value
  _ = result
}

@available(SwiftStdlib 5.1, *)
func test_detached_throwing() async -> String {
  let handle: Task<String, Error> = Task.detached {
    try await someThrowingAsyncFunc() // able to call async functions
  }

  do {
    return try await handle.value
  } catch {
    print("caught: \(error)")
  }

  return ""
}

// ==== Detached Tasks with inout Params---------------------------------------
@available(SwiftStdlib 5.1, *)
func printOrderNumber(n: inout Int) async { // expected-tns-note {{parameter 'n' is declared 'inout'}}
  Task.detached { // expected-tns-error {{escaping closure captures 'inout' parameter 'n'}}
      n+=1 // expected-tns-note {{captured here}}
      print(n) // expected-tns-note {{captured here}}
  }
}