I'm trying to figure out how one could benefit from writing custom executers. For this reason I'm writing a "queued" executer that would await for each job to fully finish before moving to the next.
final class QueueSerialExecutor: SerialExecutor {
func enqueue(_ job: UnownedJob) {
let uuid = UUID()
print("start job \(uuid)")
job._runSynchronously(on: asUnownedSerialExecutor())
print("end job \(uuid)")
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
}
But as expected, once the job hits a suspension point, _runSynchronously exits and there is no way to know if other jobs will be enqueued or not (i.e. as a result of a Task.wait). My question would be, considering UnownedJob is completely opaque (even _runSynchronously is marked as internal), how would one use a custom executer to manually manage job scheduling?
Custom executors are not currently a supported feature. When they are, there will be a function you can call on a job to run it.
Note that running a job synchronously during enqueue would not be a legal implementation.
6 Likes
bdkjones
(Bryan)
3
This thread is a top Google result and for folks who stumble here: the best reason for a custom executor is adapting older Apple APIs that use delegate callbacks on a specified dispatchQueue to the new Actor model.
CoreBluetooth and AVCaptureSession are great examples.
Without the custom executor, your delegate method implementations (which must be nonisolated to conform to the delegate protocols) must spin up lots of Task objects to hop back to the Actor and modify properties. A custom executor lets you avoid the Tasks and all of the context-switching overhead.
The technique is nicely explained here:
1 Like
NeonTetra
(Park Byeong Gwan)
4
Additionally, This Thread was Created even before Custom SerialExecutor evolution proposal.
For now it is possible to implement Custom SerialExecutor