cleanClients() (timer thread) called close()/stopAndWait() and
disconnectClient() (read thread) did the same while holding
map_mutex, while client read threads wait for the same lock in
registerServer/registerClient/disconnectClient — a guaranteed
deadlock (the -w watchdog aborts exactly this). disconnectClient
could also wait for its own read thread (self-wait in
waitThreadedReadFinished) and the destructor never stopped the timer
or drained the clients, leaving dangling lambda captures of this.
Restructure around a single drainer (the timer thread):
- cleanClients: under map_mutex only mark (move rmrf_* out, mark
ping-timeout/orphaned clients); after the unlock close
(stopAndWait) and delete the connections, then delete the servers
- disconnectClient: only mark under the lock, then request a
non-blocking stop() on the affected connections; the drainer waits
and deletes (no self-wait, nothing blocks while holding the lock)
- newConnection: start the read thread after releasing the lock
- ~DispatcherServer: stop watchdog/timer first, then close and delete
all clients before the servers (raw-pointer order) and eth
The watchdog is now a PIThread member so the destructor can stop it;
setMaxConnections takes map_mutex like its readers.
in almost all methods removed timeouts in milliseconds, replaced to PISystemTime
PITimer rewrite, remove internal impl, now only thread implementation, API similar to PIThread
PITimer API no longer pass void*
PIPeer, PIConnection improved stability on reinit and exit
PISystemTime new methods
pisd now exit without hanging