+++ /dev/null
-// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-
-#include <util/threads/lock_guard.h>
-#include <exceptions/exceptions.h>
-
-#include <exceptions/exceptions.h>
-
-#include <condition_variable>
-#include <list>
-#include <mutex>
-#include <set>
-#include <thread>
-
-using namespace isc::util;
-using namespace isc::util::thread;
-using namespace std;
-
-namespace {
-
-/// @brief test mutex class used to check the internal state of a 'fictional'
-/// mutex so that the functionality of the LockGuard can be tested
-/// the test mutex can be recursive which means that a lock can be called on the
-/// same thread and not resulting in a dead lock
-class TestMutex {
-public:
- /// @brief Constructor
- ///
- /// @param recursive sets the mutex as recursive mutex
- TestMutex(bool recursive = false) : lock_(0), dead_lock_(false),
- lock_count_(0), unlock_count_(0), recursive_(recursive) {
- }
-
- /// @brief lock the mutex
- void lock() {
- lock_guard<mutex> lk(mutex_);
- if (lock_ >= 1) {
- // mutex is already locked
- if (!recursive_) {
- // lock on a non-recursive mutex resulting in a dead lock
- dead_lock_ = true;
- isc_throw(isc::InvalidOperation,
- "recursive lock on already locked mutex resulting in "
- "dead lock");
- } else {
- // lock on a recursive mutex
- if (this_thread::get_id() != id_) {
- // lock on a recursive mutex on a different thread resulting
- // in a dead lock
- dead_lock_ = true;
- isc_throw(isc::InvalidOperation,
- "recursive lock on a different thread on already "
- "locked mutex resulting in dead lock");
- }
- }
- }
- // increment the total number of locks
- lock_count_++;
- // increment the lock state
- lock_++;
- // save the thread id
- id_ = this_thread::get_id();
- }
-
- /// @brief unlock the mutex
- void unlock() {
- lock_guard<mutex> lk(mutex_);
- if (lock_ <= 0) {
- // unlock an unlocked mutex
- isc_throw(isc::InvalidOperation, "unlock on non locked mutex "
- "resulting in undefined behavior");
- }
- if (lock_ == 1) {
- // only one thread has the lock
- // self healing mutex reseting the dead lock flag
- dead_lock_ = false;
- // reset the thread id
- id_ = std::thread::id();
- }
- // increment the total number of unlocks
- unlock_count_++;
- // decrement the lock state
- lock_--;
- }
-
- /// @brief get the mutex lock state
- ///
- /// @return the mutex lock state
- int32_t getLock() {
- lock_guard<mutex> lk(mutex_);
- return lock_;
- }
-
- /// @brief get the mutex dead lock state
- ///
- /// @return the mutex dead lock state
- bool getDeadLock() {
- lock_guard<mutex> lk(mutex_);
- return dead_lock_;
- }
-
- /// @brief get the number of locks performed on mutex
- ///
- /// @return the mutex number of locks
- uint32_t getLockCount() {
- lock_guard<mutex> lk(mutex_);
- return lock_count_;
- }
-
- /// @brief get the number of unlocks performed on mutex
- ///
- /// @return the mutex number of unlocks
- uint32_t getUnlockCount() {
- lock_guard<mutex> lk(mutex_);
- return unlock_count_;
- }
-
- /// @brief test the internal state of the mutex
- ///
- /// @param expected_lock check equality of this value with lock state
- /// @param expected_lock_count check equality of this value with lock count
- /// @param expected_unlock_count check equality of this value with unlock count
- /// @param expected_dead_lock check equality of this value with dead lock state
- void testMutexState(int32_t expected_lock,
- uint32_t expected_lock_count,
- uint32_t expected_unlock_count,
- bool expected_dead_lock) {
- ASSERT_EQ(getLock(), expected_lock);
- ASSERT_EQ(getLockCount(), expected_lock_count);
- ASSERT_EQ(getUnlockCount(), expected_unlock_count);
- ASSERT_EQ(getDeadLock(), expected_dead_lock);
- }
-
-private:
- /// @brief internal lock state of the mutex
- int32_t lock_;
-
- /// @brief state which indicates that the mutex is in dead lock
- bool dead_lock_;
-
- /// @brief total number of locks performed on the mutex
- uint32_t lock_count_;
-
- /// @brief total number of unlocks performed on the mutex
- uint32_t unlock_count_;
-
- /// @brief flag to indicate if the mutex is recursive or not
- bool recursive_;
-
- /// @brief mutex used to keep the internal state consistent
- mutex mutex_;
-
- /// @brief the id of the thread holding the mutex
- std::thread::id id_;
-};
-
-/// @brief Test Fixture for testing isc::util::thread::LockGuard
-class LockGuardTest : public ::testing::Test {
-};
-
-/// @brief test LockGuard functionality with non-recursive mutex, recursive mutex
-/// and null pointer
-TEST_F(LockGuardTest, testLock) {
- shared_ptr<TestMutex> test_mutex;
- // test non-recursive lock
- test_mutex = make_shared<TestMutex>();
- test_mutex->testMutexState(0, 0, 0, false);
- {
- // call LockGuard constructor which locks mutex
- LockGuard<TestMutex> lock(test_mutex.get());
- // expect lock 1 lock_count 1 unlock_count 0 dead_lock false
- test_mutex->testMutexState(1, 1, 0, false);
- {
- // call LockGuard constructor which locks mutex resulting in an
- // exception as the mutex is already locked (dead lock)
- EXPECT_THROW(LockGuard<TestMutex> lock(test_mutex.get()),
- isc::InvalidOperation);
- // expect lock 1 lock_count 1 unlock_count 0 dead_lock true
- // you should not be able to get here...using a real mutex
- test_mutex->testMutexState(1, 1, 0, true);
- }
- // expect lock 1 lock_count 1 unlock_count 0 dead_lock true
- // you should not be able to get here...using a real mutex
- test_mutex->testMutexState(1, 1, 0, true);
- }
- // expect lock 0 lock_count 1 unlock_count 1 dead_lock false
- // the implementation is self healing when completely unlocking the mutex
- test_mutex->testMutexState(0, 1, 1, false);
- // test recursive lock
- test_mutex = make_shared<TestMutex>(true);
- test_mutex->testMutexState(0, 0, 0, false);
- {
- // call LockGuard constructor which locks mutex
- LockGuard<TestMutex> lock(test_mutex.get());
- // expect lock 1 lock_count 1 unlock_count 0 dead_lock false
- test_mutex->testMutexState(1, 1, 0, false);
- {
- // call LockGuard constructor which locks mutex but does not block
- // as this is done on the same thread and the mutex is recursive
- EXPECT_NO_THROW(LockGuard<TestMutex> lock(test_mutex.get()));
- // expect lock 1 lock_count 2 unlock_count 1 dead_lock false
- // the destructor was already called in EXPECT_NO_THROW scope
- test_mutex->testMutexState(1, 2, 1, false);
- }
- // expect lock 1 lock_count 2 unlock_count 1 dead_lock false
- test_mutex->testMutexState(1, 2, 1, false);
- }
- // expect lock 0 lock_count 2 unlock_count 2 dead_lock false
- test_mutex->testMutexState(0, 2, 2, false);
- // test LockGuart with no mutex
- {
- LockGuard<TestMutex> lock(nullptr);
- {
- EXPECT_NO_THROW(LockGuard<TestMutex> lock(nullptr));
- }
- }
- }
-
-} // namespace