mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-23 09:49:52 +00:00
Open "lock" files for the same folder only once
PR #20414. Closes #12203.
This commit is contained in:
parent
e31b553807
commit
f04edd555f
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2017-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@ -34,31 +34,56 @@
|
|||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/io.h"
|
#include "base/utils/io.h"
|
||||||
|
|
||||||
|
QHash<Path, std::weak_ptr<QFile>> AsyncFileStorage::m_reservedPaths;
|
||||||
|
QReadWriteLock AsyncFileStorage::m_reservedPathsLock;
|
||||||
|
|
||||||
AsyncFileStorage::AsyncFileStorage(const Path &storageFolderPath, QObject *parent)
|
AsyncFileStorage::AsyncFileStorage(const Path &storageFolderPath, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_storageDir(storageFolderPath)
|
, m_storageDir(storageFolderPath)
|
||||||
, m_lockFile((m_storageDir / Path(u"storage.lock"_s)).data())
|
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_storageDir.isAbsolute());
|
Q_ASSERT(m_storageDir.isAbsolute());
|
||||||
|
|
||||||
|
const Path lockFilePath = m_storageDir / Path(u"storage.lock"_s);
|
||||||
|
|
||||||
|
{
|
||||||
|
const QReadLocker readLocker {&m_reservedPathsLock};
|
||||||
|
m_lockFile = m_reservedPaths.value(lockFilePath).lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_lockFile)
|
||||||
|
{
|
||||||
|
const QWriteLocker writeLocker {&m_reservedPathsLock};
|
||||||
|
if (std::weak_ptr<QFile> &lockFile = m_reservedPaths[lockFilePath]; lockFile.expired()) [[likely]]
|
||||||
|
{
|
||||||
if (!Utils::Fs::mkpath(m_storageDir))
|
if (!Utils::Fs::mkpath(m_storageDir))
|
||||||
throw AsyncFileStorageError(tr("Could not create directory '%1'.").arg(m_storageDir.toString()));
|
throw AsyncFileStorageError(tr("Could not create directory '%1'.").arg(m_storageDir.toString()));
|
||||||
|
|
||||||
|
auto lockFileDeleter = [](QFile *file)
|
||||||
|
{
|
||||||
|
file->close();
|
||||||
|
file->remove();
|
||||||
|
delete file;
|
||||||
|
};
|
||||||
|
m_lockFile = std::shared_ptr<QFile>(new QFile(lockFilePath.data()), std::move(lockFileDeleter));
|
||||||
|
|
||||||
// TODO: This folder locking approach does not work for UNIX systems. Implement it.
|
// TODO: This folder locking approach does not work for UNIX systems. Implement it.
|
||||||
if (!m_lockFile.open(QFile::WriteOnly))
|
if (!m_lockFile->open(QFile::WriteOnly))
|
||||||
throw AsyncFileStorageError(m_lockFile.errorString());
|
throw AsyncFileStorageError(m_lockFile->errorString());
|
||||||
|
|
||||||
|
lockFile = m_lockFile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_lockFile = lockFile.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncFileStorage::~AsyncFileStorage()
|
AsyncFileStorage::~AsyncFileStorage() = default;
|
||||||
{
|
|
||||||
m_lockFile.close();
|
|
||||||
m_lockFile.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncFileStorage::store(const Path &filePath, const QByteArray &data)
|
void AsyncFileStorage::store(const Path &filePath, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, [this, data, filePath]() { store_impl(filePath, data); }
|
QMetaObject::invokeMethod(this, [this, data, filePath] { store_impl(filePath, data); }, Qt::QueuedConnection);
|
||||||
, Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Path AsyncFileStorage::storageDir() const
|
Path AsyncFileStorage::storageDir() const
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2017-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@ -28,19 +28,23 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
#include "base/exceptions.h"
|
#include "base/exceptions.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
|
||||||
class AsyncFileStorageError : public RuntimeError
|
class AsyncFileStorageError final : public RuntimeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using RuntimeError::RuntimeError;
|
using RuntimeError::RuntimeError;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncFileStorage : public QObject
|
class AsyncFileStorage final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(AsyncFileStorage)
|
Q_DISABLE_COPY_MOVE(AsyncFileStorage)
|
||||||
@ -60,5 +64,8 @@ private:
|
|||||||
Q_INVOKABLE void store_impl(const Path &fileName, const QByteArray &data);
|
Q_INVOKABLE void store_impl(const Path &fileName, const QByteArray &data);
|
||||||
|
|
||||||
Path m_storageDir;
|
Path m_storageDir;
|
||||||
QFile m_lockFile;
|
std::shared_ptr<QFile> m_lockFile;
|
||||||
|
|
||||||
|
static QHash<Path, std::weak_ptr<QFile>> m_reservedPaths;
|
||||||
|
static QReadWriteLock m_reservedPathsLock;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user