Custom actor executors can absolutely be made FIFO. That said, I think it’s important to understand what that means. One of the main arguments that ultimately convinced us to not make Swift actors FIFO by default is that it’s really hard to actually turn that guarantee into anything useful at a higher level. That’s still going to be true with custom executors.
When you’re doing explicit enqueuing, having FIFO serial queues allows you to propagate the well-orderedness of a stream of events across an arbitrary sequence of queues. Suppose that serial queue A processes an event and ends by enqueuing on serial queue B. FIFO queues will ensure that both queues process the events in the same order, and by induction this works across any number of queues. But this relies on going directly between an identical series of serial queues; any transit through non-serial execution, or skipping an intermediate queue, completely fouls up that semantic property.
The way that Swift async functions are scheduled is really not conducive to maintaining that property. Swift async functions really like to briefly transit through non-serial executors, and optimization really wants to avoid doing jumps to actors where you’re not going to do anything. We could try to harden those rules and give more direct control, e.g. to allow new Task
s to be explicitly enqueued on a specific actor. But that feels like it would be a very brittle way to solve that problem; you’d have to understand a lot of implementation details to even try it, and it would probably be an exercise in frustration in practice.
We’re aware that the current language tools for maintaining event order aren’t great, and that’s definitely something we want to improve. But I don’t think FIFO actors actually get you there very well.