-
-
Notifications
You must be signed in to change notification settings - Fork 12
Remove need for ActorQueueSynchronization #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #40 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 9 9
Lines 657 666 +9
=========================================
+ Hits 657 666 +9
|
Sources/AsyncQueue/ActorQueue.swift
Outdated
@@ -84,10 +71,30 @@ public final class ActorQueue<ActorType: Actor>: @unchecked Sendable { | |||
/// **Must be called prior to enqueuing any work on the receiver.** | |||
/// | |||
/// - Parameter actor: The actor on which the queue's task will execute. This parameter is not retained by the receiver. | |||
/// - Warning: Calling this method more than once will result in an assertion failure. | |||
/// - Precondition: Calling this method more than once will result in a precondition failure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we're kicking off the stream execution here, precondition
feels appropriate.
Sources/AsyncQueue/ActorQueue.swift
Outdated
actor.execute { [taskStream] _ in | ||
func beginExecuting( | ||
_ operation: sending @escaping (isolated ActorType) async -> Void, | ||
in context: isolated ActorType | ||
) { | ||
// In Swift 6, a `Task` enqueued from an actor begins executing immediately on that actor. | ||
// Since we're running on our actor's context due to the isolated parmater, we can just dispatch a Task to get first-enqueued-first-start task execution. | ||
Task { | ||
await operation(context) | ||
} | ||
} | ||
|
||
for await actorTask in taskStream { | ||
await beginExecuting( | ||
actorTask.task, | ||
in: actorTask.executionContext | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this entire closure executes within the actor
context!
Sources/AsyncQueue/ActorQueue.swift
Outdated
extension Actor { | ||
nonisolated | ||
func execute( | ||
_ task: @escaping @Sendable (isolated Self?) async -> Void |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a bit nuts, but using (isolated Self?)
enables us to isolate the closure to the actor's context without retaining the actor.
Sources/AsyncQueue/ActorQueue.swift
Outdated
} | ||
|
||
for await actorTask in taskStream { | ||
await beginExecuting( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to await
here despite the fact that we're already isolated to the context
because the compiler does not have a guarantee that actorTask.executionContext ===
actor`. In practice, these are always the same and therefore we do not suspend here.
We don't need to bounce through a global queue in order to ensure FIFS (first-enqueued-first-start) behavior in our ActorQueue. Instead, we just can make the async for loop await switching to the ActorType's context.
This PR is unlikely to affect performance. This PR should make this code a bit easier to follow, however.