actor life cycles seem simple enough on the surface… you set things up in the init
and then kill them off in a Task
launched from the deinit
...
right now i have a ConnectionManager
that starts up child/nested/sub-tasks on itself every now and then:
actor ConnectionManager
{
...
}
extension ConnectionManager
{
// called whenever there is something new to monitor.
nonisolated
func monitor(_ host:Host)
{
let _:Task<Void, Never> = .init
{
await self.monitor(host)
}
}
// loops forever on an `AsyncStream`. the `ConnectionManager`
// keeps a copy of its continuation, so it can terminate them
// when needed.
private
func monitor(_ host:Host) async
{
...
}
}
since the sub-tasks run on self
, it’s not possible to get them to stop from ConnectionManager
’s own deinit
, so there is a larger object SessionPool
that sends the ‘stop iteration’ signals to ConnectionManager
on SessionPool
’s deinit
:
actor SessionPool
{
nonisolated
let connectionManager:ConnectionManager
deinit
{
let _:Task<Void, Never> = .init
{
[connectionManager] in
await connectionManager.stopAllMonitors()
}
}
}
but the Task
s created by ConnectionManager
are unstructured tasks, so there is no way to know for sure when they have all completed, only that they have been put in a state where they are no longer looping forever.
and now a problem i have discovered is that these unstructured tasks can outlive the SessionPool
for a while, which means when i try to create a new SessionPool
after the previous one died, sometimes it fails because the old connections are still lingering around, and the SessionPool
lifecycle loop needs to sleep for a second or two before the previous generation of Task
s finally dies off.
i thought about setting up a TaskGroup
that waits on an AsyncStream
that ConnectionManager
yields to in order to request a ConnectionManager.monitor(_:)
task to be scheduled, but sometimes the ConnectionManager.monitor(_:)
tasks can die on their own, and that means the TaskGroup
would just accumulate child tasks forever that never get awaited on until the SessionManager.deinit
tells the task group to shut down.
any ideas?