@@ -60,15 +60,26 @@ public final class ActorQueue<ActorType: Actor>: @unchecked Sendable {
6060 let ( taskStream, taskStreamContinuation) = AsyncStream< ActorTask> . makeStream( )
6161 self . taskStreamContinuation = taskStreamContinuation
6262
63- Task { @ActorQueueSynchronization in
63+ func beginExecuting(
64+ _ operation: sending @escaping ( isolated ActorType) async -> Void ,
65+ in context: isolated ActorType
66+ ) {
67+ // In Swift 6, a `Task` enqueued from an actor begins executing immediately on that actor.
68+ // Since we're running on our actor's context already, we can just dispatch a Task to get first-enqueued-first-start task execution.
69+ Task {
70+ await operation ( context)
71+ }
72+ }
73+
74+ Task {
75+ // In an ideal world, we would isolate this `for await` loop to the `ActorType`.
76+ // However, there's no good way to do that without retaining the actor and creating a cycle.
6477 for await actorTask in taskStream {
65- // In Swift 6, a `Task` enqueued from a global actor begins executing immediately on that global actor.
66- // Since we're running on a global actor already, we can just dispatch a Task to get first-enqueued-first-start
67- // task execution. In an ideal world, we wouldn't need a global actor and would isolate this `for await` loop on
68- // the `ActorType`. However, there's no good way to do that just yet.
69- Task {
70- await actorTask. task ( actorTask. executionContext)
71- }
78+ // Await switching to the ActorType context.
79+ await beginExecuting (
80+ actorTask. task,
81+ in: actorTask. executionContext
82+ )
7283 }
7384 }
7485 }
@@ -152,10 +163,3 @@ public final class ActorQueue<ActorType: Actor>: @unchecked Sendable {
152163 let task : @Sendable ( isolated ActorType) async -> Void
153164 }
154165}
155-
156- /// A global actor used for synchronizing task execution.
157- @globalActor
158- private struct ActorQueueSynchronization {
159- fileprivate actor Synchronization { }
160- fileprivate static let shared = Synchronization ( )
161- }
0 commit comments