Commit: e27117ff5a6f992b26d8ffd6ec8fbf68a72a81f1 Author: Bastien Montagne Date: Tue Jan 17 15:05:13 2023 +0100 Branches: tmp-flock https://developer.blender.org/rBe27117ff5a6f992b26d8ffd6ec8fbf68a72a81f1
Add 'file locking' feature to BLI_fileops. The new API allows to lock (shared or exclusive) an open file descriptor, and later unlock it. This relies on `flock` on unix systems, and `LockFileEx`/`UnlockFile` on Windows. =================================================================== M source/blender/blenlib/BLI_fileops.h M source/blender/blenlib/intern/fileops.c M source/blender/blenlib/tests/BLI_fileops_test.cc =================================================================== diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 0ff75ca16e5..6c8d3085dd6 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -221,6 +221,38 @@ void *BLI_gzopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/** + * API handling flie locking, to ensure several instances of Blender can cooperate in using a same + * file. + */ + +/** + * Attempt to lock a file in a shared way (i.e. other shared locks can be acquired on the same file + * at the same time). + * + * \param file_descriptor A file handle as returned e.g. by #BLI_open. + * \param is_blocking Whether the call should be blocking, or fail and return immediately if the + * lock cannot be acquired. + * \return true if the file was successfully locked, false otherwise. + */ +bool BLI_flock_shared(const int file_descriptor, const bool is_blocking); +/** + * Attempt to lock a file in an exclusive way (i.e. no other locks can be acquired on the same file + * at the same time, be it exclusive or shared). + * + * Same parameters and return value as for #BLI_flock_shared. + */ +bool BLI_flock_exclusive(const int file_descriptor, const bool is_blocking); +/** + * Release a lock (either shared or exclusive) previously acquired on the given file handle. + * + * This function is never blocking. + * + * \param file_descriptor is a file handle as returned e.g. by #BLI_open. + * \return true is the file was successfully unlocked, false otherwise. + */ +bool BLI_flock_release(const int file_descriptor); + /** * Returns true if the file with the specified name can be written. * This implementation uses access(2), which makes the check according diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 005de1f85b4..03cb23be5d3 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -22,6 +22,7 @@ # include "BLI_winstuff.h" # include "utf_winfunc.h" # include "utfconv.h" +# include <fileapi.h> # include <io.h> # include <shellapi.h> # include <shobjidl.h> @@ -33,6 +34,7 @@ # include <objc/runtime.h> # endif # include <dirent.h> +# include <sys/file.h> # include <sys/param.h> # include <sys/wait.h> # include <unistd.h> @@ -296,6 +298,32 @@ int BLI_access(const char *filepath, int mode) return uaccess(filepath, mode); } +bool BLI_flock_shared(const int file_descriptor, const bool is_blocking) +{ + OVERLAPPED overlapp_info = {0}; + return LockFileEx(file_descriptor, + (is_blocking ? 0 : LOCKFILE_FAIL_IMMEDIATELY), + 0, + MAX_INT, + MAX_INT, + &overlapp_info); +} +bool BLI_flock_exclusive(const int file_descriptor, const bool is_blocking) +{ + OVERLAPPED overlapp_info = {0}; + return LockFileEx(file_descriptor, + LOCKFILE_EXCLUSIVE_LOCK | (is_blocking ? 0 : LOCKFILE_FAIL_IMMEDIATELY), + 0, + MAX_INT, + MAX_INT, + &overlapp_info); +} +bool BLI_flock_release(const int file_descriptor) +{ + OVERLAPPED overlapp_info = {0}; + return UnlockFile(file_descriptor, 0, 0, MAX_INT, MAX_INT); +} + static bool delete_soft(const wchar_t *path_16, const char **error_message) { /* Deletes file or directory to recycling bin. The latter moves all contained files and @@ -931,6 +959,19 @@ int BLI_access(const char *filepath, int mode) return access(filepath, mode); } +bool BLI_flock_shared(const int file_descriptor, const bool is_blocking) +{ + return flock(file_descriptor, LOCK_SH | (is_blocking ? 0 : LOCK_NB)); +} +bool BLI_flock_exclusive(const int file_descriptor, const bool is_blocking) +{ + return flock(file_descriptor, LOCK_EX | (is_blocking ? 0 : LOCK_NB)); +} +bool BLI_flock_release(const int file_descriptor) +{ + return flock(file_descriptor, LOCK_UN); +} + int BLI_delete(const char *file, bool dir, bool recursive) { BLI_assert(!BLI_path_is_rel(file)); diff --git a/source/blender/blenlib/tests/BLI_fileops_test.cc b/source/blender/blenlib/tests/BLI_fileops_test.cc index 2a5ecb7d11d..67e4c640358 100644 --- a/source/blender/blenlib/tests/BLI_fileops_test.cc +++ b/source/blender/blenlib/tests/BLI_fileops_test.cc @@ -1,5 +1,14 @@ /* SPDX-License-Identifier: Apache-2.0 */ +#include <fcntl.h> + +#ifdef WIN32 +# include "BLI_winstuff.h" +# include <io.h> +#else +# include <unistd.h> /* FreeBSD, for write() and close(). */ +#endif + #include "BLI_fileops.hh" #include "testing/testing.h" @@ -37,4 +46,50 @@ TEST(fileops, fstream_open_charptr_filename) /* Reading the file not tested here. That's deferred to `std::fstream` anyway. */ } +TEST(fileops, flock_file) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + const std::string filepath_str = test_files_dir + "/asset_library/новый/blender_assets.cats.txt"; + const char *filepath = filepath_str.c_str(); + + const int fhandle_1 = BLI_open(filepath, O_BINARY | O_RDONLY, 0); + const int fhandle_2 = BLI_open(filepath, O_BINARY | O_RDONLY, 0); + + bool flock_result = BLI_flock_shared(fhandle_1, false); + ASSERT_TRUE(flock_result); + + flock_result = BLI_flock_shared(fhandle_2, false); + ASSERT_TRUE(flock_result); + + flock_result = BLI_flock_release(fhandle_2); + ASSERT_TRUE(flock_result); + + /* Acquiring an exclusive lock on a file which already has another handle locked should not be + * possible. */ + flock_result = BLI_flock_exclusive(fhandle_2, false); + ASSERT_FALSE(flock_result); + + flock_result = BLI_flock_release(fhandle_1); + ASSERT_TRUE(flock_result); + + flock_result = BLI_flock_exclusive(fhandle_2, false); + ASSERT_TRUE(flock_result); + + flock_result = BLI_flock_release(fhandle_2); + ASSERT_TRUE(flock_result); + + close(fhandle_1); + close(fhandle_2); + + /* Acquiring lock on closed/non-existent file handles should not be possible. */ + flock_result = BLI_flock_shared(fhandle_1, false); + ASSERT_FALSE(flock_result); + flock_result = BLI_flock_shared(fhandle_2, false); + ASSERT_FALSE(flock_result); +} + } // namespace blender::tests _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs