2021-01-16 13:34:45 +00:00
|
|
|
#ifndef VOXEL_FILE_LOCKER_H
|
|
|
|
#define VOXEL_FILE_LOCKER_H
|
|
|
|
|
2021-12-13 21:38:10 +00:00
|
|
|
#include <core/io/file_access.h>
|
2021-02-19 01:30:22 +00:00
|
|
|
#include <core/os/mutex.h>
|
2021-01-16 13:34:45 +00:00
|
|
|
#include <core/os/rw_lock.h>
|
2021-12-13 21:38:10 +00:00
|
|
|
#include <core/templates/hash_map.h>
|
2021-01-16 13:34:45 +00:00
|
|
|
|
2021-12-29 19:22:59 +00:00
|
|
|
namespace zylann {
|
|
|
|
|
2021-01-16 13:34:45 +00:00
|
|
|
// Performs software locking on paths,
|
|
|
|
// so that multiple threads (controlled by this module) wanting to access the same file will lock a shared mutex.
|
2021-12-29 19:22:59 +00:00
|
|
|
class FileLocker {
|
2021-01-16 13:34:45 +00:00
|
|
|
public:
|
2021-12-29 19:22:59 +00:00
|
|
|
~FileLocker() {
|
2021-02-19 01:30:22 +00:00
|
|
|
const String *key = nullptr;
|
|
|
|
while ((key = _files.next(key))) {
|
|
|
|
File *f = _files[*key];
|
|
|
|
memdelete(f);
|
2021-01-16 13:34:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void lock_read(String fpath) {
|
|
|
|
lock(fpath, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lock_write(String fpath) {
|
|
|
|
lock(fpath, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void unlock(String fpath) {
|
|
|
|
unlock_internal(fpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct File {
|
2021-02-19 01:30:22 +00:00
|
|
|
RWLock lock;
|
2021-01-16 13:34:45 +00:00
|
|
|
bool read_only;
|
|
|
|
};
|
|
|
|
|
|
|
|
void lock(String fpath, bool read_only) {
|
|
|
|
File *fp = nullptr;
|
|
|
|
{
|
|
|
|
MutexLock lock(_files_mutex);
|
2021-02-19 01:30:22 +00:00
|
|
|
File **fpp = _files.getptr(fpath);
|
2021-01-16 13:34:45 +00:00
|
|
|
|
2021-02-19 01:30:22 +00:00
|
|
|
if (fpp == nullptr) {
|
|
|
|
fp = memnew(File);
|
|
|
|
_files.set(fpath, fp);
|
2021-12-13 21:38:10 +00:00
|
|
|
} else {
|
2021-03-12 08:34:57 +08:00
|
|
|
fp = *fpp;
|
|
|
|
}
|
2021-01-16 13:34:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (read_only) {
|
2021-02-19 01:30:22 +00:00
|
|
|
fp->lock.read_lock();
|
2021-01-16 13:34:45 +00:00
|
|
|
// The read lock was acquired. It means nobody is writing.
|
|
|
|
fp->read_only = true;
|
|
|
|
|
|
|
|
} else {
|
2021-02-19 01:30:22 +00:00
|
|
|
fp->lock.write_lock();
|
2021-01-16 13:34:45 +00:00
|
|
|
// The write lock was acquired. It means only one thread is writing.
|
|
|
|
fp->read_only = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void unlock_internal(String fpath) {
|
|
|
|
File *fp = nullptr;
|
|
|
|
// I assume `get_path` returns the same string that was used to open it
|
|
|
|
{
|
|
|
|
MutexLock lock(_files_mutex);
|
2021-02-19 01:30:22 +00:00
|
|
|
File **fpp = _files.getptr(fpath);
|
|
|
|
if (fpp != nullptr) {
|
|
|
|
fp = *fpp;
|
|
|
|
}
|
2021-01-16 13:34:45 +00:00
|
|
|
}
|
|
|
|
ERR_FAIL_COND(fp == nullptr);
|
|
|
|
// TODO FileAccess::reopen can have been called, nullifying my efforts to enforce thread sync :|
|
|
|
|
// So for now please don't do that
|
|
|
|
|
|
|
|
if (fp->read_only) {
|
2021-02-19 01:30:22 +00:00
|
|
|
fp->lock.read_unlock();
|
2021-01-16 13:34:45 +00:00
|
|
|
} else {
|
2021-02-19 01:30:22 +00:00
|
|
|
fp->lock.write_unlock();
|
2021-01-16 13:34:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-02-19 01:30:22 +00:00
|
|
|
Mutex _files_mutex;
|
|
|
|
// Had to use dynamic allocs because HashMap does not implement move semantics
|
|
|
|
HashMap<String, File *> _files;
|
2021-01-16 13:34:45 +00:00
|
|
|
};
|
|
|
|
|
2021-12-29 19:22:59 +00:00
|
|
|
} // namespace zylann
|
|
|
|
|
2021-01-16 13:34:45 +00:00
|
|
|
#endif // VOXEL_FILE_LOCKER_H
|