5

I have the following code in a simple Mac test project:

@main
class AppDelegate: NSObject, NSApplicationDelegate {
  func applicationDidFinishLaunching(_ aNotification: Notification) {
    print("are we initially on the main thread: \(Thread.isMainThread)")

    Task {
      print("is new task on main thread: \(Thread.isMainThread)")
    }
  }
}

In the Swift language guide, I see the following:

To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:) initializer. To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:) class method.

So since the AppDelegate code is running on the main actor and I'm creating a normal (i.e. non-detached) task, I'd expect its child task to also run on the main actor.

But that's not what happens when I run this test app:

are we initially on the main thread: true
is new task on main thread: false

Based on what I've read about Swift concurrency, I expected the new task to be scheduled and run on the main actor and that Thread.isMainThread would therefore be true inside that task.

Why is that not the case?

Bill
  • 44,502
  • 24
  • 122
  • 213
  • 1
    I don't know for sure, so I'll leave it to one of the concurrency wizards - but I think that even though it's dispatched on `DispatchQueue.main` it's not necessarily on the `@MainActor`. Some of `UIKit`/`AppKit` has been annotated with main actor, but not the app delegate for example [(reference)](https://stackoverflow.com/a/67893377/9607863). I would love to know the real answer though – George Nov 22 '21 at 12:34
  • 1
    A useful answer [here](https://stackoverflow.com/a/67954900/9607863), with this excerpt: "``But `test1` is not marked for any special thread. `test1` _itself_ runs on the main thread, because it is _called_ on the main thread; but it is not _marked_ to run on the main thread. Therefore its Task operation falls back to running on a background thread.``". I'll leave it to matt on this one, if they see this – George Nov 22 '21 at 12:40
  • 1
    @George Hmm I think you're right. It's not explicitly annotated as being on the main actor. If I put a `@MainActor` annotation right before my definition of `applicationDidFinishLaunching`, then I _do_ get the expected output. It's still a bit strange, because I thought being on the main thread implied that any new tasks would be on the main actor - instead, they seem to default to detached tasks, which is weird. – Bill Nov 22 '21 at 13:00
  • 2
    For the sake of writing it in the same place, [this](https://twitter.com/dgregor79/status/1458656417825505284) Twitter thread about this topic may be a good quick read-through – George Nov 22 '21 at 13:05
  • Does this answer your question? [What determines whether a Swift 5.5 Task initializer runs on the main thread?](https://stackoverflow.com/questions/67954414/what-determines-whether-a-swift-5-5-task-initializer-runs-on-the-main-thread) – Soumya Mahunt Sep 06 '22 at 07:18

1 Answers1

0

The important distinction to be made here just running code on main thread doesn't mean code is running on MainActor. To have your method always run on MainActor you can make the applicationDidFinishLaunching method isolated to MainActor with @MainActor attribute. In swift concurrency, if you make methods/properties isolated to an actor any Task you create inside them with Task.init will also inherit the actor context. Currently you don't have your actor isolation specified and hence seeing this behavior.

Soumya Mahunt
  • 2,148
  • 12
  • 30
  • My log statement is already checking for the main _thread_ not for the main _actor_... – Bill Sep 05 '22 at 15:26
  • 1
    The idea is that Task inherits the Actor trait - but the delegate method not marked as @MainActor may not be executed with the MainActor trait in the system so that the new Task will inherit something else or nothing. – Kanstantsin Bucha May 29 '23 at 15:02