ecf1e86464
Overlapping InitializeAsync calls (the actor's fire-and-forget init plus a retry tick) could leak an opened PLC connection and orphan a probe loop: no gate guarded the lifecycle, only _gate for I/O. - _lifecycleGate (the Modbus pattern) serialises Initialize / Reinitialize / Shutdown / Dispose; teardown waits a bounded time, then tears down anyway. - Every initialize and teardown starts a lifecycle generation. Health writes (init, read/write passes, poll errors, shutdown) and the init publish carry it and are dropped once it has moved on, so an abandoned init never publishes over a later shutdown and a shutdown's final write never clobbers a newer init. A superseded init disposes what it opened and throws. - The init publish (options, tag tables, Plc, probe loop) happens under _gate; the tag tables are built fresh and swapped by one reference write instead of being cleared in place under live poll passes. - Read/write passes re-check initialization under _gate, so a pass that waited out a teardown cannot reopen a connection on a shut-down driver. - The semaphores are no longer disposed: Dispose can race a running shutdown. Refs #742 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT