core: Allow clean shutdown of the core
Relying on things to happen while the core is being destroyed is
fragile (and does not work in practice), because events are no
longer delivered to objects that are scheduled for deletion.
Add an explicit call for shutting down the core, its sessions and
their networks. Wait for everything to be cleaned up properly before
quitting the session threads, and ultimately shutting down the main
event loop.
While we're at it, refactor SessionThread to follow the worker
pattern, rather than inheriting from QThread. This avoids accidentally
calling slots in the wrong thread. Ensure that all cross-thread
communication happens through queued signals. Simplify the API, too.