MainActor check if a hop is necessary: is it special or the same as for other actors?

SE-0316 Global actors has this to say about the main actor (emphasis mine):

For systems that use the Apple's Dispatch library as the underlying concurrency implementation, the main actor uses a custom executor that wraps the main dispatch queue. It also determines when code is dynamically executing on the main actor to avoid an extra "hop" when performing an asynchronous call to a @MainActor function.

I've been trying to find the place in the Swift source code where this dynamic check to avoid extra hops is performed. The text in the proposal makes it sound (to me) that this is a check that's specific to the main actor, but I couldn't find any specific check like this.

What I did find (I think) is a generic check that works for all executors/actors, main actor or not. In swift_task_switchImpl, there's a check if the new task's the new job's executor is the current executor:

static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
                                  TaskContinuationFunction *resumeFunction,
                                  ExecutorRef newExecutor) SWIFT_OPTNONE {
  ...

  // If the current executor is compatible with running the new executor,
  // we can just immediately continue running with the resume function
  // we were passed in.
  if (!currentExecutor.mustSwitchToRun(newExecutor)) {
    return resumeFunction(resumeContext); // 'return' forces tail call
  }

  ...

Is this what SE-0316 means, or did I miss something in the MainActor implementation?

3 Likes

That's correct. The only things special about the main actor executor in the concurrency runtime are:

  • actor safety checks for @MainActor functions check what thread they're on and not just the runtime's internal notion of the current executor, because the latter isn't reliable since the runtime doesn't exclusively control scheduling onto the main thread
  • non-detached tasks inherit UserInitiated priority by default if started from the main thread
3 Likes

Thanks John.

Is emitPreconditionCheckExpectedExecutor the actor safety checks you're referring to? (This is the only place I could find that eventually calls swift_task_isCurrentExecutorImpl, which includes the main thread check.)

Yes, the somewhat-unfortunately-named -enable-actor-data-race-checks stuff.

4 Likes