From c584ef00aea6f346db99d85e673ca78de82cd4d6 Mon Sep 17 00:00:00 2001 From: Kirill Burtsev Date: Wed, 18 Oct 2023 23:06:34 +0200 Subject: [PATCH] Add separate multiprocess test for InterprocessMutex init and locks --- src/realm/util/interprocess_mutex.hpp | 3 ++ test/test_shared.cpp | 65 +++++++++++++++++++++++++++ test/util/test_path.cpp | 11 +++-- test/util/test_path.hpp | 9 +++- 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/realm/util/interprocess_mutex.hpp b/src/realm/util/interprocess_mutex.hpp index 6702d4b8906..a30ec92b650 100644 --- a/src/realm/util/interprocess_mutex.hpp +++ b/src/realm/util/interprocess_mutex.hpp @@ -82,6 +82,9 @@ class InterprocessMutex { InterprocessMutex(const InterprocessMutex&) = delete; InterprocessMutex& operator=(const InterprocessMutex&) = delete; + InterprocessMutex(InterprocessMutex&&) = default; + InterprocessMutex& operator=(InterprocessMutex&&) = default; + #if REALM_ROBUST_MUTEX_EMULATION || defined(_WIN32) struct SharedPart { }; diff --git a/test/test_shared.cpp b/test/test_shared.cpp index ad52a6cdc08..b4c518cb02e 100644 --- a/test/test_shared.cpp +++ b/test/test_shared.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include "test.hpp" #include "test_table_helper.hpp" +#include "util/spawned_process.hpp" extern unsigned int unit_test_random_seed; @@ -4345,4 +4347,67 @@ TEST(Shared_WriteToFail) CHECK(*tr == *dest); } +NONCONCURRENT_TEST_IF(Shared_LockFileConcurrentInit, testing_supports_spawn_process) +{ + auto path = realm::test_util::get_test_path(test_context.get_test_name(), ".test-dir"); + test_util::TestDirGuard test_dir(path, false); + test_dir.do_remove = SpawnedProcess::is_parent(); + auto lock_prefix = std::string(path) + "/lock"; + + struct Mutex : InterprocessMutex { + SharedPart sp; + + Mutex(Mutex&&) = default; + Mutex& operator=(Mutex&&) = default; + + Mutex(const std::string& name, const std::string& lock_prefix_path) + { + set_shared_part(sp, lock_prefix_path, name); + } + ~Mutex() + { + release_shared_part(); + } + }; + + for (size_t i = 0; i < 10; ++i) { + std::vector> spawned; + + // create multiple processes initializing multiple same purpose locks + for (size_t j = 0; j < 10; ++j) { + spawned.emplace_back( + test_util::spawn_process(test_context.test_details.test_name, util::format("child [%1]", i))); + + if (spawned.back()->is_child()) { + std::vector locks; + + // mimic the same impl detail as in DB and hope it'd trigger some assertions + for (auto tag : {"write", "control", "versions"}) { + locks.emplace_back(tag, lock_prefix); + CHECK(locks.back().is_valid()); + } + + // if somehow initialization is scrambled or there is an issues with + // underlying files then it should hang here + for (int k = 0; k < 3; ++k) { + for (auto&& mutex : locks) + mutex.lock(); + for (auto&& mutex : locks) + mutex.unlock(); + } + + exit(0); + } + } + + if (SpawnedProcess::is_parent()) { + for (auto&& process : spawned) + process->wait_for_child_to_finish(); + + // start everytime with no lock files for mutexes + test_dir.clean_dir(); + } + } +} + #endif // TEST_SHARED diff --git a/test/util/test_path.cpp b/test/util/test_path.cpp index 69e15fe7a7a..ea1c70ed7d6 100644 --- a/test/util/test_path.cpp +++ b/test/util/test_path.cpp @@ -230,11 +230,12 @@ TestPathGuard& TestPathGuard::operator=(TestPathGuard&& other) noexcept } -TestDirGuard::TestDirGuard(const std::string& path) +TestDirGuard::TestDirGuard(const std::string& path, bool init_clean) : m_path(path) { if (!try_make_dir(path)) { - clean_dir(path); + if (init_clean) + clean_dir(path); } } @@ -242,6 +243,10 @@ TestDirGuard::~TestDirGuard() noexcept { if (g_keep_files) return; + + if (!do_remove) + return; + try { clean_dir(m_path); remove_dir(m_path); @@ -266,7 +271,7 @@ void do_clean_dir(const std::string& path, const std::string& guard_string) // Try to avoid accidental removal of precious files due to bugs in // TestDirGuard or TEST_DIR macro. if (subpath.find(guard_string) == std::string::npos) - throw std::runtime_error("Bad test dir path"); + throw std::runtime_error("Bad test dir path: " + path + ", guard: " + guard_string); File::remove(subpath); } } diff --git a/test/util/test_path.hpp b/test/util/test_path.hpp index b95f8b6e4fd..651ae2d4bfe 100644 --- a/test/util/test_path.hpp +++ b/test/util/test_path.hpp @@ -120,7 +120,7 @@ class TestPathGuard { /// directory, then removes the directory. class TestDirGuard { public: - TestDirGuard(const std::string& path); + TestDirGuard(const std::string& path, bool init_clean = true); ~TestDirGuard() noexcept; operator std::string() const { @@ -131,6 +131,13 @@ class TestDirGuard { return m_path.c_str(); } + bool do_remove = true; + + void clean_dir() + { + clean_dir(m_path); + } + private: std::string m_path; void clean_dir(const std::string& path);