From: Andrei Pavel Date: Tue, 28 Jun 2022 14:46:00 +0000 (+0300) Subject: [#2448] add MultiThreadingLock X-Git-Tag: Kea-2.2.0~81 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c73b87afedda88dd11cb1dfaecefc7c699de36f;p=thirdparty%2Fkea.git [#2448] add MultiThreadingLock --- diff --git a/src/lib/util/multi_threading_mgr.cc b/src/lib/util/multi_threading_mgr.cc index 7efd048bca..e537a05acb 100644 --- a/src/lib/util/multi_threading_mgr.cc +++ b/src/lib/util/multi_threading_mgr.cc @@ -227,6 +227,12 @@ MultiThreadingCriticalSection::~MultiThreadingCriticalSection() { MultiThreadingMgr::instance().exitCriticalSection(); } +MultiThreadingLock::MultiThreadingLock(std::mutex& mutex) { + if (MultiThreadingMgr::instance().getMode()) { + lock_ = std::unique_lock(mutex); + } +} + void CSCallbackSetList::addCallbackSet(const std::string& name, const CSCallbackSet::Callback& check_cb, diff --git a/src/lib/util/multi_threading_mgr.h b/src/lib/util/multi_threading_mgr.h index fa3f3a353e..8b23c9e069 100644 --- a/src/lib/util/multi_threading_mgr.h +++ b/src/lib/util/multi_threading_mgr.h @@ -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 lock_; +}; + /// @note: everything here MUST be used ONLY from the main thread. /// When called from a thread of the pool it can deadlock. diff --git a/src/lib/util/tests/multi_threading_mgr_unittest.cc b/src/lib/util/tests/multi_threading_mgr_unittest.cc index 2dc20759ca..755d8923a3 100644 --- a/src/lib/util/tests/multi_threading_mgr_unittest.cc +++ b/src/lib/util/tests/multi_threading_mgr_unittest.cc @@ -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: