Concurrency: Blocking a detached task?

When working with async/await and actors, one of the axioms is that you never block a task for longer -- otherwise it will not have a chance to let the other tasks run.

So far so good, but: Does that also hold for detached tasks? I'm writing an actor that is supposed to control hardware. Unfortunately though, it has only a synchronous interface to its read and write operations. I want to hide this interface behind the asynchronous actor, hence planned to use withCheckedContinuation, spawn a detached task, call the blocking operation and then resume the continuation.

Is this going to work? Is it a good design or will this spawn and shutdown threads all the time? Is there a better alternative?

Detached vs non-detached doesn't really change anything here, they're all backed by the same cooperative pool. My tentative advice would be to not do anything fancy here and just have a singleton actor manage it without bothering with the detached task and continuations. You'll get slightly less concurrency in the rest of your app while it's active, but that's not the end of the world. The key things to avoid are allowing N threads to block waiting, and especially allowing N threads to block waiting for something that requires another thread to unblock it. The former will block your app until it's done, the latter will block forever.

Once custom executors (swift-evolution/0000-custom-executors.md at custom-executors · rjmccall/swift-evolution · GitHub) are ready, you can switch that actor over to one (say, backed by a regular serial dispatch queue).

2 Likes