From 46e53c3b74ac110d67d25d22167c04ea82eb79eb Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 12 Feb 2022 12:35:17 +0100 Subject: [PATCH 1/3] Pin ChannelHandle to permanent memory address and use pointers --- src/effects/backends/effectprocessor.h | 32 ++--- src/effects/effectchain.cpp | 4 +- src/effects/effectsmanager.h | 2 +- src/engine/channelhandle.h | 135 ++++++++++++-------- src/engine/channelmixer.cpp | 16 ++- src/engine/channelmixer.h | 4 +- src/engine/channels/enginechannel.h | 2 +- src/engine/effects/engineeffect.cpp | 24 ++-- src/engine/effects/engineeffect.h | 8 +- src/engine/effects/engineeffectchain.cpp | 36 +++--- src/engine/effects/engineeffectchain.h | 14 +- src/engine/effects/engineeffectsmanager.cpp | 38 +++--- src/engine/effects/engineeffectsmanager.h | 16 +-- src/engine/enginemaster.cpp | 2 +- src/engine/enginemaster.h | 18 +-- src/mixer/playermanager.cpp | 6 +- src/mixer/playermanager.h | 6 +- src/test/autodjprocessor_test.cpp | 2 +- src/test/baseeffecttest.h | 4 +- src/test/channelhandle_test.cpp | 60 ++++----- src/test/enginemicrophonetest.cpp | 2 +- src/test/nativeeffects_test.cpp | 2 +- 22 files changed, 232 insertions(+), 201 deletions(-) diff --git a/src/effects/backends/effectprocessor.h b/src/effects/backends/effectprocessor.h index fbee277f2a17..2bebdb92ca2a 100644 --- a/src/effects/backends/effectprocessor.h +++ b/src/effects/backends/effectprocessor.h @@ -74,10 +74,10 @@ class EffectProcessor { virtual void loadEngineEffectParameters( const QMap& parameters) = 0; virtual EffectState* createState(const mixxx::EngineParameters& engineParameters) = 0; - virtual void deleteStatesForInputChannel(const ChannelHandle* inputChannel) = 0; + virtual void deleteStatesForInputChannel(const ChannelHandle* pInputChannel) = 0; // Called from the audio thread - virtual bool loadStatesForInputChannel(const ChannelHandle* inputChannel, + virtual bool loadStatesForInputChannel(const ChannelHandle* pInputChannel, const EffectStatesMap* pStatesMap) = 0; /// Called from the audio thread @@ -91,8 +91,8 @@ class EffectProcessor { /// EffectProcessorImpl::process to fetch the appropriate EffectState and /// pass it on to EffectProcessorImpl::processChannel, allowing one /// EffectProcessor instance to process multiple signals simultaneously. - virtual void process(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + virtual void process(const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, const CSAMPLE* pInput, CSAMPLE* pOutput, const mixxx::EngineParameters& engineParameters, @@ -149,25 +149,25 @@ class EffectProcessorImpl : public EffectProcessor { const EffectEnableState enableState, const GroupFeatureState& groupFeatures) = 0; - void process(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + void process(const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, const CSAMPLE* pInput, CSAMPLE* pOutput, const mixxx::EngineParameters& engineParameters, const EffectEnableState enableState, const GroupFeatureState& groupFeatures) final { - EffectSpecificState* pState = m_channelStateMatrix[inputHandle][outputHandle]; + EffectSpecificState* pState = m_channelStateMatrix[pInputHandle][pOutputHandle]; VERIFY_OR_DEBUG_ASSERT(pState != nullptr) { if (kEffectDebugOutput) { qWarning() << "EffectProcessorImpl::process could not retrieve" "EffectState for input" - << inputHandle - << "and output" << outputHandle + << *pInputHandle + << "and output" << *pOutputHandle << "EffectState should have been preallocated in the" "main thread."; } pState = createSpecificState(engineParameters); - m_channelStateMatrix[inputHandle][outputHandle] = pState; + m_channelStateMatrix[pInputHandle][pOutputHandle] = pState; } processChannel(pState, pInput, pOutput, engineParameters, enableState, groupFeatures); } @@ -202,11 +202,11 @@ class EffectProcessorImpl : public EffectProcessor { return createSpecificState(engineParameters); }; - bool loadStatesForInputChannel(const ChannelHandle* inputChannel, + bool loadStatesForInputChannel(const ChannelHandle* pInputHandle, const EffectStatesMap* pStatesMap) final { if (kEffectDebugOutput) { qDebug() << "EffectProcessorImpl::loadStatesForInputChannel" << this - << "input" << *inputChannel; + << "input" << *pInputHandle; } // NOTE: ChannelHandleMap is like a map in that it associates an @@ -220,7 +220,7 @@ class EffectProcessorImpl : public EffectProcessor { // pStatesMap to build a new ChannelHandleMap with // dynamic_cast'ed states. ChannelHandleMap& effectSpecificStatesMap = - m_channelStateMatrix[*inputChannel]; + m_channelStateMatrix[pInputHandle]; // deleteStatesForInputChannel should have been called before a new // map of EffectStates was sent to this function, or this is the first @@ -256,10 +256,10 @@ class EffectProcessorImpl : public EffectProcessor { }; /// Called from main thread for garbage collection after an input channel is disabled - void deleteStatesForInputChannel(const ChannelHandle* inputChannel) final { + void deleteStatesForInputChannel(const ChannelHandle* pInputHandle) final { if (kEffectDebugOutput) { qDebug() << "EffectProcessorImpl::deleteStatesForInputChannel" - << this << *inputChannel; + << this << *pInputHandle; } // NOTE: ChannelHandleMap is like a map in that it associates an @@ -269,7 +269,7 @@ class EffectProcessorImpl : public EffectProcessor { // engine thread in loadStatesForInputChannel. ChannelHandleMap& stateMap = - m_channelStateMatrix[*inputChannel]; + m_channelStateMatrix[pInputHandle]; for (EffectSpecificState* pState : stateMap) { VERIFY_OR_DEBUG_ASSERT(pState != nullptr) { continue; diff --git a/src/effects/effectchain.cpp b/src/effects/effectchain.cpp index 6c7fd8d13a5a..1382f93763ac 100644 --- a/src/effects/effectchain.cpp +++ b/src/effects/effectchain.cpp @@ -394,7 +394,7 @@ void EffectChain::enableForInputChannel(const ChannelHandleAndGroup& handleGroup EffectsRequest* request = new EffectsRequest(); request->type = EffectsRequest::ENABLE_EFFECT_CHAIN_FOR_INPUT_CHANNEL; request->pTargetChain = m_pEngineEffectChain; - request->EnableInputChannelForChain.pChannelHandle = &handleGroup.handle(); + request->EnableInputChannelForChain.pChannelHandle = handleGroup.handle(); // Allocate EffectStates here in the main thread to avoid allocating // memory in the realtime audio callback thread. Pointers to the @@ -428,7 +428,7 @@ void EffectChain::disableForInputChannel(const ChannelHandleAndGroup& handleGrou EffectsRequest* request = new EffectsRequest(); request->type = EffectsRequest::DISABLE_EFFECT_CHAIN_FOR_INPUT_CHANNEL; request->pTargetChain = m_pEngineEffectChain; - request->DisableInputChannelForChain.pChannelHandle = &handleGroup.handle(); + request->DisableInputChannelForChain.pChannelHandle = handleGroup.handle(); m_pMessenger->writeRequest(request); } diff --git a/src/effects/effectsmanager.h b/src/effects/effectsmanager.h index 2e45b2564055..0862a56f5462 100644 --- a/src/effects/effectsmanager.h +++ b/src/effects/effectsmanager.h @@ -40,7 +40,7 @@ class EffectsManager { return m_pEngineEffectsManager; } - const ChannelHandle getMasterHandle() const { + const ChannelHandle* getMasterHandle() const { return m_pChannelHandleFactory->getOrCreateHandle("[Master]"); } diff --git a/src/engine/channelhandle.h b/src/engine/channelhandle.h index 1fc1d4abcf18..215bcc82852d 100644 --- a/src/engine/channelhandle.h +++ b/src/engine/channelhandle.h @@ -9,6 +9,8 @@ #include "util/assert.h" #include "util/compatibility/qhash.h" +constexpr int kMaxExpectedChannelGroups = 256; + // ChannelHandle defines a unique identifier for channels of audio in the engine // (e.g. headphone output, master output, deck 1, microphone 3). Previously we // used the group string of the channel in the engine to uniquely identify it @@ -28,27 +30,27 @@ /// /// A helper class, ChannelHandleFactory, keeps a running count of handles that /// have been assigned. -class ChannelHandle { +class ChannelHandle final { public: - ChannelHandle() : m_iHandle(-1) { - } - - inline bool valid() const { + bool valid() const { return m_iHandle >= 0; } - inline int handle() const { + int handle() const { + DEBUG_ASSERT(valid()); return m_iHandle; } private: - ChannelHandle(int iHandle) - : m_iHandle(iHandle) { - } - - void setHandle(int iHandle) { - m_iHandle = iHandle; + ChannelHandle() + : m_iHandle(-1) { } + // Channel handles are pinned at a fixed memory address and must + // neither be copied nor moved!!! + ChannelHandle(ChannelHandle&&) = delete; + ChannelHandle(const ChannelHandle&) = delete; + ChannelHandle& operator=(ChannelHandle&&) = delete; + ChannelHandle& operator=(const ChannelHandle&) = delete; int m_iHandle; @@ -86,20 +88,20 @@ inline qhash_seed_t qHash( // custom equality and hash methods that save the cost of touching the QString. class ChannelHandleAndGroup { public: - ChannelHandleAndGroup(const ChannelHandle& handle, const QString& name) - : m_handle(handle), + ChannelHandleAndGroup(const ChannelHandle* pHandle, const QString& name) + : m_pHandle(pHandle), m_name(name) { } - inline const QString& name() const { + const QString& name() const { return m_name; } - inline const ChannelHandle& handle() const { - return m_handle; + const ChannelHandle* handle() const { + return m_pHandle; } - const ChannelHandle m_handle; + const ChannelHandle* m_pHandle; const QString m_name; }; @@ -129,76 +131,105 @@ inline qhash_seed_t qHash( // EngineMaster. class ChannelHandleFactory { public: - ChannelHandleFactory() : m_iNextHandle(0) { - } - - ChannelHandle getOrCreateHandle(const QString& group) { - ChannelHandle& handle = m_groupToHandle[group]; - if (!handle.valid()) { - handle.setHandle(m_iNextHandle++); - DEBUG_ASSERT(handle.valid()); - DEBUG_ASSERT(!m_handleToGroup.contains(handle)); - m_handleToGroup.insert(handle, group); + ChannelHandleFactory() = default; + + /// Obtain a reference to a channel handle for a group + /// + /// The returned reference must be stable during a session and references + /// an entry from an internal map. Thus it is safe to get the address of + /// this reference. This is REQUIRED for passing ChannelHandle to + /// EffectChainManager by a pointer!!! + const ChannelHandle* getOrCreateHandle(const QString& group) { + auto groupToHandleIter = m_groupToHandle.find(group); + if (groupToHandleIter == m_groupToHandle.end()) { + groupToHandleIter = m_groupToHandle.insert(group, m_groupToHandle.size()); + DEBUG_ASSERT(!m_handleToGroup.contains(groupToHandleIter.value())); + m_handleToGroup.insert(groupToHandleIter.value(), group); + } + DEBUG_ASSERT(groupToHandleIter != m_groupToHandle.end()); + // Return reference from the map that is pinned to a fixed + // memory address!!! + const auto handleIndex = groupToHandleIter.value(); + DEBUG_ASSERT(handleIndex >= 0); + DEBUG_ASSERT(handleIndex < static_cast(std::size(m_groupHandles))); + if (!m_groupHandles[handleIndex].valid()) { + m_groupHandles[handleIndex].m_iHandle = handleIndex; + qInfo() << "Created" << m_groupHandles[handleIndex] << "for group" << group; } - return handle; + DEBUG_ASSERT(m_groupHandles[handleIndex].m_iHandle == handleIndex); + return &m_groupHandles[handleIndex]; } - ChannelHandle handleForGroup(const QString& group) const { - return m_groupToHandle.value(group, ChannelHandle()); + const ChannelHandle* handleForGroup(const QString& group) const { + auto groupToHandleIter = m_groupToHandle.find(group); + if (groupToHandleIter == m_groupToHandle.end()) { + return nullptr; + } + // Return reference from the map that is pinned to a fixed + // memory address!!! + const auto handleIndex = groupToHandleIter.value(); + DEBUG_ASSERT(handleIndex >= 0); + DEBUG_ASSERT(handleIndex < static_cast(std::size(m_groupHandles))); + return &m_groupHandles[handleIndex]; } - QString groupForHandle(const ChannelHandle& handle) const { - return m_handleToGroup.value(handle, QString()); + QString groupForHandle(const ChannelHandle* pHandle) const { + VERIFY_OR_DEBUG_ASSERT(pHandle) { + return {}; + } + return m_handleToGroup.value(pHandle->handle()); } private: - int m_iNextHandle; - QHash m_groupToHandle; - QHash m_handleToGroup; + ChannelHandle m_groupHandles[kMaxExpectedChannelGroups]; + QHash m_groupToHandle; + QHash m_handleToGroup; }; typedef std::shared_ptr ChannelHandleFactoryPointer; // An associative container mapping ChannelHandle to a template type T. Backed -// by a QVarLengthArray with ChannelHandleMap::kMaxExpectedGroups pre-allocated -// entries. Insertions are amortized O(1) time (if less than kMaxExpectedGroups +// by a QVarLengthArray with ChannelHandleMap::kMaxExpectedChannelGroups pre-allocated +// entries. Insertions are amortized O(1) time (if less than kMaxExpectedChannelGroups // exist then no allocation will occur -- insertion is a mere copy). Lookups are // O(1) and quite fast -- a simple index into an array using the handle's // integer value. template class ChannelHandleMap { - static constexpr int kMaxExpectedGroups = 256; - typedef QVarLengthArray container_type; + typedef QVarLengthArray container_type; + public: - typedef typename QVarLengthArray::const_iterator const_iterator; - typedef typename QVarLengthArray::iterator iterator; + typedef typename QVarLengthArray::const_iterator const_iterator; + typedef typename QVarLengthArray::iterator iterator; ChannelHandleMap() : m_dummy{} { } - const T& at(const ChannelHandle& handle) const { - if (!handle.valid()) { + const T& at(const ChannelHandle* pHandle) const { + if (!pHandle) { return m_dummy; } - return m_data.at(handle.handle()); + DEBUG_ASSERT(pHandle->valid()); + return m_data.at(pHandle->handle()); } - void insert(const ChannelHandle& handle, const T& value) { - if (!handle.valid()) { + void insert(const ChannelHandle* pHandle, const T& value) { + if (!pHandle) { return; } - - int iHandle = handle.handle(); + DEBUG_ASSERT(pHandle->valid()); + int iHandle = pHandle->handle(); maybeExpand(iHandle + 1); m_data[iHandle] = value; } - T& operator[](const ChannelHandle& handle) { - if (!handle.valid()) { + T& operator[](const ChannelHandle* pHandle) { + if (!pHandle) { return m_dummy; } - int iHandle = handle.handle(); + DEBUG_ASSERT(pHandle->valid()); + int iHandle = pHandle->handle(); maybeExpand(iHandle + 1); return m_data[iHandle]; } diff --git a/src/engine/channelmixer.cpp b/src/engine/channelmixer.cpp index 9a76a2c5a487..d0cbfb23a8d9 100644 --- a/src/engine/channelmixer.cpp +++ b/src/engine/channelmixer.cpp @@ -8,7 +8,7 @@ void ChannelMixer::applyEffectsAndMixChannels(const EngineMaster::GainCalculator const QVarLengthArray& activeChannels, QVarLengthArray* channelGainCache, CSAMPLE* pOutput, - const ChannelHandle& outputHandle, + const ChannelHandle* pOutputHandle, unsigned int iBufferSize, unsigned int iSampleRate, EngineEffectsManager* pEngineEffectsManager) { @@ -34,8 +34,10 @@ void ChannelMixer::applyEffectsAndMixChannels(const EngineMaster::GainCalculator newGain = gainCalculator.getGain(pChannelInfo); } gainCache.m_gain = newGain; - pEngineEffectsManager->processPostFaderAndMix(pChannelInfo->m_handle, - outputHandle, + DEBUG_ASSERT(pChannelInfo->m_pHandle); + pEngineEffectsManager->processPostFaderAndMix( + pChannelInfo->m_pHandle, + pOutputHandle, pChannelInfo->m_pBuffer, pOutput, iBufferSize, @@ -53,7 +55,7 @@ void ChannelMixer::applyEffectsInPlaceAndMixChannels( QVarLengthArray* channelGainCache, CSAMPLE* pOutput, - const ChannelHandle& outputHandle, + const ChannelHandle* pOutputHandle, unsigned int iBufferSize, unsigned int iSampleRate, EngineEffectsManager* pEngineEffectsManager) { @@ -76,8 +78,10 @@ void ChannelMixer::applyEffectsInPlaceAndMixChannels( newGain = gainCalculator.getGain(pChannelInfo); } gainCache.m_gain = newGain; - pEngineEffectsManager->processPostFaderInPlace(pChannelInfo->m_handle, - outputHandle, + DEBUG_ASSERT(pChannelInfo->m_pHandle); + pEngineEffectsManager->processPostFaderInPlace( + pChannelInfo->m_pHandle, + pOutputHandle, pChannelInfo->m_pBuffer, iBufferSize, iSampleRate, diff --git a/src/engine/channelmixer.h b/src/engine/channelmixer.h index d65cb98ab92e..15cc68e4bc2e 100644 --- a/src/engine/channelmixer.h +++ b/src/engine/channelmixer.h @@ -18,7 +18,7 @@ class ChannelMixer { QVarLengthArray* channelGainCache, CSAMPLE* pOutput, - const ChannelHandle& outputHandle, + const ChannelHandle* pOutputHandle, unsigned int iBufferSize, unsigned int iSampleRate, EngineEffectsManager* pEngineEffectsManager); @@ -30,7 +30,7 @@ class ChannelMixer { QVarLengthArray* channelGainCache, CSAMPLE* pOutput, - const ChannelHandle& outputHandle, + const ChannelHandle* pOutputHandle, unsigned int iBufferSize, unsigned int iSampleRate, EngineEffectsManager* pEngineEffectsManager); diff --git a/src/engine/channels/enginechannel.h b/src/engine/channels/enginechannel.h index 749e43ccdccf..5d5f7702d4cf 100644 --- a/src/engine/channels/enginechannel.h +++ b/src/engine/channels/enginechannel.h @@ -30,7 +30,7 @@ class EngineChannel : public EngineObject { virtual ChannelOrientation getOrientation() const; - inline const ChannelHandle& getHandle() const { + inline const ChannelHandle* getHandle() const { return m_group.handle(); } diff --git a/src/engine/effects/engineeffect.cpp b/src/engine/effects/engineeffect.cpp index b5b543c74e89..6add7be2223e 100644 --- a/src/engine/effects/engineeffect.cpp +++ b/src/engine/effects/engineeffect.cpp @@ -53,17 +53,17 @@ EffectState* EngineEffect::createState(const mixxx::EngineParameters& enginePara return m_pProcessor->createState(engineParameters); } -void EngineEffect::loadStatesForInputChannel(const ChannelHandle* inputChannel, +void EngineEffect::loadStatesForInputChannel(const ChannelHandle* pInputChannel, EffectStatesMap* pStatesMap) { if (kEffectDebugOutput) { qDebug() << "EngineEffect::loadStatesForInputChannel" << this - << "loading states for input" << *inputChannel; + << "loading states for input" << *pInputChannel; } - m_pProcessor->loadStatesForInputChannel(inputChannel, pStatesMap); + m_pProcessor->loadStatesForInputChannel(pInputChannel, pStatesMap); } -void EngineEffect::deleteStatesForInputChannel(const ChannelHandle* inputChannel) { - m_pProcessor->deleteStatesForInputChannel(inputChannel); +void EngineEffect::deleteStatesForInputChannel(const ChannelHandle* pInputChannel) { + m_pProcessor->deleteStatesForInputChannel(pInputChannel); } bool EngineEffect::processEffectsRequest(EffectsRequest& message, @@ -123,8 +123,9 @@ bool EngineEffect::processEffectsRequest(EffectsRequest& message, return false; } -bool EngineEffect::process(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, +bool EngineEffect::process( + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, const CSAMPLE* pInput, CSAMPLE* pOutput, const unsigned int numSamples, @@ -148,7 +149,7 @@ bool EngineEffect::process(const ChannelHandle& inputHandle, // internal buffer for the channel when it gets the intermediate disabling signal. EffectEnableState effectiveEffectEnableState = - m_effectEnableStateForChannelMatrix[inputHandle][outputHandle]; + m_effectEnableStateForChannelMatrix[pInputHandle][pOutputHandle]; // If the EngineEffect is fully disabled, do not let // intermediate enabling/disabing signals from the chain override @@ -182,8 +183,8 @@ bool EngineEffect::process(const ChannelHandle& inputHandle, mixxx::audio::SampleRate(sampleRate), numSamples / mixxx::kEngineChannelCount); - m_pProcessor->process(inputHandle, - outputHandle, + m_pProcessor->process(pInputHandle, + pOutputHandle, pInput, pOutput, engineParameters, @@ -214,7 +215,8 @@ bool EngineEffect::process(const ChannelHandle& inputHandle, // Now that the EffectProcessor has been sent the intermediate enabling/disabling // signal, set the channel state to fully enabled/disabled for the next engine callback. - EffectEnableState& effectOnChannelState = m_effectEnableStateForChannelMatrix[inputHandle][outputHandle]; + EffectEnableState& effectOnChannelState = + m_effectEnableStateForChannelMatrix[pInputHandle][pOutputHandle]; if (effectOnChannelState == EffectEnableState::Disabling) { effectOnChannelState = EffectEnableState::Disabled; } else if (effectOnChannelState == EffectEnableState::Enabling) { diff --git a/src/engine/effects/engineeffect.h b/src/engine/effects/engineeffect.h index 854d1e6e325b..effbe37f38c8 100644 --- a/src/engine/effects/engineeffect.h +++ b/src/engine/effects/engineeffect.h @@ -35,10 +35,10 @@ class EngineEffect final : public EffectsRequestHandler { EffectState* createState(const mixxx::EngineParameters& engineParameters); /// Called in audio thread to load EffectStates received from the main thread - void loadStatesForInputChannel(const ChannelHandle* inputChannel, + void loadStatesForInputChannel(const ChannelHandle* pInputChannel, EffectStatesMap* pStatesMap); /// Called from the main thread for garbage collection after an input channel is disabled - void deleteStatesForInputChannel(const ChannelHandle* inputChannel); + void deleteStatesForInputChannel(const ChannelHandle* pInputChannel); /// Called in audio thread bool processEffectsRequest( @@ -46,8 +46,8 @@ class EngineEffect final : public EffectsRequestHandler { EffectsResponsePipe* pResponsePipe) override; /// Called in audio thread - bool process(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + bool process(const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, const CSAMPLE* pInput, CSAMPLE* pOutput, const unsigned int numSamples, diff --git a/src/engine/effects/engineeffectchain.cpp b/src/engine/effects/engineeffectchain.cpp index 621299fd4b3b..424dd7ced454 100644 --- a/src/engine/effects/engineeffectchain.cpp +++ b/src/engine/effects/engineeffectchain.cpp @@ -146,12 +146,13 @@ bool EngineEffectChain::processEffectsRequest(EffectsRequest& message, return true; } -bool EngineEffectChain::enableForInputChannel(const ChannelHandle* inputHandle, +bool EngineEffectChain::enableForInputChannel( + const ChannelHandle* pInputHandle, EffectStatesMapArray* statesForEffectsInChain) { if (kEffectDebugOutput) { - qDebug() << "EngineEffectChain::enableForInputChannel" << this << inputHandle; + qDebug() << "EngineEffectChain::enableForInputChannel" << this << *pInputHandle; } - auto& outputMap = m_chainStatusForChannelMatrix[*inputHandle]; + auto& outputMap = m_chainStatusForChannelMatrix[pInputHandle]; for (auto&& outputChannelStatus : outputMap) { VERIFY_OR_DEBUG_ASSERT(outputChannelStatus.enableState != EffectEnableState::Enabled) { @@ -174,14 +175,14 @@ bool EngineEffectChain::enableForInputChannel(const ChannelHandle* inputHandle, VERIFY_OR_DEBUG_ASSERT(pStatesMap) { return false; } - m_effects[i]->loadStatesForInputChannel(inputHandle, pStatesMap); + m_effects[i]->loadStatesForInputChannel(pInputHandle, pStatesMap); } } return true; } -bool EngineEffectChain::disableForInputChannel(const ChannelHandle* inputHandle) { - auto& outputMap = m_chainStatusForChannelMatrix[*inputHandle]; +bool EngineEffectChain::disableForInputChannel(const ChannelHandle* pInputHandle) { + auto& outputMap = m_chainStatusForChannelMatrix[pInputHandle]; for (auto&& outputChannelStatus : outputMap) { if (outputChannelStatus.enableState != EffectEnableState::Disabled) { outputChannelStatus.enableState = EffectEnableState::Disabling; @@ -195,7 +196,7 @@ bool EngineEffectChain::disableForInputChannel(const ChannelHandle* inputHandle) } // Called from the main thread for garbage collection after an input channel is disabled -void EngineEffectChain::deleteStatesForInputChannel(const ChannelHandle* inputChannel) { +void EngineEffectChain::deleteStatesForInputChannel(const ChannelHandle* pInputHandle) { // If an output channel is not presently being processed, for example when // PFL is not active, then process() cannot be relied upon to set this // chain's EffectEnableState from Disabling to Disabled. This must be done @@ -209,26 +210,27 @@ void EngineEffectChain::deleteStatesForInputChannel(const ChannelHandle* inputCh // QMap. So it is okay that m_chainStatusForChannelMatrix may be // accessed concurrently in the audio engine thread in process(), // enableForInputChannel(), or disableForInputChannel(). - auto& outputMap = m_chainStatusForChannelMatrix[*inputChannel]; + auto& outputMap = m_chainStatusForChannelMatrix[pInputHandle]; for (auto&& outputChannelStatus : outputMap) { outputChannelStatus.enableState = EffectEnableState::Disabled; } for (EngineEffect* pEffect : qAsConst(m_effects)) { if (pEffect != nullptr) { - pEffect->deleteStatesForInputChannel(inputChannel); + pEffect->deleteStatesForInputChannel(pInputHandle); } } } EngineEffectChain::ChannelStatus& EngineEffectChain::getChannelStatus( - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle) { - ChannelStatus& status = m_chainStatusForChannelMatrix[inputHandle][outputHandle]; + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle) { + ChannelStatus& status = m_chainStatusForChannelMatrix[pInputHandle][pOutputHandle]; return status; } -bool EngineEffectChain::process(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, +bool EngineEffectChain::process( + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pIn, CSAMPLE* pOut, const unsigned int numSamples, @@ -242,7 +244,7 @@ bool EngineEffectChain::process(const ChannelHandle& inputHandle, // appropriately, for example the Echo effect clears its internal buffer for the channel // when it gets the intermediate disabling signal. - ChannelStatus& channelStatus = m_chainStatusForChannelMatrix[inputHandle][outputHandle]; + ChannelStatus& channelStatus = m_chainStatusForChannelMatrix[pInputHandle][pOutputHandle]; EffectEnableState effectiveChainEnableState = channelStatus.enableState; // If the channel is fully disabled, do not let intermediate @@ -276,8 +278,8 @@ bool EngineEffectChain::process(const ChannelHandle& inputHandle, pIntermediateOutput = m_buffer1.data(); } - if (pEffect->process(inputHandle, - outputHandle, + if (pEffect->process(pInputHandle, + pOutputHandle, pIntermediateInput, pIntermediateOutput, numSamples, diff --git a/src/engine/effects/engineeffectchain.h b/src/engine/effects/engineeffectchain.h index e9cef53baadc..29849fdb7f8f 100644 --- a/src/engine/effects/engineeffectchain.h +++ b/src/engine/effects/engineeffectchain.h @@ -36,8 +36,9 @@ class EngineEffectChain final : public EffectsRequestHandler { EffectsResponsePipe* pResponsePipe) override; /// called from audio thread - bool process(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + bool process( + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pIn, CSAMPLE* pOut, const unsigned int numSamples, @@ -64,14 +65,15 @@ class EngineEffectChain final : public EffectsRequestHandler { bool updateParameters(const EffectsRequest& message); bool addEffect(EngineEffect* pEffect, int iIndex); bool removeEffect(EngineEffect* pEffect, int iIndex); - bool enableForInputChannel(const ChannelHandle* inputHandle, + bool enableForInputChannel(const ChannelHandle* pInputHandle, EffectStatesMapArray* statesForEffectsInChain); - bool disableForInputChannel(const ChannelHandle* inputHandle); + bool disableForInputChannel(const ChannelHandle* pInputHandle); // Gets or creates a ChannelStatus entry in m_channelStatus for the provided // handle. - ChannelStatus& getChannelStatus(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle); + ChannelStatus& getChannelStatus( + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle); QString m_group; EffectEnableState m_enableState; diff --git a/src/engine/effects/engineeffectsmanager.cpp b/src/engine/effects/engineeffectsmanager.cpp index 5dae585c537e..3ea829e6a3f4 100644 --- a/src/engine/effects/engineeffectsmanager.cpp +++ b/src/engine/effects/engineeffectsmanager.cpp @@ -94,8 +94,9 @@ void EngineEffectsManager::onCallbackStart() { } } -void EngineEffectsManager::processPreFaderInPlace(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, +void EngineEffectsManager::processPreFaderInPlace( + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pInOut, const unsigned int numSamples, const unsigned int sampleRate) { @@ -103,8 +104,8 @@ void EngineEffectsManager::processPreFaderInPlace(const ChannelHandle& inputHand // This is okay because the equalizer effects do not make use of it. GroupFeatureState featureState; processInner(SignalProcessingStage::Prefader, - inputHandle, - outputHandle, + pInputHandle, + pOutputHandle, pInOut, pInOut, numSamples, @@ -113,8 +114,8 @@ void EngineEffectsManager::processPreFaderInPlace(const ChannelHandle& inputHand } void EngineEffectsManager::processPostFaderInPlace( - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pInOut, const unsigned int numSamples, const unsigned int sampleRate, @@ -122,8 +123,8 @@ void EngineEffectsManager::processPostFaderInPlace( const CSAMPLE_GAIN oldGain, const CSAMPLE_GAIN newGain) { processInner(SignalProcessingStage::Postfader, - inputHandle, - outputHandle, + pInputHandle, + pOutputHandle, pInOut, pInOut, numSamples, @@ -134,8 +135,8 @@ void EngineEffectsManager::processPostFaderInPlace( } void EngineEffectsManager::processPostFaderAndMix( - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pIn, CSAMPLE* pOut, const unsigned int numSamples, @@ -144,8 +145,8 @@ void EngineEffectsManager::processPostFaderAndMix( const CSAMPLE_GAIN oldGain, const CSAMPLE_GAIN newGain) { processInner(SignalProcessingStage::Postfader, - inputHandle, - outputHandle, + pInputHandle, + pOutputHandle, pIn, pOut, numSamples, @@ -157,8 +158,8 @@ void EngineEffectsManager::processPostFaderAndMix( void EngineEffectsManager::processInner( const SignalProcessingStage stage, - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pIn, CSAMPLE* pOut, const unsigned int numSamples, @@ -174,8 +175,9 @@ void EngineEffectsManager::processInner( SampleUtil::applyRampingGain(pIn, oldGain, newGain, numSamples); for (EngineEffectChain* pChain : chains) { if (pChain) { - if (pChain->process(inputHandle, - outputHandle, + if (pChain->process( + pInputHandle, + pOutputHandle, pIn, pOut, numSamples, @@ -211,8 +213,8 @@ void EngineEffectsManager::processInner( pIntermediateOutput = m_buffer1.data(); } - if (pChain->process(inputHandle, - outputHandle, + if (pChain->process(pInputHandle, + pOutputHandle, pIntermediateInput, pIntermediateOutput, numSamples, diff --git a/src/engine/effects/engineeffectsmanager.h b/src/engine/effects/engineeffectsmanager.h index 9eacda043c6b..17f9c607f44e 100644 --- a/src/engine/effects/engineeffectsmanager.h +++ b/src/engine/effects/engineeffectsmanager.h @@ -30,8 +30,8 @@ class EngineEffectsManager final : public EffectsRequestHandler { /// Process the prefader EngineEffectChains on the pInOut buffer, modifying /// the contents of the input buffer. void processPreFaderInPlace( - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pInOut, const unsigned int numSamples, const unsigned int sampleRate); @@ -39,8 +39,8 @@ class EngineEffectsManager final : public EffectsRequestHandler { /// Process the postfader EngineEffectChains on the pInOut buffer, modifying /// the contents of the input buffer. void processPostFaderInPlace( - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pInOut, const unsigned int numSamples, const unsigned int sampleRate, @@ -54,8 +54,8 @@ class EngineEffectsManager final : public EffectsRequestHandler { /// buffer for every channel, which would potentially require allocation on the /// audio thread because ChannelMixer supports an arbitrary number of channels. void processPostFaderAndMix( - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pIn, CSAMPLE* pOut, const unsigned int numSamples, @@ -84,8 +84,8 @@ class EngineEffectsManager final : public EffectsRequestHandler { // samples, so numSamples/2 left channel samples and numSamples/2 right // channel samples. void processInner(const SignalProcessingStage stage, - const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, CSAMPLE* pIn, CSAMPLE* pOut, const unsigned int numSamples, diff --git a/src/engine/enginemaster.cpp b/src/engine/enginemaster.cpp index 31a9dc4c054d..476d604fd75a 100644 --- a/src/engine/enginemaster.cpp +++ b/src/engine/enginemaster.cpp @@ -816,7 +816,7 @@ void EngineMaster::addChannel(EngineChannel* pChannel) { pChannel->setChannelIndex(pChannelInfo->m_index); pChannelInfo->m_pChannel = pChannel; const QString& group = pChannel->getGroup(); - pChannelInfo->m_handle = m_pChannelHandleFactory->getOrCreateHandle(group); + pChannelInfo->m_pHandle = m_pChannelHandleFactory->getOrCreateHandle(group); pChannelInfo->m_pVolumeControl = new ControlAudioTaperPot( ConfigKey(group, "volume"), -20, 0, 1); pChannelInfo->m_pVolumeControl->setDefaultValue(1.0); diff --git a/src/engine/enginemaster.h b/src/engine/enginemaster.h index 36164db2fd05..8938587224f8 100644 --- a/src/engine/enginemaster.h +++ b/src/engine/enginemaster.h @@ -109,18 +109,14 @@ class EngineMaster : public QObject, public AudioSource { CSAMPLE_GAIN getMasterGain(int channelIndex) const; struct ChannelInfo { - ChannelInfo(int index) - : m_pChannel(NULL), - m_pBuffer(NULL), - m_pVolumeControl(NULL), - m_pMuteControl(NULL), - m_index(index) { + explicit ChannelInfo(int index) + : m_index(index) { } - ChannelHandle m_handle; - EngineChannel* m_pChannel; - CSAMPLE* m_pBuffer; - ControlObject* m_pVolumeControl; - ControlPushButton* m_pMuteControl; + const ChannelHandle* m_pHandle{}; + EngineChannel* m_pChannel{}; + CSAMPLE* m_pBuffer{}; + ControlObject* m_pVolumeControl{}; + ControlPushButton* m_pMuteControl{}; GroupFeatureState m_features; int m_index; }; diff --git a/src/mixer/playermanager.cpp b/src/mixer/playermanager.cpp index ae76ecbc8bfc..9a7daa75d2dc 100644 --- a/src/mixer/playermanager.cpp +++ b/src/mixer/playermanager.cpp @@ -552,11 +552,11 @@ BaseTrackPlayer* PlayerManager::getPlayer(const QString& group) const { return getPlayer(m_pEngine->registerChannelGroup(group).handle()); } -BaseTrackPlayer* PlayerManager::getPlayer(const ChannelHandle& handle) const { +BaseTrackPlayer* PlayerManager::getPlayer(const ChannelHandle* pHandle) const { const auto locker = lockMutex(&m_mutex); - if (m_players.contains(handle)) { - return m_players[handle]; + if (m_players.contains(pHandle)) { + return m_players[pHandle]; } return nullptr; } diff --git a/src/mixer/playermanager.h b/src/mixer/playermanager.h index 723b9b799c4f..caaca49b258a 100644 --- a/src/mixer/playermanager.h +++ b/src/mixer/playermanager.h @@ -32,7 +32,7 @@ class PlayerManagerInterface { virtual ~PlayerManagerInterface() = default; virtual BaseTrackPlayer* getPlayer(const QString& group) const = 0; - virtual BaseTrackPlayer* getPlayer(const ChannelHandle& channelHandle) const = 0; + virtual BaseTrackPlayer* getPlayer(const ChannelHandle* pChannelHandle) const = 0; // Get the deck by its deck number. Decks are numbered starting with 1. virtual Deck* getDeck(unsigned int player) const = 0; @@ -98,7 +98,7 @@ class PlayerManager : public QObject, public PlayerManagerInterface { // Auxiliaries and microphones are not players. BaseTrackPlayer* getPlayer(const QString& group) const override; // Get a BaseTrackPlayer (Deck, Sampler or PreviewDeck) by its handle. - BaseTrackPlayer* getPlayer(const ChannelHandle& handle) const override; + BaseTrackPlayer* getPlayer(const ChannelHandle* pHandle) const override; // Get the deck by its deck number. Decks are numbered starting with 1. Deck* getDeck(unsigned int player) const override; @@ -278,5 +278,5 @@ class PlayerManager : public QObject, public PlayerManagerInterface { QList m_previewDecks; QList m_microphones; QList m_auxiliaries; - QMap m_players; + QMap m_players; }; diff --git a/src/test/autodjprocessor_test.cpp b/src/test/autodjprocessor_test.cpp index edef14b34b73..1629f9fa24c8 100644 --- a/src/test/autodjprocessor_test.cpp +++ b/src/test/autodjprocessor_test.cpp @@ -122,7 +122,7 @@ class MockPlayerManager : public PlayerManagerInterface { } MOCK_CONST_METHOD1(getPlayer, BaseTrackPlayer*(const QString&)); - MOCK_CONST_METHOD1(getPlayer, BaseTrackPlayer*(const ChannelHandle&)); + MOCK_CONST_METHOD1(getPlayer, BaseTrackPlayer*(const ChannelHandle*)); MOCK_CONST_METHOD1(getDeck, Deck*(unsigned int)); MOCK_CONST_METHOD1(getPreviewDeck, PreviewDeck*(unsigned int)); MOCK_CONST_METHOD1(getSampler, Sampler*(unsigned int)); diff --git a/src/test/baseeffecttest.h b/src/test/baseeffecttest.h index 25b33b0edc71..a1ba9ac21af4 100644 --- a/src/test/baseeffecttest.h +++ b/src/test/baseeffecttest.h @@ -42,8 +42,8 @@ class MockEffectProcessor : public EffectProcessor { const EffectStatesMap* pStatesMap)); MOCK_METHOD1(deleteStatesForInputChannel, void(const ChannelHandle* inputChannel)); MOCK_METHOD7(process, - void(const ChannelHandle& inputHandle, - const ChannelHandle& outputHandle, + void(const ChannelHandle* pInputHandle, + const ChannelHandle* pOutputHandle, const CSAMPLE* pInput, CSAMPLE* pOutput, const mixxx::EngineParameters& engineParameters, diff --git a/src/test/channelhandle_test.cpp b/src/test/channelhandle_test.cpp index 4d85fbde9ecf..df5fe245acda 100644 --- a/src/test/channelhandle_test.cpp +++ b/src/test/channelhandle_test.cpp @@ -4,52 +4,46 @@ #include "engine/channelhandle.h" #include "test/mixxxtest.h" -namespace { - TEST(ChannelHandleTest, BasicUsage) { ChannelHandleFactory factory; const QString group = "[Test]"; const QString group2 = "[Test2]"; - ChannelHandle nullHandle; - EXPECT_EQ(nullHandle, nullHandle); - EXPECT_FALSE(nullHandle.valid()); - - EXPECT_FALSE(factory.handleForGroup(group).valid()); + EXPECT_EQ(nullptr, factory.handleForGroup(group)); // The ChannelHandleFactory constructor creates handles for [Master] and [Headphone] - EXPECT_EQ(0, factory.getOrCreateHandle(group).handle()); - EXPECT_EQ(0, factory.getOrCreateHandle(group).handle()); - ChannelHandle testHandle = factory.handleForGroup(group); - EXPECT_TRUE(testHandle.valid()); - EXPECT_EQ(0, testHandle.handle()); - EXPECT_QSTRING_EQ(group, factory.groupForHandle(testHandle)); - EXPECT_NE(nullHandle, testHandle); - EXPECT_EQ(testHandle, testHandle); - - EXPECT_FALSE(factory.handleForGroup(group2).valid()); - EXPECT_EQ(1, factory.getOrCreateHandle(group2).handle()); - EXPECT_EQ(1, factory.getOrCreateHandle(group2).handle()); - ChannelHandle testHandle2 = factory.handleForGroup(group2); - EXPECT_TRUE(testHandle2.valid()); - EXPECT_EQ(1, testHandle2.handle()); - EXPECT_QSTRING_EQ(group2, factory.groupForHandle(testHandle2)); - EXPECT_NE(nullHandle, testHandle2); - EXPECT_EQ(testHandle2, testHandle2); - - EXPECT_NE(testHandle, testHandle2); + EXPECT_EQ(0, factory.getOrCreateHandle(group)->handle()); + EXPECT_EQ(0, factory.getOrCreateHandle(group)->handle()); + const ChannelHandle* pTestHandle = factory.handleForGroup(group); + EXPECT_NE(nullptr, pTestHandle); + EXPECT_TRUE(pTestHandle->valid()); + EXPECT_EQ(0, pTestHandle->handle()); + EXPECT_QSTRING_EQ(group, factory.groupForHandle(pTestHandle)); + EXPECT_EQ(*pTestHandle, *pTestHandle); + + EXPECT_EQ(nullptr, factory.handleForGroup(group2)); + EXPECT_EQ(1, factory.getOrCreateHandle(group2)->handle()); + EXPECT_EQ(1, factory.getOrCreateHandle(group2)->handle()); + const ChannelHandle* pTestHandle2 = factory.handleForGroup(group2); + EXPECT_NE(nullptr, pTestHandle2); + EXPECT_TRUE(pTestHandle2->valid()); + EXPECT_EQ(1, pTestHandle2->handle()); + EXPECT_QSTRING_EQ(group2, factory.groupForHandle(pTestHandle2)); + EXPECT_EQ(*pTestHandle2, *pTestHandle2); + + EXPECT_NE(*pTestHandle, *pTestHandle2); } TEST(ChannelHandleTest, ChannelHandleMap) { ChannelHandleFactory factory; - ChannelHandle test = factory.getOrCreateHandle("[Test]"); - EXPECT_TRUE(test.valid()); - ChannelHandle test2 = factory.getOrCreateHandle("[Test2]"); - EXPECT_TRUE(test2.valid()); + const ChannelHandle* test = factory.getOrCreateHandle("[Test]"); + EXPECT_TRUE(test->valid()); + const ChannelHandle* test2 = factory.getOrCreateHandle("[Test2]"); + EXPECT_TRUE(test2->valid()); ChannelHandleMap map; - EXPECT_QSTRING_EQ(QString(), map.at(ChannelHandle())); + EXPECT_QSTRING_EQ(QString(), map.at(nullptr)); map.insert(test2, "bar"); EXPECT_QSTRING_EQ("bar", map.at(test2)); @@ -65,5 +59,3 @@ TEST(ChannelHandleTest, ChannelHandleMap) { map.insert(test, "foo"); EXPECT_QSTRING_EQ("foo", map.at(test)); } - -} // namespace diff --git a/src/test/enginemicrophonetest.cpp b/src/test/enginemicrophonetest.cpp index ccdedefb31b9..d9d0738d186a 100644 --- a/src/test/enginemicrophonetest.cpp +++ b/src/test/enginemicrophonetest.cpp @@ -24,7 +24,7 @@ class EngineMicrophoneTest : public SignalPathTest { // No need for a real handle in this test. m_pMicrophone = new EngineMicrophone( - ChannelHandleAndGroup(ChannelHandle(), "[Microphone]"), m_pEffectsManager); + ChannelHandleAndGroup(nullptr, "[Microphone]"), m_pEffectsManager); m_pTalkover = ControlObject::getControl(ConfigKey("[Microphone]", "talkover")); } diff --git a/src/test/nativeeffects_test.cpp b/src/test/nativeeffects_test.cpp index 442a326ae949..679755414404 100644 --- a/src/test/nativeeffects_test.cpp +++ b/src/test/nativeeffects_test.cpp @@ -39,7 +39,7 @@ void benchmarkBuiltInEffectDefaultParameters(const mixxx::EngineParameters& engi QSet activeInputChannels; QString channel1_group = QString("[Channel1]"); - ChannelHandle channel1 = factory.getOrCreateHandle(channel1_group); + const ChannelHandle* channel1 = factory.getOrCreateHandle(channel1_group); ChannelHandleAndGroup handle_and_group(channel1, channel1_group); pEffectsManager->registerInputChannel(handle_and_group); pEffectsManager->registerOutputChannel(handle_and_group); From 1820d20e8eefeb37a2410fef4c0b6f0fec9bfaa5 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 12 Feb 2022 19:38:35 +0100 Subject: [PATCH 2/3] ChannelHandle: Update outdated comments --- src/engine/channelhandle.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/engine/channelhandle.h b/src/engine/channelhandle.h index 215bcc82852d..10de6fd0a9dc 100644 --- a/src/engine/channelhandle.h +++ b/src/engine/channelhandle.h @@ -30,6 +30,10 @@ constexpr int kMaxExpectedChannelGroups = 256; /// /// A helper class, ChannelHandleFactory, keeps a running count of handles that /// have been assigned. +/// +/// Channel handles are passed as pointers to permanent memory addresses and +/// can neither be copied nor moved!!! This is required to pass them from/to +/// the real-time thread, e.g. for managing effect configurations. class ChannelHandle final { public: bool valid() const { @@ -133,12 +137,7 @@ class ChannelHandleFactory { public: ChannelHandleFactory() = default; - /// Obtain a reference to a channel handle for a group - /// - /// The returned reference must be stable during a session and references - /// an entry from an internal map. Thus it is safe to get the address of - /// this reference. This is REQUIRED for passing ChannelHandle to - /// EffectChainManager by a pointer!!! + /// Obtain a a channel handle for a group const ChannelHandle* getOrCreateHandle(const QString& group) { auto groupToHandleIter = m_groupToHandle.find(group); if (groupToHandleIter == m_groupToHandle.end()) { From 90c0c735617bb2afd176c069f8e0bbdd50bb0ff4 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 12 Feb 2022 20:34:28 +0100 Subject: [PATCH 3/3] Prevent copy/move of ChannelHandleFactory explicitly --- src/engine/channelhandle.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/engine/channelhandle.h b/src/engine/channelhandle.h index 10de6fd0a9dc..c69b2ad99b28 100644 --- a/src/engine/channelhandle.h +++ b/src/engine/channelhandle.h @@ -133,7 +133,7 @@ inline qhash_seed_t qHash( // objects are not compatible and will produce incorrect results when compared, // stored in the same container, etc. In practice we only use one instance in // EngineMaster. -class ChannelHandleFactory { +class ChannelHandleFactory final { public: ChannelHandleFactory() = default; @@ -180,6 +180,11 @@ class ChannelHandleFactory { } private: + ChannelHandleFactory(ChannelHandleFactory&&) = delete; + ChannelHandleFactory(const ChannelHandleFactory&) = delete; + ChannelHandleFactory& operator=(ChannelHandleFactory&&) = delete; + ChannelHandleFactory& operator=(const ChannelHandleFactory&) = delete; + ChannelHandle m_groupHandles[kMaxExpectedChannelGroups]; QHash m_groupToHandle; QHash m_handleToGroup;