Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WaveShaperNode seems to cause memory corruption while upsampling and subsequently SEGFAULTs #202

Open
phildremi opened this issue Oct 17, 2023 · 35 comments
Assignees
Labels

Comments

@phildremi
Copy link
Contributor

Hello! I've been trying to make this library work for quite some time, but there's a tiny issue preventing me from using it.

Symptoms

In general, the errors I've been facing appear somewhat erratic and usually end up in a crash sooner or later (...usually sooner).

  • Audio playback is garbled on all three of my Linux systems (Kali/WSL, LinuxMint/native, ArchLinux/native)
  • In WSL, running the LabSoundExample crashes with a "double free detected" error
  • On the other systems, it also crashes with a standard SEGFAULT after writing nan values to the buffer
  • While inspecting the data structures, one can observe magic values for uninitialized memory (0xbadf00d, etc.)
  • Even on Windows (with MSYS), memory corruption can be seen in the sampler's kernel buffers

All of this is manifesting in the WaverShaperNode and related classes (UpSampler, DownSampler, DirectConvolver).

Affected Platforms

I've observed variations of this problem on all of the platforms that I have access to:

  • Windows 10 (MSYS)
  • Windows 10 (MSVC)
  • Linux (WSL)
  • Linux (native)
  • Mac OS X

It's curious that audio playback isn't garbled on Windows and Mac OS. I think that's because nan is written only on Linux.

Root Cause Analysis

It looks as though the AudioArray buffers are freed multiple times. However, commenting out all of the free calls simply moves the problem around; the faults are still happening and gdb blames the unique_ptr trying to delete this = 0x00 (i.e., nullptr).

I noticed some TODO: mutex comments. However, adding mutexes to those critical sections didn't help. I've tried all sorts of other random things, like changing audio backends or replacing the AudioArray with std::vector, and oversampling arrays with unique_ptr. One area I haven't investigated much yet is the potential for multithreading issues (as they're generally painful).

Logs

"Double free detected" (Kali Linux inside WSL VM)
fade in
fade in
playing
playing
B i -1 curve[0] -1.000000 curve[44099] 0.999973 oversample none
B i 0 curve[0] -1.000000 curve[44099] 0.999985 oversample none
B i 1 curve[0] -1.000000 curve[44099] 0.999994 oversample none
B i 2 curve[0] -1.000000 curve[44099] 1.000000 oversample none
B i -2 curve[0] -1.000000 curve[44099] 0.999955 oversample 2x
DefaultKernelSize: 128
m_kernel: 0x7ffff270c6e0
m_kernel.size(): 128
m_kernel.data(): 0x7fffc0002ee0
DefaultKernelSize: 128
m_kernel: 0x7ffff270c6e0
free(): double free detected in tcache 2

Thread 3 "LabSoundExample" received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff270d6c0 (LWP 1724)]
__pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
44      ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
#1  0x00007ffff7a9f15f in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2  0x00007ffff7a51472 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x00007ffff7a3b4b2 in __GI_abort () at ./stdlib/abort.c:79
#4  0x00007ffff7a3c1ed in __libc_message (fmt=fmt@entry=0x7ffff7bae78c "%s\n") at ../sysdeps/posix/libc_fatal.c:150
#5  0x00007ffff7aa8a75 in malloc_printerr (str=str@entry=0x7ffff7bb1598 "free(): double free detected in tcache 2") at ./malloc/malloc.c:5658
#6  0x00007ffff7aaacf6 in _int_free (av=0x7fffc0000030, p=0x7fffc0002ed0, have_lock=have_lock@entry=0) at ./malloc/malloc.c:4466
#7  0x00007ffff7aad16f in __GI___libc_free (mem=<optimized out>) at ./malloc/malloc.c:3367
#8  0x000055555559ec41 in lab::AudioArray<float>::~AudioArray() ()
#9  0x0000555555646970 in lab::UpSampler::process(float const*, float*, unsigned long) ()
#10 0x000055555560676c in lab::WaveShaperNode::processCurve2x(float const*, float*, int) ()
#11 0x0000555555606c4f in lab::WaveShaperNode::process(lab::ContextRenderLock&, int) ()
#12 0x00005555555d6325 in lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) ()
#13 0x00005555555ded83 in lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#14 0x00005555555de031 in lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#15 0x00005555555d1fee in lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) ()
#16 0x00005555555d2418 in lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) ()
#17 0x0000555555683ebd in lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) ()
#18 0x000055555568299c in lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) ()
#19 0x0000555555686ca2 in RtApiPulse::callbackEvent() ()
#20 0x0000555555686960 in pulseaudio_callback(void*) ()
#21 0x00007ffff7a9d3ec in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#22 0x00007ffff7b1da2c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
Uninitialized heap memory (MSYS/Windows)
(gdb) info registers
rax            0xfeeefeeefeeefeee  -76843841185972498
rbx            0x0                 0
rcx            0xfeeefeeefeeefeee  -76843841185972498
rdx            0x260f930           39909680
rsi            0xf9d700            16373504
rdi            0x25c79c0           39614912
rbp            0x2f6f730           0x2f6f730
rsp            0x2f6f6d0           0x2f6f6d0
r8             0x2692be0           40446944
r9             0x80                128
r10            0x1                 1
r11            0x89e73e6ba0        592290147232
r12            0x2                 2
r13            0x1                 1
r14            0x0                 0
r15            0x0                 0
rip            0x7ff7a29ea0f9      0x7ff7a29ea0f9 <lab::UpSampler::process(float const*, float*, unsigned long long)+31>
eflags         0x10202             [ IF RF ]
cs             0x33                51
ss             0x2b                43
ds             0x2b                43
es             0x2b                43
fs             0x53                83
gs             0x2b                43
(gdb) info locals
isInputBlockSizeGood = false
__FUNCTION__ = "process"
isTempBufferGood = false
isKernelGood = false
halfSize = 140701563352584
isInputBufferGood = false
inputP = 0x7ff7a2b3eff8 <std::unique_ptr<lab::AudioArray<float>, std::default_delete<lab::AudioArray<float> > >::operator->() const+24>
oddSamplesP = 0x2e8f730
(gdb) print m_kernel
Cannot access memory at address 0xfeeefeeefeeefef6
Potential stack corruption (also MSYS)
Thread 6 received signal SIGSEGV, Segmentation fault.
[Switching to Thread 22448.0x4d10]
0x00007ff63c4a293d in lab::DownSampler::process(float const*, float*, unsigned long long) ()
(gdb) bt
#0  0x00007ff63c4a293d in lab::DownSampler::process(float const*, float*, unsigned long long) ()
#1  0x00007ff63c48246b in lab::WaveShaperNode::processCurve4x(float const*, float*, int) ()
#2  0x00007ff63c4827e1 in lab::WaveShaperNode::process(lab::ContextRenderLock&, int) ()
#3  0x00007ff63c46dbb3 in lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) ()
#4  0x00007ff63c4702e3 in lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#5  0x00007ff63c46f886 in lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#6  0x00007ff63c46ac19 in lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) ()
#7  0x00007ff63c46b022 in lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) ()
#8  0x00007ff63c4bff71 in lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) ()
#9  0x00007ff63c4beb1a in lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) ()
#10 0x00007ff63c4c4760 in RtApiWasapi::wasapiThread() ()
#11 0x00007ff63c4c332d in RtApiWasapi::runWasapiThread(void*) ()
#12 0x00007ffc2ee97344 in KERNEL32!BaseThreadInitThunk () from /c/Windows/System32/KERNEL32.DLL
#13 0x00007ffc30dc26b1 in ntdll!RtlUserThreadStart () from /c/Windows/SYSTEM32/ntdll.dll
#14 0x0000000000000000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)

There's many other log dumps that I could provide, but they all show pretty much the same thing in slightly different colors.

Next Steps

I've tried poking at these crashes from many angles, but I don't know the codebase (nor audio programming) well. Any ideas?

@meshula
Copy link
Member

meshula commented Oct 18, 2023

Hi, this is very helpful. I haven't seen this under MSVC, but maybe I can repro by running a bunch of times. I think I can see where the problem lies from your traces, so thanks for that legwork.

I'm traveling for a week, so can't dig in right now, but will take a look when I am back.

@meshula meshula added the bug label Oct 18, 2023
@meshula meshula self-assigned this Oct 18, 2023
@phildremi
Copy link
Contributor Author

The problem isn't always observable under MSVC, but it's still there. I found that crashes become almost guaranteed when adding some debug output to WaveShaperNode::processCurve, for example, but that's only because the buffers have already been corrupted at that point. On Linux, this is where nan values are read, causing the audio to deteriorate even before a SEGFAULT.

I don't have a proper Visual Studio setup for debugging MSVC, but if you share your suspicions I could test on Linux/macOS.

@meshula
Copy link
Member

meshula commented Nov 11, 2023

As a first pass, could you apply this patch? This provides better guards on accessing the array's data member, and also makes the WaveShaperNode temporary buffers member objects instead of unique_ptr objects. I haven't been able to detect a deletion of the float array from process(). Does this, as you mentioned, move the problem elsewhere?

From 488f2401c8c9a80be0bdc0c069ce97da8f634511 Mon Sep 17 00:00:00 2001
From: Nick Porcino <meshula@hotmail.com>
Date: Sat, 11 Nov 2023 11:07:34 -0800
Subject: [PATCH] Don't use a unique pointer to the temp buffers in
 WaveShaperNode

---
 examples/src/ExamplesMain.cpp      |  4 +--
 include/LabSound/core/AudioArray.h | 42 +++++++++++++++++++++---------
 src/core/WaveShaperNode.cpp        | 14 +++++-----
 3 files changed, 38 insertions(+), 22 deletions(-)

diff --git a/examples/src/ExamplesMain.cpp b/examples/src/ExamplesMain.cpp
index 7b5f1c4a..fa60af19 100755
--- a/examples/src/ExamplesMain.cpp
+++ b/examples/src/ExamplesMain.cpp
@@ -37,7 +37,7 @@ int main(int argc, char *argv[]) try
     
     Example examples[] = {
 
-        { Passing::pass, Skip::yes,  new ex_devices(context, NoInput) },
+        { Passing::pass, Skip::yes, new ex_devices(context, NoInput) },
         { Passing::pass, Skip::yes, new ex_play_file(context, NoInput) },
         { Passing::pass, Skip::yes, new ex_simple(context, NoInput) },
         { Passing::pass, Skip::yes, new ex_osc_pop(context, NoInput) },
@@ -59,7 +59,7 @@ int main(int argc, char *argv[]) try
         { Passing::pass, Skip::yes, new ex_granulation_node(context, NoInput) }, // note: node is under development
         { Passing::pass, Skip::yes, new ex_poly_blep(context, NoInput) },
         { Passing::fail, Skip::yes, new ex_split_merge(context, NoInput) },
-        { Passing::fail, Skip::no, new ex_waveshaper(context, NoInput)},
+        { Passing::fail, Skip::no,  new ex_waveshaper(context, NoInput)},
     };
 
     static constexpr int iterations = 1;
diff --git a/include/LabSound/core/AudioArray.h b/include/LabSound/core/AudioArray.h
index 92e0d3cb..c2c77abf 100644
--- a/include/LabSound/core/AudioArray.h
+++ b/include/LabSound/core/AudioArray.h
@@ -17,21 +17,27 @@ class AudioArray
     T * _allocation = nullptr;
     T * _data = nullptr;
     int _size = 0;
+    T _safety;
 
 public:
-    AudioArray() = default;
+    explicit AudioArray()
+    : _allocation(nullptr)
+    , _data(nullptr)
+    , _size(0)
+    , _safety(0) {}
+    
     explicit AudioArray(int n)
     : _allocation(nullptr)
     , _data(nullptr)
-    , _size(-1)
+    , _size(0)
+    , _safety(0)
     {
         allocate(n);
     }
 
     ~AudioArray()
     {
-        if (_allocation)
-            free(_allocation);
+        free(_allocation);
     }
 
     // allocation will realloc if necessary.
@@ -43,19 +49,18 @@ public:
         const uintptr_t mask = ~0xf;
         size_t initialSize = sizeof(T) * n + alignment;
         if (_size != n) {
-            if (_allocation)
-                free(_allocation);
+            free(_allocation);
 
-            if (n) {
+            if (n > 0) {
                 _allocation = static_cast<T*>(calloc(initialSize, 1));
                 _data = (T*)((((intptr_t)_allocation) + (alignment-1)) & mask);
+                _size = n;
             }
             else {
                 _allocation = nullptr;
                 _data = nullptr;
+                _size = 0;
             }
-
-            _size = n;
         }
     }
 
@@ -65,29 +70,40 @@ public:
     // size in samples, not bytes
     int size() const { return _size; }
 
-    T & operator[](size_t i) { return data()[i]; }
+    T & operator[](size_t i) {
+        if (_data)
+            return _data[i];
+        return _safety;
+    }
 
     void zero()
     {
-        memset(data(), 0, sizeof(T) * size());
+        if (_data)
+            memset(_data, 0, sizeof(T) * size());
     }
 
     void zeroRange(unsigned start, unsigned end)
     {
+        if (_data == nullptr || _size <= 0)
+            return;
+        
         bool isSafe = (start <= end) && (end <= (unsigned) size());
         if (!isSafe)
             return;
 
-        memset(data() + start, 0, sizeof(T) * (end - start));
+        memset(_data + start, 0, sizeof(T) * (end - start));
     }
 
     void copyToRange(const T * sourceData, unsigned start, unsigned end)
     {
+        if (_data == nullptr || _size <= 0)
+            return;
+
         bool isSafe = (start <= end) && (end <= (unsigned) size());
         if (!isSafe)
             return;
 
-        memcpy(data() + start, sourceData, sizeof(T) * (end - start));
+        memcpy(_data + start, sourceData, sizeof(T) * (end - start));
     }
 
 };
diff --git a/src/core/WaveShaperNode.cpp b/src/core/WaveShaperNode.cpp
index 066effd4..0fa90ce4 100644
--- a/src/core/WaveShaperNode.cpp
+++ b/src/core/WaveShaperNode.cpp
@@ -17,8 +17,8 @@
 namespace lab {
 struct OverSamplingArrays
 {
-    std::unique_ptr<AudioFloatArray> m_tempBuffer;
-    std::unique_ptr<AudioFloatArray> m_tempBuffer2;
+    AudioFloatArray m_tempBuffer;
+    AudioFloatArray m_tempBuffer2;
     std::unique_ptr<UpSampler> m_upSampler;
     std::unique_ptr<DownSampler> m_downSampler;
     std::unique_ptr<UpSampler> m_upSampler2;
@@ -28,8 +28,8 @@ static void* createOversamplingArrays()
 {
     struct OverSamplingArrays * osa = new struct OverSamplingArrays;
     int renderQuantumSize = 128;  // from https://www.w3.org/TR/webaudio/#render-quantum-size
-    osa->m_tempBuffer = std::make_unique<AudioFloatArray>(renderQuantumSize * 2);
-    osa->m_tempBuffer2 = std::make_unique<AudioFloatArray>(renderQuantumSize * 4);
+    osa->m_tempBuffer.allocate(renderQuantumSize * 2);
+    osa->m_tempBuffer2.allocate(renderQuantumSize * 4);
     osa->m_upSampler = std::make_unique<UpSampler>(renderQuantumSize);
     osa->m_downSampler = std::make_unique<DownSampler>(renderQuantumSize * 2);
     osa->m_upSampler2 = std::make_unique<UpSampler>(renderQuantumSize * 2);
@@ -114,7 +114,7 @@ void WaveShaperNode::processCurve(const float* source, float* destination, int f
 void WaveShaperNode::processCurve2x(const float * source, float * destination, int framesToProcess)
 {
     struct OverSamplingArrays * osa = (struct OverSamplingArrays *) m_oversamplingArrays;
-    float * tempP = osa->m_tempBuffer->data();
+    float * tempP = osa->m_tempBuffer.data();
 
     osa->m_upSampler->process(source, tempP, framesToProcess);
 
@@ -127,8 +127,8 @@ void WaveShaperNode::processCurve4x(const float * source, float * destination, i
 {
     struct OverSamplingArrays * osa = (struct OverSamplingArrays *) m_oversamplingArrays;
 
-    float * tempP = osa->m_tempBuffer->data();
-    float * tempP2 = osa->m_tempBuffer2->data();
+    float * tempP = osa->m_tempBuffer.data();
+    float * tempP2 = osa->m_tempBuffer2.data();
 
     osa->m_upSampler->process(source, tempP, framesToProcess);
     osa->m_upSampler2->process(tempP, tempP2, framesToProcess * 2);
-- 
2.39.3 (Apple Git-145)

@meshula
Copy link
Member

meshula commented Nov 11, 2023

And here's a further patch to mutex setting the curve.

From 9975447ab9aa87b36b94befe0a0ffef42f1619e8 Mon Sep 17 00:00:00 2001
From: Nick Porcino <meshula@hotmail.com>
Date: Sat, 11 Nov 2023 11:59:14 -0800
Subject: [PATCH] Add mutex for set curve on wave shaper node

---
 include/LabSound/core/WaveShaperNode.h | 11 ++++----
 src/core/WaveShaperNode.cpp            | 37 +++++++++-----------------
 2 files changed, 18 insertions(+), 30 deletions(-)

diff --git a/include/LabSound/core/WaveShaperNode.h b/include/LabSound/core/WaveShaperNode.h
index bb2ec563..4cbb5909 100644
--- a/include/LabSound/core/WaveShaperNode.h
+++ b/include/LabSound/core/WaveShaperNode.h
@@ -6,9 +6,7 @@
 #define WaveShaperNode_h
 
 #include "LabSound/core/AudioNode.h"
-//#include <vector>
-//#include "../src/internal/UpSampler.h" //ouch..how to hide internal from calling application?
-//#include "../src/internal/DownSampler.h"
+#include <mutex>
 
 namespace lab {
 enum OverSampleType
@@ -38,15 +36,18 @@ public:
 
     // AudioNode
     virtual void process(ContextRenderLock &, int bufferSize) override;
-    virtual void reset(ContextRenderLock&) override {}
+    virtual void reset(ContextRenderLock &) override {}
 
 protected:
     void processCurve(const float * source, float * destination, int framesToProcess);
     virtual double tailTime(ContextRenderLock& r) const override { return 0.; }
     virtual double latencyTime(ContextRenderLock& r) const override { return 0.; }
 
+    std::mutex _curveMutex;
+    
     std::vector<float> m_curve;
-    std::vector<float>* m_newCurve = nullptr;
+    std::vector<float> m_newCurve;
+    bool _newCurveReady = false;
 
     // Oversampling.
     void * m_oversamplingArrays = nullptr;
diff --git a/src/core/WaveShaperNode.cpp b/src/core/WaveShaperNode.cpp
index 0fa90ce4..ccf2292f 100644
--- a/src/core/WaveShaperNode.cpp
+++ b/src/core/WaveShaperNode.cpp
@@ -9,7 +9,6 @@
 #include "internal/Assertions.h"
 #include <algorithm>
 #include <memory>
-#include <mutex>
 #include <vector>
 #include "internal/UpSampler.h" //ouch..how to hide internal from calling application?
 #include "internal/DownSampler.h"
@@ -60,26 +59,14 @@ WaveShaperNode::WaveShaperNode(AudioContext & ac, AudioNodeDescriptor const & de
 
 WaveShaperNode::~WaveShaperNode()
 {
-    if (m_newCurve)
-    {
-        // @TODO mutex
-        delete m_newCurve;
-    }
-    if (m_oversamplingArrays) delete m_oversamplingArrays;
+    delete (OverSamplingArrays*) m_oversamplingArrays;
 }
 
 void WaveShaperNode::setCurve(std::vector<float> & curve)
 {
-    std::vector<float>* new_curve = new std::vector<float>();
-    *new_curve = curve;
-    if (m_newCurve)
-    {
-        // @TODO mutex
-        std::vector<float>* x = nullptr;
-        std::swap(x, m_newCurve);
-        delete x;
-    }
-    m_newCurve = new_curve;
+    std::lock_guard<std::mutex> lock(_curveMutex);
+    m_newCurve = curve;
+    _newCurveReady = true;
 }
 
 void WaveShaperNode::processCurve(const float* source, float* destination, int framesToProcess)
@@ -113,7 +100,7 @@ void WaveShaperNode::processCurve(const float* source, float* destination, int f
 // https://github.com/WebKit/WebKit/blob/main/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.cpp
 void WaveShaperNode::processCurve2x(const float * source, float * destination, int framesToProcess)
 {
-    struct OverSamplingArrays * osa = (struct OverSamplingArrays *) m_oversamplingArrays;
+    OverSamplingArrays * osa = (OverSamplingArrays *) m_oversamplingArrays;
     float * tempP = osa->m_tempBuffer.data();
 
     osa->m_upSampler->process(source, tempP, framesToProcess);
@@ -123,9 +110,10 @@ void WaveShaperNode::processCurve2x(const float * source, float * destination, i
 
     osa->m_downSampler->process(tempP, destination, framesToProcess * 2);
 }
+
 void WaveShaperNode::processCurve4x(const float * source, float * destination, int framesToProcess)
 {
-    struct OverSamplingArrays * osa = (struct OverSamplingArrays *) m_oversamplingArrays;
+    OverSamplingArrays * osa = (OverSamplingArrays *) m_oversamplingArrays;
 
     float * tempP = osa->m_tempBuffer.data();
     float * tempP2 = osa->m_tempBuffer2.data();
@@ -142,13 +130,12 @@ void WaveShaperNode::processCurve4x(const float * source, float * destination, i
 
 void WaveShaperNode::process(ContextRenderLock & r, int bufferSize)
 {
-    if (m_newCurve)
+    if (_newCurveReady)
     {
-        // @TODO mutex
-        std::vector<float>* x = nullptr;
-        std::swap(x, m_newCurve);
-        m_curve = *x;
-        delete x;
+        // this could cause a pop, but setting a curve should be extremely rare
+        std::lock_guard<std::mutex> lock(_curveMutex);
+        std::swap(m_curve, m_newCurve);
+        _newCurveReady = false;
     }
 
     AudioBus* destinationBus = output(0)->bus(r);
-- 
2.39.3 (Apple Git-145)

@meshula
Copy link
Member

meshula commented Nov 11, 2023

... I've pushed the patches to main as it makes things cleaner overall, and if you have a chance to test, could you LMK if you still get the memory corruption please?

@phildremi
Copy link
Contributor Author

... I've pushed the patches to main as it makes things cleaner overall, and if you have a chance to test, could you LMK if you still get the memory corruption please?

Tested with 9975447:

  • Sporadic SEGFAULTs under MSVC can still be observed
  • I also once got a failed assertion with MSVC (DownSampler.cpp:83 - isInputBlockSizeGood)
  • Using WSL, the audio is still broken, even if a SEGFAULT seems to be harder to reproduce in gdb
  • Crashes do also still happen in WSL (more rarely), but I suspect gdb can't necessarily identify the right location

Based on the above, I assume the corruption is still happening. Would need more debugging and testing on other machines to see how the problem manifests there. I also had audio drop immediately after End UpdateGraphThread - very strange...


I haven't been able to detect a deletion of the float array from process()

Maybe that was poorly explained on my part; inside process I first observed that nan was being read on Linux, which I suspect causes the garbled audio. But this is likely just a symptom of the buffer being corrupted, so I haven't investigated further yet.


More WSL logs below. Unfortunately, they may be red herrings if the corruption results from a threading-related issue:

(gdb) bt
#0  0x00005555555dede9 in lab::AudioNodeOutput::bus(lab::ContextRenderLock&) const ()
#1  0x00005555555d6c83 in lab::AudioNode::unsilenceOutputs(lab::ContextRenderLock&) ()
#2  0x00005555555d674a in lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) ()
#3  0x00005555555ded81 in lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#4  0x00005555555de02f in lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#5  0x00005555555d1fec in lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) ()
#6  0x00005555555d2416 in lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) ()
#7  0x0000555555683da7 in lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) ()
#8  0x0000555555682886 in lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) ()
#9  0x0000555555686b8c in RtApiPulse::callbackEvent() ()
#10 0x000055555568684a in pulseaudio_callback(void*) ()
#11 0x00007ffff7a9d3ec in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#12 0x00007ffff7b1da2c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
Thread 3 "LabSoundExample" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff270d6c0 (LWP 1344)]
0x000055555563855d in lab::DownSampler::process(float const*, float*, unsigned long) ()
(gdb) bt
#0  0x000055555563855d in lab::DownSampler::process(float const*, float*, unsigned long) ()
#1  0x0000555555606892 in lab::WaveShaperNode::processCurve4x(float const*, float*, int) ()
#2  0x0000555555606c20 in lab::WaveShaperNode::process(lab::ContextRenderLock&, int) ()
#3  0x00005555555d6323 in lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) ()
#4  0x00005555555ded81 in lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#5  0x00005555555de02f in lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) ()
#6  0x00005555555d1fec in lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) ()
#7  0x00005555555d2416 in lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) ()
#8  0x0000555555683da7 in lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) ()
#9  0x0000555555682886 in lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) ()
#10 0x0000555555686b8c in RtApiPulse::callbackEvent() ()
#11 0x000055555568684a in pulseaudio_callback(void*) ()
#12 0x00007ffff7a9d3ec in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#13 0x00007ffff7b1da2c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

@phildremi
Copy link
Contributor Author

phildremi commented Nov 11, 2023

FWIW, running the example with -fsanitize=thread -fno-omit-frame-pointer yields some warnings.

I don't have time to look into these (or do testing on other machines) right now, but it might be worth trying tsan/usan/asan.

@meshula
Copy link
Member

meshula commented Nov 12, 2023

Thanks, much appreciated.

@meshula
Copy link
Member

meshula commented Nov 12, 2023

I notice that UpSampler has this code

    for (unsigned i = 0; i < sourceFramesToProcess; ++i)
        destP[i * 2 + 1] = oddSamplesP[i];

and the destination buffers are allocated only to sourceFramesToProcess * 2.

that maybe needs to be sourceFramesToProcess - 1...

@phildremi
Copy link
Contributor Author

Back to testing on native Linux now. The corruption is definitely still there. Presumably, it's the same problem. Symptoms:

  • SEGFAULT in AudioContext::currentTime() because _destinationNode is nullptr (I added an early exit as a workaround)
  • Again a failed assertion DownSampler.cpp:83 - isInputBlockSizeGood - same as the one observed under MSVC
  • In DownSampler::process, another SEGFAULT after the sourceP and m_reducedKernel have become invalid
  • SEGFAULT in AudioChannel::clearSilentFlag because this is pointing to invalid memory (?)
  • Another where m_kernel in the UpSampler has become corrupted
  • One where curveData in the WaveShaperNode points to invalid memory
  • Sometimes, the source and destination pointers in WaveShaperNode::process2x/4x are exactly the same
  • I've once observed the std::vector memory being corrupted before swapping curves in here, too

These are separate runs. Crashes are still sporadic, and audio remains garbled. I'll try running the sanitizers (with GCC) next...

@meshula
Copy link
Member

meshula commented Nov 12, 2023

Is the crash at the end of the demo? I just got repro due to deleting the ShaperNode while it was still processing in the audio thread.

Disconnecting the node before deleting it should prevent that:

        for (int i = -2; i < 3; i++)
        {
            // std::vector<float> curve = makeDistortionCurve(100.0f * i);
            makeDistortionCurveA(curve, n_samples, .25f * i + .5f);
            printf("A i %d curve[0] %f curve[44099] %f oversample none\n", i, curve[0], curve[44099]);
            waveshaper->setCurve(curve);
            Wait(delay_time_ms);
        }
        
        // ensure the waveshaper node is not being processed before deleting it
        ac.disconnect(ac.destinationNode());
        ac.synchronizeConnections();
    }

If this works for you, I would not consider it a fix, but a workaround. Deleting running nodes should Just Work.

@meshula
Copy link
Member

meshula commented Nov 12, 2023

When you say "audio is garbled" do you mean, all the time for all demos? Or sporadically, with regards to the WaveShaperNode demo?

@phildremi
Copy link
Contributor Author

phildremi commented Nov 12, 2023

Is the crash at the end of the demo? I just got repro due to deleting the ShaperNode while it was still processing in the audio thread.

Crashes seem to appear at "random" times, so not necessarily (or at least not exclusively) at the end of the demo.

Disconnecting the node before deleting it should prevent that:
...
If this works for you, I would not consider it a fix, but a workaround. Deleting running nodes should Just Work.

Making this change might at best reduce the frequency of SEGFAULTs, but unfortunately it doesn't eliminate them.

Afterwards, I still got a crash originating from what looks to be a synchronization issue. TSAN snapshot (last 2 warnings):

Running LabSoundExample with ThreadSanitizer enabled (excerpt)
==================
WARNING: ThreadSanitizer: data race (pid=27797)
  Write of size 8 at 0x7b640002fd00 by main thread:
    #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:740 (libtsan.so.2+0x3c7bf) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 lab::AudioArray<float>::~AudioArray() <null> (LabSoundExample+0x4e456) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 lab::WaveShaperNode::~WaveShaperNode() LabSound/src/core/WaveShaperNode.cpp:62 (LabSoundExample+0xe4fb0) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 void std::_Destroy<lab::WaveShaperNode>(lab::WaveShaperNode*) <null> (LabSoundExample+0x61e6e) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

  Previous write of size 4 at 0x7b640002fd00 by thread T2 (mutexes: write M0):
    #0 lab::UpSampler::process(float const*, float*, unsigned long) LabSound/src/internal/src/UpSampler.cpp:107 (LabSoundExample+0x144365) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #1 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:121 (LabSoundExample+0xe54f4) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:185 (LabSoundExample+0xe5a08) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:546 (LabSoundExample+0x9ba01) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #4 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0xa946d) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #5 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0xa7faa) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #6 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:77 (LabSoundExample+0x94eeb) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #7 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:136 (LabSoundExample+0x954b7) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #8 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:317 (LabSoundExample+0x1a1dc1) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #9 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:28 (LabSoundExample+0x19fd42) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #10 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:9420 (LabSoundExample+0x1a6d31) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #11 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:9341 (LabSoundExample+0x1a6748) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

  Mutex M0 (0x7b50000004b0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4481 (libtsan.so.2+0x4ea9f) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 __gthread_mutex_lock(pthread_mutex_t*) <null> (LabSoundExample+0x31c22) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:136 (LabSoundExample+0x954b7) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:317 (LabSoundExample+0x1a1dc1) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #4 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:28 (LabSoundExample+0x19fd42) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #5 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:9420 (LabSoundExample+0x1a6d31) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #6 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:9341 (LabSoundExample+0x1a6748) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

  Thread T2 (tid=27805, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036 (libtsan.so.2+0x3d179) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 RtApiPulse::probeDeviceOpen(unsigned int, RtApi::StreamMode, unsigned int, unsigned int, unsigned int, unsigned long, unsigned int*, RtAudio::StreamOptions*) LabSound/src/backends/RtAudio/RtAudio.cpp:9806 (LabSoundExample+0x1a8f01) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 RtApi::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:404 (LabSoundExample+0x1a5e0a) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 RtAudio::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:299 (LabSoundExample+0x1a5830) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #4 lab::AudioDevice_RtAudio::createContext() LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:167 (LabSoundExample+0x1a0fde) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #5 lab::AudioDevice_RtAudio::AudioDevice_RtAudio(lab::AudioStreamConfig const&, lab::AudioStreamConfig const&) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:188 (LabSoundExample+0x1a126f) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #6 main <null> (LabSoundExample+0x30730) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

SUMMARY: ThreadSanitizer: data race (LabSound/build/bin/LabSoundExample+0x4e456) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d) in lab::AudioArray<float>::~AudioArray()
==================
==================
WARNING: ThreadSanitizer: data race (pid=27797)
  Write of size 8 at 0x7b1400007808 by main thread:
    #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.2+0x886d6) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 lab::WaveShaperNode::~WaveShaperNode() LabSound/src/core/WaveShaperNode.cpp:62 (LabSoundExample+0xe4fbd) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 void std::_Destroy<lab::WaveShaperNode>(lab::WaveShaperNode*) <null> (LabSoundExample+0x61e6e) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

  Previous read of size 8 at 0x7b1400007808 by thread T2 (mutexes: write M0):
    #0 lab::AudioArray<float>::data() LabSound/include/LabSound/core/AudioArray.h:67 (LabSoundExample+0x6688a) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #1 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:118 (LabSoundExample+0xe54b3) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:185 (LabSoundExample+0xe5a08) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:546 (LabSoundExample+0x9ba01) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #4 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0xa946d) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #5 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0xa7faa) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #6 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:77 (LabSoundExample+0x94eeb) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #7 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:136 (LabSoundExample+0x954b7) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #8 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:317 (LabSoundExample+0x1a1dc1) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #9 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:28 (LabSoundExample+0x19fd42) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #10 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:9420 (LabSoundExample+0x1a6d31) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #11 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:9341 (LabSoundExample+0x1a6748) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

  Mutex M0 (0x7b50000004b0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4481 (libtsan.so.2+0x4ea9f) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 __gthread_mutex_lock(pthread_mutex_t*) <null> (LabSoundExample+0x31c22) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:136 (LabSoundExample+0x954b7) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:317 (LabSoundExample+0x1a1dc1) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #4 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:28 (LabSoundExample+0x19fd42) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #5 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:9420 (LabSoundExample+0x1a6d31) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #6 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:9341 (LabSoundExample+0x1a6748) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

  Thread T2 (tid=27805, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036 (libtsan.so.2+0x3d179) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 RtApiPulse::probeDeviceOpen(unsigned int, RtApi::StreamMode, unsigned int, unsigned int, unsigned int, unsigned long, unsigned int*, RtAudio::StreamOptions*) LabSound/src/backends/RtAudio/RtAudio.cpp:9806 (LabSoundExample+0x1a8f01) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #2 RtApi::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:404 (LabSoundExample+0x1a5e0a) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #3 RtAudio::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:299 (LabSoundExample+0x1a5830) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #4 lab::AudioDevice_RtAudio::createContext() LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:167 (LabSoundExample+0x1a0fde) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #5 lab::AudioDevice_RtAudio::AudioDevice_RtAudio(lab::AudioStreamConfig const&, lab::AudioStreamConfig const&) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:188 (LabSoundExample+0x1a126f) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)
    #6 main <null> (LabSoundExample+0x30730) (BuildId: d4b1fbdf637e46db9d450db0abc960d37681e85d)

SUMMARY: ThreadSanitizer: data race ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 in operator delete(void*, unsigned long)
==================
ThreadSanitizer:DEADLYSIGNAL
==27797==ERROR: ThreadSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55801f36b133 bp 0x7fd3985fe010 sp 0x7fd3985fdfa0 T27805)
==27797==The signal is caused by a READ memory access.
==27797==Hint: address points to the zero page.

This does look like the WaveShaperNode being deleted while sampling is still in process, though. The full log (2800 lines) is here.


When you say "audio is garbled" do you mean, all the time for all demos? Or sporadically, with regards to the WaveShaperNode demo?

It's always garbled (on Linux only. Windows and macOS don't have this problem). Tested with ex_play_file, ex_waveshaper, and even a completely separate "hello world" application that reads WAV, MP3 etc. and plays it back. Might be a separate issue?

I haven't looked into this more since it may resolve itself after the crashes are gone, but there were nan in the audio buffer.

@phildremi
Copy link
Contributor Author

I pulled in the latest changes (3ba7b49, previously on 9975447. SEGFAULT and garbled audio still observable. Additionally, the WaveShaperNode example now fails this assertion: LabSound/src/core/AudioNode.cpp:426 - _self->m_channelCount != 0

@meshula
Copy link
Member

meshula commented Nov 12, 2023

I pushed a fix for m_channelCount.

The full TSAN report you posted shows the creation of two pulse audio threads, and that they are frequently racing.

RtAudio creates two pulse Audio threads in the case that the input device and the output device have different id's.

I see this at the beginning of the log

10:20:45 INFO  LabSound/include/LabSound/core/AudioDevice.h:104: AudioHardwareDeviceNode() 
	* Sample Rate:     48000.000000 
	* Input Channels:  1 
	* Output Channels: 2  

The differing number of channels suggests to me that we are getting two threads due to this detail that two devices are involved.

To test a theory that there is a problem when launching to pulse audio threads, could you modify this line in ExamplesMain, which will prevent the second thread from being launched:

int main(int argc, char *argv[]) try
{
...    
    AudioStreamConfig _inputConfig;
    AudioStreamConfig _outputConfig;
    auto config = GetDefaultAudioDeviceConfiguration(false); // only create an output thread

@meshula
Copy link
Member

meshula commented Nov 12, 2023

We are using RtAudio 5.1.0. I notice the following in the 5.2.0 release notes. Perhaps we are bumping against the issues they have fixed in 5.2.0, as my current theory is that we are getting two devices when we actually only want one. I don't notice any related changes in the 6.0 release.

Release 5.2.0
see git history for complete list of changes
update to audioprobe.cpp to list devices for all APIs
miscellaneous build system updates
PulseAudio device detection fixes
various WASAPI updates (thanks to Marcus Tomlinson)

@meshula
Copy link
Member

meshula commented Nov 12, 2023

Pushed an update to 5.2.0, but not 6, as 6 has many API changes.

@meshula
Copy link
Member

meshula commented Nov 12, 2023

I'm now able to reproduce the data race locally! This should help things along :)

@phildremi
Copy link
Contributor Author

The good news is that the RtAudio upgrade seems to have resolved the "garbled audio" (Linux-only) issue, as far as I can tell.

Still on Linux, there are a few remaining problems that may be due to the corruption/data race:

  • Running ex_waveshaper, there appears a "buzzing" sound once reaching oversample2x (it remains for the rest of the test)
  • SEGFAULTs still appear, which is to be expected since a PulseAudio issue shouldn't cause crashes on MSVC/macOS
  • Occasionally, the audio drops out completely at the start of the test - can't say why exactly yet as there's no crash or error

Finally, I noticed that the debug output seems to suggest that the same thread is playing back the AudioNode twice:

(I added std::this_thread::get_id() to see if multiple threads were accessing the same node)

Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
B i -2 curve[0] -1.000000 curve[44099] 0.999955 oversample none
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
Thread ID: 139853194852096 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
B i -1 curve[0] -1.000000 curve[44099] 0.999973 oversample none
B i 0 curve[0] -1.000000 curve[44099] 0.999985 oversample none
B i 1 curve[0] -1.000000 curve[44099] 0.999994 oversample none
... 

This strikes me as rather peculiar to say the least. It's also visible in the other logs. Might be intentional, though?

@meshula
Copy link
Member

meshula commented Nov 12, 2023

i don't hear the buzzing. The -1 I introduced in the "odd samples" write could be the cause...

I'm working on the race first, the new info is also interesting, thanks

@meshula
Copy link
Member

meshula commented Nov 12, 2023

It's a relief and reassuring that it was possible to deduce a possible RtAudio initialization issue, and upgrading an RtAudio that included Pulse Audio initialization fixes resolved the garbled audio.

@meshula
Copy link
Member

meshula commented Nov 12, 2023

There is an unfortunate race in the RtAudio code, even the new one. I've corrected it using a std::atomic flag so that the RtAudio callback doesn't attempt to do processing before the RtAudio set up is complete. The upshot of this is that the temporary RtAudio context created to enumerate devices can conflict with the RtAudio device created to play back audio. Although I corrected the race, I also changed the RtAudio object itself to be a global singleton, since clearly RtAudio was not written with the idea that there might be more than one RtAudio object instantiated.

I corrected all the runtime races I encountered in device set up, and in the WaveShaperNode.

There is still a race when the WaveShaperNode is deleted, my workaround doesn't work, so I will continue looking at that.

This much, I hope, will fix all the non-shutdown related tsan issues you are encountering, and also I hope, it will resolve the "playing twice" issue you noticed. Playing twice would definitely cause garbling or crackling in many nodes that expect that they will not be invoked twice per render quantum.

Fixes to this point (not including a Shaper node deletion fix) are now pushed.

@meshula
Copy link
Member

meshula commented Nov 12, 2023

If the fix to RtAudio improves things for you, I will send a pull request upstream to the RtAudio project (or rather, I will check if it has been fixed in RtAudio 6, and take it from there).

@meshula
Copy link
Member

meshula commented Nov 12, 2023

pushed fix for race on wave shaper node deletion.

down to one race when the destination audio node is deleted on demo shutdown, and the rtaudio callback is still running.

@meshula
Copy link
Member

meshula commented Nov 13, 2023

pushed fix to RtAudio shutdown races, and all the ones in LabSound I could find.

Next step is to investigate if RtAudio 6 has corrected the races.

@meshula
Copy link
Member

meshula commented Nov 13, 2023

RtAudio 6 has introduced mutexes on mac which is more heavy-handed than my fixes. RtAudio 6 hasn't got all the races fixed. Not going to upgrade to 6 unless someone points to an urgent need, since I'd rather not spend time fixing 6...

@phildremi
Copy link
Contributor Author

phildremi commented Nov 13, 2023

Thanks for your effort! It seems there are significantly fewer data races, though some must still exist. Remaining issues:

  • Oversampling introduces the "buzzing sound" still, as previously mentioned
  • Playback seems to sometimes start before UpdateGraphThread finished, causing audio to drop out (see logs below)
  • I got one crash with pure virtual method called and terminate called without an active exception = another race?
  • The output still shows Scheduler: fade in and Scheduler: playing twice, from the same thread... same as before

Here's some more logs that might help (all while on 565cfde):

Firstly, this is one run where the audio dropped for the entire test
New Thread 0x7ffff31d3700 (LWP 57295)]
[New Thread 0x7ffff29d2700 (LWP 57296)]
[New Thread 0x7ffff21d1700 (LWP 57297)]
08:39:10 TRACE LabSound/src/core/AudioContext.cpp:534: Begin UpdateGraphThread
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
B i -2 curve[0] -1.000000 curve[44099] 0.999955 oversample none
08:39:10 TRACE LabSound/src/core/AudioContext.cpp:583: End UpdateGraphThread
// Short "bleeping" sound here, then audio is dead (and remains dead for the entirety of the run)
[Thread 0x7ffff21d1700 (LWP 57297) exited]
B i -1 curve[0] -1.000000 curve[44099] 0.999973 oversample none
B i 0 curve[0] -1.000000 curve[44099] 0.999985 oversample none
B i 1 curve[0] -1.000000 curve[44099] 0.999994 oversample none
B i 2 curve[0] -1.000000 curve[44099] 1.000000 oversample none
For comparison, this is a "normal" run where audio playback works as expected
New Thread 0x7ffff31d3700 (LWP 57555)]
[New Thread 0x7ffff29d2700 (LWP 57556)]
[New Thread 0x7ffff21d1700 (LWP 57557)]
11:44:39 TRACE LabSound/src/core/AudioContext.cpp:534: Begin UpdateGraphThread
B i -2 curve[0] -1.000000 curve[44099] 0.999955 oversample none
11:44:39 TRACE LabSound/src/core/AudioContext.cpp:583: End UpdateGraphThread
[Thread 0x7ffff21d1700 (LWP 57557) exited]
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:160
Scheduler: fade in
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
Thread ID: 140737263773440 - File: LabSound/src/core/AudioNode.cpp:183
Scheduler: playing
B i -1 curve[0] -1.000000 curve[44099] 0.999973 oversample none
B i 0 curve[0] -1.000000 curve[44099] 0.999985 oversample none
B i 1 curve[0] -1.000000 curve[44099] 0.999994 oversample none

Lastly, here are the full TSAN logs for dropout/no dropout/the virtual method crash: f34e1fede723dc9565cf382783544775


If the fix to RtAudio improves things for you, I will send a pull request upstream to the RtAudio project (or rather, I will check if it has been fixed in RtAudio 6, and take it from there).

It's a bit difficult to determine whether things improved as the crashes have been sporadic:

  • I'm running stress tests to find them, which sometimes do SEGFAULT (or otherwise crash) still
  • When manually running in gdb, I can't seem to get any though - so no additional backtraces, for now
  • The most common problem is audio dropouts, followed by the occasional crash while running the test script

I'd say it definitely feels like there's a lot less actual crashing now. However, clearly there are still some concurrency issues left.

@phildremi
Copy link
Contributor Author

Just observed another crash, this time with MSVC:

A i 0 curve[0] -0.342995 curve[44099] 0.342989 oversample none
A i 1 curve[0] -0.344421 curve[44099] 0.344417 oversample none
A i 2 curve[0] -0.345305 curve[44099] 0.345301 oversample none
15:33:06 TRACE LabSound__LabSound\src\core\AudioContext.cpp:170: Begin AudioContext::~AudioContext()
15:33:06 TRACE LabSound__LabSound\src\core\AudioContext.cpp:262: AudioContext::uninitialize()
15:33:06 INFO  LabSound__LabSound\src\core\AudioContext.cpp:195: Finish AudioContext::~AudioContext()
D:\a\_work\1\s\src\vctools\crt\github\stl\src\mutex.cpp(61): mutex destroyed while busy

This happened after the example completed, with the same duplicate playback/buzzing problem as on Linux.

@meshula
Copy link
Member

meshula commented Nov 13, 2023

Thanks for the update.

Since you mention having an issue with an MSVC build, that gives me an avenue to see if I can get the buzzing repro'd. I have a bunch of windows machines here.

I'm running stress tests to find them, which sometimes do SEGFAULT (or otherwise crash) still

How are you doing this? If you have a command line or script to share, I can start doing the same thing here.

@phildremi
Copy link
Contributor Author

How are you doing this? If you have a command line or script to share, I can start doing the same thing here.

Just running the program in a loop. The current script is here - but beware, the windows versions are completely untested.

The last few SEGFAULTs appeared after 52 and 66 iterations, so it can take quite a while. Still seems to be the same problem:

23:16:11 INFO  LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:59: using rtaudio api linux_pulse
23:16:11 INFO  LabSound/include/LabSound/core/AudioDevice.h:104: AudioHardwareDeviceNode() 
	* Sample Rate:     48000.000000 
	* Input Channels:  0 
	* Output Channels: 2   
23:16:11 INFO  LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:128: using output device idx: 0
23:16:11 INFO  LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:130: using output device name: Family 17h (Models 10h-1fh) HD Audio Controller Analog Stereo
23:16:11 INFO  LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:135: using input device idx: -1
23:16:11 TRACE LabSound/src/core/AudioContext.cpp:534: Begin UpdateGraphThread
23:16:11 TRACE LabSound/src/core/AudioContext.cpp:583: End UpdateGraphThread
23:16:27 TRACE LabSound/src/core/AudioContext.cpp:170: corrupted double-linked list
Begin AudioContext::~AudioContext()
// Crashed here, although there may be buffered output that wasn't flushed

@meshula
Copy link
Member

meshula commented Nov 14, 2023

Cool. ExamplesMain also has an iteration count for this purpose (

static constexpr int iterations = 1;
) If bumping the iterations there to a 100 results in a crash it might make it easier to get tsan logs.

@phildremi
Copy link
Contributor Author

Huh... I actually didn't see that, but it wouldn't catch crashes in the setup/teardown part of the example program. Speaking of which, the remaining SEGFAULTs might be due to the "deleted node is still playing" problem you mentioned earlier. TSAN log:

Use-after-free when deleting the WaveShaperNode while it's still playing
WARNING: ThreadSanitizer: heap-use-after-free (pid=21829)
  Write of size 8 at 0x7b7400030c00 by thread T2 (mutexes: write M0):
    #0 memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:899 (libtsan.so.2+0x42769) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:891 (libtsan.so.2+0x42769)
    #2 memcpy /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34 (LabSoundExample+0x15c874) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 lab::DirectConvolver::process(lab::AudioArray<float>*, float const*, float*, unsigned long) LabSound/src/internal/src/DirectConvolver.cpp:350 (LabSoundExample+0x15c874)
    #4 lab::UpSampler::process(float const*, float*, unsigned long) LabSound/src/internal/src/UpSampler.cpp:111 (LabSoundExample+0x126d16) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:131 (LabSoundExample+0xcfaf7) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:188 (LabSoundExample+0xd0791) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:547 (LabSoundExample+0x8cda6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0x96d0c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0x95ba6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #10 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:71 (LabSoundExample+0x882a0) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #11 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #12 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #13 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #14 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #15 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Previous write of size 8 at 0x7b7400030c00 by main thread:
    #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:740 (libtsan.so.2+0x3c7bf) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 lab::AudioArray<float>::~AudioArray() LabSound/include/LabSound/core/AudioArray.h:40 (LabSoundExample+0xd1a07) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 lab::DirectConvolver::~DirectConvolver() LabSound/src/internal/DirectConvolver.h:39 (LabSoundExample+0xd1a07)
    #3 lab::UpSampler::~UpSampler() LabSound/src/internal/UpSampler.h:41 (LabSoundExample+0xd1a07)
    #4 void std::_Destroy<lab::UpSampler>(lab::UpSampler*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0xd1a07)
    #5 void std::allocator_traits<std::allocator<void> >::destroy<lab::UpSampler>(std::allocator<void>&, lab::UpSampler*) /usr/include/c++/13/bits/alloc_traits.h:674 (LabSoundExample+0xd1a07)
    #6 std::_Sp_counted_ptr_inplace<lab::UpSampler, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/include/c++/13/bits/shared_ptr_base.h:613 (LabSoundExample+0xd1a07)
    #7 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:175 (LabSoundExample+0xceb9f) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/13/bits/shared_ptr_base.h:361 (LabSoundExample+0xceb9f)
    #9 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/13/bits/shared_ptr_base.h:1071 (LabSoundExample+0xceb9f)
    #10 std::__shared_ptr<lab::UpSampler, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/13/bits/shared_ptr_base.h:1524 (LabSoundExample+0xceb9f)
    #11 std::shared_ptr<lab::UpSampler>::~shared_ptr() /usr/include/c++/13/bits/shared_ptr.h:175 (LabSoundExample+0xceb9f)
    #12 lab::OverSamplingArrays::~OverSamplingArrays() LabSound/src/core/WaveShaperNode.cpp:17 (LabSoundExample+0xceb9f)
    #13 lab::WaveShaperNode::~WaveShaperNode() LabSound/src/core/WaveShaperNode.cpp:62 (LabSoundExample+0xceb9f)
    #14 void std::_Destroy<lab::WaveShaperNode>(lab::WaveShaperNode*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0x3fd23) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #15 void std::allocator_traits<std::allocator<void> >::destroy<lab::WaveShaperNode>(std::allocator<void>&, lab::WaveShaperNode*) /usr/include/c++/13/bits/alloc_traits.h:674 (LabSoundExample+0x3fd23)
    #16 std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/include/c++/13/bits/shared_ptr_base.h:613 (LabSoundExample+0x3fd23)
    #17 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:175 (LabSoundExample+0x459dc) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #18 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/13/bits/shared_ptr_base.h:361 (LabSoundExample+0x459dc)
    #19 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/13/bits/shared_ptr_base.h:1071 (LabSoundExample+0x459dc)
    #20 std::__shared_ptr<lab::AudioNode, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/13/bits/shared_ptr_base.h:1524 (LabSoundExample+0x459dc)
    #21 std::shared_ptr<lab::AudioNode>::~shared_ptr() /usr/include/c++/13/bits/shared_ptr.h:175 (LabSoundExample+0x459dc)
    #22 void std::_Destroy<std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0x459dc)
    #23 void std::_Destroy_aux<false>::__destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:163 (LabSoundExample+0x459dc)
    #24 void std::_Destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:196 (LabSoundExample+0x459dc)
    #25 void std::_Destroy<std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*, std::allocator<std::shared_ptr<lab::AudioNode> >&) /usr/include/c++/13/bits/alloc_traits.h:947 (LabSoundExample+0x459dc)
    #26 std::vector<std::shared_ptr<lab::AudioNode>, std::allocator<std::shared_ptr<lab::AudioNode> > >::~vector() /usr/include/c++/13/bits/stl_vector.h:732 (LabSoundExample+0x459dc)
    #27 labsound_example::~labsound_example() LabSound/examples/src/ExamplesCommon.h:85 (LabSoundExample+0x459dc)
    #28 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #29 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7)
    #30 main LabSound/examples/src/ExamplesMain.cpp:75 (LabSoundExample+0x3b26b) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Mutex M0 (0x7b50000004b0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4481 (libtsan.so.2+0x4ea9f) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/13/bits/gthr-default.h:749 (LabSoundExample+0x87fca) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 std::mutex::lock() /usr/include/c++/13/bits/std_mutex.h:113 (LabSoundExample+0x87fca)
    #3 lab::ContextRenderLock::ContextRenderLock(lab::AudioContext*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) LabSound/include/LabSound/extended/AudioContextLock.h:70 (LabSoundExample+0x87fca)
    #4 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:41 (LabSoundExample+0x87fca)
    #5 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Thread T2 (tid=21832, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036 (libtsan.so.2+0x3d179) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 RtApiPulse::probeDeviceOpen(unsigned int, RtApi::StreamMode, unsigned int, unsigned int, unsigned int, unsigned long, unsigned int*, RtAudio::StreamOptions*) LabSound/src/backends/RtAudio/RtAudio.cpp:9304 (LabSoundExample+0x1708fb) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 RtApi::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:387 (LabSoundExample+0x1694ef) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 RtAudio::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:292 (LabSoundExample+0x1698a3) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioDevice_RtAudio::createContext() LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:175 (LabSoundExample+0x162206) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioDevice_RtAudio::AudioDevice_RtAudio(lab::AudioStreamConfig const&, lab::AudioStreamConfig const&) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:196 (LabSoundExample+0x1628e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 main LabSound/examples/src/ExamplesMain.cpp:29 (LabSoundExample+0x3a426) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

SUMMARY: ThreadSanitizer: heap-use-after-free /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34 in memcpy
==================
==================
WARNING: ThreadSanitizer: heap-use-after-free (pid=21829)
  Write of size 8 at 0x7b7400031600 by thread T2 (mutexes: write M0):
    #0 memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:899 (libtsan.so.2+0x42769) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:891 (libtsan.so.2+0x42769)
    #2 memcpy /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34 (LabSoundExample+0x126d7c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 lab::UpSampler::process(float const*, float*, unsigned long) LabSound/src/internal/src/UpSampler.cpp:117 (LabSoundExample+0x126d7c)
    #4 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:131 (LabSoundExample+0xcfaf7) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:188 (LabSoundExample+0xd0791) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:547 (LabSoundExample+0x8cda6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0x96d0c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0x95ba6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:71 (LabSoundExample+0x882a0) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #10 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #11 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #12 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #13 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #14 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Previous write of size 8 at 0x7b7400031600 by main thread:
    #0 free ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:740 (libtsan.so.2+0x3c7bf) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 lab::AudioArray<float>::~AudioArray() LabSound/include/LabSound/core/AudioArray.h:40 (LabSoundExample+0xd19e3) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 lab::UpSampler::~UpSampler() LabSound/src/internal/UpSampler.h:41 (LabSoundExample+0xd19e3)
    #3 void std::_Destroy<lab::UpSampler>(lab::UpSampler*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0xd19e3)
    #4 void std::allocator_traits<std::allocator<void> >::destroy<lab::UpSampler>(std::allocator<void>&, lab::UpSampler*) /usr/include/c++/13/bits/alloc_traits.h:674 (LabSoundExample+0xd19e3)
    #5 std::_Sp_counted_ptr_inplace<lab::UpSampler, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/include/c++/13/bits/shared_ptr_base.h:613 (LabSoundExample+0xd19e3)
    #6 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:175 (LabSoundExample+0xceb9f) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/13/bits/shared_ptr_base.h:361 (LabSoundExample+0xceb9f)
    #8 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/13/bits/shared_ptr_base.h:1071 (LabSoundExample+0xceb9f)
    #9 std::__shared_ptr<lab::UpSampler, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/13/bits/shared_ptr_base.h:1524 (LabSoundExample+0xceb9f)
    #10 std::shared_ptr<lab::UpSampler>::~shared_ptr() /usr/include/c++/13/bits/shared_ptr.h:175 (LabSoundExample+0xceb9f)
    #11 lab::OverSamplingArrays::~OverSamplingArrays() LabSound/src/core/WaveShaperNode.cpp:17 (LabSoundExample+0xceb9f)
    #12 lab::WaveShaperNode::~WaveShaperNode() LabSound/src/core/WaveShaperNode.cpp:62 (LabSoundExample+0xceb9f)
    #13 void std::_Destroy<lab::WaveShaperNode>(lab::WaveShaperNode*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0x3fd23) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #14 void std::allocator_traits<std::allocator<void> >::destroy<lab::WaveShaperNode>(std::allocator<void>&, lab::WaveShaperNode*) /usr/include/c++/13/bits/alloc_traits.h:674 (LabSoundExample+0x3fd23)
    #15 std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() /usr/include/c++/13/bits/shared_ptr_base.h:613 (LabSoundExample+0x3fd23)
    #16 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:175 (LabSoundExample+0x459dc) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #17 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/13/bits/shared_ptr_base.h:361 (LabSoundExample+0x459dc)
    #18 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/13/bits/shared_ptr_base.h:1071 (LabSoundExample+0x459dc)
    #19 std::__shared_ptr<lab::AudioNode, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/13/bits/shared_ptr_base.h:1524 (LabSoundExample+0x459dc)
    #20 std::shared_ptr<lab::AudioNode>::~shared_ptr() /usr/include/c++/13/bits/shared_ptr.h:175 (LabSoundExample+0x459dc)
    #21 void std::_Destroy<std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0x459dc)
    #22 void std::_Destroy_aux<false>::__destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:163 (LabSoundExample+0x459dc)
    #23 void std::_Destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:196 (LabSoundExample+0x459dc)
    #24 void std::_Destroy<std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*, std::allocator<std::shared_ptr<lab::AudioNode> >&) /usr/include/c++/13/bits/alloc_traits.h:947 (LabSoundExample+0x459dc)
    #25 std::vector<std::shared_ptr<lab::AudioNode>, std::allocator<std::shared_ptr<lab::AudioNode> > >::~vector() /usr/include/c++/13/bits/stl_vector.h:732 (LabSoundExample+0x459dc)
    #26 labsound_example::~labsound_example() LabSound/examples/src/ExamplesCommon.h:85 (LabSoundExample+0x459dc)
    #27 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #28 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7)
    #29 main LabSound/examples/src/ExamplesMain.cpp:75 (LabSoundExample+0x3b26b) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Mutex M0 (0x7b50000004b0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4481 (libtsan.so.2+0x4ea9f) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/13/bits/gthr-default.h:749 (LabSoundExample+0x87fca) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 std::mutex::lock() /usr/include/c++/13/bits/std_mutex.h:113 (LabSoundExample+0x87fca)
    #3 lab::ContextRenderLock::ContextRenderLock(lab::AudioContext*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) LabSound/include/LabSound/extended/AudioContextLock.h:70 (LabSoundExample+0x87fca)
    #4 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:41 (LabSoundExample+0x87fca)
    #5 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Thread T2 (tid=21832, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036 (libtsan.so.2+0x3d179) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 RtApiPulse::probeDeviceOpen(unsigned int, RtApi::StreamMode, unsigned int, unsigned int, unsigned int, unsigned long, unsigned int*, RtAudio::StreamOptions*) LabSound/src/backends/RtAudio/RtAudio.cpp:9304 (LabSoundExample+0x1708fb) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 RtApi::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:387 (LabSoundExample+0x1694ef) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 RtAudio::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:292 (LabSoundExample+0x1698a3) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioDevice_RtAudio::createContext() LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:175 (LabSoundExample+0x162206) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioDevice_RtAudio::AudioDevice_RtAudio(lab::AudioStreamConfig const&, lab::AudioStreamConfig const&) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:196 (LabSoundExample+0x1628e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 main LabSound/examples/src/ExamplesMain.cpp:29 (LabSoundExample+0x3a426) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

SUMMARY: ThreadSanitizer: heap-use-after-free /usr/include/x86_64-linux-gnu/bits/string_fortified.h:34 in memcpy
==================
==================
WARNING: ThreadSanitizer: heap-use-after-free (pid=21829)
  Read of size 8 at 0x7b2800000550 by thread T2 (mutexes: write M0):
    #0 std::vector<float, std::allocator<float> >::data() /usr/include/c++/13/bits/stl_vector.h:1258 (LabSoundExample+0xcf4c0) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #1 lab::WaveShaperNode::processCurve(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:74 (LabSoundExample+0xcf4c0)
    #2 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:134 (LabSoundExample+0xcfb10) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:188 (LabSoundExample+0xd0791) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:547 (LabSoundExample+0x8cda6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0x96d0c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0x95ba6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:71 (LabSoundExample+0x882a0) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #10 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #11 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #12 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Previous write of size 8 at 0x7b2800000550 by main thread:
    #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.2+0x886d6) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 std::__new_allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/13/bits/new_allocator.h:168 (LabSoundExample+0x412ce) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::deallocate(std::allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&, std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/13/bits/alloc_traits.h:516 (LabSoundExample+0x412ce)
    #3 std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::~__allocated_ptr() /usr/include/c++/13/bits/allocated_ptr.h:74 (LabSoundExample+0x412ce)
    #4 std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_destroy() /usr/include/c++/13/bits/shared_ptr_base.h:623 (LabSoundExample+0x412ce)
    #5 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:191 (LabSoundExample+0x45a30) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:172 (LabSoundExample+0x45a30)
    #7 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/13/bits/shared_ptr_base.h:361 (LabSoundExample+0x45a30)
    #8 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/13/bits/shared_ptr_base.h:1071 (LabSoundExample+0x45a30)
    #9 std::__shared_ptr<lab::AudioNode, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/13/bits/shared_ptr_base.h:1524 (LabSoundExample+0x45a30)
    #10 std::shared_ptr<lab::AudioNode>::~shared_ptr() /usr/include/c++/13/bits/shared_ptr.h:175 (LabSoundExample+0x45a30)
    #11 void std::_Destroy<std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0x45a30)
    #12 void std::_Destroy_aux<false>::__destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:163 (LabSoundExample+0x45a30)
    #13 void std::_Destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:196 (LabSoundExample+0x45a30)
    #14 void std::_Destroy<std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*, std::allocator<std::shared_ptr<lab::AudioNode> >&) /usr/include/c++/13/bits/alloc_traits.h:947 (LabSoundExample+0x45a30)
    #15 std::vector<std::shared_ptr<lab::AudioNode>, std::allocator<std::shared_ptr<lab::AudioNode> > >::~vector() /usr/include/c++/13/bits/stl_vector.h:732 (LabSoundExample+0x45a30)
    #16 labsound_example::~labsound_example() LabSound/examples/src/ExamplesCommon.h:85 (LabSoundExample+0x45a30)
    #17 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #18 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7)
    #19 main LabSound/examples/src/ExamplesMain.cpp:75 (LabSoundExample+0x3b26b) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Mutex M0 (0x7b50000004b0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4481 (libtsan.so.2+0x4ea9f) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/13/bits/gthr-default.h:749 (LabSoundExample+0x87fca) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 std::mutex::lock() /usr/include/c++/13/bits/std_mutex.h:113 (LabSoundExample+0x87fca)
    #3 lab::ContextRenderLock::ContextRenderLock(lab::AudioContext*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) LabSound/include/LabSound/extended/AudioContextLock.h:70 (LabSoundExample+0x87fca)
    #4 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:41 (LabSoundExample+0x87fca)
    #5 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Thread T2 (tid=21832, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036 (libtsan.so.2+0x3d179) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 RtApiPulse::probeDeviceOpen(unsigned int, RtApi::StreamMode, unsigned int, unsigned int, unsigned int, unsigned long, unsigned int*, RtAudio::StreamOptions*) LabSound/src/backends/RtAudio/RtAudio.cpp:9304 (LabSoundExample+0x1708fb) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 RtApi::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:387 (LabSoundExample+0x1694ef) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 RtAudio::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:292 (LabSoundExample+0x1698a3) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioDevice_RtAudio::createContext() LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:175 (LabSoundExample+0x162206) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioDevice_RtAudio::AudioDevice_RtAudio(lab::AudioStreamConfig const&, lab::AudioStreamConfig const&) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:196 (LabSoundExample+0x1628e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 main LabSound/examples/src/ExamplesMain.cpp:29 (LabSoundExample+0x3a426) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

SUMMARY: ThreadSanitizer: heap-use-after-free /usr/include/c++/13/bits/stl_vector.h:1258 in std::vector<float, std::allocator<float> >::data()
==================
==================
WARNING: ThreadSanitizer: heap-use-after-free (pid=21829)
  Read of size 8 at 0x7b2800000558 by thread T2 (mutexes: write M0):
    #0 std::vector<float, std::allocator<float> >::size() const /usr/include/c++/13/bits/stl_vector.h:990 (LabSoundExample+0xcf4cd) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #1 lab::WaveShaperNode::processCurve(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:75 (LabSoundExample+0xcf4cd)
    #2 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:134 (LabSoundExample+0xcfb10) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:188 (LabSoundExample+0xd0791) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:547 (LabSoundExample+0x8cda6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0x96d0c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0x95ba6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:71 (LabSoundExample+0x882a0) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #10 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #11 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #12 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Previous write of size 8 at 0x7b2800000558 by main thread:
    #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.2+0x886d6) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 std::__new_allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/13/bits/new_allocator.h:168 (LabSoundExample+0x412ce) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::deallocate(std::allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&, std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>*, unsigned long) /usr/include/c++/13/bits/alloc_traits.h:516 (LabSoundExample+0x412ce)
    #3 std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >::~__allocated_ptr() /usr/include/c++/13/bits/allocated_ptr.h:74 (LabSoundExample+0x412ce)
    #4 std::_Sp_counted_ptr_inplace<lab::WaveShaperNode, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_destroy() /usr/include/c++/13/bits/shared_ptr_base.h:623 (LabSoundExample+0x412ce)
    #5 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:191 (LabSoundExample+0x45a30) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use() /usr/include/c++/13/bits/shared_ptr_base.h:172 (LabSoundExample+0x45a30)
    #7 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/13/bits/shared_ptr_base.h:361 (LabSoundExample+0x45a30)
    #8 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/13/bits/shared_ptr_base.h:1071 (LabSoundExample+0x45a30)
    #9 std::__shared_ptr<lab::AudioNode, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/13/bits/shared_ptr_base.h:1524 (LabSoundExample+0x45a30)
    #10 std::shared_ptr<lab::AudioNode>::~shared_ptr() /usr/include/c++/13/bits/shared_ptr.h:175 (LabSoundExample+0x45a30)
    #11 void std::_Destroy<std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:151 (LabSoundExample+0x45a30)
    #12 void std::_Destroy_aux<false>::__destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:163 (LabSoundExample+0x45a30)
    #13 void std::_Destroy<std::shared_ptr<lab::AudioNode>*>(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*) /usr/include/c++/13/bits/stl_construct.h:196 (LabSoundExample+0x45a30)
    #14 void std::_Destroy<std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode> >(std::shared_ptr<lab::AudioNode>*, std::shared_ptr<lab::AudioNode>*, std::allocator<std::shared_ptr<lab::AudioNode> >&) /usr/include/c++/13/bits/alloc_traits.h:947 (LabSoundExample+0x45a30)
    #15 std::vector<std::shared_ptr<lab::AudioNode>, std::allocator<std::shared_ptr<lab::AudioNode> > >::~vector() /usr/include/c++/13/bits/stl_vector.h:732 (LabSoundExample+0x45a30)
    #16 labsound_example::~labsound_example() LabSound/examples/src/ExamplesCommon.h:85 (LabSoundExample+0x45a30)
    #17 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #18 ex_waveshaper::~ex_waveshaper() LabSound/examples/src/Examples.hpp:1686 (LabSoundExample+0x45bf7)
    #19 main LabSound/examples/src/ExamplesMain.cpp:75 (LabSoundExample+0x3b26b) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Mutex M0 (0x7b50000004b0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4481 (libtsan.so.2+0x4ea9f) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/13/bits/gthr-default.h:749 (LabSoundExample+0x87fca) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 std::mutex::lock() /usr/include/c++/13/bits/std_mutex.h:113 (LabSoundExample+0x87fca)
    #3 lab::ContextRenderLock::ContextRenderLock(lab::AudioContext*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) LabSound/include/LabSound/extended/AudioContextLock.h:70 (LabSoundExample+0x87fca)
    #4 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:41 (LabSoundExample+0x87fca)
    #5 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

  Thread T2 (tid=21832, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036 (libtsan.so.2+0x3d179) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #1 RtApiPulse::probeDeviceOpen(unsigned int, RtApi::StreamMode, unsigned int, unsigned int, unsigned int, unsigned long, unsigned int*, RtAudio::StreamOptions*) LabSound/src/backends/RtAudio/RtAudio.cpp:9304 (LabSoundExample+0x1708fb) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 RtApi::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:387 (LabSoundExample+0x1694ef) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 RtAudio::openStream(RtAudio::StreamParameters*, RtAudio::StreamParameters*, unsigned long, unsigned int, unsigned int*, int (*)(void*, void*, unsigned int, double, unsigned int, void*), void*, RtAudio::StreamOptions*, void (*)(RtAudioError::Type, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)) LabSound/src/backends/RtAudio/RtAudio.cpp:292 (LabSoundExample+0x1698a3) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioDevice_RtAudio::createContext() LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:175 (LabSoundExample+0x162206) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioDevice_RtAudio::AudioDevice_RtAudio(lab::AudioStreamConfig const&, lab::AudioStreamConfig const&) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:196 (LabSoundExample+0x1628e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 main LabSound/examples/src/ExamplesMain.cpp:29 (LabSoundExample+0x3a426) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)

SUMMARY: ThreadSanitizer: heap-use-after-free /usr/include/c++/13/bits/stl_vector.h:990 in std::vector<float, std::allocator<float> >::size() const
==================
ThreadSanitizer:DEADLYSIGNAL
==21829==ERROR: ThreadSanitizer: SEGV on unknown address 0x7f54b282d0ec (pc 0x55e0a052654d bp 0x00000000ac43 sp 0x7f54c53fdfe0 T21832)
==21829==The signal is caused by a READ memory access.
    #0 lab::WaveShaperNode::processCurve(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:97 (LabSoundExample+0xcf54d) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #1 lab::WaveShaperNode::processCurve4x(float const*, float*, int) LabSound/src/core/WaveShaperNode.cpp:134 (LabSoundExample+0xcfb10) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #2 lab::WaveShaperNode::process(lab::ContextRenderLock&, int) LabSound/src/core/WaveShaperNode.cpp:188 (LabSoundExample+0xd0791) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #3 lab::AudioNode::processIfNecessary(lab::ContextRenderLock&, int) LabSound/src/core/AudioNode.cpp:547 (LabSoundExample+0x8cda6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #4 lab::AudioNodeOutput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeOutput.cpp:122 (LabSoundExample+0x96d0c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #5 lab::AudioNodeInput::pull(lab::ContextRenderLock&, lab::AudioBus*, int) LabSound/src/core/AudioNodeInput.cpp:153 (LabSoundExample+0x95ba6) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #6 lab::pull_graph(lab::AudioContext*, lab::AudioNodeInput*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&, lab::AudioSourceProvider*) LabSound/src/core/AudioDevice.cpp:71 (LabSoundExample+0x882a0) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #7 lab::AudioDestinationNode::render(lab::AudioSourceProvider*, lab::AudioBus*, lab::AudioBus*, int, lab::SamplingInfo const&) LabSound/src/core/AudioDevice.cpp:131 (LabSoundExample+0x885e2) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #8 lab::AudioDevice_RtAudio::render(lab::AudioSourceProvider*, int, void*, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:331 (LabSoundExample+0x161951) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #9 lab::rt_audio_callback(void*, void*, unsigned int, double, unsigned int, void*) LabSound/src/backends/RtAudio/AudioDevice_RtAudio.cpp:32 (LabSoundExample+0x161c9c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #10 RtApiPulse::callbackEvent() LabSound/src/backends/RtAudio/RtAudio.cpp:8886 (LabSoundExample+0x16cdac) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #11 pulseaudio_callback LabSound/src/backends/RtAudio/RtAudio.cpp:8815 (LabSoundExample+0x16da2c) (BuildId: 22b165cfb8a4c940174fe156aad98fe5140a40f0)
    #12 __tsan_thread_start_func ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1026 (libtsan.so.2+0x373e9) (BuildId: 5e847b809f54bed9069b4ad5e6285626c3e4f0ef)
    #13 start_thread /build/glibc-BHL3KM/glibc-2.31/nptl/pthread_create.c:477 (libpthread.so.0+0x8608) (BuildId: f5b7b9b3efe01ef7aec691dc8f4e272c518f8cdf)
    #14 __clone <null> (libc.so.6+0x11f132) (BuildId: e678fe54a5d2c2092f8e47eb0b33105e380f7340)

ThreadSanitizer can not provide additional info.
SUMMARY: ThreadSanitizer: SEGV LabSound/src/core/WaveShaperNode.cpp:97 in lab::WaveShaperNode::processCurve(float const*, float*, int)
==21829==ABORTING

As far as I can tell, this would explain the crashes at the end (but not the dropped audio, which warrants further investigation).

Deleting running nodes should Just Work

What should a proper fix look like? I mean one that works for every case, not the "hack". Can they just be stopped/disconnected?

@meshula
Copy link
Member

meshula commented Nov 15, 2023

Stopped/disconnected should fix it yes.

Looking at it more deeply:

Every AudioNode has the structural pattern:

public interfaces
    get/set connect/disconnect
data for processing

The public interfaces have to live as long as the C++ object the developer is holding onto in their code
The data for processing has to live as long as the last audio buffer relying on it

Trying to synchronize those lifespans is how I've been trying to solve concurrency.

The fundamental problem is that the public interfaces are conflated with the data for processing. In other words, one can set a value, but processing has to reach into the Param or Setting to get the value.

I think what has to happen, structurally, across the board, is that the public interfaces need to queue operations to the processor, and the data for processing should be separably held, presumably in an object held by a shared pointer, so that the data itself is deleted when processing is over, and there is not synchronization required with the holding Node object.

@phildremi
Copy link
Contributor Author

That sounds like a major rework, so unfortunately a bit above what I would be able to contribute with my limited knowledge. If you want to sketch out a prototype/demo I can probably get a better understanding of the challenges involved, but no problem if there isn't time. I'm investigating the "dropped audio" bug, which I suspect could be related to this issue, based on my observations:

  • The TSAN logs display the exact same warning right before a dropout happens
  • As far as I understand, PulseAudio failing to send/receive would explain why there's no audio whatsoever
  • ... as well as why I can't seem to reproduce it on Windows (MSVC), PulseAudio not being a thing here

I still want to do more testing, especially on macOS, to see if it's happening there. But maybe I'm on the wrong path completely.

@meshula
Copy link
Member

meshula commented Nov 16, 2023

tsan is looking clean on mac, at least :)

Interesting note on pulse from the SDL report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants