mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 07:03:41 +02:00
Prevent deadlocks from different locking orders in FileCache
This commit is contained in:
parent
0597c3105f
commit
a675dfe7d2
2 changed files with 42 additions and 51 deletions
|
@ -87,6 +87,12 @@ FileCache::~FileCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCache::initialize() {
|
void FileCache::initialize() {
|
||||||
|
Lock lock(_mutex);
|
||||||
|
if (_initialized) {
|
||||||
|
qCWarning(file_cache) << "File cache already initialized";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QDir dir(_dirpath.c_str());
|
QDir dir(_dirpath.c_str());
|
||||||
|
|
||||||
if (dir.exists()) {
|
if (dir.exists()) {
|
||||||
|
@ -120,21 +126,24 @@ FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath)
|
||||||
file->_cache = this;
|
file->_cache = this;
|
||||||
emit dirty();
|
emit dirty();
|
||||||
|
|
||||||
Lock lock(_filesMutex);
|
|
||||||
_files[file->getKey()] = file;
|
_files[file->getKey()] = file;
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bool overwrite) {
|
FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bool overwrite) {
|
||||||
assert(_initialized);
|
Lock lock(_mutex);
|
||||||
|
|
||||||
|
FilePointer file;
|
||||||
|
if (!_initialized) {
|
||||||
|
qCWarning(file_cache) << "File cache used before initialization";
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
std::string filepath = getFilepath(metadata.key);
|
std::string filepath = getFilepath(metadata.key);
|
||||||
|
|
||||||
Lock lock(_filesMutex);
|
|
||||||
|
|
||||||
// if file already exists, return it
|
// if file already exists, return it
|
||||||
FilePointer file = getFile(metadata.key);
|
file = getFile(metadata.key);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (!overwrite) {
|
if (!overwrite) {
|
||||||
qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), metadata.key.c_str());
|
qCWarning(file_cache, "[%s] Attempted to overwrite %s", _dirname.c_str(), metadata.key.c_str());
|
||||||
|
@ -158,12 +167,15 @@ FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bo
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FilePointer FileCache::getFile(const Key& key) {
|
FilePointer FileCache::getFile(const Key& key) {
|
||||||
assert(_initialized);
|
Lock lock(_mutex);
|
||||||
|
|
||||||
FilePointer file;
|
FilePointer file;
|
||||||
|
if (!_initialized) {
|
||||||
Lock lock(_filesMutex);
|
qCWarning(file_cache) << "File cache used before initialization";
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
// check if file exists
|
// check if file exists
|
||||||
const auto it = _files.find(key);
|
const auto it = _files.find(key);
|
||||||
|
@ -172,7 +184,10 @@ FilePointer FileCache::getFile(const Key& key) {
|
||||||
if (file) {
|
if (file) {
|
||||||
file->touch();
|
file->touch();
|
||||||
// if it exists, it is active - remove it from the cache
|
// if it exists, it is active - remove it from the cache
|
||||||
removeUnusedFile(file);
|
if (_unusedFiles.erase(file)) {
|
||||||
|
_numUnusedFiles -= 1;
|
||||||
|
_unusedFilesSize -= file->getLength();
|
||||||
|
}
|
||||||
qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str());
|
qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str());
|
||||||
emit dirty();
|
emit dirty();
|
||||||
} else {
|
} else {
|
||||||
|
@ -188,31 +203,19 @@ std::string FileCache::getFilepath(const Key& key) {
|
||||||
return _dirpath + DIR_SEP + key + EXT_SEP + _ext;
|
return _dirpath + DIR_SEP + key + EXT_SEP + _ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a non-public function that uses the mutex because it's
|
||||||
|
// essentially a public function specifically to a File object
|
||||||
void FileCache::addUnusedFile(const FilePointer& file) {
|
void FileCache::addUnusedFile(const FilePointer& file) {
|
||||||
{
|
Lock lock(_mutex);
|
||||||
Lock lock(_filesMutex);
|
|
||||||
_files[file->getKey()] = file;
|
_files[file->getKey()] = file;
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Lock lock(_unusedFilesMutex);
|
|
||||||
_unusedFiles.insert(file);
|
_unusedFiles.insert(file);
|
||||||
_numUnusedFiles += 1;
|
_numUnusedFiles += 1;
|
||||||
_unusedFilesSize += file->getLength();
|
_unusedFilesSize += file->getLength();
|
||||||
}
|
|
||||||
clean();
|
clean();
|
||||||
|
|
||||||
emit dirty();
|
emit dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCache::removeUnusedFile(const FilePointer& file) {
|
|
||||||
Lock lock(_unusedFilesMutex);
|
|
||||||
if (_unusedFiles.erase(file)) {
|
|
||||||
_numUnusedFiles -= 1;
|
|
||||||
_unusedFilesSize -= file->getLength();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t FileCache::getOverbudgetAmount() const {
|
size_t FileCache::getOverbudgetAmount() const {
|
||||||
size_t result = 0;
|
size_t result = 0;
|
||||||
|
|
||||||
|
@ -241,21 +244,14 @@ void FileCache::eject(const FilePointer& file) {
|
||||||
const auto& length = file->getLength();
|
const auto& length = file->getLength();
|
||||||
const auto& key = file->getKey();
|
const auto& key = file->getKey();
|
||||||
|
|
||||||
{
|
|
||||||
Lock lock(_filesMutex);
|
|
||||||
if (0 != _files.erase(key)) {
|
if (0 != _files.erase(key)) {
|
||||||
_numTotalFiles -= 1;
|
_numTotalFiles -= 1;
|
||||||
_totalFilesSize -= length;
|
_totalFilesSize -= length;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Lock unusedLock(_unusedFilesMutex);
|
|
||||||
if (0 != _unusedFiles.erase(file)) {
|
if (0 != _unusedFiles.erase(file)) {
|
||||||
_numUnusedFiles -= 1;
|
_numUnusedFiles -= 1;
|
||||||
_unusedFilesSize -= length;
|
_unusedFilesSize -= length;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCache::clean() {
|
void FileCache::clean() {
|
||||||
|
@ -266,7 +262,6 @@ void FileCache::clean() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lock unusedLock(_unusedFilesMutex);
|
|
||||||
using Queue = std::priority_queue<FilePointer, std::vector<FilePointer>, FilePointerComparator>;
|
using Queue = std::priority_queue<FilePointer, std::vector<FilePointer>, FilePointerComparator>;
|
||||||
Queue queue;
|
Queue queue;
|
||||||
for (const auto& file : _unusedFiles) {
|
for (const auto& file : _unusedFiles) {
|
||||||
|
@ -283,7 +278,7 @@ void FileCache::clean() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCache::wipe() {
|
void FileCache::wipe() {
|
||||||
Lock unusedFilesLock(_unusedFilesMutex);
|
Lock lock(_mutex);
|
||||||
while (!_unusedFiles.empty()) {
|
while (!_unusedFiles.empty()) {
|
||||||
eject(*_unusedFiles.begin());
|
eject(*_unusedFiles.begin());
|
||||||
}
|
}
|
||||||
|
@ -294,7 +289,6 @@ void FileCache::clear() {
|
||||||
clean();
|
clean();
|
||||||
|
|
||||||
// Mark everything remaining as persisted
|
// Mark everything remaining as persisted
|
||||||
Lock unusedFilesLock(_unusedFilesMutex);
|
|
||||||
for (auto& file : _unusedFiles) {
|
for (auto& file : _unusedFiles) {
|
||||||
file->_shouldPersist = true;
|
file->_shouldPersist = true;
|
||||||
file->_cache = nullptr;
|
file->_cache = nullptr;
|
||||||
|
|
|
@ -108,7 +108,6 @@ private:
|
||||||
|
|
||||||
FilePointer addFile(Metadata&& metadata, const std::string& filepath);
|
FilePointer addFile(Metadata&& metadata, const std::string& filepath);
|
||||||
void addUnusedFile(const FilePointer& file);
|
void addUnusedFile(const FilePointer& file);
|
||||||
void removeUnusedFile(const FilePointer& file);
|
|
||||||
void clean();
|
void clean();
|
||||||
void clear();
|
void clear();
|
||||||
// Remove a file from the cache
|
// Remove a file from the cache
|
||||||
|
@ -125,16 +124,14 @@ private:
|
||||||
std::atomic<size_t> _totalFilesSize { 0 };
|
std::atomic<size_t> _totalFilesSize { 0 };
|
||||||
std::atomic<size_t> _unusedFilesSize { 0 };
|
std::atomic<size_t> _unusedFilesSize { 0 };
|
||||||
|
|
||||||
std::string _ext;
|
const std::string _ext;
|
||||||
std::string _dirname;
|
const std::string _dirname;
|
||||||
std::string _dirpath;
|
const std::string _dirpath;
|
||||||
bool _initialized { false };
|
bool _initialized { false };
|
||||||
|
|
||||||
|
Mutex _mutex;
|
||||||
Map _files;
|
Map _files;
|
||||||
Mutex _filesMutex;
|
|
||||||
|
|
||||||
Set _unusedFiles;
|
Set _unusedFiles;
|
||||||
Mutex _unusedFilesMutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class File : public QObject {
|
class File : public QObject {
|
||||||
|
|
Loading…
Reference in a new issue