So would it be correct to say that formal actor isolation is for correctness and avoiding data races, while the executor control in this proposal is only for performance, and does not affect the formal isolation?
The proposal does seem to suggest that in several places, but I think it could be a bit clearer about it.
It also raises a question about assumeIsolated
. Here's what the proposal says:
It is possible however to write a custom SerialExecutor
and conform ot to the TaskExecutor
protocol at the same time, if indeed one intended to use it for both purposes. The serial executor conformance can be used for purposes of isolation (including the asserting and "assuming" of isolation), and the task executor conformance allows using a type to provide a hint where tasks should execute although cannot be used to fulfil isolation requirements.
I imagine we wouldn't want people to use executor preference APIs in order to ensure isolation, and would prefer they used a more formal method for that (e.g. annotating their code with @SomeGlobalActor
and await
-ing where hops may occur -- even if those hops are dynamically determined to be unnecessary).
So should the assumeIsolation
APIs perhaps ignore SerialExecutor
conformances that arise solely due to task preference?
And one more thing, from the "Future Directions" section:
Task executor preference and global actors
It is more efficient to write Task(on: MainActor.shared) {}
than it is to Task { @MainActor in }
because the latter will first launch the task on the inferred context (either enclosing actor, or global concurrent executor), and then hop to the main actor. The on MainActor
spelling allows Swift to immediately enqueue on the actor itself.
Is there any reason the compiler is prohibited from optimising the latter spelling to be just as efficient as the former?
AFAICT, if you write Task { @MainActor in }
, it is not possible for any of the enclosing code to run on the inferred initial context (whatever it happens to be), because it immediately hops to the specified global actor. I thought the compiler was allowed to remove redundant hopping, and that in cases such as this, it is not obliged to enqueue the hop on the initial context.