Second attempt at fixing Windows Start/Stop crashes (#881)
* Use shared setHelperRealTime in internal WASAPI threads. * Capture this instead of all by reference in internal WASAPI threads. * Clean up refcounts of COM objects. * Missed release of default audio device. * Event handle needs to be closed on stop(). * Fix cross-compile definitions to allow asan to be used in the first place. * Make sure TX/RX thread stops before audio devices do.ms-hamlib-crash-fix
parent
3084177af3
commit
6b2debfdb4
|
|
@ -118,6 +118,9 @@ set( _windlls
|
||||||
|
|
||||||
# Filter Python DLLs as they're already included via install()
|
# Filter Python DLLs as they're already included via install()
|
||||||
python312.dll
|
python312.dll
|
||||||
|
|
||||||
|
# Additional DLLs needed by Address Sanitizer
|
||||||
|
api-ms-win-core-synch-l1-2-0.dll
|
||||||
)
|
)
|
||||||
list(REMOVE_ITEM _deps ${_windlls})
|
list(REMOVE_ITEM _deps ${_windlls})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ set(CMAKE_AR ${triple}-ar)
|
||||||
set(CMAKE_RANLIB ${triple}-ranlib)
|
set(CMAKE_RANLIB ${triple}-ranlib)
|
||||||
set(CMAKE_RC_COMPILER ${triple}-windres)
|
set(CMAKE_RC_COMPILER ${triple}-windres)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-Wno-unused-command-line-argument -gcodeview")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-command-line-argument -gcodeview")
|
||||||
set(CMAKE_CXX_FLAGS "-Wno-unused-command-line-argument -gcodeview")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument -gcodeview")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS -Wl,--pdb=)
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--pdb=")
|
||||||
|
|
||||||
# For make package use.
|
# For make package use.
|
||||||
set(CMAKE_OBJDUMP ${triple}-objdump)
|
set(CMAKE_OBJDUMP ${triple}-objdump)
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ set(CMAKE_AR ${triple}-ar)
|
||||||
set(CMAKE_RANLIB ${triple}-ranlib)
|
set(CMAKE_RANLIB ${triple}-ranlib)
|
||||||
set(CMAKE_RC_COMPILER ${triple}-windres)
|
set(CMAKE_RC_COMPILER ${triple}-windres)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-Wno-unused-command-line-argument -gcodeview")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-command-line-argument -gcodeview")
|
||||||
set(CMAKE_CXX_FLAGS "-Wno-unused-command-line-argument -gcodeview")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument -gcodeview")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS -Wl,--pdb=)
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--pdb=")
|
||||||
|
|
||||||
# For make package use.
|
# For make package use.
|
||||||
set(CMAKE_OBJDUMP ${triple}-objdump)
|
set(CMAKE_OBJDUMP ${triple}-objdump)
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
// Nanoseconds per REFERENCE_TIME unit
|
// Nanoseconds per REFERENCE_TIME unit
|
||||||
#define NS_PER_REFTIME (100)
|
#define NS_PER_REFTIME (100)
|
||||||
|
|
||||||
thread_local HANDLE WASAPIAudioDevice::helperTask_ = nullptr;
|
thread_local HANDLE WASAPIAudioDevice::HelperTask_ = nullptr;
|
||||||
|
|
||||||
WASAPIAudioDevice::WASAPIAudioDevice(IAudioClient* client, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels)
|
WASAPIAudioDevice::WASAPIAudioDevice(IAudioClient* client, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels)
|
||||||
: client_(client)
|
: client_(client)
|
||||||
|
|
@ -45,13 +45,12 @@ WASAPIAudioDevice::WASAPIAudioDevice(IAudioClient* client, IAudioEngine::AudioDi
|
||||||
, numChannels_(numChannels)
|
, numChannels_(numChannels)
|
||||||
, bufferFrameCount_(0)
|
, bufferFrameCount_(0)
|
||||||
, initialized_(false)
|
, initialized_(false)
|
||||||
, lowLatencyTask_(nullptr)
|
|
||||||
, latencyFrames_(0)
|
, latencyFrames_(0)
|
||||||
, renderCaptureEvent_(nullptr)
|
, renderCaptureEvent_(nullptr)
|
||||||
, isRenderCaptureRunning_(false)
|
, isRenderCaptureRunning_(false)
|
||||||
, semaphore_(nullptr)
|
, semaphore_(nullptr)
|
||||||
{
|
{
|
||||||
// empty
|
client_->AddRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
WASAPIAudioDevice::~WASAPIAudioDevice()
|
WASAPIAudioDevice::~WASAPIAudioDevice()
|
||||||
|
|
@ -291,7 +290,7 @@ void WASAPIAudioDevice::start()
|
||||||
|
|
||||||
// Start render/capture thread.
|
// Start render/capture thread.
|
||||||
isRenderCaptureRunning_ = true;
|
isRenderCaptureRunning_ = true;
|
||||||
renderCaptureThread_ = std::thread([&]() {
|
renderCaptureThread_ = std::thread([this]() {
|
||||||
log_info("Starting render/capture thread");
|
log_info("Starting render/capture thread");
|
||||||
|
|
||||||
HRESULT res = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
HRESULT res = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
|
@ -299,14 +298,21 @@ void WASAPIAudioDevice::start()
|
||||||
{
|
{
|
||||||
log_warn("Could not initialize COM (res = %d)", res);
|
log_warn("Could not initialize COM (res = %d)", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily raise priority of task
|
// Increment refcounts of COM objects used by thread
|
||||||
DWORD taskIndex = 0;
|
// to avoid instability during stop/restart.
|
||||||
lowLatencyTask_ = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
|
client_->AddRef();
|
||||||
if (lowLatencyTask_ == nullptr)
|
if (renderClient_ != nullptr)
|
||||||
{
|
{
|
||||||
log_warn("Could not increase thread priority");
|
renderClient_->AddRef();
|
||||||
}
|
}
|
||||||
|
if (captureClient_ != nullptr)
|
||||||
|
{
|
||||||
|
captureClient_->AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily raise priority of task
|
||||||
|
setHelperRealTime();
|
||||||
|
|
||||||
while (isRenderCaptureRunning_)
|
while (isRenderCaptureRunning_)
|
||||||
{
|
{
|
||||||
|
|
@ -326,12 +332,19 @@ void WASAPIAudioDevice::start()
|
||||||
|
|
||||||
log_info("Exiting render/capture thread");
|
log_info("Exiting render/capture thread");
|
||||||
|
|
||||||
if (lowLatencyTask_ != nullptr)
|
clearHelperRealTime();
|
||||||
|
|
||||||
|
// Decrement refcounts prior to exit.
|
||||||
|
client_->Release();
|
||||||
|
if (renderClient_ != nullptr)
|
||||||
{
|
{
|
||||||
AvRevertMmThreadCharacteristics(lowLatencyTask_);
|
renderClient_->Release();
|
||||||
lowLatencyTask_ = nullptr;
|
|
||||||
}
|
}
|
||||||
|
if (captureClient_ != nullptr)
|
||||||
|
{
|
||||||
|
captureClient_->Release();
|
||||||
|
}
|
||||||
|
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -369,6 +382,12 @@ void WASAPIAudioDevice::stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (renderCaptureEvent_ != nullptr)
|
||||||
|
{
|
||||||
|
CloseHandle(renderCaptureEvent_);
|
||||||
|
renderCaptureEvent_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (renderClient_ != nullptr)
|
if (renderClient_ != nullptr)
|
||||||
{
|
{
|
||||||
renderClient_->Release();
|
renderClient_->Release();
|
||||||
|
|
@ -412,8 +431,8 @@ int WASAPIAudioDevice::getLatencyInMicroseconds()
|
||||||
void WASAPIAudioDevice::setHelperRealTime()
|
void WASAPIAudioDevice::setHelperRealTime()
|
||||||
{
|
{
|
||||||
DWORD taskIndex = 0;
|
DWORD taskIndex = 0;
|
||||||
helperTask_ = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
|
HelperTask_ = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
|
||||||
if (helperTask_ == nullptr)
|
if (HelperTask_ == nullptr)
|
||||||
{
|
{
|
||||||
log_warn("Could not increase thread priority");
|
log_warn("Could not increase thread priority");
|
||||||
}
|
}
|
||||||
|
|
@ -445,10 +464,10 @@ void WASAPIAudioDevice::stopRealTimeWork()
|
||||||
|
|
||||||
void WASAPIAudioDevice::clearHelperRealTime()
|
void WASAPIAudioDevice::clearHelperRealTime()
|
||||||
{
|
{
|
||||||
if (helperTask_ != nullptr)
|
if (HelperTask_ != nullptr)
|
||||||
{
|
{
|
||||||
AvRevertMmThreadCharacteristics(helperTask_);
|
AvRevertMmThreadCharacteristics(HelperTask_);
|
||||||
helperTask_ = nullptr;
|
HelperTask_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,6 @@ private:
|
||||||
int numChannels_;
|
int numChannels_;
|
||||||
UINT32 bufferFrameCount_;
|
UINT32 bufferFrameCount_;
|
||||||
bool initialized_;
|
bool initialized_;
|
||||||
HANDLE lowLatencyTask_;
|
|
||||||
int latencyFrames_;
|
int latencyFrames_;
|
||||||
std::thread renderCaptureThread_;
|
std::thread renderCaptureThread_;
|
||||||
HANDLE renderCaptureEvent_;
|
HANDLE renderCaptureEvent_;
|
||||||
|
|
@ -89,7 +88,7 @@ private:
|
||||||
void renderAudio_();
|
void renderAudio_();
|
||||||
void captureAudio_();
|
void captureAudio_();
|
||||||
|
|
||||||
static thread_local HANDLE helperTask_;
|
static thread_local HANDLE HelperTask_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WASAPI_AUDIO_DEVICE_H
|
#endif // WASAPI_AUDIO_DEVICE_H
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,7 @@ AudioDeviceSpecification WASAPIAudioEngine::getDefaultAudioDevice(AudioDirection
|
||||||
if (defaultSpec.name == spec.name)
|
if (defaultSpec.name == spec.name)
|
||||||
{
|
{
|
||||||
prom->set_value(spec);
|
prom->set_value(spec);
|
||||||
|
defaultDevice->Release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,6 +304,9 @@ std::shared_ptr<IAudioDevice> WASAPIAudioEngine::getAudioDevice(wxString deviceN
|
||||||
|
|
||||||
auto devPtr = new WASAPIAudioDevice(client, direction, sampleRate, numChannels);
|
auto devPtr = new WASAPIAudioDevice(client, direction, sampleRate, numChannels);
|
||||||
result = std::shared_ptr<IAudioDevice>(devPtr);
|
result = std::shared_ptr<IAudioDevice>(devPtr);
|
||||||
|
|
||||||
|
client->Release();
|
||||||
|
device->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prom->set_value(result);
|
prom->set_value(result);
|
||||||
|
|
@ -395,7 +399,7 @@ AudioDeviceSpecification WASAPIAudioEngine::getDeviceSpecification_(IMMDevice* d
|
||||||
return AudioDeviceSpecification::GetInvalidDevice();
|
return AudioDeviceSpecification::GetInvalidDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
WAVEFORMATEX* streamFormat;
|
WAVEFORMATEX* streamFormat = nullptr;
|
||||||
hr = audioClient->GetMixFormat(&streamFormat);
|
hr = audioClient->GetMixFormat(&streamFormat);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
12
src/main.cpp
12
src/main.cpp
|
|
@ -2855,6 +2855,9 @@ void MainFrame::stopRxStream()
|
||||||
|
|
||||||
if (m_txThread)
|
if (m_txThread)
|
||||||
{
|
{
|
||||||
|
m_txThread->terminateThread();
|
||||||
|
m_txThread->Wait();
|
||||||
|
|
||||||
if (txInSoundDevice)
|
if (txInSoundDevice)
|
||||||
{
|
{
|
||||||
txInSoundDevice->stop();
|
txInSoundDevice->stop();
|
||||||
|
|
@ -2867,15 +2870,15 @@ void MainFrame::stopRxStream()
|
||||||
txOutSoundDevice.reset();
|
txOutSoundDevice.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_txThread->terminateThread();
|
|
||||||
m_txThread->Wait();
|
|
||||||
|
|
||||||
delete m_txThread;
|
delete m_txThread;
|
||||||
m_txThread = nullptr;
|
m_txThread = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_rxThread)
|
if (m_rxThread)
|
||||||
{
|
{
|
||||||
|
m_rxThread->terminateThread();
|
||||||
|
m_rxThread->Wait();
|
||||||
|
|
||||||
if (rxInSoundDevice)
|
if (rxInSoundDevice)
|
||||||
{
|
{
|
||||||
rxInSoundDevice->stop();
|
rxInSoundDevice->stop();
|
||||||
|
|
@ -2888,9 +2891,6 @@ void MainFrame::stopRxStream()
|
||||||
rxOutSoundDevice.reset();
|
rxOutSoundDevice.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_rxThread->terminateThread();
|
|
||||||
m_rxThread->Wait();
|
|
||||||
|
|
||||||
delete m_txThread;
|
delete m_txThread;
|
||||||
m_rxThread = nullptr;
|
m_rxThread = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -515,7 +515,8 @@ void* TxRxThread::Entry()
|
||||||
|
|
||||||
void TxRxThread::OnExit()
|
void TxRxThread::OnExit()
|
||||||
{
|
{
|
||||||
// No actions required for exit.
|
// Free allocated buffer.
|
||||||
|
inputSamples_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxRxThread::terminateThread()
|
void TxRxThread::terminateThread()
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,12 @@ public:
|
||||||
new short[std::max(inputSampleRate_, outputSampleRate_)],
|
new short[std::max(inputSampleRate_, outputSampleRate_)],
|
||||||
std::default_delete<short[]>());
|
std::default_delete<short[]>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ~TxRxThread()
|
||||||
|
{
|
||||||
|
// Free allocated buffer
|
||||||
|
inputSamples_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// thread execution starts here
|
// thread execution starts here
|
||||||
void *Entry();
|
void *Entry();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue