Skip to content

v1.5.0 - Robustness & Performance Overhaul

Choose a tag to compare

@giang17 giang17 released this 05 Apr 16:26
· 14 commits to master since this release

Summary

Major robustness and performance release with 14 fixes across all priority levels. Replaces the Sleep-polling architecture with event-driven signaling, eliminates mutex usage in the JACK realtime thread, and fixes multiple memory leaks and crash scenarios.

Tested: Reaper + Serum2 + Pianoteq9 at 64 samples / 48kHz — zero xruns in Docker with RT scheduling.

Critical

  • Lock-free atomics in JACK realtime thread — replaces pthread_mutex to eliminate priority inversion and xruns
  • eventfd-based signaling — replaces Sleep(1) polling with poll(eventfd), reducing latency jitter from ~1ms to microseconds and eliminating ~1000 unnecessary syscalls/sec
  • JACK shutdown callback — graceful handling when JACK server crashes instead of deadlocking
  • Init loop fix — clear stale JACK activation flags in asio_start() to prevent host reset loop

High

  • Memory leak fix — pe_audio_buffers freed in DisposeBuffers() (leaked on every sample rate change)
  • Race condition fix — host_driver_state marked volatile for cross-thread visibility
  • Null-pointer guards — abort on jack_port_register() failure instead of segfault in RT callback
  • Crash guard — safe path extraction in configure_driver() for paths without extension/backslash
  • Init error leak fix — jack_input/output_ports freed in all callback registration error paths

Medium

  • fork() safety — replaced vfork() with fork() + SA_NOCLDWAIT to prevent deadlocks and zombie processes
  • mlockall removed — eliminated mlockall(MCL_FUTURE) that locked ~107MB for the entire process; JACK handles RT memory locking internally
  • 64-byte aligned buffers — _aligned_malloc(64) matches JACK buffer alignment for optimal SSE/AVX memcpy

Low

  • ASIO spec compliance — removed double buffer-switch priming in Start() that violated the ASIO specification
  • 64-bit sample position — fixed GetSamplePosition() high word (was hardcoded 0, overflowed after ~25h at 48kHz)
  • Thread creator mutex — protected global jack_thread_creator_privates against concurrent initialization

Docker Note

For RT audio in Docker, add ulimits to your docker-compose.yml:

ulimits:
  rtprio:
    soft: 95
    hard: 95
  memlock:
    soft: -1
    hard: -1