How about not using actors? e.g. serialising to a private queue:
class SharedData {
private var value = 0
private var queue = DispatchQueue(label: "SharedData_Private")
func setValue(_ newValue: Int) {
queue.sync { self.value = newValue }
}
func setValue(_ newValue: Int) async {
await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
queue.async {
self.value = newValue
continuation.resume()
}
}
}
}
This pattern would ensure that synchronous calls are serialised on a new, private thread (not from the global pool), while async callers who are on pooled threads can yield those execution resources while they wait (rather than block).
It's a bit ugly - it's basically implementing your own actor, but it at least gives you the control you need to ship an async API without a new major version and without resorting to private interfaces. Eventually you could replace it with the language's provided actors.