]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2448] add MultiThreadingLock
authorAndrei Pavel <andrei@isc.org>
Tue, 28 Jun 2022 14:46:00 +0000 (17:46 +0300)
committerAndrei Pavel <andrei@isc.org>
Thu, 7 Jul 2022 11:48:20 +0000 (11:48 +0000)
src/lib/util/multi_threading_mgr.cc
src/lib/util/multi_threading_mgr.h
src/lib/util/tests/multi_threading_mgr_unittest.cc

index 7efd048bca96ecac1515f314e24ad1774a05daae..e537a05acb5000c827702ebb511993a34bd3e7bd 100644 (file)
@@ -227,6 +227,12 @@ MultiThreadingCriticalSection::~MultiThreadingCriticalSection() {
     MultiThreadingMgr::instance().exitCriticalSection();
 }
 
+MultiThreadingLock::MultiThreadingLock(std::mutex& mutex) {
+    if (MultiThreadingMgr::instance().getMode()) {
+        lock_ = std::unique_lock<std::mutex>(mutex);
+    }
+}
+
 void
 CSCallbackSetList::addCallbackSet(const std::string& name,
                                   const CSCallbackSet::Callback& check_cb,
index fa3f3a353ead5b7239b0a048b9fa15e19fd52cb1..8b23c9e069741e1f48d9693ad0e2f1a4a9949429 100644 (file)
@@ -108,7 +108,8 @@ private:
 ///
 /// This singleton class holds the multi-threading mode.
 ///
-/// The standard way to use it is:
+/// See the @ref MultiThreadingLock class for a standard way of protecting code
+/// with a mutex. Or if you want to make it look like you're writing more code:
 /// @code
 /// if (MultiThreadingMgr::instance().getMode()) {
 ///     multi-threaded code
@@ -325,6 +326,20 @@ private:
     CSCallbackSetList cs_callbacks_;
 };
 
+/// @brief RAII lock object to protect the code in the same scope with a mutex
+struct MultiThreadingLock {
+    /// @brief Constructor locks the mutex if multi-threading is enabled.
+    ///
+    /// The lock is automatically unlocked in the default destructor.
+    ///
+    /// @param mutex the mutex to be locked
+    MultiThreadingLock(std::mutex& mutex);
+
+private:
+    /// @brief object keeping the mutex locked for its entire lifetime
+    std::unique_lock<std::mutex> lock_;
+};
+
 /// @note: everything here MUST be used ONLY from the main thread.
 /// When called from a thread of the pool it can deadlock.
 
index 2dc20759ca0e67810a3f697acf5174b862a52686..755d8923a3811c01340faedeaa19d1acd7a9a45a 100644 (file)
@@ -324,6 +324,40 @@ TEST(MultiThreadingMgrTest, criticalSection) {
     MultiThreadingMgr::instance().apply(false, 0, 0);
 }
 
+/// @brief Checks that the lock works only when multi-threading is enabled and
+/// only during its lifetime.
+TEST(MultiThreadingLockTest, scope) {
+    // Check that the mutex is unlocked by default at first.
+    std::mutex mutex;
+    ASSERT_TRUE(mutex.try_lock());
+    mutex.unlock();
+
+    EXPECT_NO_THROW(MultiThreadingMgr::instance().setMode(false));
+
+    // Check that the lock does not locks the mutex if multi-threading is disabled.
+    {
+        MultiThreadingLock lock(mutex);
+        ASSERT_TRUE(mutex.try_lock());
+        mutex.unlock();
+    }
+
+    // Check that the mutex is still unlocked when the lock goes out of scope.
+    ASSERT_TRUE(mutex.try_lock());
+    mutex.unlock();
+
+    EXPECT_NO_THROW(MultiThreadingMgr::instance().setMode(true));
+
+    // Check that the lock actively locks the mutex if multi-threading is enabled.
+    {
+        MultiThreadingLock lock(mutex);
+        ASSERT_FALSE(mutex.try_lock());
+    }
+
+    // Check that the mutex is unlocked when the lock goes out of scope.
+    ASSERT_TRUE(mutex.try_lock());
+    mutex.unlock();
+}
+
 /// @brief Test fixture for exercised CriticalSection callbacks.
 class CriticalSectionCallbackTest : public ::testing::Test {
 public: