Tuesday, November 8, 2022

Swift Concurrency Tips

Wojciech Kulik (Hacker News):

If you add @MainActor attribute to a function, it is not the same as wrapping the whole method in DispatchQueue.main.async. If your method contains await keyword, the code will be split into two pieces – one before await and one after await, and as mentioned before, once the method hits await another pending call can start running even before the first one is finished.


You should remember to always mark a code with @MainActor if you require the Main Thread. You shouldn’t make implicit assumptions. The problem is when you suddenly start adding async-await to an existing project that was implemented without using Swift Concurrency. You can easily get into trouble if you don’t pay enough attention when mixing old code with async-await.


First of all, you should avoid long-running synchronous work in Task. For that, you should use GCD and custom queues. This way you will prevent blocking unintentionally some TaskPriority. However, you should be also aware that having too many custom queues is not recommended because of possible Thread Explosion. Therefore, you may try using OperationQueue to limit concurrency.


[If] you test your code on a simulator each TaskPriority will be limited to only 1 task running at the same time.


Update (2022-11-09): Rob Jonson:

Another misleading article saying @MainActor guarantees a function runs on the main thread. It doesn’t. It only guarantees that code you compile will call on the main thread. System callbacks can still call this function on any thread. Apple should document/emphasise this.

3 Comments RSS · Twitter

So did they actually simplify concurrency with all these new features or did they just shuffle the deck and make it easy to write incorrect code just in a different way?

mea culpa: after chastising Wojciech for overstating what @MainActor guarantees, I overstated the guarantee myself!

I said :
> It only guarantees that code you compile will call on the main thread.

Even that isn't necessarily true!


@iamaperson From reading all of this, several of the issues have to do with the very nature of async-await, like being careful about assumptions about (1) the thread you are running on, and (2) the need to be re-entrant (including on the same thread!). The issue with main thread enforcement really looks like a "bug" (or at best a leaky abstraction). Most problematic are performance issues coming from potential thread explosions, since they are "inherited" from GCD... Otherwise, apparently, we are spared race conditions and deadlocks. I am sure I am missing some stuff, but that's my takeaway in very short.

Leave a Comment