In a call chain of async methods, where none of the functions in the call stack suspend, how can we instruct the runtime not to do a thread switch?
Given the code below:
import SwiftUI
@main
struct MyApp: App {
@StateObject var appCoordinator = AppCoordinator()
var body: some Scene {
WindowGroup {
EmptyView()
.task {
await appCoordinator.start(settings: UserDefaults.standard)
}
}
}
}
@MainActor
final class AppCoordinator: ObservableObject {
func start(settings: Settings) async {
try? await settings.write(bool: false, atKey: "isFirstRun")
}
}
protocol Settings {
func write(bool: Bool, atKey key: String) async throws
}
extension UserDefaults: Settings {
func write(bool: Bool, atKey key: String) async throws {
set(bool, forKey: key)
}
}
, the following happen:
- the
.task
body calls theappCoordinator.start()
function from the main thread (since the task inherits the current actor context) appCoordinator
callssettings.write
from the main thread (asAppCoordinator
is@MainActor
)- however once the execution reaches the body of the
write(bool:atKey:)
function, the execution is moved to another thread.
I know that we should not make assumptions about on which thread the async functions execute, however for cases like this, the thread switch affects performance, and IMO is not needed. Even if an actor would've implement the protocol, its executor would've been the one that would ensure the atomicity of the actor's operations.
I could decorate the write(bool:atKey:)
function with @MainActor
, however that doesn't sound right.
I could also drop the async
requirement over the protocol, however in the actual project, the UserDefults
instance is injected via a protocol that needs to declare async throw
functions.
Bottom line, given a call chain if async
methods, where none of the functions suspend, is there a way to prevent the thread switches?