@@ -60,29 +60,12 @@ public final class ActorQueue<ActorType: Actor>: @unchecked Sendable {
60
60
let ( taskStream, taskStreamContinuation) = AsyncStream< ActorTask> . makeStream( )
61
61
self . taskStreamContinuation = taskStreamContinuation
62
62
63
- func beginExecuting(
64
- _ operation: sending @escaping ( isolated ActorType) async -> Void ,
65
- in context: isolated ActorType,
66
- priority: TaskPriority ?
67
- ) {
68
- // In Swift 6, a `Task` enqueued from an actor begins executing immediately on that actor.
69
- // Since we're running on our actor's context already, we can just dispatch a Task to get first-enqueued-first-start task execution.
70
- Task ( priority: priority) {
71
- await operation ( context)
72
- }
73
- }
74
-
75
63
Task {
76
64
// In an ideal world, we would isolate this `for await` loop to the `ActorType`.
77
65
// However, there's no good way to do that without retaining the actor and creating a cycle.
78
66
for await actorTask in taskStream {
79
67
// Await switching to the ActorType context.
80
- await beginExecuting (
81
- actorTask. task,
82
- in: actorTask. executionContext,
83
- priority: actorTask. priority
84
- )
85
- await actorTask. sempahore. signal ( )
68
+ await actorTask. task ( actorTask. executionContext, actorTask. semaphore, actorTask. priority)
86
69
}
87
70
}
88
71
}
@@ -104,34 +87,36 @@ public final class ActorQueue<ActorType: Actor>: @unchecked Sendable {
104
87
weakExecutionContext = actor
105
88
}
106
89
107
- // MARK: Fileprivate
108
-
109
- fileprivate let taskStreamContinuation : AsyncStream < ActorTask > . Continuation
110
-
111
- /// The actor on whose isolated context our tasks run, force-unwrapped.
112
- /// Utilize this accessor to retrieve the weak execution context in order to avoid repeating the below comment.
113
- fileprivate var executionContext : ActorType {
114
- // Crashing here means that this queue is being sent tasks either before an execution context has been set, or
115
- // after the execution context has deallocated. An ActorQueue's execution context should be set in the adopted
116
- // actor's `init` method, and the ActorQueue should not exceed the lifecycle of the adopted actor.
117
- weakExecutionContext!
118
- }
90
+ // MARK: Internal
119
91
120
- fileprivate struct ActorTask : Sendable {
92
+ struct ActorTask : Sendable {
121
93
init (
122
94
executionContext: ActorType ,
123
95
priority: TaskPriority ? ,
124
- task: @escaping @Sendable ( isolated ActorType) async -> Void
96
+ task: @escaping @Sendable ( isolated ActorType, Semaphore , TaskPriority ? ) async -> Void
125
97
) {
126
98
self . executionContext = executionContext
127
99
self . priority = priority
128
100
self . task = task
129
101
}
130
-
102
+
131
103
let executionContext : ActorType
132
- let sempahore = Semaphore ( )
104
+ let semaphore = Semaphore ( )
133
105
let priority : TaskPriority ?
134
- let task : @Sendable ( isolated ActorType) async -> Void
106
+ let task : @Sendable ( isolated ActorType, Semaphore, TaskPriority? ) async -> Void
107
+ }
108
+
109
+ // MARK: Fileprivate
110
+
111
+ fileprivate let taskStreamContinuation : AsyncStream < ActorTask > . Continuation
112
+
113
+ /// The actor on whose isolated context our tasks run, force-unwrapped.
114
+ /// Utilize this accessor to retrieve the weak execution context in order to avoid repeating the below comment.
115
+ fileprivate var executionContext : ActorType {
116
+ // Crashing here means that this queue is being sent tasks either before an execution context has been set, or
117
+ // after the execution context has deallocated. An ActorQueue's execution context should be set in the adopted
118
+ // actor's `init` method, and the ActorQueue should not exceed the lifecycle of the adopted actor.
119
+ weakExecutionContext!
135
120
}
136
121
137
122
// MARK: Private
@@ -180,14 +165,23 @@ extension Task {
180
165
let task = ActorQueue< ActorType> . ActorTask(
181
166
executionContext: actorQueue. executionContext,
182
167
priority: priority,
183
- task: { executionContext in
184
- await delivery. sendValue ( operation ( executionContext) )
168
+ task: { executionContext, semaphore, priority in
169
+ await semaphore. wait ( )
170
+ delivery. execute ( { @Sendable executionContext in
171
+ await delivery. sendValue ( operation ( executionContext) )
172
+ } , in: executionContext, priority: priority)
185
173
}
186
174
)
175
+
187
176
actorQueue. taskStreamContinuation. yield ( task)
188
177
self . init ( priority: priority) {
189
- await task. sempahore. wait ( )
190
- return await delivery. getValue ( )
178
+ await withTaskCancellationHandler (
179
+ operation: {
180
+ await task. semaphore. signal ( )
181
+ return await delivery. getValue ( )
182
+ } ,
183
+ onCancel: delivery. cancel
184
+ )
191
185
}
192
186
}
193
187
@@ -227,19 +221,27 @@ extension Task {
227
221
let task = ActorQueue< ActorType> . ActorTask(
228
222
executionContext: actorQueue. executionContext,
229
223
priority: priority,
230
- task: { executionContext in
231
- do {
232
- try await delivery. sendValue ( operation ( executionContext) )
233
- } catch {
234
- await delivery. sendFailure ( error)
235
- }
224
+ task: { executionContext, semaphore, priority in
225
+ await semaphore. wait ( )
226
+ delivery. execute ( { @Sendable executionContext in
227
+ do {
228
+ try await delivery. sendValue ( operation ( executionContext) )
229
+ } catch {
230
+ await delivery. sendFailure ( error)
231
+ }
232
+ } , in: executionContext, priority: priority)
236
233
}
237
234
)
238
235
239
236
actorQueue. taskStreamContinuation. yield ( task)
240
237
self . init ( priority: priority) {
241
- await task. sempahore. wait ( )
242
- return try await delivery. getValue ( )
238
+ try await withTaskCancellationHandler (
239
+ operation: {
240
+ await task. semaphore. signal ( )
241
+ return try await delivery. getValue ( )
242
+ } ,
243
+ onCancel: delivery. cancel
244
+ )
243
245
}
244
246
}
245
247
@@ -279,14 +281,22 @@ extension Task {
279
281
let task = ActorQueue< MainActor> . ActorTask(
280
282
executionContext: actorQueue. executionContext,
281
283
priority: priority,
282
- task: { executionContext in
283
- await delivery. sendValue ( operation ( ) )
284
+ task: { executionContext, semaphore, priority in
285
+ await semaphore. wait ( )
286
+ delivery. execute ( { @Sendable executionContext in
287
+ await delivery. sendValue ( operation ( ) )
288
+ } , in: executionContext, priority: priority)
284
289
}
285
290
)
286
291
actorQueue. taskStreamContinuation. yield ( task)
287
292
self . init ( priority: priority) {
288
- await task. sempahore. wait ( )
289
- return await delivery. getValue ( )
293
+ return await withTaskCancellationHandler (
294
+ operation: {
295
+ await task. semaphore. signal ( )
296
+ return await delivery. getValue ( )
297
+ } ,
298
+ onCancel: delivery. cancel
299
+ )
290
300
}
291
301
}
292
302
@@ -326,19 +336,27 @@ extension Task {
326
336
let task = ActorQueue< MainActor> . ActorTask(
327
337
executionContext: actorQueue. executionContext,
328
338
priority: priority,
329
- task: { executionContext in
330
- do {
331
- try await delivery. sendValue ( operation ( ) )
332
- } catch {
333
- await delivery. sendFailure ( error)
334
- }
339
+ task: { executionContext, semaphore, priority in
340
+ await semaphore. wait ( )
341
+ delivery. execute ( { @Sendable executionContext in
342
+ do {
343
+ try await delivery. sendValue ( operation ( ) )
344
+ } catch {
345
+ await delivery. sendFailure ( error)
346
+ }
347
+ } , in: executionContext, priority: priority)
335
348
}
336
349
)
337
350
338
351
actorQueue. taskStreamContinuation. yield ( task)
339
352
self . init ( priority: priority) {
340
- await task. sempahore. wait ( )
341
- return try await delivery. getValue ( )
353
+ try await withTaskCancellationHandler (
354
+ operation: {
355
+ await task. semaphore. signal ( )
356
+ return try await delivery. getValue ( )
357
+ } ,
358
+ onCancel: delivery. cancel
359
+ )
342
360
}
343
361
}
344
362
}
0 commit comments