Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/control/controlproxy.cpp
src/control/controlpushbutton.cpp
src/control/controlttrotary.cpp
src/control/grouphandle.cpp
src/controllers/controller.cpp
src/controllers/controllerenumerator.cpp
src/controllers/controllerinputmappingtablemodel.cpp
Expand Down
81 changes: 81 additions & 0 deletions src/control/grouphandle.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "control/grouphandle.h"

#include <QAtomicInt>
#include <QHash>
#include <QMutex>
#include <QMutexLocker>

namespace {

QMutex allGroupHandlesByNameMutex;

typedef QHash<QString, GroupHandle> AllGroupHandlesByName;
AllGroupHandlesByName allGroupHandlesByName;

QAtomicInt allGroupHandlesFrozen;

} // namespace

GroupHandle getOrCreateGroupHandleByName(const QString& name, bool create) {
DEBUG_ASSERT(name == name.trimmed());
if (allGroupHandlesFrozen.loadRelaxed()) {
// lock-free
DEBUG_ASSERT(!create);
auto iter = allGroupHandlesByName.find(name);
if (iter != allGroupHandlesByName.end()) {
return iter.value();
}
} else {
// blocking
auto locked = QMutexLocker(&allGroupHandlesByNameMutex);
auto iter = allGroupHandlesByName.find(name);
if (iter != allGroupHandlesByName.end()) {
// Unlock mutex before logging (I/O)
locked.unlock();
qDebug() << "Found existing group handle" << iter.value();
return iter.value();
}
if (create && !allGroupHandlesFrozen.loadAcquire()) {
const auto index = allGroupHandlesByName.size();
// This will leak memory, i.e. descriptors will never be
// freed during a session until application shutdown.
const auto handle = new mixxx::grouphandle_private::Descriptor{index, name};
allGroupHandlesByName.insert(name, handle);
// Unlock mutex before logging (I/O)
locked.unlock();
qInfo() << "Created new group handle" << handle;
return handle;
}
}
qWarning() << "Unknown group name" << name;
return kNullGroupHandle;
}

void freezeAllGroupHandles() {
const auto locked = QMutexLocker(&allGroupHandlesByNameMutex);
allGroupHandlesByName.squeeze();
allGroupHandlesFrozen.storeRelease(1);
}

int resetAllGroupHandles() {
auto locked = QMutexLocker(&allGroupHandlesByNameMutex);
AllGroupHandlesByName tmpGroupHandlesByName;
tmpGroupHandlesByName.swap(allGroupHandlesByName);
allGroupHandlesFrozen.storeRelease(0);
locked.unlock();
for (const auto groupHandle : qAsConst(tmpGroupHandlesByName)) {
qInfo() << "Deleting" << groupHandle;
delete groupHandle;
}
return tmpGroupHandlesByName.size();
}

namespace mixxx {
namespace grouphandle_private {

QDebug operator<<(QDebug dbg, const mixxx::grouphandle_private::Descriptor& arg) {
return dbg << "GroupHandle{" << arg.m_index << arg.m_name << "}";
}

} // namespace grouphandle_private
} // namespace mixxx
137 changes: 137 additions & 0 deletions src/control/grouphandle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#pragma once

#include <QString>
#include <QtDebug>

#include "util/assert.h"
#include "util/compatibility/qhash.h"

namespace mixxx {
namespace grouphandle_private {
class Descriptor;
} // namespace grouphandle_private
} // namespace mixxx

typedef const mixxx::grouphandle_private::Descriptor* GroupHandle;

constexpr GroupHandle kNullGroupHandle = nullptr;

GroupHandle getOrCreateGroupHandleByName(
const QString& name,
bool create = true);

inline GroupHandle getGroupHandleByName(const QString& name) {
return getOrCreateGroupHandleByName(name, false);
}

/// Finalize the registration of groups
///
/// After invoking this functions no handles for new groups
/// could be created. Accessing the existing handles by their
/// name will be lock-free from now on.
void freezeAllGroupHandles();

/// Free all group handles
///
/// Do not call while handles are in use!!! Mainly needed for testing.
///
/// Returns the number of groups.
int resetAllGroupHandles();

namespace mixxx {
namespace grouphandle_private {

class Descriptor final {
public:
static constexpr int kInvalidIndex = -1;

// Trailing return type declaration is required for Clang 14
friend auto ::getOrCreateGroupHandleByName(
const QString& name,
bool create) -> GroupHandle;

Descriptor() = default;
Descriptor(Descriptor&&) = delete;
Descriptor(const Descriptor&) = delete;
Descriptor& operator=(Descriptor&&) = delete;
Descriptor& operator=(const Descriptor&) = delete;

/// Index
///
/// 0-based integer identifier. The maximum number is limited by the
/// total number of different descriptors, i.e. the total number of
/// distinct group names.
friend int indexOfGroupHandle(GroupHandle handle) {
if (!handle) {
return kInvalidIndex;
}
DEBUG_ASSERT(handle->valid());
return handle->m_index;
}

/// Group name
///
/// String identifier.
friend QString nameOfGroupHandle(GroupHandle handle) {
if (!handle) {
return {};
}
DEBUG_ASSERT(handle->valid());
return handle->m_name;
}

friend bool operator<(const Descriptor& lhs, const Descriptor& rhs) {
return lhs.m_index < rhs.m_index;
}

friend bool operator==(const Descriptor& lhs, const Descriptor& rhs) {
return lhs.m_index == rhs.m_index;
}

friend qhash_seed_t qHash(const Descriptor& arg, qhash_seed_t seed = 0) {
return qHash(arg.m_index, seed);
}

friend QDebug operator<<(QDebug dbg, const Descriptor& arg);

private:
bool valid() const {
DEBUG_ASSERT(m_index == kInvalidIndex || m_index >= 0);
DEBUG_ASSERT((m_index == kInvalidIndex) == m_name.isEmpty());
return m_index != kInvalidIndex;
}

Descriptor(int index, QString name)
: m_index(index),
m_name(std::move(name)) {
}

int m_index = kInvalidIndex;
QString m_name;
};

inline bool operator!=(
const Descriptor& lhs,
const Descriptor& rhs) {
return !(lhs == rhs);
}

} // namespace grouphandle_private

} // namespace mixxx

inline qhash_seed_t qHash(GroupHandle arg, qhash_seed_t seed = 0) {
if (arg) {
return qHash(*arg, seed);
} else {
return qHash(mixxx::grouphandle_private::Descriptor{}, seed);
}
}

inline QDebug operator<<(QDebug dbg, GroupHandle arg) {
if (arg) {
return dbg << *arg;
} else {
return dbg << mixxx::grouphandle_private::Descriptor{};
}
}
6 changes: 1 addition & 5 deletions src/coreservices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@

#include <QtX11Extras/QX11Info>

#include "engine/channelhandle.h"
// Xlibint.h predates C++ and defines macros which conflict
// with references to std::max and std::min
#undef max
Expand Down Expand Up @@ -253,16 +252,13 @@ void CoreServices::initialize(QApplication* pApp) {

m_pControlIndicatorTimer = std::make_shared<mixxx::ControlIndicatorTimer>(this);

auto pChannelHandleFactory = std::make_shared<ChannelHandleFactory>();

emit initializationProgressUpdate(20, tr("effects"));
m_pEffectsManager = std::make_shared<EffectsManager>(pConfig, pChannelHandleFactory);
m_pEffectsManager = std::make_shared<EffectsManager>(pConfig);

m_pEngine = std::make_shared<EngineMaster>(
pConfig,
"[Master]",
m_pEffectsManager.get(),
pChannelHandleFactory,
true);

emit initializationProgressUpdate(30, tr("audio interface"));
Expand Down
Loading