FileLocker doesn't depend on Godot
parent
4e3d4d4ed8
commit
5965bcf98f
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue