145 lines
4.3 KiB
C++
145 lines
4.3 KiB
C++
#include "utils/file_locker.hpp"
|
|
#include <filesystem>
|
|
|
|
namespace utils {
|
|
|
|
namespace {
|
|
void DeleteFromSystem(const std::filesystem::path &path) {
|
|
if (!utils::DeleteFile(path)) {
|
|
spdlog::warn("Couldn't delete file {}!", path);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
////// FileRetainer //////
|
|
void FileRetainer::DeleteFile(const std::filesystem::path &path) {
|
|
if (!std::filesystem::exists(path)) {
|
|
spdlog::info("File {} doesn't exist.", path);
|
|
return;
|
|
}
|
|
|
|
auto absolute_path = std::filesystem::absolute(path);
|
|
if (active_accessors_.load()) {
|
|
files_for_deletion_.WithLock([&](auto &files) { files.emplace(std::move(absolute_path)); });
|
|
return;
|
|
}
|
|
std::unique_lock guard(main_lock_);
|
|
DeleteOrAddToQueue(absolute_path);
|
|
}
|
|
|
|
FileRetainer::FileLocker FileRetainer::AddLocker() {
|
|
const size_t current_locker_id = next_locker_id_.fetch_add(1);
|
|
lockers_.WithLock([&](auto &lockers) { lockers.emplace(current_locker_id, LockerEntry{}); });
|
|
return FileLocker{this, current_locker_id};
|
|
}
|
|
|
|
FileRetainer::~FileRetainer() { MG_ASSERT(files_for_deletion_->empty(), "Files weren't properly deleted"); }
|
|
|
|
[[nodiscard]] bool FileRetainer::FileLocked(const std::filesystem::path &path) {
|
|
return lockers_.WithLock([&](auto &lockers) {
|
|
for (const auto &[_, locker_entry] : lockers) {
|
|
if (locker_entry.LocksFile(path)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
void FileRetainer::DeleteOrAddToQueue(const std::filesystem::path &path) {
|
|
if (FileLocked(path)) {
|
|
files_for_deletion_.WithLock([&](auto &files) { files.emplace(path); });
|
|
} else {
|
|
DeleteFromSystem(path);
|
|
}
|
|
}
|
|
|
|
void FileRetainer::CleanQueue() {
|
|
std::unique_lock guard(main_lock_);
|
|
files_for_deletion_.WithLock([&](auto &files) {
|
|
for (auto it = files.cbegin(); it != files.cend();) {
|
|
if (!FileLocked(*it)) {
|
|
DeleteFromSystem(*it);
|
|
it = files.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
////// LockerEntry //////
|
|
void FileRetainer::LockerEntry::LockPath(const std::filesystem::path &path) {
|
|
auto absolute_path = std::filesystem::absolute(path);
|
|
if (std::filesystem::is_directory(absolute_path)) {
|
|
directories_.emplace(std::move(absolute_path));
|
|
return;
|
|
}
|
|
files_.emplace(std::move(absolute_path));
|
|
}
|
|
|
|
bool FileRetainer::LockerEntry::RemovePath(const std::filesystem::path &path) {
|
|
auto absolute_path = std::filesystem::absolute(path);
|
|
if (std::filesystem::is_directory(absolute_path)) {
|
|
return directories_.erase(absolute_path);
|
|
}
|
|
|
|
return files_.erase(absolute_path);
|
|
}
|
|
|
|
bool FileRetainer::LockerEntry::LocksFile(const std::filesystem::path &path) const {
|
|
MG_ASSERT(path.is_absolute(), "Absolute path needed to check if the file is locked.");
|
|
|
|
if (files_.count(path)) {
|
|
return true;
|
|
}
|
|
|
|
for (const auto &directory : directories_) {
|
|
auto directory_path_it = directory.begin();
|
|
auto path_it = path.begin();
|
|
while (directory_path_it != directory.end() && path_it != path.end()) {
|
|
if (*directory_path_it != *path_it) {
|
|
break;
|
|
}
|
|
++directory_path_it;
|
|
++path_it;
|
|
}
|
|
|
|
if (directory_path_it == directory.end()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////// FileLocker //////
|
|
FileRetainer::FileLocker::~FileLocker() {
|
|
file_retainer_->lockers_.WithLock([this](auto &lockers) { lockers.erase(locker_id_); });
|
|
file_retainer_->CleanQueue();
|
|
}
|
|
|
|
FileRetainer::FileLockerAccessor FileRetainer::FileLocker::Access() {
|
|
return FileLockerAccessor{file_retainer_, locker_id_};
|
|
}
|
|
|
|
////// FileLockerAccessor //////
|
|
FileRetainer::FileLockerAccessor::FileLockerAccessor(FileRetainer *retainer, size_t locker_id)
|
|
: file_retainer_{retainer}, retainer_guard_{retainer->main_lock_}, locker_id_{locker_id} {
|
|
file_retainer_->active_accessors_.fetch_add(1);
|
|
}
|
|
|
|
bool FileRetainer::FileLockerAccessor::AddPath(const std::filesystem::path &path) {
|
|
if (!std::filesystem::exists(path)) return false;
|
|
file_retainer_->lockers_.WithLock([&](auto &lockers) { lockers[locker_id_].LockPath(path); });
|
|
return true;
|
|
}
|
|
|
|
bool FileRetainer::FileLockerAccessor::RemovePath(const std::filesystem::path &path) {
|
|
return file_retainer_->lockers_.WithLock([&](auto &lockers) { return lockers[locker_id_].RemovePath(path); });
|
|
}
|
|
|
|
FileRetainer::FileLockerAccessor::~FileLockerAccessor() { file_retainer_->active_accessors_.fetch_sub(1); }
|
|
|
|
} // namespace utils
|