From 0c6789744879568a9c8d109bc5a9dd4f699d23b7 Mon Sep 17 00:00:00 2001 From: Ednunp <29843396+Ednunp@users.noreply.github.com> Date: Wed, 15 Jul 2026 23:50:21 +0100 Subject: [PATCH] ASIO close: step-by-step logging + drain before release (Audient crash) The 'set ASIO driver to none => app dies completely' crash is a NATIVE crash inside the driver's own Stop/Dispose (Audient), which bypasses every managed try/catch and leaves no crash file. Two changes to pin it down and reduce it: - Log each close step (unhook / stop / release / released) so the next crash's log ends right after the native call that died, naming it exactly. - Sleep 60ms after unhooking the callback, before Stop/Dispose, so the native close isn't racing an in-flight ASIO buffer callback (a common crash trigger). Diagnostic + mitigation; the underlying native driver crash needs the Audient hardware to confirm. Gate 37/37. Co-Authored-By: Claude Opus 4.8 --- src/RemSound.Sender/AsioCaptureBackend.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/RemSound.Sender/AsioCaptureBackend.cs b/src/RemSound.Sender/AsioCaptureBackend.cs index 8292bfe..b22aaff 100644 --- a/src/RemSound.Sender/AsioCaptureBackend.cs +++ b/src/RemSound.Sender/AsioCaptureBackend.cs @@ -209,9 +209,20 @@ internal sealed class AsioCaptureBackend : ICaptureBackend { if (asio is not null) { + // Step-by-step logging: closing some drivers (notably Audient) can crash NATIVELY inside their + // own Stop/Dispose — a native access violation blows past these try/catch blocks and kills the + // process with no managed stack (why the ASIO->none crash leaves no crash file). Logging each + // step means the log ends right AFTER the line for whichever native call died, pinpointing it. + onDiagnostic?.Invoke("asio close: unhooking callback"); try { asio.AudioAvailable -= OnAudioAvailable; } catch { /* ignore */ } - try { asio.Stop(); } catch { /* ignore */ } - try { asio.Dispose(); } catch { /* ignore */ } + // Let any ASIO buffer callback already in flight finish before we stop/release the driver, so + // the native close isn't racing a live callback (a common trigger for the crash). + System.Threading.Thread.Sleep(60); + onDiagnostic?.Invoke("asio close: stopping stream"); + try { asio.Stop(); } catch (Exception ex) { onDiagnostic?.Invoke($"asio close: stop threw {ex.GetType().Name}: {ex.Message}"); } + onDiagnostic?.Invoke("asio close: releasing driver (dispose)"); + try { asio.Dispose(); } catch (Exception ex) { onDiagnostic?.Invoke($"asio close: dispose threw {ex.GetType().Name}: {ex.Message}"); } + onDiagnostic?.Invoke("asio close: driver released cleanly"); asio = null; } uptime.Stop();