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 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-15 23:50:21 +01:00
co-authored by Claude Opus 4.8
parent d9c78a5956
commit 0c67897448
+13 -2
View File
@@ -209,9 +209,20 @@ internal sealed class AsioCaptureBackend : ICaptureBackend
{ {
if (asio is not null) 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.AudioAvailable -= OnAudioAvailable; } catch { /* ignore */ }
try { asio.Stop(); } catch { /* ignore */ } // Let any ASIO buffer callback already in flight finish before we stop/release the driver, so
try { asio.Dispose(); } catch { /* ignore */ } // 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; asio = null;
} }
uptime.Stop(); uptime.Stop();