From fc6a9f87979153ae21251dd3d9a018af07fb96f0 Mon Sep 17 00:00:00 2001 From: Mooneer Salem Date: Fri, 23 May 2025 08:18:36 -0700 Subject: [PATCH] Bring back locking in TxRxThread just in case. --- src/pipeline/TxRxThread.cpp | 47 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/pipeline/TxRxThread.cpp b/src/pipeline/TxRxThread.cpp index 8a12b8fb..9d830647 100644 --- a/src/pipeline/TxRxThread.cpp +++ b/src/pipeline/TxRxThread.cpp @@ -43,6 +43,7 @@ using namespace std::chrono_literals; #include "ToneInterfererStep.h" #include "ComputeRfSpectrumStep.h" #include "FreeDVReceiveStep.h" +#include "ExclusiveAccessStep.h" #include "MuteStep.h" #include "LinkStep.h" @@ -96,6 +97,8 @@ extern FreeDVInterface freedvInterface; #include #include "../main.h" +extern wxMutex txModeChangeMutex; +extern wxMutex g_mutexProtectingCallbackData; extern wxWindow* g_parent; #include @@ -121,6 +124,16 @@ extern int resample(SRC_STATE *src, void TxRxThread::initializePipeline_() { + // Function definitions shared across both pipelines. + auto callbackLockFn = []() { + // Prevent priority inversions by bounding the time we can wait for a lock. + g_mutexProtectingCallbackData.LockTimeout(5); + }; + + auto callbackUnlockFn = []() { + g_mutexProtectingCallbackData.Unlock(); + }; + if (m_tx) { pipeline_ = std::shared_ptr(new AudioPipeline(inputSampleRate_, outputSampleRate_)); @@ -145,7 +158,8 @@ void TxRxThread::initializePipeline_() std::shared_ptr(recordMicTap), std::shared_ptr(bypassRecordMic) ); - pipeline_->appendPipelineStep(std::shared_ptr(eitherOrRecordMic)); + auto recordMicLockStep = new ExclusiveAccessStep(eitherOrRecordMic, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(recordMicLockStep)); // Mic In playback step (optional) auto eitherOrBypassPlay = new AudioPipeline(inputSampleRate_, inputSampleRate_); @@ -169,7 +183,8 @@ void TxRxThread::initializePipeline_() []() { return g_playFileToMicIn && (g_sfPlayFile != NULL); }, std::shared_ptr(eitherOrPlayMicIn), std::shared_ptr(eitherOrBypassPlay)); - pipeline_->appendPipelineStep(std::shared_ptr(eitherOrPlayStep)); + auto playMicLockStep = new ExclusiveAccessStep(eitherOrPlayStep, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(playMicLockStep)); // Speex step (optional) auto eitherOrProcessSpeex = new AudioPipeline(inputSampleRate_, inputSampleRate_); @@ -182,7 +197,8 @@ void TxRxThread::initializePipeline_() []() { return wxGetApp().appConfiguration.filterConfiguration.speexppEnable; }, std::shared_ptr(eitherOrProcessSpeex), std::shared_ptr(eitherOrBypassSpeex)); - pipeline_->appendPipelineStep(std::shared_ptr(eitherOrSpeexStep)); + auto speexLockStep = new ExclusiveAccessStep(eitherOrSpeexStep, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(speexLockStep)); // Equalizer step (optional based on filter state) auto equalizerStep = new EqualizerStep( @@ -192,7 +208,8 @@ void TxRxThread::initializePipeline_() &g_rxUserdata->sbqMicInMid, &g_rxUserdata->sbqMicInTreble, &g_rxUserdata->sbqMicInVol); - pipeline_->appendPipelineStep(std::shared_ptr(equalizerStep)); + auto equalizerLockStep = new ExclusiveAccessStep(equalizerStep, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(equalizerLockStep)); // Take TX audio post-equalizer and send it to RX for possible monitoring use. if (equalizedMicAudioLink_ != nullptr) @@ -247,7 +264,8 @@ void TxRxThread::initializePipeline_() []() { return g_recFileFromModulator && (g_sfRecFileFromModulator != NULL); }, std::shared_ptr(recordModulatedTapPipeline), std::shared_ptr(bypassRecordModulated)); - pipeline_->appendPipelineStep(std::shared_ptr(eitherOrRecordModulated)); + auto recordModulatedLockStep = new ExclusiveAccessStep(eitherOrRecordModulated, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(recordModulatedLockStep)); // TX attenuation step auto txAttenuationStep = new LevelAdjustStep(outputSampleRate_, []() { @@ -285,7 +303,8 @@ void TxRxThread::initializePipeline_() std::shared_ptr(recordRadioTap), std::shared_ptr(bypassRecordRadio) ); - pipeline_->appendPipelineStep(std::shared_ptr(eitherOrRecordRadio)); + auto recordRadioLockStep = new ExclusiveAccessStep(eitherOrRecordRadio, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(recordRadioLockStep)); // Play from radio step (optional) auto eitherOrBypassPlayRadio = new AudioPipeline(inputSampleRate_, inputSampleRate_); @@ -307,12 +326,16 @@ void TxRxThread::initializePipeline_() auto eitherOrPlayRadioStep = new EitherOrStep( []() { + // Prevent priority inversions by bounding the time we can wait for a lock. + g_mutexProtectingCallbackData.LockTimeout(5); auto result = g_playFileFromRadio && (g_sfPlayFileFromRadio != NULL); + g_mutexProtectingCallbackData.Unlock(); return result; }, std::shared_ptr(eitherOrPlayRadio), std::shared_ptr(eitherOrBypassPlayRadio)); - pipeline_->appendPipelineStep(std::shared_ptr(eitherOrPlayRadioStep)); + auto playRadioLockStep = new ExclusiveAccessStep(eitherOrPlayRadioStep, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(playRadioLockStep)); // Resample for plot step (demod in) auto resampleForPlotStep = new ResampleForPlotStep(g_plotDemodInFifo); @@ -428,7 +451,8 @@ void TxRxThread::initializePipeline_() &g_rxUserdata->sbqSpkOutMid, &g_rxUserdata->sbqSpkOutTreble, &g_rxUserdata->sbqSpkOutVol); - pipeline_->appendPipelineStep(std::shared_ptr(equalizerStep)); + auto equalizerLockStep = new ExclusiveAccessStep(equalizerStep, callbackLockFn, callbackUnlockFn); + pipeline_->appendPipelineStep(std::shared_ptr(equalizerLockStep)); // Resample for plot step (speech out) auto resampleForPlotOutStep = new ResampleForPlotStep(g_plotSpeechOutFifo); @@ -491,8 +515,7 @@ void* TxRxThread::Entry() void TxRxThread::OnExit() { - // Free allocated buffer. - inputSamples_ = nullptr; + // No actions required for exit. } void TxRxThread::terminateThread() @@ -569,6 +592,8 @@ void TxRxThread::txProcessing_() if (((g_nSoundCards == 2) && ((g_half_duplex && g_tx) || !g_half_duplex || g_voice_keyer_tx || g_recVoiceKeyerFile || g_recFileFromMic))) { // Lock the mode mutex so that TX state doesn't change on us during processing. + txModeChangeMutex.Lock(); + if (pipeline_ == nullptr) { initializePipeline_(); @@ -661,6 +686,8 @@ void TxRxThread::txProcessing_() codec2_fifo_write(cbData->outfifo1, outputSamples.get(), nout); } } + + txModeChangeMutex.Unlock(); } else {