📝Cooperative concurrency makes it hard to have transparent asynchrony

tags

Function color

In JS, it’s “impossible” to fully erase sync/async boundary.

Because the runtime is single-threaded and cooperative, a path that doesn’t have any awaits is guaranteed to be “atomic.” An await introduces a yield point that allows the runtime to do something else, and this can have visible side effects, variables may change, etc. So calling an async function (or rather awaiting it) is fundamentally different from calling a regular function.

In JS, sync code usually completes quickly and generally has very little means to hold on to the thread for long (the only option is CPU-heavy work).

In some cases, this makes it important for the caller that the function does not break the current atomic block (i.e. does not yield).

Emacs suffers from the same issue as JavaScript. Being single-threaded and cooperative, a lot of code assumes that they are the only code that currently runs. The control is yielded only at well-defined points. This makes yield points semantically significant, so it needs to be taken with care.

Languages that have non-cooperative concurrency—even if single-threaded—force programs to be ready that they can be suspended and other work may happen at any point. This makes await points non-special.

Languages with either non-cooperative concurrency or true parallelism are much more viable to make asynchrony transparent.