[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