[Bf-blender-cvs] [e27117ff5a6] tmp-flock: Add 'file locking' feature to BLI_fileops.
Bastien Montagne
noreply at git.blender.org
Tue Jan 17 15:11:00 CET 2023
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
More information about the Bf-blender-cvs
mailing list