summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/library/coverartcache.cpp8
-rw-r--r--src/library/trackcollection.cpp49
-rw-r--r--src/library/trackcollection.h16
-rw-r--r--src/library/trackcollectionmanager.cpp23
-rw-r--r--src/library/trackcollectionmanager.h4
-rw-r--r--src/musicbrainz/tagfetcher.cpp23
-rw-r--r--src/musicbrainz/web/musicbrainzrecordingstask.cpp8
-rw-r--r--src/network/jsonwebtask.cpp10
-rw-r--r--src/network/webtask.cpp14
-rw-r--r--src/track/globaltrackcache.cpp46
-rw-r--r--src/track/globaltrackcache.h35
-rw-r--r--src/util/thread_affinity.h20
12 files changed, 174 insertions, 82 deletions
diff --git a/src/library/coverartcache.cpp b/src/library/coverartcache.cpp
index 4e523dfd6c..bcf0093d3b 100644
--- a/src/library/coverartcache.cpp
+++ b/src/library/coverartcache.cpp
@@ -1,13 +1,14 @@
+#include "library/coverartcache.h"
+
#include <QFutureWatcher>
#include <QPixmapCache>
#include <QtConcurrentRun>
#include <QtDebug>
-#include "library/coverartcache.h"
#include "library/coverartutils.h"
#include "util/compatibility.h"
#include "util/logger.h"
-
+#include "util/thread_affinity.h"
namespace {
@@ -242,8 +243,7 @@ void CoverArtCache::coverLoaded() {
res.coverArt.loadedImage.image = placeholderImage;
}
// Create pixmap, GUI thread only!
- DEBUG_ASSERT(QThread::currentThread() ==
- QCoreApplication::instance()->thread());
+ DEBUG_ASSERT_MAIN_THREAD_AFFINITY();
DEBUG_ASSERT(!res.coverArt.loadedImage.image.isNull());
pixmap = QPixmap::fromImage(res.coverArt.loadedImage.image);
// Don't cache full size covers (resizedToWidth = 0)
diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp
index 415dcb3c3d..82d2a25f95 100644
--- a/src/library/trackcollection.cpp
+++ b/src/library/trackcollection.cpp
@@ -1,5 +1,4 @@
#include <QApplication>
-#include <QThread>
#include "library/trackcollection.h"
@@ -65,14 +64,14 @@ TrackCollection::~TrackCollection() {
}
void TrackCollection::repairDatabase(QSqlDatabase database) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
kLogger.info() << "Repairing database";
m_crates.repairDatabase(database);
}
void TrackCollection::connectDatabase(QSqlDatabase database) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
kLogger.info() << "Connecting database";
m_database = database;
@@ -86,7 +85,7 @@ void TrackCollection::connectDatabase(QSqlDatabase database) {
}
void TrackCollection::disconnectDatabase() {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
kLogger.info() << "Disconnecting database";
m_database = QSqlDatabase();
@@ -95,7 +94,7 @@ void TrackCollection::disconnectDatabase() {
}
void TrackCollection::connectTrackSource(QSharedPointer<BaseTrackCache> pTrackSource) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
VERIFY_OR_DEBUG_ASSERT(m_pTrackSource.isNull()) {
kLogger.warning() << "Track source has already been connected";
@@ -130,7 +129,7 @@ void TrackCollection::connectTrackSource(QSharedPointer<BaseTrackCache> pTrackSo
}
QWeakPointer<BaseTrackCache> TrackCollection::disconnectTrackSource() {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
auto pWeakPtr = m_pTrackSource.toWeakRef();
if (m_pTrackSource) {
@@ -142,6 +141,8 @@ QWeakPointer<BaseTrackCache> TrackCollection::disconnectTrackSource() {
}
bool TrackCollection::addDirectory(const QString& dir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
SqlTransaction transaction(m_database);
switch (m_directoryDao.addDirectory(dir)) {
case SQL_ERROR:
@@ -158,6 +159,8 @@ bool TrackCollection::addDirectory(const QString& dir) {
}
bool TrackCollection::removeDirectory(const QString& dir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
SqlTransaction transaction(m_database);
switch (m_directoryDao.removeDirectory(dir)) {
case SQL_ERROR:
@@ -172,7 +175,7 @@ bool TrackCollection::removeDirectory(const QString& dir) {
}
void TrackCollection::relocateDirectory(QString oldDir, QString newDir) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// We only call this method if the user has picked a relocated directory via
// a file dialog. This means the system sandboxer (if we are sandboxed) has
@@ -236,7 +239,7 @@ QList<TrackId> TrackCollection::resolveTrackIdsFromLocations(
}
bool TrackCollection::hideTracks(const QList<TrackId>& trackIds) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Warn if tracks have a playlist membership
QSet<int> allPlaylistIds;
@@ -302,11 +305,13 @@ bool TrackCollection::hideTracks(const QList<TrackId>& trackIds) {
}
void TrackCollection::hideAllTracks(const QDir& rootDir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
m_trackDao.hideAllTracks(rootDir);
}
bool TrackCollection::unhideTracks(const QList<TrackId>& trackIds) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
VERIFY_OR_DEBUG_ASSERT(m_trackDao.unhideTracks(trackIds)) {
return false;
@@ -330,7 +335,7 @@ bool TrackCollection::unhideTracks(const QList<TrackId>& trackIds) {
bool TrackCollection::purgeTracks(
const QList<TrackId>& trackIds) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Transactional
SqlTransaction transaction(m_database);
@@ -369,6 +374,8 @@ bool TrackCollection::purgeTracks(
bool TrackCollection::purgeAllTracks(
const QDir& rootDir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
QList<TrackRef> trackRefs = m_trackDao.getAllTrackRefs(rootDir);
QList<TrackId> trackIds;
trackIds.reserve(trackRefs.size());
@@ -382,7 +389,7 @@ bool TrackCollection::purgeAllTracks(
bool TrackCollection::insertCrate(
const Crate& crate,
CrateId* pCrateId) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Transactional
SqlTransaction transaction(m_database);
@@ -409,7 +416,7 @@ bool TrackCollection::insertCrate(
bool TrackCollection::updateCrate(
const Crate& crate) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Transactional
SqlTransaction transaction(m_database);
@@ -431,7 +438,7 @@ bool TrackCollection::updateCrate(
bool TrackCollection::deleteCrate(
CrateId crateId) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Transactional
SqlTransaction transaction(m_database);
@@ -454,7 +461,7 @@ bool TrackCollection::deleteCrate(
bool TrackCollection::addCrateTracks(
CrateId crateId,
const QList<TrackId>& trackIds) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Transactional
SqlTransaction transaction(m_database);
@@ -477,7 +484,7 @@ bool TrackCollection::addCrateTracks(
bool TrackCollection::removeCrateTracks(
CrateId crateId,
const QList<TrackId>& trackIds) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
// Transactional
SqlTransaction transaction(m_database);
@@ -500,7 +507,7 @@ bool TrackCollection::removeCrateTracks(
bool TrackCollection::updateAutoDjCrate(
CrateId crateId,
bool isAutoDjSource) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
Crate crate;
VERIFY_OR_DEBUG_ASSERT(crates().readCrateById(crateId, &crate)) {
@@ -514,18 +521,22 @@ bool TrackCollection::updateAutoDjCrate(
}
void TrackCollection::saveTrack(Track* pTrack) {
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
m_trackDao.saveTrack(pTrack);
}
TrackPointer TrackCollection::getTrackById(
TrackId trackId) const {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_trackDao.getTrackById(trackId);
}
TrackPointer TrackCollection::getTrackByRef(
const TrackRef& trackRef) const {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_trackDao.getTrackByRef(trackRef);
}
@@ -537,12 +548,16 @@ TrackId TrackCollection::getTrackIdByRef(
TrackPointer TrackCollection::getOrAddTrack(
const TrackRef& trackRef,
bool* pAlreadyInLibrary) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_trackDao.getOrAddTrack(trackRef, pAlreadyInLibrary);
}
TrackId TrackCollection::addTrack(
const TrackPointer& pTrack,
bool unremove) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
m_trackDao.addTracksPrepare();
const auto trackId = m_trackDao.addTracksAddTrack(pTrack, unremove);
m_trackDao.addTracksFinish(!trackId.isValid());
diff --git a/src/library/trackcollection.h b/src/library/trackcollection.h
index 9195e2660d..4e8af126ac 100644
--- a/src/library/trackcollection.h
+++ b/src/library/trackcollection.h
@@ -5,14 +5,15 @@
#include <QSharedPointer>
#include <QSqlDatabase>
-#include "preferences/usersettings.h"
#include "library/crate/cratestorage.h"
-#include "library/dao/trackdao.h"
-#include "library/dao/cuedao.h"
-#include "library/dao/playlistdao.h"
#include "library/dao/analysisdao.h"
+#include "library/dao/cuedao.h"
#include "library/dao/directorydao.h"
#include "library/dao/libraryhashdao.h"
+#include "library/dao/playlistdao.h"
+#include "library/dao/trackdao.h"
+#include "preferences/usersettings.h"
+#include "util/thread_affinity.h"
// forward declaration(s)
class BaseTrackCache;
@@ -36,23 +37,29 @@ class TrackCollection : public QObject,
void disconnectDatabase() override;
QSqlDatabase database() const {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_database;
}
const CrateStorage& crates() const {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_crates;
}
TrackDAO& getTrackDAO() {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_trackDao;
}
PlaylistDAO& getPlaylistDAO() {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_playlistDao;
}
DirectoryDAO& getDirectoryDAO() {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_directoryDao;
}
AnalysisDao& getAnalysisDAO() {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_analysisDao;
}
@@ -60,6 +67,7 @@ class TrackCollection : public QObject,
QWeakPointer<BaseTrackCache> disconnectTrackSource();
QSharedPointer<BaseTrackCache> getTrackSource() const {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_pTrackSource;
}
diff --git a/src/library/trackcollectionmanager.cpp b/src/library/trackcollectionmanager.cpp
index e912e7c3e3..a2c348717c 100644
--- a/src/library/trackcollectionmanager.cpp
+++ b/src/library/trackcollectionmanager.cpp
@@ -183,6 +183,7 @@ void TrackCollectionManager::saveEvictedTrack(Track* pTrack) noexcept {
void TrackCollectionManager::saveTrack(
Track* pTrack,
TrackMetadataExportMode mode) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(pTrack);
DEBUG_ASSERT(pTrack->getDateAdded().isValid());
@@ -274,14 +275,20 @@ void TrackCollectionManager::exportTrackMetadata(
}
bool TrackCollectionManager::addDirectory(const QString& dir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_pInternalCollection->addDirectory(dir);
}
bool TrackCollectionManager::removeDirectory(const QString& dir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_pInternalCollection->removeDirectory(dir);
}
void TrackCollectionManager::relocateDirectory(QString oldDir, QString newDir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
kLogger.debug()
<< "Relocating directory in internal track collection:"
<< oldDir
@@ -305,18 +312,26 @@ void TrackCollectionManager::relocateDirectory(QString oldDir, QString newDir) {
}
bool TrackCollectionManager::hideTracks(const QList<TrackId>& trackIds) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_pInternalCollection->hideTracks(trackIds);
}
bool TrackCollectionManager::unhideTracks(const QList<TrackId>& trackIds) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
return m_pInternalCollection->unhideTracks(trackIds);
}
void TrackCollectionManager::hideAllTracks(const QDir& rootDir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
m_pInternalCollection->hideAllTracks(rootDir);
}
void TrackCollectionManager::purgeTracks(const QList<TrackRef>& trackRefs) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
if (trackRefs.isEmpty()) {
return;
}
@@ -358,6 +373,8 @@ void TrackCollectionManager::purgeTracks(const QList<TrackRef>& trackRefs) {
}
void TrackCollectionManager::purgeAllTracks(const QDir& rootDir) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
kLogger.debug()
<< "Purging directory"
<< rootDir
@@ -384,6 +401,8 @@ void TrackCollectionManager::purgeAllTracks(const QDir& rootDir) {
TrackPointer TrackCollectionManager::getOrAddTrack(
const TrackRef& trackRef,
bool* pAlreadyInLibrary) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
bool alreadyInLibrary;
if (pAlreadyInLibrary) {
alreadyInLibrary = *pAlreadyInLibrary;
@@ -420,6 +439,8 @@ void TrackCollectionManager::slotScanTrackAdded(TrackPointer pTrack) {
}
void TrackCollectionManager::slotScanTracksUpdated(QSet<TrackId> updatedTrackIds) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
// Already updated in m_pInternalCollection
if (updatedTrackIds.isEmpty()) {
return;
@@ -460,6 +481,8 @@ void TrackCollectionManager::slotScanTracksUpdated(QSet<TrackId> updatedTrackIds
void TrackCollectionManager::slotScanTracksRelocated(
QList<RelocatedTrack> relocatedTracks) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
+
// Already replaced in m_pInternalCollection
if (m_externalCollections.isEmpty()) {
return;
diff --git a/src/library/trackcollectionmanager.h b/src/library/trackcollectionmanager.h
index eeda99ac73..751df279a5 100644
--- a/src/library/trackcollectionmanager.h
+++ b/src/library/trackcollectionmanager.h
@@ -3,7 +3,6 @@
#include <QDir>
#include <QList>
#include <QSet>
-
#include <memory>
#include "library/relocatedtrack.h"
@@ -11,6 +10,7 @@
#include "track/globaltrackcache.h"
#include "util/db/dbconnectionpool.h"
#include "util/parented_ptr.h"
+#include "util/thread_affinity.h"
class LibraryScanner;
class TrackCollection;
@@ -37,10 +37,12 @@ class TrackCollectionManager: public QObject,
~TrackCollectionManager() override;
TrackCollection* internalCollection() {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_pInternalCollection;
}
const QList<ExternalTrackCollection*>& externalCollections() const {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
return m_externalCollections;
}
diff --git a/src/musicbrainz/tagfetcher.cpp b/src/musicbrainz/tagfetcher.cpp
index f447930ffe..1bf11d9546 100644
--- a/src/musicbrainz/tagfetcher.cpp
+++ b/src/musicbrainz/tagfetcher.cpp
@@ -4,6 +4,7 @@
#include <QtConcurrentRun>
#include "musicbrainz/chromaprinter.h"
+#include "util/thread_affinity.h"
namespace {
@@ -20,7 +21,7 @@ TagFetcher::TagFetcher(QObject* parent)
void TagFetcher::startFetch(
TrackPointer pTrack) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
cancel();
m_pTrack = pTrack;
@@ -39,7 +40,7 @@ void TagFetcher::startFetch(
}
void TagFetcher::cancel() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
m_pTrack.reset();
m_fingerprintWatcher.disconnect(this);
m_fingerprintWatcher.cancel();
@@ -56,7 +57,7 @@ void TagFetcher::cancel() {
}
void TagFetcher::slotFingerprintReady() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
if (!m_pTrack ||
!m_fingerprintWatcher.isFinished()) {
return;
@@ -100,7 +101,7 @@ void TagFetcher::slotFingerprintReady() {
void TagFetcher::slotAcoustIdTaskSucceeded(
QList<QUuid> recordingIds) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pAcoustIdTask.get() ==
qobject_cast<mixxx::AcoustIdLookupTask*>(sender()));
@@ -141,7 +142,7 @@ void TagFetcher::slotAcoustIdTaskSucceeded(
void TagFetcher::slotAcoustIdTaskFailed(
mixxx::network::JsonWebResponse response) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pAcoustIdTask.get() ==
qobject_cast<mixxx::AcoustIdLookupTask*>(sender()));
@@ -155,7 +156,7 @@ void TagFetcher::slotAcoustIdTaskFailed(
}
void TagFetcher::slotAcoustIdTaskAborted() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pAcoustIdTask.get() ==
qobject_cast<mixxx::AcoustIdLookupTask*>(sender()));
@@ -174,7 +175,7 @@ void TagFetcher::slotAcoustIdTaskNetworkError(
QByteArray errorContent) {
Q_UNUSED(requestUrl);
Q_UNUSED(errorContent);
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pAcoustIdTask.get() ==
qobject_cast<mixxx::AcoustIdLookupTask*>(sender()));
@@ -188,7 +189,7 @@ void TagFetcher::slotAcoustIdTaskNetworkError(
}
void TagFetcher::slotMusicBrainzTaskAborted() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pMusicBrainzTask.get() ==
qobject_cast<mixxx::MusicBrainzRecordingsTask*>(sender()));
@@ -207,7 +208,7 @@ void TagFetcher::slotMusicBrainzTaskNetworkError(
QByteArray errorContent) {
Q_UNUSED(requestUrl);
Q_UNUSED(errorContent);
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pMusicBrainzTask.get() ==
qobject_cast<mixxx::MusicBrainzRecordingsTask*>(sender()));
@@ -224,7 +225,7 @@ void TagFetcher::slotMusicBrainzTaskFailed(
mixxx::network::WebResponse response,
int errorCode,
QString errorMessage) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pMusicBrainzTask.get() ==
qobject_cast<mixxx::MusicBrainzRecordingsTask*>(sender()));
@@ -239,7 +240,7 @@ void TagFetcher::slotMusicBrainzTaskFailed(
void TagFetcher::slotMusicBrainzTaskSucceeded(
QList<mixxx::musicbrainz::TrackRelease> guessedTrackReleases) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_pMusicBrainzTask.get() ==
qobject_cast<mixxx::MusicBrainzRecordingsTask*>(sender()));
diff --git a/src/musicbrainz/web/musicbrainzrecordingstask.cpp b/src/musicbrainz/web/musicbrainzrecordingstask.cpp
index bdce39d78d..d3d6b0efd1 100644
--- a/src/musicbrainz/web/musicbrainzrecordingstask.cpp
+++ b/src/musicbrainz/web/musicbrainzrecordingstask.cpp
@@ -1,7 +1,6 @@
#include "musicbrainz/web/musicbrainzrecordingstask.h"
#include <QMetaMethod>
-#include <QThread>
#include <QXmlStreamReader>
#include "defs_urls.h"
@@ -11,6 +10,7 @@
#include "util/assert.h"
#include "util/compatibility.h"
#include "util/logger.h"
+#include "util/thread_affinity.h"
#include "util/version.h"
namespace mixxx {
@@ -85,7 +85,7 @@ bool MusicBrainzRecordingsTask::doStart(
QNetworkAccessManager* networkAccessManager,
int parentTimeoutMillis) {
m_parentTimeoutMillis = parentTimeoutMillis;
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(networkAccessManager);
VERIFY_OR_DEBUG_ASSERT(!m_pendingNetworkReply) {
kLogger.warning()
@@ -150,7 +150,7 @@ QUrl MusicBrainzRecordingsTask::doAbort() {
}
QUrl MusicBrainzRecordingsTask::doTimeOut() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
QUrl requestUrl;
if (m_pendingNetworkReply) {
requestUrl = timeOutPendingNetworkReply(m_pendingNetworkReply);
@@ -162,7 +162,7 @@ QUrl MusicBrainzRecordingsTask::doTimeOut() {
}
void MusicBrainzRecordingsTask::slotNetworkReplyFinished() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
const QPair<QNetworkReply*, network::HttpStatusCode>
networkReplyWithStatusCode = receiveNetworkReply();
auto* const networkReply = networkReplyWithStatusCode.first;
diff --git a/src/network/jsonwebtask.cpp b/src/network/jsonwebtask.cpp
index dec24f602c..7a843145a3 100644
--- a/src/network/jsonwebtask.cpp
+++ b/src/network/jsonwebtask.cpp
@@ -6,7 +6,6 @@
#include <QMetaMethod>
#include <QMimeDatabase>
#include <QNetworkRequest>
-#include <QThread>
#include <QTimerEvent>
#include <mutex> // std::once_flag
@@ -16,6 +15,7 @@
#endif
#include "util/counter.h"
#include "util/logger.h"
+#include "util/thread_affinity.h"
namespace mixxx {
@@ -240,7 +240,7 @@ bool JsonWebTask::doStart(
QNetworkAccessManager* networkAccessManager,
int parentTimeoutMillis) {
Q_UNUSED(parentTimeoutMillis);
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(networkAccessManager);
VERIFY_OR_DEBUG_ASSERT(!m_pendingNetworkReply) {
kLogger.warning()
@@ -287,7 +287,7 @@ bool JsonWebTask::doStart(
}
QUrl JsonWebTask::doAbort() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
QUrl requestUrl;
if (m_pendingNetworkReply) {
requestUrl = abortPendingNetworkReply(m_pendingNetworkReply);
@@ -301,7 +301,7 @@ QUrl JsonWebTask::doAbort() {
}
QUrl JsonWebTask::doTimeOut() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
QUrl requestUrl;
if (m_pendingNetworkReply) {
requestUrl = timeOutPendingNetworkReply(m_pendingNetworkReply);
@@ -313,7 +313,7 @@ QUrl JsonWebTask::doTimeOut() {
}
void JsonWebTask::slotNetworkReplyFinished() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
const QPair<QNetworkReply*, HttpStatusCode> networkReplyWithStatusCode =
receiveNetworkReply();
auto* const networkReply = networkReplyWithStatusCode.first;
diff --git a/src/network/webtask.cpp b/src/network/webtask.cpp
index 1b0950b642..49cc3fc819 100644
--- a/src/network/webtask.cpp
+++ b/src/network/webtask.cpp
@@ -1,12 +1,12 @@
#include "network/webtask.h"
-#include <QThread>
#include <QTimerEvent>
#include <mutex> // std::once_flag
#include "util/assert.h"
#include "util/counter.h"
#include "util/logger.h"
+#include "util/thread_affinity.h"
namespace mixxx {
@@ -166,7 +166,7 @@ void WebTask::invokeAbort() {
}
void WebTask::slotStart(int timeoutMillis) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_status != Status::Pending);
VERIFY_OR_DEBUG_ASSERT(m_networkAccessManager) {
onNetworkError(
@@ -207,7 +207,7 @@ void WebTask::slotStart(int timeoutMillis) {
QUrl WebTask::abortPendingNetworkReply(
QNetworkReply* pendingNetworkReply) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(pendingNetworkReply);
if (pendingNetworkReply->isRunning()) {
pendingNetworkReply->abort();
@@ -219,7 +219,7 @@ QUrl WebTask::abortPendingNetworkReply(
QUrl WebTask::timeOutPendingNetworkReply(
QNetworkReply* pendingNetworkReply) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(pendingNetworkReply);
if (pendingNetworkReply->isRunning()) {
//pendingNetworkReply->abort();
@@ -230,7 +230,7 @@ QUrl WebTask::timeOutPendingNetworkReply(
}
QUrl WebTask::abort() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
if (m_status != Status::Pending) {
DEBUG_ASSERT(m_timeoutTimerId == kInvalidTimerId);
return QUrl();
@@ -252,7 +252,7 @@ void WebTask::slotAbort() {
}
void WebTask::timerEvent(QTimerEvent* event) {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
const auto timerId = event->timerId();
DEBUG_ASSERT(timerId != kInvalidTimerId);
if (timerId != m_timeoutTimerId) {
@@ -270,7 +270,7 @@ void WebTask::timerEvent(QTimerEvent* event) {
}
QPair<QNetworkReply*, HttpStatusCode> WebTask::receiveNetworkReply() {
- DEBUG_ASSERT(thread() == QThread::currentThread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(m_status != Status::Idle);
auto* const networkReply = qobject_cast<QNetworkReply*>(sender());
HttpStatusCode statusCode = kHttpStatusCodeInvalid;
diff --git a/src/track/globaltrackcache.cpp b/src/track/globaltrackcache.cpp
index 257c58af5b..e15df7026b 100644
--- a/src/track/globaltrackcache.cpp
+++ b/src/track/globaltrackcache.cpp
@@ -1,11 +1,10 @@
#include "track/globaltrackcache.h"
-#include <QApplication>
-#include <QThread>
+#include <QCoreApplication>
#include "util/assert.h"
#include "util/logger.h"
-
+#include "util/thread_affinity.h"
namespace {
@@ -209,7 +208,8 @@ void GlobalTrackCache::createInstance(
//static
void GlobalTrackCache::destroyInstance() {
- DEBUG_ASSERT(s_pInstance);
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(s_pInstance);
+
kLogger.info() << "Destroying instance";
// Processing all pending events is required to evict all
// remaining references from the cache.
@@ -220,7 +220,6 @@ void GlobalTrackCache::destroyInstance() {
// Reset the static/global pointer before entering the destructor
s_pInstance = nullptr;
// Delete the singular instance
- DEBUG_ASSERT(QThread::currentThread() == pInstance->thread());
pInstance->deleteLater();
}
@@ -261,20 +260,21 @@ void GlobalTrackCache::evictAndSaveCachedTrack(GlobalTrackCacheEntryPointer cach
QMetaObject::invokeMethod(
s_pInstance,
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
- "evictAndSave"
+ "slotEvictAndSave",
#else
[cacheEntryPtr = std::move(cacheEntryPtr)] {
- s_pInstance->evictAndSave(cacheEntryPtr);
- }
+ s_pInstance->slotEvictAndSave(cacheEntryPtr);
+ },
#endif
// Qt will choose either a direct or a queued connection
// depending on the thread from which this method has
// been invoked!
- , Qt::AutoConnection
+ Qt::AutoConnection
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
- , Q_ARG(GlobalTrackCacheEntryPointer, std::move(cacheEntryPtr))
+ ,
+ Q_ARG(GlobalTrackCacheEntryPointer, std::move(cacheEntryPtr))
#endif
- );
+ );
} else {
// After the singular instance has been destroyed we are
// not able to save pending changes. The track is deleted
@@ -371,7 +371,7 @@ void GlobalTrackCache::saveEvictedTrack(Track* pEvictedTrack) const {
}
void GlobalTrackCache::deactivate() {
- DEBUG_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread());
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
if (isEmpty()) {
return;
@@ -625,7 +625,7 @@ void GlobalTrackCache::resolve(
// function might be called from any thread, even from worker
// threads without an event loop. We need to move the newly
// created object to the main thread.
- savingPtr->moveToThread(QApplication::instance()->thread());
+ savingPtr->moveToThread(QCoreApplication::instance()->thread());
pCacheResolver->initLookupResult(
GlobalTrackCacheLookupResult::MISS,
@@ -670,16 +670,14 @@ void GlobalTrackCache::purgeTrackId(TrackId trackId) {
}
}
-void GlobalTrackCache::evictAndSave(
+void GlobalTrackCache::slotEvictAndSave(
GlobalTrackCacheEntryPointer cacheEntryPtr) {
+ DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
DEBUG_ASSERT(cacheEntryPtr);
- // We need to be sure this is always called from the main thread
- // because we can only access the DB from it and we must not lose the
- // the lock until all changes are persistently stored in file and DB
- // to not hand out the track again with old metadata.
- DEBUG_ASSERT(QApplication::instance()->thread() == QThread::currentThread());
-
+ // GlobalTrackCacheSaver::saveEvictedTrack() requires that
+ // exclusive access is guaranteed for the duration of the
+ // whole invocation!
GlobalTrackCacheLocker cacheLocker;
if (!cacheEntryPtr->expired()) {
@@ -708,8 +706,12 @@ void GlobalTrackCache::evictAndSave(
DEBUG_ASSERT(!isCached(cacheEntryPtr->getPlainPtr()));
saveEvictedTrack(cacheEntryPtr->getPlainPtr());
- // here the cacheEntryPtr goes out of scope, the cache entry is
- // deleted including the owned track
+ // Explicitly release the cacheEntryPtr including the owned
+ // track object while the cache is still locked.
+ cacheEntryPtr.reset();
+
+ // Finally the exclusive lock on the cache is released implicitly
+ // when exiting the scope of this method.
}
bool GlobalTrackCache::tryEvict(Track* plainPtr) {
diff --git a/src/track/globaltrackcache.h b/src/track/globaltrackcache.h
index 2cfa85dac6..7c3c170254 100644
--- a/src/track/globaltrackcache.h
+++ b/src/track/globaltrackcache.h
@@ -174,19 +174,40 @@ private:
TrackRef m_trackRef;
};
+/// Callback interface for pre-delete actions
class /*interface*/ GlobalTrackCacheSaver {
private:
friend class GlobalTrackCache;
- virtual void saveEvictedTrack(Track* pEvictedTrack) noexcept = 0;
-protected:
- virtual ~GlobalTrackCacheSaver() = default;
+ /// Perform actions that are necessary to save any pending
+ /// modifications of a Track object before it finally gets
+ /// deleted.
+ ///
+ /// GlobalTrackCache ensures that the given pointer is valid
+ /// and the last and only reference to this Track object.
+ /// While invoked the GlobalTrackCache is locked to ensure
+ /// that this particular track is not accessible while
+ /// saving the Track object, e.g. by updating the database
+ /// and exporting file tags.
+ ///
+ /// This callback method will always be invoked from the
+ /// event loop thread of the owning GlobalTrackCache instance.
+ /// Typically the GlobalTrackCache lives on the main thread
+ /// that also controls access to the database.
+ /// NOTE(2020-06-06): If these assumptions about thread affinity
+ /// are no longer valid the design decisions need to be revisited
+ /// carefully!
+ virtual void saveEvictedTrack(
+ Track* pEvictedTrack) noexcept = 0;
+
+ protected:
+ virtual ~GlobalTrackCacheSaver() = default;
};
class GlobalTrackCache : public QObject {
Q_OBJECT
-public:
+ public:
static void createInstance(
GlobalTrackCacheSaver* pSaver,
// A custom deleter is only needed for tests without an event loop!
@@ -202,10 +223,10 @@ public:
// Deleter callbacks for the smart-pointer
static void evictAndSaveCachedTrack(GlobalTrackCacheEntryPointer cacheEntryPtr);
-private slots:
- void evictAndSave(GlobalTrackCacheEntryPointer cacheEntryPtr);
+ private slots:
+ void slotEvictAndSave(GlobalTrackCacheEntryPointer cacheEntryPtr);
-private:
+ private:
friend class GlobalTrackCacheLocker;
friend class GlobalTrackCacheResolver;
diff --git a/src/util/thread_affinity.h b/src/util/thread_affinity.h
new file mode 100644
index 0000000000..41dc496fd9
--- /dev/null
+++ b/