From ce82207d030e007b775660640e203f8b08b02603 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 18 Jun 2026 17:14:35 -0400 Subject: [PATCH] fix: clean stale JACK SHM before client creation in profile handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jack.Client('pi-multifx') fails with server_error (0x21) when stale jack_sem.0_default_pi-multifx segments remain in /dev/shm from a previous client instance. This commonly happens after a profile change (buffer+rate) because: 1. stop_jack() kills jackd and cleans SHM 2. start_jack() starts new JACK server 3. Old process's JackAudioClient.__del__ (garbage collection) recreates the pi-multifx semaphore in /dev/shm 4. jack_client.start() → jack.Client('pi-multifx') finds stale SHM and fails with server_error → no pi-multifx ports → NAM never called → 0% nam_cpu → 'nam is dying' Fix: - Clean /dev/shm/jack* in profile handler right before jack_client.start() - JackAudioClient.start() also retries once with SHM cleanup on server_error, so other callers are also protected. --- src/system/audio.py | 19 ++++++++++++++++++- src/web/server.py | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/system/audio.py b/src/system/audio.py index 65fe328..d91d567 100644 --- a/src/system/audio.py +++ b/src/system/audio.py @@ -882,7 +882,24 @@ class JackAudioClient: self._client = jack.Client(self._client_name) except jack.JackOpenError as exc: logger.error("Cannot open JACK client: %s", exc) - return + # Stale SHM from a previous client instance can cause + # server_error (0x21). Clean and retry once. + if "server_error" in str(exc) or "0x21" in str(exc): + try: + from pathlib import Path + import shutil + for p in Path("/dev/shm").glob("jack*"): + if p.is_dir(): + shutil.rmtree(p, ignore_errors=True) + else: + p.unlink(missing_ok=True) + logger.info("Cleaned stale JACK SHM, retrying client creation...") + self._client = jack.Client(self._client_name) + except Exception as retry_exc: + logger.error("JACK client retry also failed: %s", retry_exc) + return + else: + return # Register capture ports self._inports.clear() diff --git a/src/web/server.py b/src/web/server.py index b0a6e6e..a43115f 100644 --- a/src/web/server.py +++ b/src/web/server.py @@ -1748,6 +1748,20 @@ class WebServer: target_profile["rate"], ) + # Clean stale client SHM before creating new JACK client. + # The old process's jack.Client __del__ can recreate the + # pi-multifx semaphore after stop_jack() cleaned it. + try: + from pathlib import Path + import shutil + for p in Path("/dev/shm").glob("jack*"): + if p.is_dir(): + shutil.rmtree(p, ignore_errors=True) + else: + p.unlink(missing_ok=True) + except Exception: + pass + # Restart JACK audio client if jack_client: try: