Replace the mutex-guarded callback model of peer.Conn with a per-peer
event loop that exclusively owns all mutable connection state. External
callers and transport workers post typed events into a non-blocking,
coalescing mailbox instead of contending on conn.mu:
- offers/answers coalesce to the newest message, a new offer flushes
queued candidates of the superseded session
- candidates are applied in arrival order from a bounded FIFO
- transport state changes are never dropped
- the blocking relay dial runs on a helper goroutine with a single dial
in flight; signaling I/O (offer/answer sends) runs off the loop
conn.mu now only guards the open/close lifecycle. Close posts a close
event and waits for the loop teardown; the loop also tears down on
engine context cancellation and releases resources of unprocessed
events.
Delete the Handshaker listener machinery (Listen loop, unbuffered
drop-on-busy channels, AsyncOfferListener with its double-processing of
the first offer), the never-wired dispatcher package and the unused
ICEMonitor.ReconnectCh. Fix a goroutine leak in the WG watcher test
that raced with tests mutating the package-level check timing vars.
When an ICE agent connection was in progress, new offers were being ignored. This was incorrect logic because the remote agent could be restarted at any time.
In this change, whenever a new session ID is received, the ongoing handshake is closed and a new one is started.