Commit c8a7b041 by bernhard-gatzhammer

Improved shared mutex in base_c and base_cpp

EMBB-453
parent 554c76f7
...@@ -193,12 +193,16 @@ int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex); ...@@ -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. * 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 * \pre \c shared_mutex is initialized
* \post If successful, \c shared_mutex is locked for writing * \post If successful, \c shared_mutex is locked for writing
* *
* \param shared_mutex Pointer to shared mutex * \param shared_mutex Pointer to shared mutex
* \return EMBB_SUCCESS if shared_mutex could be locked for writing \n * \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 * EMBB_ERROR if an error occurred
* *
* \threadsafe * \threadsafe
......
...@@ -32,7 +32,8 @@ namespace base { ...@@ -32,7 +32,8 @@ namespace base {
namespace test { namespace test {
SharedMutexTest::SharedMutexTest() SharedMutexTest::SharedMutexTest()
: counter_(0), : shared_mutex_(),
counter_(0),
num_threads_(partest::TestSuite::GetDefaultNumThreads()), num_threads_(partest::TestSuite::GetDefaultNumThreads()),
num_iterations_(partest::TestSuite::GetDefaultNumIterations()) { num_iterations_(partest::TestSuite::GetDefaultNumIterations()) {
CreateUnit("Shared read") CreateUnit("Shared read")
...@@ -41,19 +42,19 @@ SharedMutexTest::SharedMutexTest() ...@@ -41,19 +42,19 @@ SharedMutexTest::SharedMutexTest()
num_threads_, num_iterations_) num_threads_, num_iterations_)
.Post(&SharedMutexTest::TestSharedReadPost, this); .Post(&SharedMutexTest::TestSharedReadPost, this);
CreateUnit("Multiple writer") CreateUnit("Multiple writer")
.Pre(&SharedMutexTest::TestMultipleWriterPre, this) .Pre(&SharedMutexTest::TestExclusiveWriterPre, this)
.Add(&SharedMutexTest::TestMultipleWriterReaderMethod, this, .Add(&SharedMutexTest::TestExclusiveWriterReaderMethod, this,
num_threads_ / 2, num_iterations_) num_threads_ / 2, num_iterations_)
.Add(&SharedMutexTest::TestMultipleWriterWriterMethod, this, .Add(&SharedMutexTest::TestExclusiveWriterWriterMethod, this,
num_threads_ / 2, num_iterations_) num_threads_ / 2, num_iterations_)
.Post(&SharedMutexTest::TestMultipleWriterPost, this); .Post(&SharedMutexTest::TestExclusiveWriterPost, this);
CreateUnit("Single writer") // CreateUnit("Single writer")
.Pre(&SharedMutexTest::TestSingleWriterPre, this) // .Pre(&SharedMutexTest::TestSingleWriterPre, this)
.Add(&SharedMutexTest::TestSingleWriterReaderMethod, this, // .Add(&SharedMutexTest::TestSingleWriterReaderMethod, this,
num_threads_, num_iterations_) // num_threads_, num_iterations_)
.Add(&SharedMutexTest::TestSingleWriterWriterMethod, this, // .Add(&SharedMutexTest::TestSingleWriterWriterMethod, this,
1, num_iterations_) // 1, num_iterations_)
.Post(&SharedMutexTest::TestSingleWriterPost, this); // .Post(&SharedMutexTest::TestSingleWriterPost, this);
} }
void SharedMutexTest::TestSharedReadPre() { void SharedMutexTest::TestSharedReadPre() {
...@@ -63,10 +64,13 @@ void SharedMutexTest::TestSharedReadPre() { ...@@ -63,10 +64,13 @@ void SharedMutexTest::TestSharedReadPre() {
void SharedMutexTest::TestSharedReadThreadMethod() { void SharedMutexTest::TestSharedReadThreadMethod() {
int success = embb_shared_mutex_try_lock_shared(&shared_mutex_); 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; success = embb_shared_mutex_lock_shared(&shared_mutex_);
while (--spin != 0); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading.");
success = embb_shared_mutex_unlock_shared(&shared_mutex_); success = embb_shared_mutex_unlock_shared(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading).");
...@@ -76,14 +80,14 @@ void SharedMutexTest::TestSharedReadPost() { ...@@ -76,14 +80,14 @@ void SharedMutexTest::TestSharedReadPost() {
embb_shared_mutex_destroy(&shared_mutex_); embb_shared_mutex_destroy(&shared_mutex_);
} }
void SharedMutexTest::TestMultipleWriterPre() { void SharedMutexTest::TestExclusiveWriterPre() {
int success = embb_shared_mutex_init(&shared_mutex_); int success = embb_shared_mutex_init(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex.");
counter_ = 0; counter_ = 0;
} }
void SharedMutexTest::TestMultipleWriterReaderMethod() { void SharedMutexTest::TestExclusiveWriterReaderMethod() {
// Just add some contention // Just add some contention
int success = embb_shared_mutex_lock_shared(&shared_mutex_); int success = embb_shared_mutex_lock_shared(&shared_mutex_);
...@@ -93,7 +97,7 @@ void SharedMutexTest::TestMultipleWriterReaderMethod() { ...@@ -93,7 +97,7 @@ void SharedMutexTest::TestMultipleWriterReaderMethod() {
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); 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_); int success = embb_shared_mutex_lock(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing.");
...@@ -103,43 +107,43 @@ void SharedMutexTest::TestMultipleWriterWriterMethod() { ...@@ -103,43 +107,43 @@ void SharedMutexTest::TestMultipleWriterWriterMethod() {
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); 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), PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2),
"Counter value is inconsistent."); "Counter value is inconsistent.");
embb_shared_mutex_destroy(&shared_mutex_); embb_shared_mutex_destroy(&shared_mutex_);
} }
void SharedMutexTest::TestSingleWriterPre() { //void SharedMutexTest::TestSingleWriterPre() {
int success = embb_shared_mutex_init(&shared_mutex_); // int success = embb_shared_mutex_init(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); // PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex.");
//
counter_ = 0; // counter_ = 0;
} //}
void SharedMutexTest::TestSingleWriterReaderMethod() { //void SharedMutexTest::TestSingleWriterReaderMethod() {
// Just add some contention // // Just add some contention
//
int success = embb_shared_mutex_lock_shared(&shared_mutex_); // int success = embb_shared_mutex_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 lock for reading.");
//
success = embb_shared_mutex_unlock_shared(&shared_mutex_); // success = embb_shared_mutex_unlock_shared(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); // PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading).");
} //}
//
void SharedMutexTest::TestSingleWriterWriterMethod() { //void SharedMutexTest::TestSingleWriterWriterMethod() {
int success = embb_shared_mutex_try_lock(&shared_mutex_); // int success = embb_shared_mutex_lock(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); // PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing.");
//
++counter_; // ++counter_;
//
success = embb_shared_mutex_unlock(&shared_mutex_); // success = embb_shared_mutex_unlock(&shared_mutex_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); // PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing).");
} //}
//
void SharedMutexTest::TestSingleWriterPost() { //void SharedMutexTest::TestSingleWriterPost() {
PT_ASSERT_EQ_MSG(counter_, num_iterations_, "Counter value is inconsistent."); // PT_ASSERT_EQ_MSG(counter_, num_iterations_, "Counter value is inconsistent.");
embb_shared_mutex_destroy(&shared_mutex_); // embb_shared_mutex_destroy(&shared_mutex_);
} //}
} // namespace test } // namespace test
} // namespace base } // namespace base
......
...@@ -58,22 +58,22 @@ class SharedMutexTest : public partest::TestCase { ...@@ -58,22 +58,22 @@ class SharedMutexTest : public partest::TestCase {
* waiting). The readers use lock_shared() and test if that always obtains the * waiting). The readers use lock_shared() and test if that always obtains the
* shared lock (after waiting). * shared lock (after waiting).
*/ */
void TestMultipleWriterPre(); void TestExclusiveWriterPre();
void TestMultipleWriterReaderMethod(); void TestExclusiveWriterReaderMethod();
void TestMultipleWriterWriterMethod(); void TestExclusiveWriterWriterMethod();
void TestMultipleWriterPost(); void TestExclusiveWriterPost();
/** // /**
* Multiple readers, single writer. // * Multiple readers, single writer.
* // *
* The single writer uses try_lock() and tests if that always obtains the // * 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 // * lock. The readers use lock_shared() and test if that always obtains the
* shared lock (after waiting). // * shared lock (after waiting).
*/ // */
void TestSingleWriterPre(); // void TestSingleWriterPre();
void TestSingleWriterReaderMethod(); // void TestSingleWriterReaderMethod();
void TestSingleWriterWriterMethod(); // void TestSingleWriterWriterMethod();
void TestSingleWriterPost(); // void TestSingleWriterPost();
embb_shared_mutex_t shared_mutex_; embb_shared_mutex_t shared_mutex_;
size_t counter_; size_t counter_;
......
...@@ -114,6 +114,83 @@ bool UniqueLock<Mutex>::OwnsLock() const { ...@@ -114,6 +114,83 @@ bool UniqueLock<Mutex>::OwnsLock() const {
return locked_; return locked_;
} }
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock()
: shared_mutex_(NULL), locked_(false) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex)
: shared_mutex_(&shared_mutex), locked_(false) {
shared_mutex_->LockShared();
locked_ = true;
}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex, DeferLockTag)
: shared_mutex_(&shared_mutex), locked_(false) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex, TryLockTag)
: shared_mutex_(&shared_mutex), locked_(shared_mutex_->TryLockShared()) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex, AdoptLockTag)
: shared_mutex_(&shared_mutex), locked_(true) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::~SharedLock() {
if (OwnsLock()) {
shared_mutex_->UnlockShared();
}
}
template<typename SharedMutex>
void SharedLock<SharedMutex>::Lock() {
if (shared_mutex_ == NULL || locked_) {
EMBB_THROW(ErrorException, "Mutex not set or locked");
}
shared_mutex_->LockShared();
locked_ = true;
}
template<typename SharedMutex>
bool SharedLock<SharedMutex>::TryLock() {
if (shared_mutex_ == NULL || locked_) {
EMBB_THROW(ErrorException, "Mutex not set or locked");
}
locked_ = shared_mutex_->TryLockShared();
return locked_;
}
template<typename SharedMutex>
void SharedLock<SharedMutex>::Unlock() {
if (shared_mutex_ == NULL || !locked_) {
EMBB_THROW(ErrorException, "Mutex not set or unlocked");
}
shared_mutex_->UnlockShared();
locked_ = false;
}
template<typename SharedMutex>
void SharedLock<SharedMutex>::Swap(SharedLock& other) {
std::swap(shared_mutex_, other.shared_mutex_);
std::swap(locked_, other.locked_);
}
template<typename SharedMutex>
SharedMutex* SharedLock<SharedMutex>::Release() {
SharedMutex* to_release = shared_mutex_;
shared_mutex_ = NULL;
locked_ = false;
return to_release;
}
template<typename SharedMutex>
bool SharedLock<SharedMutex>::OwnsLock() const {
assert(!(locked_ && (shared_mutex_ == NULL)));
return locked_;
}
} // namespace base } // namespace base
} // namespace embb } // namespace embb
......
/*
* 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 <assert.h>
#include <algorithm> // For std::swap()
namespace embb {
namespace base {
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock()
: shared_mutex_(NULL), locked_(false) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex)
: shared_mutex_(&shared_mutex), locked_(false) {
shared_mutex_->LockShared();
locked_ = true;
}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex, DeferLockTag)
: shared_mutex_(&shared_mutex), locked_(false) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex, TryLockTag)
: shared_mutex_(&shared_mutex), locked_(shared_mutex_->TryLockShared()) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::SharedLock(SharedMutex& shared_mutex, AdoptLockTag)
: shared_mutex_(&shared_mutex), locked_(true) {}
template<typename SharedMutex>
SharedLock<SharedMutex>::~SharedLock() {
if (OwnsLock()) {
shared_mutex_->UnlockShared();
}
}
template<typename SharedMutex>
void SharedLock<SharedMutex>::LockShared() {
if (shared_mutex_ == NULL || locked_) {
EMBB_THROW(ErrorException, "Mutex not set or locked");
}
shared_mutex_->LockShared();
locked_ = true;
}
template<typename SharedMutex>
bool SharedLock<SharedMutex>::TryLockShared() {
if (shared_mutex_ == NULL || locked_) {
EMBB_THROW(ErrorException, "Mutex not set or locked");
}
locked_ = shared_mutex_->TryLockShared();
return locked_;
}
template<typename SharedMutex>
void SharedLock<SharedMutex>::UnlockShared() {
if (shared_mutex_ == NULL || !locked_) {
EMBB_THROW(ErrorException, "Mutex not set or unlocked");
}
shared_mutex_->UnlockShared();
locked_ = false;
}
template<typename SharedMutex>
void SharedLock<SharedMutex>::Swap(SharedLock& other) {
std::swap(shared_mutex_, other.shared_mutex_);
std::swap(locked_, other.locked_);
}
template<typename SharedMutex>
SharedMutex* SharedLock<SharedMutex>::Release() {
SharedMutex* to_release = shared_mutex_;
shared_mutex_ = NULL;
locked_ = false;
return to_release;
}
template<typename SharedMutex>
bool SharedLock<SharedMutex>::OwnsLock() const {
assert(!(locked_ && (shared_mutex_ == NULL)));
return locked_;
}
} // namespace base
} // namespace embb
#endif // EMBB_BASE_INTERNAL_SHARED_MUTEX_INL_H_
...@@ -246,6 +246,125 @@ class RecursiveMutex : public internal::MutexBase { ...@@ -246,6 +246,125 @@ class RecursiveMutex : public internal::MutexBase {
RecursiveMutex& operator=(const RecursiveMutex&); 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. * Scoped lock (according to the RAII principle) using a mutex.
...@@ -483,6 +602,143 @@ class UniqueLock { ...@@ -483,6 +602,143 @@ class UniqueLock {
friend class embb::base::ConditionVariable; 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<SharedMutex>.
*
* \notthreadsafe
*
* \tparam SharedMutex Type of the \c SharedMutex object being wrapped
*
* \see SharedMutex, UniqueLock
*
* \ingroup CPP_BASE_MUTEX
*/
template<typename SharedMutex = embb::base::SharedMutex>
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 base
} // namespace embb } // namespace embb
......
/*
* 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 <embb/base/internal/platform.h>
#include <embb/base/exceptions.h>
#include <embb/base/mutex.h>
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<SharedMutex>.
*
* \notthreadsafe
*
* \tparam SharedMutex Type of the shared mutex object being wrapped
*
* \see SharedMutex, UniqueLock
*
* \ingroup CPP_BASE_MUTEX
*/
template<typename SharedMutex = embb::base::SharedMutex>
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 <embb/base/internal/shared_mutex-inl.h>
#endif // EMBB_BASE_SHARED_MUTEX_H_
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <embb/base/shared_mutex.h> #include <embb/base/mutex.h>
#include <embb/base/c/shared_mutex.h> #include <embb/base/c/mutex.h>
namespace embb { namespace embb {
namespace base { namespace base {
......
...@@ -38,21 +38,21 @@ SharedMutexTest::SharedMutexTest() ...@@ -38,21 +38,21 @@ SharedMutexTest::SharedMutexTest()
num_threads_(partest::TestSuite::GetDefaultNumThreads()), num_threads_(partest::TestSuite::GetDefaultNumThreads()),
num_iterations_(partest::TestSuite::GetDefaultNumIterations()) { num_iterations_(partest::TestSuite::GetDefaultNumIterations()) {
CreateUnit("Shared read") CreateUnit("Shared read")
.Add(&SharedMutexTest::TestSharedRead_ThreadMethod, this, .Add(&SharedMutexTest::TestSharedReadThreadMethod, this,
num_threads_, num_iterations_); num_threads_, num_iterations_);
CreateUnit("Exclusive write") CreateUnit("Exclusive write")
.Pre(&SharedMutexTest::TestExclusiveWrite_Pre, this) .Pre(&SharedMutexTest::TestExclusiveWritePre, this)
.Add(&SharedMutexTest::TestExclusiveWrite_ReaderMethod, this, .Add(&SharedMutexTest::TestExclusiveWriteReaderMethod, this,
num_threads_ / 2, num_iterations_) num_threads_ / 2, num_iterations_)
.Add(&SharedMutexTest::TestExclusiveWrite_WriterMethod, this, .Add(&SharedMutexTest::TestExclusiveWriteWriterMethod, this,
num_threads_ / 2, num_iterations_) num_threads_ / 2, num_iterations_)
.Post(&SharedMutexTest::TestExclusiveWrite_Post, this); .Post(&SharedMutexTest::TestExclusiveWritePost, this);
CreateUnit("SharedLock") CreateUnit("SharedLock")
.Add(&SharedMutexTest::TestSharedLock_ThreadMethod, this, .Add(&SharedMutexTest::TestSharedLockThreadMethod, this,
num_threads_, num_iterations_); num_threads_, num_iterations_);
} }
void SharedMutexTest::TestSharedRead_ThreadMethod() { void SharedMutexTest::TestSharedReadThreadMethod() {
SharedLock<embb::base::SharedMutex> lock(shared_mutex_, embb::base::try_lock); SharedLock<embb::base::SharedMutex> lock(shared_mutex_, embb::base::try_lock);
PT_ASSERT_EQ_MSG(lock.OwnsLock(), true, "Failed to lock for reading."); PT_ASSERT_EQ_MSG(lock.OwnsLock(), true, "Failed to lock for reading.");
...@@ -60,11 +60,11 @@ void SharedMutexTest::TestSharedRead_ThreadMethod() { ...@@ -60,11 +60,11 @@ void SharedMutexTest::TestSharedRead_ThreadMethod() {
while (--spin != 0); while (--spin != 0);
} }
void SharedMutexTest::TestExclusiveWrite_Pre() { void SharedMutexTest::TestExclusiveWritePre() {
counter_ = 0; counter_ = 0;
} }
void SharedMutexTest::TestExclusiveWrite_ReaderMethod() { void SharedMutexTest::TestExclusiveWriteReaderMethod() {
// Just add some contention // Just add some contention
SharedLock<embb::base::SharedMutex> lock(shared_mutex_, embb::base::try_lock); SharedLock<embb::base::SharedMutex> lock(shared_mutex_, embb::base::try_lock);
...@@ -74,33 +74,33 @@ void SharedMutexTest::TestExclusiveWrite_ReaderMethod() { ...@@ -74,33 +74,33 @@ void SharedMutexTest::TestExclusiveWrite_ReaderMethod() {
} }
} }
void SharedMutexTest::TestExclusiveWrite_WriterMethod() { void SharedMutexTest::TestExclusiveWriteWriterMethod() {
UniqueLock<embb::base::SharedMutex> lock(shared_mutex_); UniqueLock<embb::base::SharedMutex> lock(shared_mutex_);
++counter_; ++counter_;
} }
void SharedMutexTest::TestExclusiveWrite_Post() { void SharedMutexTest::TestExclusiveWritePost() {
PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2), PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2),
"Counter value is inconsistent."); "Counter value is inconsistent.");
} }
void SharedMutexTest::TestSharedLock_ThreadMethod() { void SharedMutexTest::TestSharedLockThreadMethod() {
// Test basic usage // Test basic usage
{ {
SharedLock<> lock(shared_mutex_); SharedLock<> lock(shared_mutex_);
PT_EXPECT_EQ(lock.OwnsLock(), true); PT_EXPECT_EQ(lock.OwnsLock(), true);
lock.UnlockShared(); lock.Unlock();
PT_EXPECT_EQ(lock.OwnsLock(), false); PT_EXPECT_EQ(lock.OwnsLock(), false);
lock.LockShared(); lock.Lock();
PT_EXPECT_EQ(lock.OwnsLock(), true); PT_EXPECT_EQ(lock.OwnsLock(), true);
lock.UnlockShared(); lock.Unlock();
PT_EXPECT_EQ(lock.OwnsLock(), false); 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(locked_after_try, true);
PT_EXPECT_EQ(lock.OwnsLock(), true); PT_EXPECT_EQ(lock.OwnsLock(), true);
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#include <partest/partest.h> #include <partest/partest.h>
#include <embb/base/c/internal/platform.h> #include <embb/base/c/internal/platform.h>
#include <embb/base/shared_mutex.h> #include <embb/base/mutex.h>
namespace embb { namespace embb {
...@@ -41,14 +41,14 @@ class SharedMutexTest : public partest::TestCase { ...@@ -41,14 +41,14 @@ class SharedMutexTest : public partest::TestCase {
SharedMutexTest(); SharedMutexTest();
private: private:
void TestSharedRead_ThreadMethod(); void TestSharedReadThreadMethod();
void TestExclusiveWrite_Pre(); void TestExclusiveWritePre();
void TestExclusiveWrite_ReaderMethod(); void TestExclusiveWriteReaderMethod();
void TestExclusiveWrite_WriterMethod(); void TestExclusiveWriteWriterMethod();
void TestExclusiveWrite_Post(); void TestExclusiveWritePost();
void TestSharedLock_ThreadMethod(); void TestSharedLockThreadMethod();
SharedMutex shared_mutex_; SharedMutex shared_mutex_;
size_t counter_; size_t counter_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment