Calling a MainActor function when already on main thread without a dispatch

I have a function (func A) annotated as MainActor that updates some state. In another function (func B) that is not MainActor, and it's also not async, I need to query a dependency for some state and potentially update my state which needs to be done on the MainActor. If the user had called func B on the main thread, I would like to not have to dispatch to the main queue in order to call func A. The user would expect that the state would be correct after calling func B.

Essentially, what I would like to do is the equivalent of something like this:

// func B is synchronous and non main actor
func B() {
    var someState = dependency.query()
    
    // func A is @MainActor
    if Thread.isMainThread {
        // This call to A does not compile
        self.A(someState)
    } else {
        DispatchQueue.main.async {
            // This call to A compiles
            self.A(someState)
        }
    }
}

Is there a way to achieve this?

You probably don't want to mix actors, threads, and queues. You should simply be able to call a @MainActor function and have it do the right thing, unless I'm misunderstanding the question. The compiler will enforce inserting await as needed.

Where the above code is pasted is not in an async context, so I can't do an await. I will try to update the question to make that more clear.

Ah I see. This boils down to the function coloring problem then. I've somewhat lost track of the current state of the art on that question, so I'll defer to someone else.

No. To do that, B would have to be both synchronous and asynchronous at the same time.

Since A is isolated to an actor, self.A(someState) has a suspension point. If some other access to the actor is already pending when B gets to this point, B has no way of waiting across the suspension.

So, B has to be asynchronous. Chances are, if you allow asynchronicity flow "upstream" from B to callers of B and so on, you'll find a natural place to transition from synchronous to asynchronous (probably by using something like Task { … }.

Thanks for the reply.

I see what you are saying. Unfortunately in this case function B is actually the initializer. I could make that async, or I could make it MainActor. However, we didn't really want to have to do either for this particular class.

Terms of Service

Privacy Policy

Cookie Policy