- Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathasync_tasks.swift
148 lines (120 loc) · 5.34 KB
/
async_tasks.swift
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 -target %target-swift-5.1-abi-triple %s -o /dev/null -verify -emit-sil -DALLOW_TYPECHECKER_ERRORS -verify-additional-prefix typechecker-
// RUN: %target-swift-frontend -strict-concurrency=complete -target %target-swift-5.1-abi-triple %s -o /dev/null -verify -emit-sil -DALLOW_TYPECHECKER_ERRORS -verify-additional-prefix typechecker-
// RUN: %target-swift-frontend -strict-concurrency=complete -target %target-swift-5.1-abi-triple %s -o /dev/null -verify -emit-sil -verify-additional-prefix tns-
// REQUIRES: concurrency
// REQUIRES: asserts
@available(SwiftStdlib 5.1,*)
func someAsyncFunc()async->String{""}
structMyError:Error{}
@available(SwiftStdlib 5.1,*)
func someThrowingAsyncFunc()asyncthrows->String{throwMyError()}
// ==== Unsafe Continuations ---------------------------------------------------
structVegetable{}
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])asyncthrows->[Vegetable]{
tryawaitwithUnsafeThrowingContinuation{ continuation in
varveggies:[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}}
lets=awaitsomeAsyncFunc() // expected-typechecker-note {{'async' inferred from asynchronous operation used here}}
continuation.resume(returning: s)
}
#endif
let _:String=awaitwithUnsafeContinuation{ continuation in
continuation.resume(returning:"")
}
// rdar://76475495 - suppress warnings for invalid expressions
#if ALLOW_TYPECHECKER_ERRORS
func test_invalid_async_no_warnings()async->Int{
returnawaitwithUnsafeContinuation{
$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()asyncthrows{
let _:String=tryawaitwithUnsafeThrowingContinuation{ continuation in
continuation.resume(returning:"")
}
let _:String=tryawaitwithUnsafeThrowingContinuation{ continuation in
continuation.resume(throwing:MyError())
}
// using resume(with:)
let _:String=tryawaitwithUnsafeThrowingContinuation{ continuation in
letresult:Result<String,MyError>=.success("")
continuation.resume(with: result)
}
let _:String=tryawaitwithUnsafeThrowingContinuation{ 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 ------------------------------------------------------------
classNotSendable{}
@available(SwiftStdlib 5.1,*)
func test_nonsendableContinuation()asyncthrows{
let _:NotSendable=tryawaitwithUnsafeThrowingContinuation{ continuation in
continuation.resume(returning:NotSendable())
}
let _:NotSendable=tryawaitwithUnsafeThrowingContinuation{ continuation in
Task{
continuation.resume(returning:NotSendable()) // okay
}
}
}
// ==== Detached Tasks ---------------------------------------------------------
@available(SwiftStdlib 5.1,*)
func test_detached()asyncthrows{
lethandle=Task.detached{
awaitsomeAsyncFunc() // able to call async functions
}
letresult:String=await handle.value
_ = result
}
@available(SwiftStdlib 5.1,*)
func test_detached_throwing()async->String{
lethandle:Task<String,Error>=Task.detached{
tryawaitsomeThrowingAsyncFunc() // able to call async functions
}
do{
returntryawait handle.value
}catch{
print("caught: \(error)")
}
return""
}
// ==== Detached Tasks with inout Params---------------------------------------
@available(SwiftStdlib 5.1,*)
func printOrderNumber(n:inoutInt)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}}
}
}