Embed RNS cleanly: fix signal-thread crash, tame atexit/stdout, persist on stop

Root cause of setup failure: Reticulum.__init__ calls signal.signal() with no
main-thread guard, but we (correctly) initialise off the event loop in an
executor thread, where signal.signal() raises ValueError. It failed after
setting the __instance singleton, which both broke setup and caused the
"Attempt to reinitialise Reticulum" error on every retry.

- Temporarily neutralise signal.signal during RNS init so init completes in the
  executor, and so RNS does not hijack HA's SIGINT/SIGTERM (needed for clean
  shutdown under Kubernetes).
- Unregister RNS's and LXMF's atexit exit handlers, which otherwise persist
  state with blocking file I/O on the event-loop thread and detach HA's
  stdout/stderr at interpreter exit (the loop-blocking warnings seen in logs).
- Persist RNS/LXMF state ourselves off-loop on the homeassistant_stop event.

Documents the remaining non-daemon RNS worker threads as a known shutdown note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude
2026-07-22 18:46:06 +03:00
parent f75235f9df
commit a80f65c442
4 changed files with 95 additions and 17 deletions

View File

@@ -9,7 +9,7 @@ from typing import Any
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@@ -121,6 +121,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
)
# Persist RNS/LXMF state off-loop when Home Assistant stops (we unregister
# RNS's own atexit handlers, which would otherwise do this with blocking I/O
# on the event loop and detach HA's stdout).
async def _on_ha_stop(_event) -> None:
await manager.async_persist()
runtime.unsubs.append(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _on_ha_stop)
)
entry.async_on_unload(entry.add_update_listener(_async_options_updated))
_async_register_services(hass)