Commit bb8e6ffe authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Ensure tasks are properly terminated on close, fix incorrect mutex in...

Ensure tasks are properly terminated on close, fix incorrect mutex in thumbnailcache causing corruption
parent 3db8a140
......@@ -514,6 +514,7 @@ QStringList ProjectItemModel::getEnclosingFolderInfo(const QModelIndex &index) c
void ProjectItemModel::clean()
{
// QWriteLocker locker(&m_lock);
pCore->taskManager.slotCancelJobs();
std::vector<std::shared_ptr<AbstractProjectItem>> toDelete;
toDelete.reserve(size_t(rootItem->childCount()));
for (int i = 0; i < rootItem->childCount(); ++i) {
......
......@@ -73,7 +73,9 @@ KdenliveDoc::KdenliveDoc(QString projectFolder, QUndoGroup *undoGroup, const QSt
, m_guideModel(new MarkerListModel(m_commandStack, this))
{
connect(m_guideModel.get(), &MarkerListModel::modelChanged, this, &KdenliveDoc::guidesChanged);
connect(this, &KdenliveDoc::updateCompositionMode, parent, &MainWindow::slotUpdateCompositeAction);
if (parent) {
connect(this, &KdenliveDoc::updateCompositionMode, parent, &MainWindow::slotUpdateCompositeAction);
}
connect(m_commandStack.get(), &QUndoStack::indexChanged, this, &KdenliveDoc::slotModified);
connect(m_commandStack.get(), &DocUndoStack::invalidate, this, &KdenliveDoc::checkPreviewStack, Qt::DirectConnection);
// connect(m_commandStack, SIGNAL(cleanChanged(bool)), this, SLOT(setModified(bool)));
......@@ -125,7 +127,9 @@ KdenliveDoc::KdenliveDoc(const QUrl &url, QDomDocument& newDom, QString projectF
, m_guideModel(new MarkerListModel(m_commandStack, this))
{
connect(m_guideModel.get(), &MarkerListModel::modelChanged, this, &KdenliveDoc::guidesChanged);
connect(this, &KdenliveDoc::updateCompositionMode, parent, &MainWindow::slotUpdateCompositeAction);
if (parent) {
connect(this, &KdenliveDoc::updateCompositionMode, parent, &MainWindow::slotUpdateCompositeAction);
}
connect(m_commandStack.get(), &QUndoStack::indexChanged, this, &KdenliveDoc::slotModified);
connect(m_commandStack.get(), &DocUndoStack::invalidate, this, &KdenliveDoc::checkPreviewStack, Qt::DirectConnection);
......@@ -1757,7 +1761,7 @@ void KdenliveDoc::initCacheDirs()
cacheDir.mkdir(QStringLiteral("proxy"));
}
QDir KdenliveDoc::getCacheDir(CacheType type, bool *ok) const
const QDir KdenliveDoc::getCacheDir(CacheType type, bool *ok) const
{
QString basePath;
QString kdenliveCacheDir;
......
......@@ -177,7 +177,7 @@ public:
void selectPreviewProfile();
void displayMessage(const QString &text, MessageType type = DefaultMessage, int timeOut = 0);
/** @brief Get a cache directory for this project. */
QDir getCacheDir(CacheType type, bool *ok) const;
const QDir getCacheDir(CacheType type, bool *ok) const;
/** @brief Create standard cache dirs for the project */
void initCacheDirs();
/** @brief Get a list of all proxy hash used in this project */
......
......@@ -45,7 +45,7 @@ AbstractTask::AbstractTask(const ObjectId &owner, JOBTYPE type, QObject *object)
, m_running(false)
, m_type(type)
{
setAutoDelete(true);
setAutoDelete(false);
switch (type) {
case AbstractTask::LOADJOB:
m_priority = 10;
......@@ -68,12 +68,13 @@ AbstractTask::AbstractTask(const ObjectId &owner, JOBTYPE type, QObject *object)
void AbstractTask::cancelJob(bool softDelete)
{
m_isCanceled.testAndSetAcquire(0, 1);
if (softDelete) {
m_softDelete.testAndSetAcquire(0, 1);
if (m_isCanceled.testAndSetAcquire(0, 1)) {
if (softDelete) {
m_softDelete.testAndSetAcquire(0, 1);
}
qDebug() << "====== SETTING TASK CANCELED: " << m_isCanceled << ", TYPE: " << m_type;
emit jobCanceled();
}
qDebug() << "====== SETTING TASK CANCELED: " << m_isCanceled << ", TYPE: " << m_type;
emit jobCanceled();
}
const ObjectId AbstractTask::ownerId() const
......
......@@ -47,6 +47,7 @@ protected:
bool m_successful;
QAtomicInt m_isCanceled;
QAtomicInt m_softDelete;
QMutex m_runMutex;
bool m_isForce;
bool m_running;
void run() override;
......
......@@ -52,12 +52,13 @@ void AudioLevelsTask::start(const ObjectId &owner, QObject *object, bool force)
void AudioLevelsTask::run()
{
m_running = true;
// 2 channels interleaved of uchar values
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
// 2 channels interleaved of uchar values
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip == nullptr) {
// Clip was deleted
......
......@@ -66,7 +66,7 @@ void CacheTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip)
m_progress = 100 * count / size;
QMetaObject::invokeMethod(m_object, "updateJobProgress");
count++;
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
break;
}
if (ThumbnailCache::get()->hasThumbnail(clipId, i)) {
......@@ -97,11 +97,14 @@ void CacheTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip)
void CacheTask::run()
{
if (!m_isCanceled) {
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip) {
generateThumbnail(binClip);
}
if (m_isCanceled || !pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip) {
generateThumbnail(binClip);
}
pCore->taskManager.taskDone(m_owner.second, this);
return;
......
......@@ -225,6 +225,9 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip, std::
{
// Fetch thumbnail
qDebug() << "===== \nREADY FOR THUMB" << binClip->clipType() << "\n\n=========";
if (m_isCanceled.loadAcquire() || pCore->taskManager.isBlocked()) {
return;
}
int frameNumber = m_in > -1 ? m_in : qMax(0, binClip->getProducerIntProperty(QStringLiteral("kdenlive:thumbnailFrame")));
if (producer->get_int("video_index") > -1) {
QImage thumb = ThumbnailCache::get()->getThumbnail(binClip->hashForThumbs(), QString::number(m_owner.second), frameNumber);
......@@ -239,7 +242,7 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip, std::
if (mltService == QLatin1String("avformat")) {
mltService = QStringLiteral("avformat-novalidate");
}
std::unique_ptr<Mlt::Producer> thumbProd;
std::unique_ptr<Mlt::Producer> thumbProd = nullptr;
Mlt::Profile *profile = pCore->thumbProfile();
if (mltService.startsWith(QLatin1String("xml"))) {
int profileWidth = profile->width();
......@@ -248,6 +251,9 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip, std::
profile->set_width(profileWidth);
profile->set_height(profileHeight);
} else {
if (m_isCanceled.loadAcquire() || pCore->taskManager.isBlocked()) {
return;
}
thumbProd.reset(new Mlt::Producer(*profile, mltService.toUtf8().constData(), mltResource.toUtf8().constData()));
}
if (thumbProd) {
......@@ -277,7 +283,7 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip, std::
int imageWidth(pCore->thumbProfile()->width());
int fullWidth(qRound(imageHeight * pCore->getCurrentDar()));
QImage result = KThumb::getFrame(frame.data(), imageWidth, imageHeight, fullWidth);
if (result.isNull() && !m_isCanceled) {
if (result.isNull() && !m_isCanceled.loadAcquire()) {
qDebug() << "+++++\nINVALID RESULT IMAGE\n++++++++++++++";
result = QImage(fullWidth, imageHeight, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::red);
......@@ -286,18 +292,13 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip, std::
p.drawText(0, 0, fullWidth, imageHeight, Qt::AlignCenter, i18n("Invalid"));
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage, result), Q_ARG(int, m_in),
Q_ARG(int, m_out), Q_ARG(bool, false));
} else if (binClip.get() && !m_isCanceled) {
} else if (binClip.get() && !m_isCanceled.loadAcquire()) {
// We don't follow m_isCanceled there,
qDebug() << "=== GOT THUMB FOR: " << m_in << "x" << m_out;
QMetaObject::invokeMethod(binClip.get(), "setThumbnail", Qt::QueuedConnection, Q_ARG(QImage, result), Q_ARG(int, m_in),
Q_ARG(int, m_out), Q_ARG(bool, false));
ThumbnailCache::get()->storeThumbnail(QString::number(m_owner.second), frameNumber, result, false);
}
if (m_isCanceled) {
abort();
} else {
pCore->taskManager.taskDone(m_owner.second, this);
}
}
}
}
......@@ -306,20 +307,27 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip> binClip, std::
void ClipLoadTask::run()
{
if (m_isCanceled || pCore->taskManager.isBlocked()) {
if (m_isCanceled.loadAcquire() == 1 || pCore->taskManager.isBlocked()) {
abort();
return;
}
QMutexLocker lock(&m_runMutex);
// QThread::currentThread()->setPriority(QThread::HighestPriority);
if (m_thumbOnly) {
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip && binClip->statusReady()) {
if (m_isCanceled.loadAcquire() == 1 || pCore->taskManager.isBlocked()) {
abort();
return;
}
generateThumbnail(binClip, binClip->originalProducer());
return;
}
if (m_isCanceled.loadAcquire() == 1 || pCore->taskManager.isBlocked()) {
abort();
} else {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
return;
}
m_running = true;
emit pCore->projectItemModel()->resetPlayOrLoopZone(QString::number(m_owner.second));
......@@ -462,7 +470,7 @@ void ClipLoadTask::run()
break;
}
if (m_isCanceled) {
if (m_isCanceled.loadAcquire()) {
abort();
return;
}
......@@ -647,7 +655,7 @@ void ClipLoadTask::run()
// List streams
int streams = producer->get_int("meta.media.nb_streams");
QList<int> audio_list, video_list;
for (int i = 0; i < streams && !m_isCanceled; ++i) {
for (int i = 0; i < streams && !m_isCanceled.loadAcquire(); ++i) {
QByteArray propertyName = QStringLiteral("meta.media.%1.stream.type").arg(i).toLocal8Bit();
QString stype = producer->get(propertyName.data());
if (stype == QLatin1String("audio")) {
......@@ -657,7 +665,7 @@ void ClipLoadTask::run()
}
}
if (vindex > -1 && !m_isCanceled) {
if (vindex > -1 && !m_isCanceled.loadAcquire()) {
char property[200];
snprintf(property, sizeof(property), "meta.media.%d.stream.frame_rate", vindex);
fps = producer->get_double(property);
......@@ -704,7 +712,7 @@ void ClipLoadTask::run()
Q_ARG(QString, i18n("File <b>%1</b> has a variable frame rate.", QFileInfo(resource).fileName())));
}
if (fps <= 0 && !m_isCanceled) {
if (fps <= 0 && !m_isCanceled.loadAcquire()) {
if (producer->get_double("meta.media.frame_rate_den") > 0) {
fps = producer->get_double("meta.media.frame_rate_num") / producer->get_double("meta.media.frame_rate_den");
} else {
......@@ -719,7 +727,7 @@ void ClipLoadTask::run()
vindex = -1;
}
}
if (!m_isCanceled) {
if (!m_isCanceled.loadAcquire()) {
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip) {
QMetaObject::invokeMethod(binClip.get(), "setProducer", Qt::QueuedConnection, Q_ARG(std::shared_ptr<Mlt::Producer>, producer), Q_ARG(bool, true));
......@@ -740,10 +748,10 @@ void ClipLoadTask::run()
void ClipLoadTask::abort()
{
m_progress = 100;
pCore->taskManager.taskDone(m_owner.second, this);
if (pCore->taskManager.isBlocked()) {
return;
}
pCore->taskManager.taskDone(m_owner.second, this);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
QString resource = Xml::getXmlProperty(m_xml, QStringLiteral("resource"));
......@@ -751,7 +759,7 @@ void ClipLoadTask::abort()
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip) {
QMetaObject::invokeMethod(binClip.get(), "setInvalid", Qt::QueuedConnection);
if (!m_isCanceled) {
if (!m_isCanceled.loadAcquire()) {
// User tried to add an invalid clip, remove it.
pCore->projectItemModel()->requestBinClipDeletion(binClip, undo, redo);
} else {
......
......@@ -187,10 +187,11 @@ void CutTask::start(const ObjectId &owner, int in, int out, QObject *object, boo
void CutTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
qDebug() << " + + + + + + + + STARTING STAB TASK";
......
......@@ -51,10 +51,11 @@ void FilterTask::start(const ObjectId &owner, const QString &binId, const std::w
void FilterTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
QString url;
......
......@@ -42,10 +42,11 @@ void ProxyTask::start(const ObjectId &owner, QObject *object, bool force)
void ProxyTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
if (binClip == nullptr) {
......
......@@ -107,10 +107,11 @@ void SceneSplitTask::start(QObject *object, bool force)
void SceneSplitTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
const QString source = binClip->url();
......
......@@ -165,10 +165,11 @@ void SpeedTask::start(QObject *object, bool force)
void SpeedTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
qDebug() << " + + + + + + + + STARTING STAB TASK";
......
......@@ -95,10 +95,11 @@ void StabilizeTask::start(QObject *, bool force)
void StabilizeTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
qDebug() << " + + + + + + + + STARTING STAB TASK";
......
......@@ -46,6 +46,10 @@ void TaskManager::updateConcurrency()
void TaskManager::discardJobs(const ObjectId &owner, AbstractTask::JOBTYPE type, bool softDelete, const QVector<AbstractTask::JOBTYPE> exceptions)
{
qDebug() << "========== READY FOR TASK DISCARD ON: " << owner.second;
if (m_blockUpdates) {
// We are already deleting all tasks
return;
}
m_tasksListLock.lockForRead();
// See if there is already a task for this MLT service and resource.
if (m_taskList.find(owner.second) == m_taskList.end()) {
......@@ -64,7 +68,7 @@ void TaskManager::discardJobs(const ObjectId &owner, AbstractTask::JOBTYPE type,
t->cancelJob(softDelete);
qDebug() << "========== DELETING JOB!!!!";
// Block until the task is finished
// t->m_runMutex.lock();
t->m_runMutex.lock();
}
}
}
......@@ -99,7 +103,6 @@ TaskManagerStatus TaskManager::jobStatus(const ObjectId &owner) const
for (AbstractTask *t : taskList) {
if (t->m_running) {
return TaskManagerStatus::Running;
;
}
}
return TaskManagerStatus::Pending;
......@@ -119,26 +122,35 @@ void TaskManager::updateJobCount()
void TaskManager::taskDone(int cid, AbstractTask *task)
{
// This will be executed in the QRunnable job thread
if (m_blockUpdates) {
// We are closing, tasks will be handled on close
return;
}
m_tasksListLock.lockForWrite();
Q_ASSERT(m_taskList.find(cid) != m_taskList.end());
m_taskList[cid].erase(std::remove(m_taskList[cid].begin(), m_taskList[cid].end(), task), m_taskList[cid].end());
if (m_taskList[cid].size() == 0) {
m_taskList.erase(cid);
}
task->deleteLater();
m_tasksListLock.unlock();
QMetaObject::invokeMethod(this, "updateJobCount");
}
void TaskManager::slotCancelJobs(const QVector<AbstractTask::JOBTYPE> exceptions)
{
m_tasksListLock.lockForRead();
// See if there is already a task for this MLT service and resource.
m_blockUpdates = true;
m_tasksListLock.lockForWrite();
for (const auto &task : m_taskList) {
for (AbstractTask *t : task.second) {
if (!exceptions.contains(t->m_type)) {
// If so, then just add ourselves to be notified upon completion.
t->cancelJob();
if (m_taskList.find(task.first) != m_taskList.end()) {
if (!exceptions.contains(t->m_type)) {
// If so, then just add ourselves to be notified upon completion.
t->cancelJob();
t->m_runMutex.lock();
t->m_runMutex.unlock();
delete t;
}
}
}
}
......@@ -148,12 +160,18 @@ void TaskManager::slotCancelJobs(const QVector<AbstractTask::JOBTYPE> exceptions
m_transcodePool.waitForDone();
}
m_taskList.clear();
m_taskPool.clear();
m_blockUpdates = false;
updateJobCount();
}
void TaskManager::startTask(int ownerId, AbstractTask *task)
{
if (m_blockUpdates) {
// We are closing, tasks will be handled on close
delete task;
return;
}
m_tasksListLock.lockForWrite();
if (m_taskList.find(ownerId) == m_taskList.end()) {
// First task for this clip
......
......@@ -56,10 +56,11 @@ void TranscodeTask::start(const ObjectId &owner, const QString &suffix, const QS
void TranscodeTask::run()
{
if (m_isCanceled) {
if (m_isCanceled || pCore->taskManager.isBlocked()) {
pCore->taskManager.taskDone(m_owner.second, this);
return;
}
QMutexLocker lock(&m_runMutex);
m_running = true;
auto binClip = pCore->projectItemModel()->getClipByBinID(QString::number(m_owner.second));
ClipType::ProducerType type = binClip->clipType();
......
......@@ -1136,9 +1136,9 @@ std::shared_ptr<DocUndoStack> ProjectManager::undoStack()
return current()->commandStack();
}
QDir ProjectManager::cacheDir(bool audio, bool *ok)
const QDir ProjectManager::cacheDir(bool audio, bool *ok) const
{
return current()->getCacheDir(audio ? CacheAudio : CacheThumbs, ok);
return m_project->getCacheDir(audio ? CacheAudio : CacheThumbs, ok);
}
void ProjectManager::saveWithUpdatedProfile(const QString &updatedProfile)
......
......@@ -79,7 +79,7 @@ public:
*/
virtual std::shared_ptr<DocUndoStack> undoStack();
virtual QDir cacheDir(bool audio, bool *ok);
virtual const QDir cacheDir(bool audio, bool *ok) const;
/** @brief This will create a backup file with fps appended to project name,
* and save the project with an updated profile info, then reopen it.
......
......@@ -106,7 +106,7 @@ std::unique_ptr<ThumbnailCache> &ThumbnailCache::get()
bool ThumbnailCache::hasThumbnail(const QString &binId, int pos, bool volatileOnly) const
{
QReadLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = pos < 0 ? getAudioKey(binId, &ok).constFirst() : getKey(binId, pos, &ok);
if (ok && m_volatileCache->contains(key)) {
......@@ -122,7 +122,7 @@ bool ThumbnailCache::hasThumbnail(const QString &binId, int pos, bool volatileOn
QImage ThumbnailCache::getAudioThumbnail(const QString &binId, bool volatileOnly) const
{
QReadLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = getAudioKey(binId, &ok).constFirst();
if (ok && m_volatileCache->contains(key)) {
......@@ -164,7 +164,7 @@ QImage ThumbnailCache::getThumbnail(QString hash, const QString &binId, int pos,
return QImage();
}
hash.append(QString("#%1.jpg").arg(pos));
QReadLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
if (m_volatileCache->contains(hash)) {
return m_volatileCache->get(hash);
}
......@@ -181,12 +181,13 @@ QImage ThumbnailCache::getThumbnail(QString hash, const QString &binId, int pos,
locker.unlock();
return QImage(thumbFolder.absoluteFilePath(hash));
}
locker.unlock();
return QImage();
}
QImage ThumbnailCache::getThumbnail(const QString &binId, int pos, bool volatileOnly) const
{
QReadLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
bool ok = false;
auto key = getKey(binId, pos, &ok);
if (ok && m_volatileCache->contains(key)) {
......@@ -209,7 +210,7 @@ QImage ThumbnailCache::getThumbnail(const QString &binId, int pos, bool volatile
void ThumbnailCache::storeThumbnail(const QString &binId, int pos, const QImage &img, bool persistent)
{
QWriteLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
bool ok = false;
const QString key = getKey(binId, pos, &ok);
if (!ok) {
......@@ -249,7 +250,7 @@ void ThumbnailCache::saveCachedThumbs(const std::unordered_map<QString, std::vec
if (!ok) {
return;
}
QReadLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
for (auto &key : keys) {
bool ok;
for (const auto &pos : key.second) {
......@@ -275,7 +276,7 @@ void ThumbnailCache::saveCachedThumbs(const std::unordered_map<QString, std::vec
void ThumbnailCache::invalidateThumbsForClip(const QString &binId)
{
QWriteLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
if (m_storedVolatile.find(binId) != m_storedVolatile.end()) {
bool ok = false;
for (int pos : m_storedVolatile.at(binId)) {
......@@ -315,7 +316,7 @@ void ThumbnailCache::invalidateThumbsForClip(const QString &binId)
void ThumbnailCache::clearCache()
{
QWriteLocker locker(&m_mutex);
QMutexLocker locker(&m_mutex);
m_volatileCache->clear();
m_storedVolatile.clear();
m_storedOnDisk.clear();
......@@ -376,7 +377,7 @@ QStringList ThumbnailCache::getAudioKey(const QString &binId, bool *ok)
}
// static
QDir ThumbnailCache::getDir(bool audio, bool *ok)
const QDir ThumbnailCache::getDir(bool audio, bool *ok)
{
return pCore->projectManager()->cacheDir(audio, ok);
}
......@@ -8,7 +8,7 @@
#include "definitions.h"
#include <QDir>
#include <QImage>
#include <QReadWriteLock>
#include <QMutex>
#include <QUrl>
#include <memory>
#include <mutex>
......@@ -76,14 +76,14 @@ protected:
static QStringList getAudioKey(const QString &binId, bool *ok);
// Return the dir where the persistent cache lives
static QDir getDir(bool audio, bool *ok);
static const QDir getDir(bool audio, bool *ok);
static std::unique_ptr<ThumbnailCache> instance;
static std::once_flag m_onceFlag; // flag to create the repository only once;
class Cache_t;
std::unique_ptr<Cache_t> m_volatileCache;
mutable QReadWriteLock m_mutex;
mutable QMutex m_mutex;
// the following maps keeps track of the positions that we store for each clip in volatile caches.
// Note that we don't track deletions due to items dropped from the cache. So the maps can contain more items that are currently stored.
......