"""Global hardware-access gate. Every command that touches the SAS bus — smartctl, sg_ses, zpool, ledctl — must run inside this gate. It serializes hardware I/O so the poller can never fire a thundering herd of subprocesses at fragile SAS expanders (which on some shelves drop out and suspend pools under concurrent load). Concurrency defaults to 1 (fully serialized). It can be raised via POLL_CONCURRENCY for healthier backplanes, but 1 is the safe default. The gate is a per-process asyncio.Semaphore — only the poller process calls hardware, so a per-process gate is sufficient. The semaphore is created lazily on first use so it binds to the running event loop. """ import asyncio import os _sem: asyncio.Semaphore | None = None def gate() -> asyncio.Semaphore: """Return the shared hardware semaphore (use as `async with gate():`).""" global _sem if _sem is None: n = max(1, int(os.environ.get("POLL_CONCURRENCY", "1"))) _sem = asyncio.Semaphore(n) return _sem