Guarantee of resume function on MainActor

Hi All, I have a question regarding when suspending a async on the main thread.

Let's say for example I have the following code:

@MainActor
func loadContent() async {
    // do some..
   let content = await api.someLongRunningTaskThatWillReturnAContent()
   self.content = content
  // More work
}

Since swift specifies that once your func is resume after a suspension point it not guarantee about what thread will continue running that resumed function, that means loadContent() async can run in any thread? or @MainActor ensures / guarantee that will continue the execution on the main thread?.

Thanks!

1 Like

Okay, I am relatively new here and unfortunately haven't had the chance to use much of the new concurrency in the wild, but I'll try to give an answer, or rather a frame challenge:
Why do you want to know? :smile:

Real, short tldr answer: I'm pretty sure in this case yes, it does run on the main thread.
However, that's probably better phrased as "it will continue on the main actor". (The fact that this is then also basically the main thread is an implementation detail.)

My hunch is that, besides pure academic curiosity, you perhaps misunderstand the meaning of Swift's guarantee (you refer to the first grey box here, right?) or rather lack thereof. It sounds a bit as if you're concerned about a potential hole here that could cause a data race, correct?

I don't think that's the case, though, and the note in the language reference doesn't imply any problem.
What is meant, I presume, is that the runtime will potentially continue from an await suspension point on another thread when it makes sense and is safe to do so. In your example you have, however, explicitly specified that the code in loadContent should be confined to the main actor, so I am betting that it does ensure that.

I think it's a little like in the D&D rules: Specific trumps general. The general rule here is that an await might be continued from on any thread. But the specific rule of actors is that they guarantee confinement of access to a specific task (note I avoid saying thread here) to guarantee there's no data race. That one trumps the general statement about await.

Now I hope I didn't make an ass out of myself and need to be corrected by more experienced people here... :smiley:

Hey @Gero thanks for the reply.

Yeah, I totally agree with you. For me is unclear (in this aspect) what swift docs are trying to say. I can infer that since we marked the func as a @MainActor this will guarantee that once the func resume, it will continue on the same thread (Main in this case).

You're welcome, @nexon, I hope somebody else confirms my understanding, but I am pretty certain that it's correct. Otherwise it would be a rather huge problem as it would imply you should never ever await from within any function of any actor. Here my lack of practical experience bites me, but I can hardly imagine that would be the case.

As a general aside I have noticed that folks oven stumble over associating threads with actors or, in the past, queues within GCD. The documentation saying things like "the runtime/GCD does not guarantee the code is executed on the same thread" or similar things then makes them fret as they collides with their understanding.
However, such documentation is needed and actually makes a lot more sense if start to think of threads as more of a resource, similar to a CPU core: They are "working units" that can be used by the system as needed, and GCD and swift concurrency do their best to use them in as smart a way as possible. To help you reason about what's going on these frameworks offer new ways of abstraction and you need to rely more on those than try to map everything to "on what thread is this running?". At least that was my experience when I started to use GCD back in the day.
Of course each abstraction can have its leaks and sometimes you have to reason about the underlying stuff, but overall my impression of the new concurrency (and also GCD, btw) was that it's pretty strong in that regard (unlike, for example, the old Java dates... brrr...).

Yes, an actor-isolated function will come back to the actor after a suspension.

3 Likes