1
1
import Foundation
2
+ import AsyncAlgorithms
2
3
3
4
/// An object that controls cooperative cancellation of multiple registered tasks and linked object registered tasks.
4
5
///
@@ -36,16 +37,19 @@ public struct CancellationSource: AsyncObject, Cancellable, Loggable {
36
37
internal typealias Continuation = GlobalContinuation < Void , Error >
37
38
/// The cancellable work with invocation context.
38
39
internal typealias WorkItem = (
39
- Cancellable , id: UUID , file: String , function: String , line: UInt
40
+ any Cancellable , id: UUID , file: String , function: String , line: UInt
40
41
)
41
42
42
43
/// The lifetime task that is cancelled when
43
44
/// `CancellationSource` is cancelled.
44
45
@usableFromInline
45
- var lifetime : Task < Void , Error > !
46
+ let lifetime : Task < Void , Error >
46
47
/// The stream continuation used to register work items
47
48
/// for cooperative cancellation.
48
- var pipe : AsyncStream < WorkItem > . Continuation !
49
+ let pipe : AsyncStream < WorkItem > . Continuation
50
+ /// The channel that controls waiting on the `CancellationSource`.
51
+ /// Once `CancellationSource` is cancelled, channel finishes.
52
+ let waiter : AsyncChannel < Void >
49
53
50
54
/// A Boolean value that indicates whether cancellation is already
51
55
/// invoked on the source.
@@ -61,24 +65,57 @@ public struct CancellationSource: AsyncObject, Cancellable, Loggable {
61
65
///
62
66
/// - Returns: The newly created cancellation source.
63
67
public init ( ) {
64
- let stream = AsyncStream< WorkItem> { self . pipe = $0 }
65
- self . lifetime = Task . detached {
66
- try await withThrowingTaskGroup ( of: Void . self) { group in
67
- for await item in stream {
68
- group. addTask {
69
- try ? await waitHandlingCancelation (
70
- for: item. 0 , associatedId: item. id,
71
- file: item. file,
72
- function: item. function,
73
- line: item. line
74
- )
68
+ var continuation : AsyncStream < WorkItem > . Continuation !
69
+ let stream = AsyncStream< WorkItem> { continuation = $0 }
70
+ let channel = AsyncChannel < Void > ( )
71
+ self . pipe = continuation
72
+ self . waiter = channel
73
+
74
+ func lifetime( ) -> Task < Void , Error > {
75
+ return Task . detached {
76
+ await withThrowingTaskGroup ( of: Void . self) { group in
77
+ for await item in stream {
78
+ group. addTask {
79
+ try ? await waitHandlingCancelation (
80
+ for: item. 0 , associatedId: item. id,
81
+ file: item. file,
82
+ function: item. function,
83
+ line: item. line
84
+ )
85
+ }
75
86
}
87
+
88
+ group. cancelAll ( )
76
89
}
90
+ channel. finish ( )
91
+ }
92
+ }
77
93
78
- group. cancelAll ( )
79
- try await group. waitForAll ( )
94
+ #if swift(>=5.8)
95
+ if #available( macOS 13 . 3 , iOS 16 . 4 , tvOS 16 . 4 , watchOS 9 . 4 , * ) {
96
+ self . lifetime = Task . detached {
97
+ await withDiscardingTaskGroup { group in
98
+ for await item in stream {
99
+ group. addTask {
100
+ try ? await waitHandlingCancelation (
101
+ for: item. 0 , associatedId: item. id,
102
+ file: item. file,
103
+ function: item. function,
104
+ line: item. line
105
+ )
106
+ }
107
+ }
108
+
109
+ group. cancelAll ( )
110
+ }
111
+ channel. finish ( )
80
112
}
113
+ } else {
114
+ self . lifetime = lifetime ( )
81
115
}
116
+ #else
117
+ self . lifetime = lifetime ( )
118
+ #endif
82
119
}
83
120
84
121
/// Register cancellable work for cooperative cancellation
@@ -163,11 +200,17 @@ public struct CancellationSource: AsyncObject, Cancellable, Loggable {
163
200
file: String = #fileID,
164
201
function: String = #function,
165
202
line: UInt = #line
166
- ) async {
203
+ ) async throws {
167
204
let id = UUID ( )
168
205
log ( " Waiting " , id: id, file: file, function: function, line: line)
169
- let _ = await lifetime. result
170
- log ( " Completed " , id: id, file: file, function: function, line: line)
206
+ await waiter. send ( ( ) )
207
+ do {
208
+ try Task . checkCancellation ( )
209
+ log ( " Completed " , id: id, file: file, function: function, line: line)
210
+ } catch {
211
+ log ( " Cancelled " , id: id, file: file, function: function, line: line)
212
+ throw error
213
+ }
171
214
}
172
215
}
173
216
0 commit comments