FileLocker doesn't depend on Godot

master
Marc Gilleron 2022-04-19 00:58:29 +01:00
parent 4e3d4d4ed8
commit 5965bcf98f
7 changed files with 51 additions and 54 deletions

View File

@ -265,7 +265,7 @@ private:
};
struct VoxelFileLockerRead {
VoxelFileLockerRead(String path) : _path(path) {
VoxelFileLockerRead(const std::string &path) : _path(path) {
VoxelServer::get_singleton().get_file_locker().lock_read(path);
}
@ -273,11 +273,11 @@ struct VoxelFileLockerRead {
VoxelServer::get_singleton().get_file_locker().unlock(_path);
}
String _path;
std::string _path;
};
struct VoxelFileLockerWrite {
VoxelFileLockerWrite(String path) : _path(path) {
VoxelFileLockerWrite(const std::string &path) : _path(path) {
VoxelServer::get_singleton().get_file_locker().lock_write(path);
}
@ -285,7 +285,7 @@ struct VoxelFileLockerWrite {
VoxelServer::get_singleton().get_file_locker().unlock(_path);
}
String _path;
std::string _path;
};
// Helper class to store tasks and schedule them in a single batch

View File

@ -47,7 +47,9 @@ FileResult check_magic_and_version(
return FILE_OK;
}
Error check_directory_created(const String &directory_path) {
Error check_directory_created(const std::string &p_directory_path) {
const String directory_path(p_directory_path.c_str());
Ref<DirAccess> d = DirAccess::create_for_path(directory_path);
if (d == nullptr) {
@ -68,7 +70,7 @@ Error check_directory_created(const String &directory_path) {
}
namespace voxel {
Error check_directory_created_using_file_locker(const String &directory_path) {
Error check_directory_created_using_file_locker(const std::string &directory_path) {
VoxelFileLockerWrite file_wlock(directory_path);
return check_directory_created(directory_path);
}

View File

@ -50,7 +50,7 @@ FileResult check_magic_and_version(
namespace voxel {
// Specific to voxel because it uses a global lock found only in VoxelServer
Error check_directory_created_using_file_locker(const String &directory_path);
Error check_directory_created_using_file_locker(const std::string &directory_path);
} // namespace voxel
void insert_bytes(FileAccess &f, size_t count, size_t temp_chunk_size = 512);

View File

@ -193,7 +193,8 @@ Error RegionFile::open(const String &fpath, bool create_if_not_found) {
CRASH_COND(f != nullptr);
// Checking folders, needed for region "forests"
const Error dir_err = check_directory_created_using_file_locker(fpath.get_base_dir());
const CharString fpath_base_dir = fpath.get_base_dir().utf8();
const Error dir_err = check_directory_created_using_file_locker(fpath_base_dir.get_data());
if (dir_err != OK) {
return ERR_CANT_CREATE;
}

View File

@ -279,21 +279,23 @@ FileResult VoxelStreamRegionFiles::save_meta() {
d["channel_depths"] = channel_depths;
JSON json;
String json_string = json.stringify(d, "\t", true);
const String json_string = json.stringify(d, "\t", true);
// Make sure the directory exists
{
Error err = check_directory_created_using_file_locker(_directory_path);
const CharString directory_path_utf8 = _directory_path.utf8();
Error err = check_directory_created_using_file_locker(directory_path_utf8.get_data());
if (err != OK) {
ERR_PRINT("Could not save meta");
return FILE_CANT_OPEN;
}
}
String meta_path = _directory_path.plus_file(META_FILE_NAME);
const String meta_path = _directory_path.plus_file(META_FILE_NAME);
const CharString meta_path_utf8 = meta_path.utf8();
Error err;
VoxelFileLockerWrite file_wlock(meta_path);
VoxelFileLockerWrite file_wlock(meta_path_utf8.get_data());
Ref<FileAccess> f = FileAccess::open(meta_path, FileAccess::WRITE, &err);
if (f.is_null()) {
ERR_PRINT(String("Could not save {0}").format(varray(meta_path)));
@ -335,12 +337,13 @@ FileResult VoxelStreamRegionFiles::load_meta() {
// Ensure you cleanup previous world before loading another
CRASH_COND(_region_cache.size() > 0);
String meta_path = _directory_path.plus_file(META_FILE_NAME);
const String meta_path = _directory_path.plus_file(META_FILE_NAME);
String json_string;
{
Error err;
VoxelFileLockerRead file_rlock(meta_path);
const CharString meta_path_utf8 = meta_path.utf8();
VoxelFileLockerRead file_rlock(meta_path_utf8.get_data());
Ref<FileAccess> f = FileAccess::open(meta_path, FileAccess::READ, &err);
if (f.is_null()) {
return FILE_CANT_OPEN;
@ -357,8 +360,7 @@ FileResult VoxelStreamRegionFiles::load_meta() {
const String json_err_msg = json.get_error_message();
const int json_err_line = json.get_error_line();
if (json_err != OK) {
ERR_PRINT(
String("Error when parsing {0}: line {1}: {2}").format(varray(meta_path, json_err_line, json_err_msg)));
ZN_PRINT_ERROR(format("Error when parsing {}: line {}: {}", meta_path, json_err_line, json_err_msg));
return FILE_INVALID_DATA;
}

View File

@ -48,11 +48,12 @@ void VoxelStreamBlockFiles::load_voxel_block(VoxelStream::VoxelQueryData &q) {
ERR_FAIL_COND(q.lod >= _meta.lod_count);
ERR_FAIL_COND(block_size != q.voxel_buffer.get_size());
Vector3i block_pos = get_block_position(q.origin_in_voxels) >> q.lod;
String file_path = get_block_file_path(block_pos, q.lod);
const Vector3i block_pos = get_block_position(q.origin_in_voxels) >> q.lod;
const String file_path = get_block_file_path(block_pos, q.lod);
const CharString file_path_utf8 = file_path.utf8();
Ref<FileAccess> f;
VoxelFileLockerRead file_rlock(file_path);
VoxelFileLockerRead file_rlock(file_path_utf8.get_data());
{
Error err;
f = FileAccess::open(file_path, FileAccess::READ, &err);
@ -125,13 +126,15 @@ void VoxelStreamBlockFiles::save_voxel_block(VoxelStream::VoxelQueryData &q) {
const String file_path = get_block_file_path(block_pos, q.lod);
{
const Error err = check_directory_created_using_file_locker(file_path.get_base_dir());
const CharString file_path_base_dir = file_path.get_base_dir().utf8();
const Error err = check_directory_created_using_file_locker(file_path_base_dir.get_data());
ERR_FAIL_COND(err != OK);
}
{
Ref<FileAccess> f;
VoxelFileLockerWrite file_wlock(file_path);
const CharString file_path_utf8 = file_path.utf8();
VoxelFileLockerWrite file_wlock(file_path_utf8.get_data());
{
Error err;
// Create file if not exists, always truncate
@ -177,18 +180,20 @@ FileResult VoxelStreamBlockFiles::save_meta() {
// Make sure the directory exists
{
Error err = check_directory_created_using_file_locker(_directory_path);
const CharString directory_path_utf8 = _directory_path.utf8();
const Error err = check_directory_created_using_file_locker(directory_path_utf8.get_data());
if (err != OK) {
ERR_PRINT("Could not save meta");
return FILE_CANT_OPEN;
}
}
String meta_path = _directory_path.plus_file(META_FILE_NAME);
const String meta_path = _directory_path.plus_file(META_FILE_NAME);
{
Error err;
VoxelFileLockerWrite file_wlock(meta_path);
const CharString meta_path_utf8 = meta_path.utf8();
VoxelFileLockerWrite file_wlock(meta_path_utf8.get_data());
Ref<FileAccess> f = FileAccess::open(meta_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V(f == nullptr, FILE_CANT_OPEN);
@ -221,12 +226,13 @@ FileResult VoxelStreamBlockFiles::load_or_create_meta() {
FileResult VoxelStreamBlockFiles::load_meta() {
CRASH_COND(_directory_path.is_empty());
String meta_path = _directory_path.plus_file(META_FILE_NAME);
const String meta_path = _directory_path.plus_file(META_FILE_NAME);
Meta meta;
{
Error open_result;
VoxelFileLockerRead file_rlock(meta_path);
const CharString meta_path_utf8 = meta_path.utf8();
VoxelFileLockerRead file_rlock(meta_path_utf8.get_data());
Ref<FileAccess> f = FileAccess::open(meta_path, FileAccess::READ, &open_result);
// Had to add ERR_FILE_CANT_OPEN because that's what Godot actually returns when the file doesn't exist...
if (!_meta_saved && (open_result == ERR_FILE_NOT_FOUND || open_result == ERR_FILE_CANT_OPEN)) {

View File

@ -5,7 +5,8 @@
#include "thread/mutex.h"
#include "thread/rw_lock.h"
#include <core/templates/hash_map.h>
#include <string>
#include <unordered_map>
namespace zylann {
@ -13,23 +14,15 @@ namespace zylann {
// so that multiple threads (controlled by this module) wanting to access the same file will lock a shared mutex.
class FileLocker {
public:
~FileLocker() {
const String *key = nullptr;
while ((key = _files.next(key))) {
File *f = _files[*key];
memdelete(f);
}
}
void lock_read(String fpath) {
void lock_read(const std::string &fpath) {
lock(fpath, true);
}
void lock_write(String fpath) {
void lock_write(const std::string &fpath) {
lock(fpath, false);
}
void unlock(String fpath) {
void unlock(const std::string &fpath) {
unlock_internal(fpath);
}
@ -39,18 +32,13 @@ private:
bool read_only;
};
void lock(String fpath, bool read_only) {
void lock(const std::string &fpath, bool read_only) {
File *fp = nullptr;
{
MutexLock lock(_files_mutex);
File **fpp = _files.getptr(fpath);
if (fpp == nullptr) {
fp = memnew(File);
_files.set(fpath, fp);
} else {
fp = *fpp;
}
// Get or create.
// Note, we never remove entries from the map
fp = &_files[fpath];
}
if (read_only) {
@ -65,14 +53,13 @@ private:
}
}
void unlock_internal(String fpath) {
void unlock_internal(const std::string &fpath) {
File *fp = nullptr;
// I assume `get_path` returns the same string that was used to open it
{
MutexLock lock(_files_mutex);
File **fpp = _files.getptr(fpath);
if (fpp != nullptr) {
fp = *fpp;
auto it = _files.find(fpath);
if (it != _files.end()) {
fp = &it->second;
}
}
ZN_ASSERT_RETURN(fp != nullptr);
@ -88,8 +75,7 @@ private:
private:
Mutex _files_mutex;
// Had to use dynamic allocs because HashMap does not implement move semantics
HashMap<String, File *> _files;
std::unordered_map<std::string, File> _files;
};
} // namespace zylann