Skip to content

Fix PCM buffer thread safety with proper mutex#990

Merged
kblaschke merged 1 commit intomasterfrom
fix/pcm-thread-safety
Apr 10, 2026
Merged

Fix PCM buffer thread safety with proper mutex#990
kblaschke merged 1 commit intomasterfrom
fix/pcm-thread-safety

Conversation

@struktured
Copy link
Copy Markdown
Contributor

Summary

The PCM circular input buffer is written by the audio thread (via projectm_pcm_add_*) and read by the render thread (via RenderFrameUpdateFrameAudioDataCopyNewWaveformData). The previous design used std::atomic<size_t> m_start alone to coordinate, but this is insufficient — the buffer contents themselves are not atomic, so concurrent reads/writes are a data race in C++ and have been observed to cause audio corruption and crashes under sustained load.

This adds a std::mutex to protect the input buffer, locked in AddToBuffer (audio write path) and around CopyNewWaveformData in UpdateFrameAudioData (render read path). The lock is released before the heavier spectrum/FFT work, so the audio thread is only blocked for the duration of a sample copy (microseconds).

Changes

  • 2 files: src/libprojectM/Audio/PCM.{cpp,hpp} (+13, -5)
  • Add std::mutex m_pcmMutex to PCM
  • Lock in AddToBuffer and around CopyNewWaveformData reads
  • Drop std::atomic<size_t> m_start (now protected by the mutex) and remove unused <atomic> include

Design notes

A lock-free SPSC design was considered but rejected for this use case:

  • The reader copies the entire ring buffer each frame (not a trailing window), so simple release/acquire publication still races on the float values themselves
  • Per-sample std::atomic<float> would kill SIMD and roughly double memory
  • A seqlock would work but is significantly more complex than the bug warrants
  • The critical section here is microseconds; audio threads tolerate this fine for a visualizer (vs. pro-audio realtime contexts)

Test plan

  • Clean build on Linux (GCC, RelWithDebInfo)
  • Runtime test on NVIDIA RTX 3090, KDE Wayland — sustained PipeWire input, no audio corruption
  • No regressions in beat detection / spectrum response

Copy link
Copy Markdown
Member

@kblaschke kblaschke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the low-priority, but recently more frequently reported issues to be fixed. LGTM, will merge.

The circular audio buffer was protected only by a std::atomic on the
write index, which is insufficient: a multi-sample AddToBuffer() call
can be interrupted mid-write by UpdateFrameAudioData() reading the
buffer, producing torn waveform data.

Replace the atomic index with a std::mutex that protects both the
buffer writes in AddToBuffer() and the bulk copy in
UpdateFrameAudioData(). The lock is held only for the buffer access,
not for the downstream spectrum analysis.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kblaschke kblaschke force-pushed the fix/pcm-thread-safety branch from 6d00417 to 16f40af Compare April 10, 2026 23:06
@kblaschke kblaschke merged commit 16f40af into master Apr 10, 2026
41 checks passed
@kblaschke kblaschke deleted the fix/pcm-thread-safety branch April 10, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants