Should task groups inherit actor?

Are the tasks created in a task group expected inherit the actor from the caller?

The following code shows that the first 2 prints are on the main thread, as I expect, but not the third:

Task { @MainActor in
  print(Thread.current)
  await withTaskGroup(of: Void.self) { group in
    print(Thread.current)
    group.addTask {
      print(Thread.current)
    }
  }
}

<_NSMainThread: 0x6000002744c0>{number = 1, name = main}
<_NSMainThread: 0x6000002744c0>{number = 1, name = main}
<NSThread: 0x600000259c40>{number = 7, name = (null)}

I couldn't find anything in the documentation, and considering that Task {...} inherits the actor I would expect that an API named .addTask {...} to also inherit the actor.

FWIW, I have confirmed that .addTask {...} does inherit the task priority and task locals, which is nice.

3 Likes

They don’t inherit the actor, and I don’t think it would make sense if they did. Child tasks are designed to run concurrently/in parallel. If they all ran on the same actor, they'd have to run one at a time (sequentially or interleaved).

This slide from WWDC 2021: Explore structured concurrency confirms that group tasks and async let don't inherit the actor:

9 Likes

Awesome, thanks for the slide @ole! Very helpful.

1 Like

@Matt_Gallagher’s recent tweet reminded me of it:

Swift Async avoids many pre-existing terms like "thread", "queue", or "concurrent" so understanding the details of this table is critical. It's easy to end up with everything unstructured on the main actor when you wanted cancellable background work.

1 Like