diff --git a/base_c/include/embb/base/c/mutex.h b/base_c/include/embb/base/c/mutex.h index e2cc648..6dae96d 100644 --- a/base_c/include/embb/base/c/mutex.h +++ b/base_c/include/embb/base/c/mutex.h @@ -193,12 +193,16 @@ int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex); /** * Tries to lock the shared mutex for writing and returns immediately. * + * The try to lock fails not only if the shared mutex was already locked for + * writing, but also in case it was locked shared for reading. + * * \pre \c shared_mutex is initialized * \post If successful, \c shared_mutex is locked for writing * * \param shared_mutex Pointer to shared mutex * \return EMBB_SUCCESS if shared_mutex could be locked for writing \n - * EMBB_BUSY if shared_mutex could not be locked for writing \n + * EMBB_BUSY if shared_mutex could not be locked for writing, because + * the mutex is locked for writing or reading \n * EMBB_ERROR if an error occurred * * \threadsafe diff --git a/base_c/test/shared_mutex_test.cc b/base_c/test/shared_mutex_test.cc index fd9986c..19ab6cb 100644 --- a/base_c/test/shared_mutex_test.cc +++ b/base_c/test/shared_mutex_test.cc @@ -32,7 +32,8 @@ namespace base { namespace test { SharedMutexTest::SharedMutexTest() - : counter_(0), + : shared_mutex_(), + counter_(0), num_threads_(partest::TestSuite::GetDefaultNumThreads()), num_iterations_(partest::TestSuite::GetDefaultNumIterations()) { CreateUnit("Shared read") @@ -41,19 +42,19 @@ SharedMutexTest::SharedMutexTest() num_threads_, num_iterations_) .Post(&SharedMutexTest::TestSharedReadPost, this); CreateUnit("Multiple writer") - .Pre(&SharedMutexTest::TestMultipleWriterPre, this) - .Add(&SharedMutexTest::TestMultipleWriterReaderMethod, this, + .Pre(&SharedMutexTest::TestExclusiveWriterPre, this) + .Add(&SharedMutexTest::TestExclusiveWriterReaderMethod, this, num_threads_ / 2, num_iterations_) - .Add(&SharedMutexTest::TestMultipleWriterWriterMethod, this, + .Add(&SharedMutexTest::TestExclusiveWriterWriterMethod, this, num_threads_ / 2, num_iterations_) - .Post(&SharedMutexTest::TestMultipleWriterPost, this); - CreateUnit("Single writer") - .Pre(&SharedMutexTest::TestSingleWriterPre, this) - .Add(&SharedMutexTest::TestSingleWriterReaderMethod, this, - num_threads_, num_iterations_) - .Add(&SharedMutexTest::TestSingleWriterWriterMethod, this, - 1, num_iterations_) - .Post(&SharedMutexTest::TestSingleWriterPost, this); + .Post(&SharedMutexTest::TestExclusiveWriterPost, this); +// CreateUnit("Single writer") +// .Pre(&SharedMutexTest::TestSingleWriterPre, this) +// .Add(&SharedMutexTest::TestSingleWriterReaderMethod, this, +// num_threads_, num_iterations_) +// .Add(&SharedMutexTest::TestSingleWriterWriterMethod, this, +// 1, num_iterations_) +// .Post(&SharedMutexTest::TestSingleWriterPost, this); } void SharedMutexTest::TestSharedReadPre() { @@ -63,10 +64,13 @@ void SharedMutexTest::TestSharedReadPre() { void SharedMutexTest::TestSharedReadThreadMethod() { int success = embb_shared_mutex_try_lock_shared(&shared_mutex_); - PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading."); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to try-lock for reading."); + + success = embb_shared_mutex_unlock_shared(&shared_mutex_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); - int spin = 10000; - while (--spin != 0); + success = embb_shared_mutex_lock_shared(&shared_mutex_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading."); success = embb_shared_mutex_unlock_shared(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); @@ -76,14 +80,14 @@ void SharedMutexTest::TestSharedReadPost() { embb_shared_mutex_destroy(&shared_mutex_); } -void SharedMutexTest::TestMultipleWriterPre() { +void SharedMutexTest::TestExclusiveWriterPre() { int success = embb_shared_mutex_init(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); counter_ = 0; } -void SharedMutexTest::TestMultipleWriterReaderMethod() { +void SharedMutexTest::TestExclusiveWriterReaderMethod() { // Just add some contention int success = embb_shared_mutex_lock_shared(&shared_mutex_); @@ -93,7 +97,7 @@ void SharedMutexTest::TestMultipleWriterReaderMethod() { PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); } -void SharedMutexTest::TestMultipleWriterWriterMethod() { +void SharedMutexTest::TestExclusiveWriterWriterMethod() { int success = embb_shared_mutex_lock(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); @@ -103,43 +107,43 @@ void SharedMutexTest::TestMultipleWriterWriterMethod() { PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); } -void SharedMutexTest::TestMultipleWriterPost() { +void SharedMutexTest::TestExclusiveWriterPost() { PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2), "Counter value is inconsistent."); embb_shared_mutex_destroy(&shared_mutex_); } -void SharedMutexTest::TestSingleWriterPre() { - int success = embb_shared_mutex_init(&shared_mutex_); - PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); - - counter_ = 0; -} - -void SharedMutexTest::TestSingleWriterReaderMethod() { - // Just add some contention - - int success = embb_shared_mutex_lock_shared(&shared_mutex_); - PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading."); - - success = embb_shared_mutex_unlock_shared(&shared_mutex_); - PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); -} - -void SharedMutexTest::TestSingleWriterWriterMethod() { - int success = embb_shared_mutex_try_lock(&shared_mutex_); - PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); - - ++counter_; - - success = embb_shared_mutex_unlock(&shared_mutex_); - PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); -} - -void SharedMutexTest::TestSingleWriterPost() { - PT_ASSERT_EQ_MSG(counter_, num_iterations_, "Counter value is inconsistent."); - embb_shared_mutex_destroy(&shared_mutex_); -} +//void SharedMutexTest::TestSingleWriterPre() { +// int success = embb_shared_mutex_init(&shared_mutex_); +// PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); +// +// counter_ = 0; +//} + +//void SharedMutexTest::TestSingleWriterReaderMethod() { +// // Just add some contention +// +// int success = embb_shared_mutex_lock_shared(&shared_mutex_); +// PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading."); +// +// success = embb_shared_mutex_unlock_shared(&shared_mutex_); +// PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); +//} +// +//void SharedMutexTest::TestSingleWriterWriterMethod() { +// int success = embb_shared_mutex_lock(&shared_mutex_); +// PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); +// +// ++counter_; +// +// success = embb_shared_mutex_unlock(&shared_mutex_); +// PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); +//} +// +//void SharedMutexTest::TestSingleWriterPost() { +// PT_ASSERT_EQ_MSG(counter_, num_iterations_, "Counter value is inconsistent."); +// embb_shared_mutex_destroy(&shared_mutex_); +//} } // namespace test } // namespace base diff --git a/base_c/test/shared_mutex_test.h b/base_c/test/shared_mutex_test.h index 43e29d7..fd4d535 100644 --- a/base_c/test/shared_mutex_test.h +++ b/base_c/test/shared_mutex_test.h @@ -58,22 +58,22 @@ class SharedMutexTest : public partest::TestCase { * waiting). The readers use lock_shared() and test if that always obtains the * shared lock (after waiting). */ - void TestMultipleWriterPre(); - void TestMultipleWriterReaderMethod(); - void TestMultipleWriterWriterMethod(); - void TestMultipleWriterPost(); + void TestExclusiveWriterPre(); + void TestExclusiveWriterReaderMethod(); + void TestExclusiveWriterWriterMethod(); + void TestExclusiveWriterPost(); - /** - * Multiple readers, single writer. - * - * The single writer uses try_lock() and tests if that always obtains the - * lock. The readers use lock_shared() and test if that always obtains the - * shared lock (after waiting). - */ - void TestSingleWriterPre(); - void TestSingleWriterReaderMethod(); - void TestSingleWriterWriterMethod(); - void TestSingleWriterPost(); +// /** +// * Multiple readers, single writer. +// * +// * The single writer uses try_lock() and tests if that always obtains the +// * lock. The readers use lock_shared() and test if that always obtains the +// * shared lock (after waiting). +// */ +// void TestSingleWriterPre(); +// void TestSingleWriterReaderMethod(); +// void TestSingleWriterWriterMethod(); +// void TestSingleWriterPost(); embb_shared_mutex_t shared_mutex_; size_t counter_; diff --git a/base_cpp/include/embb/base/internal/mutex-inl.h b/base_cpp/include/embb/base/internal/mutex-inl.h index 86f66ac..ae68268 100644 --- a/base_cpp/include/embb/base/internal/mutex-inl.h +++ b/base_cpp/include/embb/base/internal/mutex-inl.h @@ -114,6 +114,83 @@ bool UniqueLock::OwnsLock() const { return locked_; } +template +SharedLock::SharedLock() + : shared_mutex_(NULL), locked_(false) {} + +template +SharedLock::SharedLock(SharedMutex& shared_mutex) + : shared_mutex_(&shared_mutex), locked_(false) { + shared_mutex_->LockShared(); + locked_ = true; +} + +template +SharedLock::SharedLock(SharedMutex& shared_mutex, DeferLockTag) + : shared_mutex_(&shared_mutex), locked_(false) {} + +template +SharedLock::SharedLock(SharedMutex& shared_mutex, TryLockTag) + : shared_mutex_(&shared_mutex), locked_(shared_mutex_->TryLockShared()) {} + +template +SharedLock::SharedLock(SharedMutex& shared_mutex, AdoptLockTag) + : shared_mutex_(&shared_mutex), locked_(true) {} + +template +SharedLock::~SharedLock() { + if (OwnsLock()) { + shared_mutex_->UnlockShared(); + } +} + +template +void SharedLock::Lock() { + if (shared_mutex_ == NULL || locked_) { + EMBB_THROW(ErrorException, "Mutex not set or locked"); + } + shared_mutex_->LockShared(); + locked_ = true; +} + +template +bool SharedLock::TryLock() { + if (shared_mutex_ == NULL || locked_) { + EMBB_THROW(ErrorException, "Mutex not set or locked"); + } + locked_ = shared_mutex_->TryLockShared(); + return locked_; +} + +template +void SharedLock::Unlock() { + if (shared_mutex_ == NULL || !locked_) { + EMBB_THROW(ErrorException, "Mutex not set or unlocked"); + } + shared_mutex_->UnlockShared(); + locked_ = false; +} + +template +void SharedLock::Swap(SharedLock& other) { + std::swap(shared_mutex_, other.shared_mutex_); + std::swap(locked_, other.locked_); +} + +template +SharedMutex* SharedLock::Release() { + SharedMutex* to_release = shared_mutex_; + shared_mutex_ = NULL; + locked_ = false; + return to_release; +} + +template +bool SharedLock::OwnsLock() const { + assert(!(locked_ && (shared_mutex_ == NULL))); + return locked_; +} + } // namespace base } // namespace embb diff --git a/base_cpp/include/embb/base/internal/shared_mutex-inl.h b/base_cpp/include/embb/base/internal/shared_mutex-inl.h deleted file mode 100644 index 5a3ea0b..0000000 --- a/base_cpp/include/embb/base/internal/shared_mutex-inl.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2014-2015, Siemens AG. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef EMBB_BASE_INTERNAL_SHARED_MUTEX_INL_H_ -#define EMBB_BASE_INTERNAL_SHARED_MUTEX_INL_H_ - -#include -#include // For std::swap() - -namespace embb { -namespace base { - -template -SharedLock::SharedLock() - : shared_mutex_(NULL), locked_(false) {} - -template -SharedLock::SharedLock(SharedMutex& shared_mutex) - : shared_mutex_(&shared_mutex), locked_(false) { - shared_mutex_->LockShared(); - locked_ = true; -} - -template -SharedLock::SharedLock(SharedMutex& shared_mutex, DeferLockTag) - : shared_mutex_(&shared_mutex), locked_(false) {} - -template -SharedLock::SharedLock(SharedMutex& shared_mutex, TryLockTag) - : shared_mutex_(&shared_mutex), locked_(shared_mutex_->TryLockShared()) {} - -template -SharedLock::SharedLock(SharedMutex& shared_mutex, AdoptLockTag) - : shared_mutex_(&shared_mutex), locked_(true) {} - -template -SharedLock::~SharedLock() { - if (OwnsLock()) { - shared_mutex_->UnlockShared(); - } -} - -template -void SharedLock::LockShared() { - if (shared_mutex_ == NULL || locked_) { - EMBB_THROW(ErrorException, "Mutex not set or locked"); - } - shared_mutex_->LockShared(); - locked_ = true; -} - -template -bool SharedLock::TryLockShared() { - if (shared_mutex_ == NULL || locked_) { - EMBB_THROW(ErrorException, "Mutex not set or locked"); - } - locked_ = shared_mutex_->TryLockShared(); - return locked_; -} - -template -void SharedLock::UnlockShared() { - if (shared_mutex_ == NULL || !locked_) { - EMBB_THROW(ErrorException, "Mutex not set or unlocked"); - } - shared_mutex_->UnlockShared(); - locked_ = false; -} - -template -void SharedLock::Swap(SharedLock& other) { - std::swap(shared_mutex_, other.shared_mutex_); - std::swap(locked_, other.locked_); -} - -template -SharedMutex* SharedLock::Release() { - SharedMutex* to_release = shared_mutex_; - shared_mutex_ = NULL; - locked_ = false; - return to_release; -} - -template -bool SharedLock::OwnsLock() const { - assert(!(locked_ && (shared_mutex_ == NULL))); - return locked_; -} - -} // namespace base -} // namespace embb - -#endif // EMBB_BASE_INTERNAL_SHARED_MUTEX_INL_H_ diff --git a/base_cpp/include/embb/base/mutex.h b/base_cpp/include/embb/base/mutex.h index 1d63027..0ec0f2a 100644 --- a/base_cpp/include/embb/base/mutex.h +++ b/base_cpp/include/embb/base/mutex.h @@ -246,6 +246,125 @@ class RecursiveMutex : public internal::MutexBase { RecursiveMutex& operator=(const RecursiveMutex&); }; +/** + * Shared mutex providing shared-read/exclusive-write access synchronization. + * + * Allows simultaneous shared access for concurrent readers, but only exclusive + * access for writers. Cannot be locked recursively. Fairness and/or FIFO order + * guarantees are platform specific and should be assumed non-existent. + * + * \see Mutex + * + * \ingroup CPP_BASE_MUTEX + */ +class SharedMutex { + public: + /** + * Creates the shared mutex object which is initially in an unlocked state. + * + * \memory Potentially allocates dynamic memory + * + * \notthreadsafe + */ + SharedMutex(); + + /** + * Destroys internal representation. + */ + ~SharedMutex(); + + /** + * Waits until the shared mutex can be locked for writing and locks it. + * + * \pre \c shared_mutex is not locked by the current thread. + * \post \c shared_mutex is locked for writing. + * + * \threadsafe + * + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the shared mutex for writing and returns immediately. + * + * The try to lock fails not only if the shared mutex was already locked for + * writing, but also in case it was locked shared for reading. + * + * \pre \c shared_mutex is not locked by the current thread. + * \post If successful, \c shared_mutex is locked for writing. + * + * \return \c true if shared mutex was locked for reading \n + * \c false if shared_mutex could not be locked for writing, because + * the mutex was already locked for writing or reading \n + * + * \threadsafe + * + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks the shared mutex locked for writing. + * + * \pre \c shared_mutex has been locked for writing by the current thread. + * \post \c shared_mutex is unlocked. + * + * \threadsafe + * + * \see Lock(), TryLock() + */ + void Unlock(); + + /** + * Waits until the shared mutex can be locked for reading and locks it. + * + * \pre The \c shared_mutex is not locked by the current thread. + * \post The \c shared_mutex is locked for reading. + * + * \threadsafe + * + * \see TryLockShared(), UnlockShared() + */ + void LockShared(); + + /** + * Tries to lock the shared mutex for reading and returns immediately. + * + * \pre \c shared_mutex is not locked by the current thread. + * \post If successful, \c shared_mutex is locked for reading. + * + * \return \c true if shared mutex was locked for reading \n + * \c false otherwise + * + * \threadsafe + * + * \see LockShared(), UnlockShared() + */ + bool TryLockShared(); + + /** + * Unlocks the shared mutex locked for reading. + * + * \pre \c shared_mutex has been locked for reading by the current thread. + * \post \c shared_mutex is unlocked. + * + * \threadsafe + * + * \see LockShared(), TryLockShared() + */ + void UnlockShared(); + + private: + /** + * Disables copy construction and assignment. + */ + SharedMutex(const SharedMutex&); + SharedMutex& operator=(const SharedMutex&); + + /** Actual shared mutex implementation from base_c */ + internal::SharedMutexType shared_mutex_; +}; /** * Scoped lock (according to the RAII principle) using a mutex. @@ -483,6 +602,143 @@ class UniqueLock { friend class embb::base::ConditionVariable; }; +/** + * Ownership wrapper for a \c SharedMutex with shared locking semantics. + * + * An ownership wrapper with exclusive semantics is given by + * \c UniqueLock. + * + * \notthreadsafe + * + * \tparam SharedMutex Type of the \c SharedMutex object being wrapped + * + * \see SharedMutex, UniqueLock + * + * \ingroup CPP_BASE_MUTEX + */ +template +class SharedLock { + public: + /** + * Creates a lock without assigned shared mutex. + * + * A shared mutex can be assigned to the lock using the method Swap(). + */ + SharedLock(); + + /** + * Creates a lock from an unlocked shared mutex and locks it for reading. + * + * \pre \c shared_mutex is unlocked + * \post \c shared_mutex is locked for reading + * + * \param[IN] shared_mutex Shared mutex to be managed + */ + explicit SharedLock(SharedMutex& shared_mutex); + + /** + * Creates a lock from an unlocked shared mutex without locking it. + * + * \pre \c shared_mutex is unlocked + * \post \c shared_mutex is unlocked + * + * \param[IN] shared_mutex Shared mutex to be managed + * \param[IN] tag Tag to select correct constructor + */ + SharedLock(SharedMutex& shared_mutex, DeferLockTag tag); + + /** + * Creates a lock from an unlocked shared mutex and tries to lock it + * for reading. + * + * \pre \c shared_mutex is unlocked + * \post If successful, \c shared_mutex is locked for reading + * + * \param[IN] shared_mutex Shared mutex to be managed + * \param[IN] tag Tag to select correct constructor + */ + SharedLock(SharedMutex& shared_mutex, TryLockTag tag); + + /** + * Creates a lock from an already locked shared mutex. + * + * \pre \c shared_mutex is locked for reading + * \post \c shared_mutex is locked for reading + * + * \param[IN] shared_mutex Shared mutex to be managed + * \param[IN] tag Tag to select correct constructor + */ + SharedLock(SharedMutex& shared_mutex, AdoptLockTag tag); + + /** + * Unlocks the shared mutex if owned. + */ + ~SharedLock(); + + /** + * Waits until the shared mutex can be locked for reading and locks it. + * + * \throws ErrorException, if no shared mutex is set or it is already locked + */ + void Lock(); + + /** + * Tries to lock the shared mutex for reading and returns immediately. + * + * If the shared mutex is locked for writing, or a write lock is pending, this + * method + * + * \return \c true if shared mutex was locked for reading \n + * \c false if shared mutex could not be locked for reading due to + * esablished or pending write lock of other thread. + * + * \throws ErrorException, if no shared mutex is set or it is already locked + */ + bool TryLock(); + + /** + * Unlocks the shared mutex locked for reading. + * + * \throws ErrorException, if no shared mutex is set or it is not yet locked + */ + void Unlock(); + + /** + * Exchange shared mutex ownership with another shared lock + * + * \param other Shared lock to exchange ownership with + */ + void Swap(SharedLock& other); + + /** + * Gives up ownership of the shared mutex and returns a pointer to it. + * + * \return A pointer to the owned shared mutex. (If no shared mutex was + * owned, returns NULL). + */ + SharedMutex* Release(); + + /** + * Checks whether the shared mutex is owned and locked. + * + * \return \c true if shared mutex is locked, otherwise \c false. + */ + bool OwnsLock() const; + + private: + /** + * Disable copy construction and assignment. + */ + SharedLock(const SharedLock&); + SharedLock& operator=(const SharedLock&); + + /** Pointer to the owned shared mutex */ + SharedMutex* shared_mutex_; + + /** Locked flag (is true if and only if the owned shared mutex is locked) */ + bool locked_; +}; + } // namespace base } // namespace embb diff --git a/base_cpp/include/embb/base/shared_mutex.h b/base_cpp/include/embb/base/shared_mutex.h deleted file mode 100644 index 4e2138d..0000000 --- a/base_cpp/include/embb/base/shared_mutex.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2014-2015, Siemens AG. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef EMBB_BASE_SHARED_MUTEX_H_ -#define EMBB_BASE_SHARED_MUTEX_H_ - -#include -#include -#include - -namespace embb { -namespace base { - -/** - * \defgroup CPP_BASE_SHARED_MUTEX Shared mutex - * - * Shared mutexes for thread synchronization - * - * Provides an abstraction from platform-specific shared mutex implementations. - * Shared mutexes provide two levels of access: exclusive access - only one - * thread can own the mutex; shared access - several threads can share ownership - * of the same mutex. Shared mutexes facilitate the "single writer, multiple - * readers" scenarios. - * - * \ingroup CPP_BASE - */ - -/** - * Shared mutex providing shared-read/exclusive-write access synchronization. - * - * Allows simultaneous shared access for concurrent readers, but only exclusive - * access for writers. Cannot be locked recursively. Fairness and/or FIFO order - * guarantees are platform specific and should be assumed non-existent. - * - * \see Mutex - * - * \ingroup CPP_BASE_SHARED_MUTEX - */ -class SharedMutex { - public: - /** - * Creates the shared mutex object which is initially in an unlocked state. - * - * \memory Potentially allocates dynamic memory - * - * \notthreadsafe - */ - SharedMutex(); - - /** - * Destroys internal representation. - */ - ~SharedMutex(); - - /** - * Waits until the shared mutex can be locked for writing and locks it. - * - * \pre \c shared_mutex is initialized and not locked by the current thread. - * \post \c shared_mutex is locked for writing. - * - * \threadsafe - * - * \see TryLock(), Unlock() - */ - void Lock(); - - /** - * Tries to lock the shared mutex for writing and returns immediately. - * - * \pre \c shared_mutex is not locked by the current thread. - * \post If successful, \c shared_mutex is locked for writing. - * - * \return \c true if shared mutex was locked for reading \n - * \c false otherwise - * - * \threadsafe - * - * \see Lock(), Unlock() - */ - bool TryLock(); - - /** - * Unlocks the shared mutex locked for writing. - * - * \pre \c shared_mutex has been locked for writing by the current thread. - * \post \c shared_mutex is unlocked. - * - * \threadsafe - * - * \see Lock(), TryLock() - */ - void Unlock(); - - /** - * Waits until the shared mutex can be locked for reading and locks it. - * - * \pre The \c shared_mutex is not locked by the current thread. - * \post The \c shared_mutex is locked for reading. - * - * \threadsafe - * - * \see TryLockShared(), UnlockShared() - */ - void LockShared(); - - /** - * Tries to lock the shared mutex for reading and returns immediately. - * - * \pre \c shared_mutex is not locked by the current thread. - * \post If successful, \c shared_mutex is locked for reading. - * - * \return \c true if shared mutex was locked for reading \n - * \c false otherwise - * - * \threadsafe - * - * \see LockShared(), UnlockShared() - */ - bool TryLockShared(); - - /** - * Unlocks the shared mutex locked for reading. - * - * \pre \c shared_mutex has been locked for reading by the current thread. - * \post \c shared_mutex is unlocked. - * - * \threadsafe - * - * \see LockShared(), TryLockShared() - */ - void UnlockShared(); - - private: - /** - * Disables copy construction and assignment. - */ - SharedMutex(const SharedMutex&); - SharedMutex& operator=(const SharedMutex&); - - /** Actual shared mutex implementation from base_c */ - internal::SharedMutexType shared_mutex_; -}; - -/** - * Ownership wrapper for a shared mutex that works on the shared access level - * (i.e. it locks a shared mutex in shared "readers" mode). - * - * For exclusive ownership wrapping, use UniqueLock. - * - * \notthreadsafe - * - * \tparam SharedMutex Type of the shared mutex object being wrapped - * - * \see SharedMutex, UniqueLock - * - * \ingroup CPP_BASE_MUTEX - */ -template -class SharedLock { - public: - /** - * Creates a lock without assigned shared mutex. - * - * A shared mutex can be assigned to the lock using the method Swap(). - */ - SharedLock(); - - /** - * Creates a lock from an unlocked shared mutex and locks it for reading. - * - * \pre \c shared_mutex is unlocked - * \post \c shared_mutex is locked for reading - * - * \param[IN] shared_mutex Shared mutex to be managed - */ - explicit SharedLock(SharedMutex& shared_mutex); - - /** - * Creates a lock from an unlocked shared mutex without locking it. - * - * \pre \c shared_mutex is unlocked - * \post \c shared_mutex is unlocked - * - * \param[IN] shared_mutex Shared mutex to be managed - * \param[IN] tag Tag to select correct constructor - */ - SharedLock(SharedMutex& shared_mutex, DeferLockTag tag); - - /** - * Creates a lock from an unlocked shared mutex and tries to lock it - * for reading. - * - * \pre \c shared_mutex is unlocked - * \post If successful, \c shared_mutex is locked for reading - * - * \param[IN] shared_mutex Shared mutex to be managed - * \param[IN] tag Tag to select correct constructor - */ - SharedLock(SharedMutex& shared_mutex, TryLockTag tag); - - /** - * Creates a lock from an already locked shared mutex. - * - * \pre \c shared_mutex is locked for reading - * \post \c shared_mutex is locked for reading - * - * \param[IN] shared_mutex Shared mutex to be managed - * \param[IN] tag Tag to select correct constructor - */ - SharedLock(SharedMutex& shared_mutex, AdoptLockTag tag); - - /** - * Unlocks the shared mutex if owned. - */ - ~SharedLock(); - - /** - * Waits until the shared mutex can be locked for reading and locks it. - * - * \throws ErrorException, if no shared mutex is set or it is already locked - */ - void LockShared(); - - /** - * Tries to lock the shared mutex for reading and returns immediately. - * - * \return \c true if shared mutex was locked for reading \n - * \c false otherwise - * - * \throws ErrorException, if no shared mutex is set or it is already locked - */ - bool TryLockShared(); - - /** - * Unlocks the shared mutex locked for reading. - * - * \throws ErrorException, if no shared mutex is set or it is not yet locked - */ - void UnlockShared(); - - /** - * Exchange shared mutex ownership with another shared lock - * - * \param other Shared lock to exchange ownership with - */ - void Swap(SharedLock& other); - - /** - * Gives up ownership of the shared mutex and returns a pointer to it. - * - * \return A pointer to the owned shared mutex. (If no shared mutex was - * owned, returns NULL). - */ - SharedMutex* Release(); - - /** - * Checks whether the shared mutex is owned and locked. - * - * \return \c true if shared mutex is locked, otherwise \c false. - */ - bool OwnsLock() const; - - private: - /** - * Disable copy construction and assignment. - */ - SharedLock(const SharedLock&); - SharedLock& operator=(const SharedLock&); - - /** Pointer to the owned shared mutex */ - SharedMutex* shared_mutex_; - /** Locked flag (is true if and only if the owned shared mutex is locked) */ - bool locked_; -}; - -} // namespace base -} // namespace embb - -#include - -#endif // EMBB_BASE_SHARED_MUTEX_H_ diff --git a/base_cpp/src/shared_mutex.cc b/base_cpp/src/shared_mutex.cc index 63f6978..d126dad 100644 --- a/base_cpp/src/shared_mutex.cc +++ b/base_cpp/src/shared_mutex.cc @@ -24,8 +24,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include -#include +#include +#include namespace embb { namespace base { diff --git a/base_cpp/test/shared_mutex_test.cc b/base_cpp/test/shared_mutex_test.cc index b4524a7..489d676 100644 --- a/base_cpp/test/shared_mutex_test.cc +++ b/base_cpp/test/shared_mutex_test.cc @@ -38,21 +38,21 @@ SharedMutexTest::SharedMutexTest() num_threads_(partest::TestSuite::GetDefaultNumThreads()), num_iterations_(partest::TestSuite::GetDefaultNumIterations()) { CreateUnit("Shared read") - .Add(&SharedMutexTest::TestSharedRead_ThreadMethod, this, + .Add(&SharedMutexTest::TestSharedReadThreadMethod, this, num_threads_, num_iterations_); CreateUnit("Exclusive write") - .Pre(&SharedMutexTest::TestExclusiveWrite_Pre, this) - .Add(&SharedMutexTest::TestExclusiveWrite_ReaderMethod, this, + .Pre(&SharedMutexTest::TestExclusiveWritePre, this) + .Add(&SharedMutexTest::TestExclusiveWriteReaderMethod, this, num_threads_ / 2, num_iterations_) - .Add(&SharedMutexTest::TestExclusiveWrite_WriterMethod, this, + .Add(&SharedMutexTest::TestExclusiveWriteWriterMethod, this, num_threads_ / 2, num_iterations_) - .Post(&SharedMutexTest::TestExclusiveWrite_Post, this); + .Post(&SharedMutexTest::TestExclusiveWritePost, this); CreateUnit("SharedLock") - .Add(&SharedMutexTest::TestSharedLock_ThreadMethod, this, + .Add(&SharedMutexTest::TestSharedLockThreadMethod, this, num_threads_, num_iterations_); } -void SharedMutexTest::TestSharedRead_ThreadMethod() { +void SharedMutexTest::TestSharedReadThreadMethod() { SharedLock lock(shared_mutex_, embb::base::try_lock); PT_ASSERT_EQ_MSG(lock.OwnsLock(), true, "Failed to lock for reading."); @@ -60,11 +60,11 @@ void SharedMutexTest::TestSharedRead_ThreadMethod() { while (--spin != 0); } -void SharedMutexTest::TestExclusiveWrite_Pre() { +void SharedMutexTest::TestExclusiveWritePre() { counter_ = 0; } -void SharedMutexTest::TestExclusiveWrite_ReaderMethod() { +void SharedMutexTest::TestExclusiveWriteReaderMethod() { // Just add some contention SharedLock lock(shared_mutex_, embb::base::try_lock); @@ -74,33 +74,33 @@ void SharedMutexTest::TestExclusiveWrite_ReaderMethod() { } } -void SharedMutexTest::TestExclusiveWrite_WriterMethod() { +void SharedMutexTest::TestExclusiveWriteWriterMethod() { UniqueLock lock(shared_mutex_); ++counter_; } -void SharedMutexTest::TestExclusiveWrite_Post() { +void SharedMutexTest::TestExclusiveWritePost() { PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2), "Counter value is inconsistent."); } -void SharedMutexTest::TestSharedLock_ThreadMethod() { +void SharedMutexTest::TestSharedLockThreadMethod() { // Test basic usage { SharedLock<> lock(shared_mutex_); PT_EXPECT_EQ(lock.OwnsLock(), true); - lock.UnlockShared(); + lock.Unlock(); PT_EXPECT_EQ(lock.OwnsLock(), false); - lock.LockShared(); + lock.Lock(); PT_EXPECT_EQ(lock.OwnsLock(), true); - lock.UnlockShared(); + lock.Unlock(); PT_EXPECT_EQ(lock.OwnsLock(), false); - bool locked_after_try = lock.TryLockShared(); + bool locked_after_try = lock.TryLock(); PT_EXPECT_EQ(locked_after_try, true); PT_EXPECT_EQ(lock.OwnsLock(), true); diff --git a/base_cpp/test/shared_mutex_test.h b/base_cpp/test/shared_mutex_test.h index 7b6df22..2766d46 100644 --- a/base_cpp/test/shared_mutex_test.h +++ b/base_cpp/test/shared_mutex_test.h @@ -29,7 +29,7 @@ #include #include -#include +#include namespace embb { @@ -41,14 +41,14 @@ class SharedMutexTest : public partest::TestCase { SharedMutexTest(); private: - void TestSharedRead_ThreadMethod(); + void TestSharedReadThreadMethod(); - void TestExclusiveWrite_Pre(); - void TestExclusiveWrite_ReaderMethod(); - void TestExclusiveWrite_WriterMethod(); - void TestExclusiveWrite_Post(); + void TestExclusiveWritePre(); + void TestExclusiveWriteReaderMethod(); + void TestExclusiveWriteWriterMethod(); + void TestExclusiveWritePost(); - void TestSharedLock_ThreadMethod(); + void TestSharedLockThreadMethod(); SharedMutex shared_mutex_; size_t counter_;