Using `async` functions from synchronous functions (and breaking all the rules)

I believe that actors are a good basic design tool and the right way to structure the high-level concurrency in many systems — if you're exposing a type with a concurrent interface, I think you should be strongly considering making that type an actor. But there are definitely still a lot of uses for synchronous locks, especially in the implementation of systems that intend to largely provide synchronous interfaces. More importantly, Swift will eventually have some sort of native lock type; we've focused on actors for good reasons, but I don't want people to avoid locks just because actors feel more native right now. If you have a well-designed lock type (like Apple's OSAllocatedUnfairLock, although I personally would like that a lot more if it really committed and forced you to use the withLock API that provides the state inout), you should feel free to use it fearlessly. Locks are less composable tools than actors in some ways, and you generally need to be more thoughtful about what work you do while holding them, but the guarantee of synchronous behavior is a very powerful tool that's sometimes just what you need.

(And there's a more general point there; good concurrency design includes understanding when to deliberately make things synchronous, e.g. in order to force them to happen uninterrupted, or to force clients to "come prepared".)

I'm not trying to pass judgment on the APIs in question here; I haven't looked at them at all. If there's a good reason for them to become async, then absolutely, make them async. I just wanted to clarify how I, personally, think about how actors fit into a design and when to use sync/async functions.

14 Likes