Question about custom executors

SE-304 (structured concurrency) describes the requirements for "exclusive" executors, which were then renamed to "serial" executors some time around SE-306 (actors) and ultimately exposed as the SerialExecutor protocol in SE-392 (custom actor executors).

The requirements have remained the same as far as I could tell:

  • Job.runSynchronously must happen-after enqueue() and
  • execution must follow a total order on serial / exclusive executors

Does the runtime itself actually depends on the latter property, or is that requirement in place just to guarantee actor isolation semantics?

In other words, could I use a private "serial" executor that does not offer the second property IF the actors using it only contain concurrent-safe structures (e.g. Atomics or otherwise data protected by locks)? I get that it's not really an "actor" in this case, but custom executors can't really exist outside of actors yet.

My use case is to have an async / await interface, while avoiding thread hopping by using a pool of naive "run synchronously on calling thread" executors

This would presumably be a temporary workaround (famous last words) until the "Executor Switching" proposal mentioned at the end of 392 gets ironed out.

The runtime relies on a single task’s execution being well-ordered. I don’t think we rely on actors being well-ordered other than to isolate actor-isolated state, but I’m not sure I can officially bless giving an actor a non-serial executor just to get control over execution. It’s probably the only existing way to get the effect you’re looking for, though. Task executors are probably the right official feature you’re looking for rather than executor-switching.

1 Like

Thank you! Since it's for a hobby project, I'll not not use it :wink:

Another (possibly easier to spec / implement) option that would cover my use case would be:


I'd like to retract my previous statement, or at least restrict my proposal to something along the lines of:

Continuation.resumeSynchronouslyIfPossible() throws / -> Bool

Instead, I propose that this behavior be documented and grandfathered in for reasons that I will personally publish (eventually) unless someone else beats me to it, in which case I'll support their argument.