So I have an actor that has a long running async stream that is used to update some of its internal state. As such, I need to weakly capture self so that I am not creating a reference cycle.
I noticed that even if I create a task within the actors isolation context, I still have to away calls to the actors own functions.
Here is an example:
actor MyActor {
nonisolated(unsafe) private var streamTask: Task<Void, Never>?
init() {
Task {
await setupStream()
}
}
deinit {
streamTask?.cancel()
}
private func test() {
print("test")
}
private func setupStream() {
streamTask = Task { [weak self] in
let stream = AsyncThrowingStream {
try await Task.sleep(for: .seconds(1))
return Int.random(in: 0...100)
}
do {
for try await value in stream {
await self?.test() // since this task is created in the actor's context, why do I have to await this?
}
} catch {
await streamTask?.cancel() // same here
}
}
}
}
There isn't currently a way to explicitly make a closure isolated to a particular actor. However, even if there was, the isolation analysis is not prepared to handle a weak reference to the isolated actor, because in some sense this changes the isolation of the function.
Ah... i didnt realize it was the weak self that was causing this.
In my case, this stream would actually last the lifetime of the application, and there will only ever be one instance of this actor. So maybe its fine to strongly capture self.
There is a pitch for closure isolation control that includes the ability to declare an explicit isolated capture. It didn't make 6.0, but we're still pursuing it.