I'm using a global actor to synchronise state across different types. However there are cases where I'd like to have several instances of the global actor, not just a singleton. I note you can't apply a global actor to another actor.
@globalActor
actor Foo {
static let shared = Foo()
}
Is the following semantically equivalent, save for allowing multiple instances?
@globalActor
actor FooActor {
static let shared = FooActor()
}
@FooActor
final class Foo {
nonisolated init() {}
}
Global actors are necessarily singleton, but you can have arbitrarily many objects (and global variables) isolated to that singleton. In your example, all isolated uses of Foo objects will run on the singleton FooActor actor. So accesses to Foo objects will be isolated, but they'll have the same isolation, which means accesses to different objects can't occur concurrently with each other. If that's acceptable, then global actors may be an excellent solution for you. If it isn't, then you'll need to use multiple actor instances.
Actor instances can still have arbitrarily many objects isolated to them, but you have to manage the isolation differently: instead of being able to just use a global-actor attribute, you'll have to make the objects non-Sendable so that they can't be referenced from multiple concurrent contexts. This is an inherent limitation to the simplicity of use offered by global actors.
Thanks for the speedy reply John (great CppNow talk, BTW).
So in my application, having a single isolation is not really a problem, indeed a singleton would be fine if I improved some rough edges in the API.
Having said that, what I was hoping to do was explicitly isolate helper types created by Foo instances to a specific instance of Foo. The only way I could come up is having the helpers implement something like adoptExecutionContext(of:) in ActorQueue. But that's syntactically a bit more cumbersome to use.
Okay. Then you should be able to just make those classes non-Sendable. The actor will have references to those objects internally, and the sendability restriction will stop you from sharing them outside of the isolation of the actor.
I problem I ran into, at least I think, was that the helper classes needed to use an unsafe pointer that escaped from the actor, and unless I made them also isolated to the same context I saw races (as async calls in the helpers ran on the generic context).
Anyway, I think I have a couple of options with global actors that work. Thanks again for being generous with your time, I don’t know how you find time to post here!