Commit b30e8a25 by bernhard-gatzhammer

Revert "Readers-writers lock: updated interface to match C++14 standard"

This reverts commit ae2f1813.
parent 04542380
...@@ -55,6 +55,6 @@ ...@@ -55,6 +55,6 @@
#include <embb/base/c/thread.h> #include <embb/base/c/thread.h>
#include <embb/base/c/thread_specific_storage.h> #include <embb/base/c/thread_specific_storage.h>
#include <embb/base/c/time.h> #include <embb/base/c/time.h>
#include <embb/base/c/shared_mutex.h> #include <embb/base/c/rwlock.h>
#endif /* EMBB_BASE_C_BASE_H_ */ #endif /* EMBB_BASE_C_BASE_H_ */
...@@ -66,7 +66,7 @@ typedef struct embb_thread_t { ...@@ -66,7 +66,7 @@ typedef struct embb_thread_t {
typedef DWORD embb_thread_id_t; typedef DWORD embb_thread_id_t;
typedef CRITICAL_SECTION embb_mutex_t; typedef CRITICAL_SECTION embb_mutex_t;
typedef CONDITION_VARIABLE embb_condition_t; typedef CONDITION_VARIABLE embb_condition_t;
typedef SRWLOCK embb_shared_mutex_t; typedef SRWLOCK embb_rwlock_t;
#define EMBB_DURATION_MIN_NANOSECONDS 1000 #define EMBB_DURATION_MIN_NANOSECONDS 1000
...@@ -91,7 +91,7 @@ typedef struct embb_thread_t { ...@@ -91,7 +91,7 @@ typedef struct embb_thread_t {
typedef pthread_t embb_thread_id_t; typedef pthread_t embb_thread_id_t;
typedef pthread_mutex_t embb_mutex_t; typedef pthread_mutex_t embb_mutex_t;
typedef pthread_cond_t embb_condition_t; typedef pthread_cond_t embb_condition_t;
typedef pthread_rwlock_t embb_shared_mutex_t; typedef pthread_rwlock_t embb_rwlock_t;
#define EMBB_DURATION_MIN_NANOSECONDS 1 #define EMBB_DURATION_MIN_NANOSECONDS 1
......
...@@ -24,19 +24,16 @@ ...@@ -24,19 +24,16 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef EMBB_BASE_C_SHARED_MUTEX_H_ #ifndef EMBB_BASE_C_RWLOCK_H_
#define EMBB_BASE_C_SHARED_MUTEX_H_ #define EMBB_BASE_C_RWLOCK_H_
/** /**
* \defgroup C_BASE_SHARED_MUTEX Shared mutex * \defgroup C_BASE_RWLOCK Readers-Writers Lock
* *
* Shared mutexes for thread synchronization * Shared-read/exclusive-write lock for thread synchronization
* *
* Provides an abstraction from platform-specific shared mutex implementations. * Provides an abstraction from platform-specific readers-writers lock
* Shared mutexes provide two levels of access: exclusive access - only one * implementations.
* 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 C_BASE * \ingroup C_BASE
* \{ * \{
...@@ -51,139 +48,139 @@ extern "C" { ...@@ -51,139 +48,139 @@ extern "C" {
#ifdef DOXYGEN #ifdef DOXYGEN
/** /**
* Opaque type representing a shared mutex. * Opaque type representing an rwlock.
*/ */
typedef opaque_type embb_shared_mutex_t; typedef opaque_type embb_rwlock_t;
#endif // DOXYGEN #endif // DOXYGEN
/** /**
* Initializes a shared mutex * Initializes an rwlock
* *
* \post \c shared_mutex is initialized * \post \c rwlock is initialized
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if shared mutex could be initialized \n * \return EMBB_SUCCESS if rwlock could be initialized \n
* EMBB_ERROR otherwise * EMBB_ERROR otherwise
* *
* \memory (Potentially) allocates dynamic memory * \memory (Potentially) allocates dynamic memory
* *
* \notthreadsafe * \notthreadsafe
* *
* \see embb_shared_mutex_destroy() * \see embb_rwlock_destroy()
*/ */
int embb_shared_mutex_init(embb_shared_mutex_t* shared_mutex); int embb_rwlock_init(embb_rwlock_t* rwlock);
/** /**
* Waits until the shared mutex can be locked for writing and locks it. * Waits until the rwlock can be locked for reading and locks it.
* *
* \pre \c shared_mutex is initialized and not locked by the current thread. * \pre \c rwlock is initialized and not locked by the current thread.
* \post If successful, \c shared_mutex is locked for writing. * \post If successful, \c rwlock is locked for reading.
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if shared_mutex could be locked for writing \n * \return EMBB_SUCCESS if rwlock could be locked for reading \n
* EMBB_ERROR otherwise * EMBB_ERROR otherwise
* *
* \threadsafe * \threadsafe
* *
* \see embb_shared_mutex_try_lock(), embb_shared_mutex_unlock() * \see embb_rwlock_try_lock_read(), embb_rwlock_unlock_read()
*/ */
int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex); int embb_rwlock_lock_read(embb_rwlock_t* rwlock);
/** /**
* Tries to lock the shared mutex for writing and returns immediately. * Waits until the rwlock can be locked for writing and locks it.
* *
* \pre \c shared_mutex is initialized * \pre \c rwlock is initialized and not locked by the current thread.
* \post If successful, \c shared_mutex is locked for writing * \post If successful, \c rwlock is locked for writing.
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if shared_mutex could be locked for writing \n * \return EMBB_SUCCESS if rwlock could be locked for writing \n
* EMBB_BUSY if shared_mutex could not be locked for writing \n * EMBB_ERROR otherwise
* EMBB_ERROR if an error occurred
* *
* \threadsafe * \threadsafe
* *
* \see embb_shared_mutex_lock(), embb_shared_mutex_unlock() * \see embb_rwlock_try_lock_write(), embb_rwlock_unlock_write()
*/ */
int embb_shared_mutex_try_lock(embb_shared_mutex_t* shared_mutex); int embb_rwlock_lock_write(embb_rwlock_t* rwlock);
/** /**
* Unlocks the shared mutex locked for writing. * Tries to lock the rwlock for reading and returns immediately.
* *
* \pre \c shared_mutex has been locked for writing by the current thread. * \pre \c rwlock is initialized
* \post If successful, \c shared_mutex is unlocked. * \post If successful, \c rwlock is locked for reading
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if the operation was successful \n * \return EMBB_SUCCESS if rwlock could be locked for reading \n
* EMBB_ERROR otherwise * EMBB_BUSY if rwlock could not be locked for reading \n
* EMBB_ERROR if an error occurred
* *
* \threadsafe * \threadsafe
* *
* \see embb_shared_mutex_lock(), embb_shared_mutex_try_lock() * \see embb_rwlock_lock_read(), embb_rwlock_unlock_read()
*/ */
int embb_shared_mutex_unlock(embb_shared_mutex_t* shared_mutex); int embb_rwlock_try_lock_read(embb_rwlock_t* rwlock);
/** /**
* Waits until the shared mutex can be locked for reading and locks it. * Tries to lock the rwlock for writing and returns immediately.
* *
* \pre \c shared_mutex is initialized and not locked by the current thread. * \pre \c rwlock is initialized
* \post If successful, \c shared_mutex is locked for reading. * \post If successful, \c rwlock is locked for writing
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if shared_mutex could be locked for reading \n * \return EMBB_SUCCESS if rwlock could be locked for writing \n
* EMBB_ERROR otherwise * EMBB_BUSY if rwlock could not be locked for writing \n
* EMBB_ERROR if an error occurred
* *
* \threadsafe * \threadsafe
* *
* \see embb_shared_mutex_try_lock_shared(), embb_shared_mutex_unlock_shared() * \see embb_rwlock_lock_write(), embb_rwlock_unlock_write()
*/ */
int embb_shared_mutex_lock_shared(embb_shared_mutex_t* shared_mutex); int embb_rwlock_try_lock_write(embb_rwlock_t* rwlock);
/** /**
* Tries to lock the shared mutex for reading and returns immediately. * Unlocks an rwlock locked for reading.
* *
* \pre \c shared_mutex is initialized * \pre \c rwlock has been locked for reading by the current thread.
* \post If successful, \c shared_mutex is locked for reading * \post If successful, \c rwlock is unlocked.
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if shared_mutex could be locked for reading \n * \return EMBB_SUCCESS if the operation was successful \n
* EMBB_BUSY if shared_mutex could not be locked for reading \n * EMBB_ERROR otherwise
* EMBB_ERROR if an error occurred
* *
* \threadsafe * \threadsafe
* *
* \see embb_shared_mutex_lock_shared(), embb_shared_mutex_unlock_shared() * \see embb_rwlock_lock_read(), embb_rwlock_try_lock_read()
*/ */
int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex); int embb_rwlock_unlock_read(embb_rwlock_t* rwlock);
/** /**
* Unlocks the shared mutex locked for reading. * Unlocks an rwlock locked for writing.
* *
* \pre \c shared_mutex has been locked for reading by the current thread. * \pre \c rwlock has been locked for writing by the current thread.
* \post If successful, \c shared_mutex is unlocked. * \post If successful, \c rwlock is unlocked.
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* \return EMBB_SUCCESS if the operation was successful \n * \return EMBB_SUCCESS if the operation was successful \n
* EMBB_ERROR otherwise * EMBB_ERROR otherwise
* *
* \threadsafe * \threadsafe
* *
* \see embb_shared_mutex_lock_shared(), embb_shared_mutex_try_lock_shared() * \see embb_rwlock_lock_write(), embb_rwlock_try_lock_write()
*/ */
int embb_shared_mutex_unlock_shared(embb_shared_mutex_t* shared_mutex); int embb_rwlock_unlock_write(embb_rwlock_t* rwlock);
/** /**
* Destroys the shared mutex and frees its resources. * Destroys an rwlock and frees its resources.
* *
* \pre \c shared_mutex has been initialized * \pre \c rwlock has been initialized
* \post \c shared_mutex is uninitialized * \post \c rwlock is uninitialized
* *
* \param shared_mutex Pointer to shared mutex * \param rwlock Pointer to rwlock
* *
* \notthreadsafe * \notthreadsafe
* *
* \see embb_shared_mutex_init() * \see embb_rwlock_init()
*/ */
void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex); void embb_rwlock_destroy(embb_rwlock_t* rwlock);
#ifdef __cplusplus #ifdef __cplusplus
} // Close extern "C" } // Close extern "C"
...@@ -193,4 +190,4 @@ void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex); ...@@ -193,4 +190,4 @@ void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex);
* \} * \}
*/ */
#endif // EMBB_BASE_C_SHARED_MUTEX_H_ #endif // EMBB_BASE_C_RWLOCK_H_
...@@ -24,106 +24,98 @@ ...@@ -24,106 +24,98 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <embb/base/c/shared_mutex.h> #include <embb/base/c/rwlock.h>
#include <assert.h> #include <assert.h>
#include <embb/base/c/internal/unused.h> #include <embb/base/c/internal/unused.h>
#ifdef EMBB_PLATFORM_THREADING_WINTHREADS #ifdef EMBB_PLATFORM_THREADING_WINTHREADS
int embb_shared_mutex_init(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_init(embb_rwlock_t* rwlock) {
InitializeSRWLock(shared_mutex); InitializeSRWLock(rwlock);
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_lock_read(embb_rwlock_t* rwlock) {
AcquireSRWLockExclusive(shared_mutex); AcquireSRWLockShared(rwlock);
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_try_lock(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_lock_write(embb_rwlock_t* rwlock) {
BOOLEAN success; AcquireSRWLockExclusive(rwlock);
success = TryAcquireSRWLockExclusive(shared_mutex);
if (success == FALSE) return EMBB_ERROR;
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_unlock(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_try_lock_read(embb_rwlock_t* rwlock) {
ReleaseSRWLockExclusive(shared_mutex); BOOLEAN success;
success = TryAcquireSRWLockShared(rwlock);
if (success == FALSE) return EMBB_ERROR;
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_lock_shared(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_try_lock_write(embb_rwlock_t* rwlock) {
AcquireSRWLockShared(shared_mutex); BOOLEAN success;
success = TryAcquireSRWLockExclusive(rwlock);
if (success == FALSE) return EMBB_ERROR;
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_unlock_read(embb_rwlock_t* rwlock) {
BOOLEAN success; ReleaseSRWLockShared(rwlock);
success = TryAcquireSRWLockShared(shared_mutex);
if (success == FALSE) return EMBB_ERROR;
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_unlock_shared(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_unlock_write(embb_rwlock_t* rwlock) {
ReleaseSRWLockShared(shared_mutex); ReleaseSRWLockExclusive(rwlock);
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex) { void embb_rwlock_destroy(embb_rwlock_t* rwlock) {
// Quoting MSDN: "SRW locks do not need to be explicitly destroyed". // Quoting MSDN: "SRW locks do not need to be explicitly destroyed".
EMBB_UNUSED(shared_mutex); EMBB_UNUSED(rwlock);
} }
#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ #endif /* EMBB_PLATFORM_THREADING_WINTHREADS */
#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS #ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS
int embb_shared_mutex_init(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_init(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_init(shared_mutex, NULL); int result = pthread_rwlock_init(rwlock, NULL);
if (result != 0) { if (result != 0) {
return EMBB_ERROR; return EMBB_ERROR;
} }
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_lock_read(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_wrlock(shared_mutex); int result = pthread_rwlock_rdlock(rwlock);
if (result != 0) { if (result != 0) {
return EMBB_ERROR; return EMBB_ERROR;
} }
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_try_lock(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_lock_write(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_trywrlock(shared_mutex); int result = pthread_rwlock_wrlock(rwlock);
if (result == 0) {
return EMBB_SUCCESS;
}
if (result == EBUSY) {
return EMBB_BUSY;
}
return EMBB_ERROR;
}
int embb_shared_mutex_unlock(embb_shared_mutex_t* shared_mutex) {
int result = pthread_rwlock_unlock(shared_mutex);
if (result != 0) { if (result != 0) {
return EMBB_ERROR; return EMBB_ERROR;
} }
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
int embb_shared_mutex_lock_shared(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_try_lock_read(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_rdlock(shared_mutex); int result = pthread_rwlock_tryrdlock(rwlock);
if (result != 0) { if (result == 0) {
return EMBB_ERROR; return EMBB_SUCCESS;
} }
return EMBB_SUCCESS; if (result == EBUSY) {
return EMBB_BUSY;
}
return EMBB_ERROR;
} }
int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_try_lock_write(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_tryrdlock(shared_mutex); int result = pthread_rwlock_trywrlock(rwlock);
if (result == 0) { if (result == 0) {
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
...@@ -133,16 +125,24 @@ int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex) { ...@@ -133,16 +125,24 @@ int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex) {
return EMBB_ERROR; return EMBB_ERROR;
} }
int embb_shared_mutex_unlock_shared(embb_shared_mutex_t* shared_mutex) { int embb_rwlock_unlock_read(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_unlock(shared_mutex); int result = pthread_rwlock_unlock(rwlock);
if (result != 0) {
return EMBB_ERROR;
}
return EMBB_SUCCESS;
}
int embb_rwlock_unlock_write(embb_rwlock_t* rwlock) {
int result = pthread_rwlock_unlock(rwlock);
if (result != 0) { if (result != 0) {
return EMBB_ERROR; return EMBB_ERROR;
} }
return EMBB_SUCCESS; return EMBB_SUCCESS;
} }
void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex) { void embb_rwlock_destroy(embb_rwlock_t* rwlock) {
pthread_rwlock_destroy(shared_mutex); pthread_rwlock_destroy(rwlock);
} }
#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ #endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include <condition_var_test.h> #include <condition_var_test.h>
#include <thread_test.h> #include <thread_test.h>
#include <thread_specific_storage_test.h> #include <thread_specific_storage_test.h>
#include <shared_mutex_test.h> #include <rwlock_test.h>
#include <embb/base/c/thread.h> #include <embb/base/c/thread.h>
#include <embb/base/c/log.h> #include <embb/base/c/log.h>
#include <iostream> #include <iostream>
...@@ -52,7 +52,7 @@ using embb::base::test::CoreSetTest; ...@@ -52,7 +52,7 @@ using embb::base::test::CoreSetTest;
using embb::base::test::ConditionVarTest; using embb::base::test::ConditionVarTest;
using embb::base::test::ThreadTest; using embb::base::test::ThreadTest;
using embb::base::test::ThreadSpecificStorageTest; using embb::base::test::ThreadSpecificStorageTest;
using embb::base::test::SharedMutexTest; using embb::base::test::RWLockTest;
PT_MAIN("Base C") { PT_MAIN("Base C") {
embb_log_set_log_level(EMBB_LOG_LEVEL_WARNING); embb_log_set_log_level(EMBB_LOG_LEVEL_WARNING);
...@@ -70,7 +70,7 @@ PT_MAIN("Base C") { ...@@ -70,7 +70,7 @@ PT_MAIN("Base C") {
PT_RUN(ConditionVarTest); PT_RUN(ConditionVarTest);
PT_RUN(ThreadTest); PT_RUN(ThreadTest);
PT_RUN(ThreadSpecificStorageTest); PT_RUN(ThreadSpecificStorageTest);
PT_RUN(SharedMutexTest); PT_RUN(RWLockTest);
PT_EXPECT(embb_get_bytes_allocated() == 0); PT_EXPECT(embb_get_bytes_allocated() == 0);
} }
...@@ -24,85 +24,85 @@ ...@@ -24,85 +24,85 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <shared_mutex_test.h> #include <rwlock_test.h>
#include <embb/base/c/errors.h> #include <embb/base/c/errors.h>
namespace embb { namespace embb {
namespace base { namespace base {
namespace test { namespace test {
SharedMutexTest::SharedMutexTest() RWLockTest::RWLockTest()
: counter_(0), : 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")
.Pre(&SharedMutexTest::TestSharedRead_Pre, this) .Pre(&RWLockTest::TestSharedRead_Pre, this)
.Add(&SharedMutexTest::TestSharedRead_ThreadMethod, this, .Add(&RWLockTest::TestSharedRead_ThreadMethod, this,
num_threads_, num_iterations_) num_threads_, num_iterations_)
.Post(&SharedMutexTest::TestSharedRead_Post, this); .Post(&RWLockTest::TestSharedRead_Post, this);
CreateUnit("Exclusive write") CreateUnit("Exclusive write")
.Pre(&SharedMutexTest::TestExclusiveWrite_Pre, this) .Pre(&RWLockTest::TestExclusiveWrite_Pre, this)
.Add(&SharedMutexTest::TestExclusiveWrite_ReaderMethod, this, .Add(&RWLockTest::TestExclusiveWrite_ReaderMethod, this,
num_threads_ / 2, num_iterations_) num_threads_, num_iterations_)
.Add(&SharedMutexTest::TestExclusiveWrite_WriterMethod, this, .Add(&RWLockTest::TestExclusiveWrite_WriterMethod, this,
num_threads_ / 2, num_iterations_) num_threads_, num_iterations_)
.Post(&SharedMutexTest::TestExclusiveWrite_Post, this); .Post(&RWLockTest::TestExclusiveWrite_Post, this);
} }
void SharedMutexTest::TestSharedRead_Pre() { void RWLockTest::TestSharedRead_Pre() {
int success = embb_shared_mutex_init(&shared_mutex_); int success = embb_rwlock_init(&rwlock_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize rwlock.");
} }
void SharedMutexTest::TestSharedRead_ThreadMethod() { void RWLockTest::TestSharedRead_ThreadMethod() {
int success = embb_shared_mutex_try_lock_shared(&shared_mutex_); int success = embb_rwlock_try_lock_read(&rwlock_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading.");
int spin = 10000; int spin = 10000;
while (--spin != 0); while (--spin != 0);
success = embb_shared_mutex_unlock_shared(&shared_mutex_); success = embb_rwlock_unlock_read(&rwlock_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading).");
} }
void SharedMutexTest::TestSharedRead_Post() { void RWLockTest::TestSharedRead_Post() {
embb_shared_mutex_destroy(&shared_mutex_); embb_rwlock_destroy(&rwlock_);
} }
void SharedMutexTest::TestExclusiveWrite_Pre() { void RWLockTest::TestExclusiveWrite_Pre() {
int success = embb_shared_mutex_init(&shared_mutex_); int success = embb_rwlock_init(&rwlock_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize rwlock.");
counter_ = 0; counter_ = 0;
} }
void SharedMutexTest::TestExclusiveWrite_ReaderMethod() { void RWLockTest::TestExclusiveWrite_ReaderMethod() {
// Just add some contention // Just add some contention
int success = embb_shared_mutex_try_lock_shared(&shared_mutex_); int success = embb_rwlock_try_lock_read(&rwlock_);
if (success != EMBB_SUCCESS) return; if (success != EMBB_SUCCESS) return;
int spin = 10000; int spin = 10000;
while (--spin != 0); while (--spin != 0);
success = embb_shared_mutex_unlock_shared(&shared_mutex_); success = embb_rwlock_unlock_read(&rwlock_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading).");
} }
void SharedMutexTest::TestExclusiveWrite_WriterMethod() { void RWLockTest::TestExclusiveWrite_WriterMethod() {
int success = embb_shared_mutex_lock(&shared_mutex_); int success = embb_rwlock_lock_write(&rwlock_);
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_rwlock_unlock_write(&rwlock_);
PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing).");
} }
void SharedMutexTest::TestExclusiveWrite_Post() { void RWLockTest::TestExclusiveWrite_Post() {
PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2), PT_ASSERT_EQ_MSG(counter_, num_iterations_ * num_threads_,
"Counter value is inconsistent."); "Counter value is inconsistent.");
embb_shared_mutex_destroy(&shared_mutex_); embb_rwlock_destroy(&rwlock_);
} }
} // namespace test } // namespace test
......
...@@ -24,21 +24,21 @@ ...@@ -24,21 +24,21 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef BASE_C_TEST_SHARED_MUTEX_TEST_H_ #ifndef BASE_C_TEST_RWLOCK_TEST_H_
#define BASE_C_TEST_SHARED_MUTEX_TEST_H_ #define BASE_C_TEST_RWLOCK_TEST_H_
#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/c/shared_mutex.h> #include <embb/base/c/rwlock.h>
namespace embb { namespace embb {
namespace base { namespace base {
namespace test { namespace test {
class SharedMutexTest : public partest::TestCase { class RWLockTest : public partest::TestCase {
public: public:
SharedMutexTest(); RWLockTest();
private: private:
void TestSharedRead_Pre(); void TestSharedRead_Pre();
...@@ -50,7 +50,7 @@ class SharedMutexTest : public partest::TestCase { ...@@ -50,7 +50,7 @@ class SharedMutexTest : public partest::TestCase {
void TestExclusiveWrite_WriterMethod(); void TestExclusiveWrite_WriterMethod();
void TestExclusiveWrite_Post(); void TestExclusiveWrite_Post();
embb_shared_mutex_t shared_mutex_; embb_rwlock_t rwlock_;
size_t counter_; size_t counter_;
size_t num_threads_; size_t num_threads_;
size_t num_iterations_; size_t num_iterations_;
...@@ -60,4 +60,4 @@ class SharedMutexTest : public partest::TestCase { ...@@ -60,4 +60,4 @@ class SharedMutexTest : public partest::TestCase {
} // namespace base } // namespace base
} // namespace embb } // namespace embb
#endif // BASE_C_TEST_SHARED_MUTEX_TEST_H_ #endif // BASE_C_TEST_RWLOCK_TEST_H_
...@@ -59,6 +59,6 @@ ...@@ -59,6 +59,6 @@
#include <embb/base/thread.h> #include <embb/base/thread.h>
#include <embb/base/thread_specific_storage.h> #include <embb/base/thread_specific_storage.h>
#include <embb/base/time.h> #include <embb/base/time.h>
#include <embb/base/shared_mutex.h> #include <embb/base/rwlock.h>
#endif // EMBB_BASE_BASE_H_ #endif // EMBB_BASE_BASE_H_
...@@ -44,7 +44,7 @@ typedef embb_thread_t ThreadType; ...@@ -44,7 +44,7 @@ typedef embb_thread_t ThreadType;
typedef DWORD IDType; typedef DWORD IDType;
typedef embb_mutex_t MutexType; typedef embb_mutex_t MutexType;
typedef embb_condition_t ConditionVariableType; typedef embb_condition_t ConditionVariableType;
typedef embb_shared_mutex_t SharedMutexType; typedef embb_rwlock_t RWLockType;
} // namespace internal } // namespace internal
} // namespace base } // namespace base
...@@ -60,7 +60,7 @@ typedef embb_thread_t ThreadType; ...@@ -60,7 +60,7 @@ typedef embb_thread_t ThreadType;
typedef embb_thread_id_t IDType; typedef embb_thread_id_t IDType;
typedef embb_mutex_t MutexType; typedef embb_mutex_t MutexType;
typedef embb_condition_t ConditionVariableType; typedef embb_condition_t ConditionVariableType;
typedef embb_shared_mutex_t SharedMutexType; typedef embb_rwlock_t RWLockType;
} // namespace internal } // namespace internal
} // namespace base } // namespace base
......
/*
* 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_
...@@ -24,281 +24,187 @@ ...@@ -24,281 +24,187 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef EMBB_BASE_SHARED_MUTEX_H_ #ifndef EMBB_BASE_RWLOCK_H_
#define EMBB_BASE_SHARED_MUTEX_H_ #define EMBB_BASE_RWLOCK_H_
#include <embb/base/internal/platform.h> #include <embb/base/internal/platform.h>
#include <embb/base/exceptions.h> #include <embb/base/exceptions.h>
#include <embb/base/mutex.h>
namespace embb { namespace embb {
namespace base { namespace base {
/** /**
* \defgroup CPP_BASE_SHARED_MUTEX Shared mutex * \defgroup CPP_BASE_RWLOCK Readers-Writers Lock
* *
* Shared mutexes for thread synchronization * Shared-read/exclusive-write lock 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 * \ingroup CPP_BASE
*/ */
/** /**
* Shared mutex providing shared-read/exclusive-write access synchronization. * Shared-read/exclusive-write (readers-writers) lock.
* *
* Allows simultaneous shared access for concurrent readers, but only exclusive * Allows simultaneous shared access for concurrent readers, but only exclusive
* access for writers. Cannot be locked recursively. Fairness and/or FIFO order * access for writers. Cannot be locked recursively. Fairness and/or FIFO order
* guarantees are platform specific and should be assumed non-existent. * guaranties are platform specific and should be assumed non-existent.
*
* \see Mutex
* *
* \ingroup CPP_BASE_SHARED_MUTEX * \ingroup CPP_BASE_RWLOCK
*/ */
class SharedMutex { class RWLock {
public: public:
/** /**
* Creates the shared mutex object which is initially in an unlocked state. * Creates an rwlock which is in unlocked state.
* *
* \memory Potentially allocates dynamic memory * \memory Potentially allocates dynamic memory
*
* \notthreadsafe * \notthreadsafe
*/ */
SharedMutex(); RWLock();
/** /**
* Destroys internal representation. * Destroys internal representation.
*/ */
~SharedMutex(); ~RWLock();
/** /**
* Waits until the shared mutex can be locked for writing and locks it. * Waits until the rwlock can be locked for reading and locks it.
* *
* \pre \c shared_mutex is initialized and not locked by the current thread. * \pre The \c rwlock is not locked by the current thread.
* \post \c shared_mutex is locked for writing. * \post The \c rwlock is locked for reading.
* *
* \threadsafe * \threadsafe
* *
* \see TryLock(), Unlock() * \see TryLockRead(), UnlockRead()
*/ */
void Lock(); void LockRead();
/** /**
* Tries to lock the shared mutex for writing and returns immediately. * Waits until the rwlock can be locked for writing and locks it.
*
* \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 * \pre \c rwlock is initialized and not locked by the current thread.
* \c false otherwise * \post \c rwlock is locked for writing.
* *
* \threadsafe * \threadsafe
* *
* \see Lock(), Unlock() * \see TryLockWrite(), UnlockWrite()
*/ */
bool TryLock(); void LockWrite();
/** /**
* Unlocks the shared mutex locked for writing. * Tries to lock the rwlock for reading and returns immediately.
*
* \pre \c shared_mutex has been locked for writing by the current thread.
* \post \c shared_mutex is unlocked.
*
* \threadsafe
* *
* \see Lock(), TryLock() * \pre \c rwlock is not locked by the current thread.
*/ * \post If successful, \c rwlock is locked for reading.
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. * \return \c true if rwlock was locked for reading \n
* \post The \c shared_mutex is locked for reading. * \c false otherwise
* *
* \threadsafe * \threadsafe
* *
* \see TryLockShared(), UnlockShared() * \see LockRead(), UnlockRead()
*/ */
void LockShared(); bool TryLockRead();
/** /**
* Tries to lock the shared mutex for reading and returns immediately. * Tries to lock the rwlock for writing and returns immediately.
* *
* \pre \c shared_mutex is not locked by the current thread. * \pre \c rwlock is not locked by the current thread.
* \post If successful, \c shared_mutex is locked for reading. * \post If successful, \c rwlock is locked for writing.
* *
* \return \c true if shared mutex was locked for reading \n * \return \c true if rwlock was locked for reading \n
* \c false otherwise * \c false otherwise
* *
* \threadsafe * \threadsafe
* *
* \see LockShared(), UnlockShared() * \see LockWrite(), UnlockWrite()
*/ */
bool TryLockShared(); bool TryLockWrite();
/** /**
* Unlocks the shared mutex locked for reading. * Unlocks an rwlock locked for reading.
* *
* \pre \c shared_mutex has been locked for reading by the current thread. * \pre \c rwlock has been locked for reading by the current thread.
* \post \c shared_mutex is unlocked. * \post \c rwlock is unlocked.
* *
* \threadsafe * \threadsafe
* *
* \see LockShared(), TryLockShared() * \see LockRead(), TryLockRead()
*/
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(); void UnlockRead();
/** /**
* Creates a lock from an unlocked shared mutex and locks it for reading. * Unlocks an rwlock locked for writing.
* *
* \pre \c shared_mutex is unlocked * \pre \c rwlock has been locked for writing by the current thread.
* \post \c shared_mutex is locked for reading * \post \c rwlock is unlocked.
* *
* \param[IN] shared_mutex Shared mutex to be managed * \threadsafe
*/
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 * \see LockWrite(), TryLockWrite()
*/ */
void LockShared(); void UnlockWrite();
/**
* 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. * Adaptor class for the readers lock that can be used with "LockGuard" and/or
* "UniqueLock" ownership wrappers.
* *
* \throws ErrorException, if no shared mutex is set or it is not yet locked * \see LockGuard, UniqueLock
*/ */
void UnlockShared(); class ReaderLock {
public:
ReaderLock(RWLock& rwlock);
void Lock();
bool TryLock();
void Unlock();
private:
ReaderLock(const ReaderLock&);
ReaderLock& operator=(const ReaderLock&);
RWLock& rwlock_;
};
/** /**
* Exchange shared mutex ownership with another shared lock * Adaptor class for the writers lock that can be used with "LockGuard" and/or
* "UniqueLock" ownership wrappers.
* *
* \param other Shared lock to exchange ownership with * \see LockGuard, UniqueLock
*/ */
void Swap(SharedLock& other); class WriterLock {
public:
WriterLock(RWLock& rwlock);
void Lock();
bool TryLock();
void Unlock();
private:
WriterLock(const WriterLock&);
WriterLock& operator=(const WriterLock&);
RWLock& rwlock_;
};
/** /**
* Gives up ownership of the shared mutex and returns a pointer to it. * Returns an adaptor for the readers lock to be used with "LockGuard" and/or
* * "UniqueLock" ownership wrappers.
* \return A pointer to the owned shared mutex. (If no shared mutex was
* owned, returns NULL).
*/ */
SharedMutex* Release(); ReaderLock& GetReaderLock();
/** /**
* Checks whether the shared mutex is owned and locked. * Returns an adaptor for the writes lock to be used with "LockGuard" and/or
* * "UniqueLock" ownership wrappers.
* \return \c true if shared mutex is locked, otherwise \c false.
*/ */
bool OwnsLock() const; WriterLock& GetWriterLock();
private: private:
/** /**
* Disable copy construction and assignment. * Disables copy construction and assignment.
*/ */
SharedLock(const SharedLock&); RWLock(const RWLock&);
SharedLock& operator=(const SharedLock&); RWLock& operator=(const RWLock&);
/** Pointer to the owned shared mutex */ internal::RWLockType rwlock_; /**< Actual rwlock implementation from base_c */
SharedMutex* shared_mutex_; ReaderLock reader_lock_; /**< Lock adaptor for readers */
/** Locked flag (is true if and only if the owned shared mutex is locked) */ WriterLock writer_lock_; /**< Lock adaptor for writers */
bool locked_;
}; };
} // namespace base } // namespace base
} // namespace embb } // namespace embb
#include <embb/base/internal/shared_mutex-inl.h> #endif // EMBB_BASE_RWLOCK_H_
#endif // EMBB_BASE_SHARED_MUTEX_H_
...@@ -24,45 +24,73 @@ ...@@ -24,45 +24,73 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <embb/base/shared_mutex.h> #include <embb/base/rwlock.h>
#include <embb/base/c/shared_mutex.h> #include <embb/base/c/rwlock.h>
namespace embb { namespace embb {
namespace base { namespace base {
SharedMutex::SharedMutex() RWLock::RWLock()
: shared_mutex_() { : rwlock_(),
embb_shared_mutex_init(&shared_mutex_); // Disable "this is used in base member initializer" warning.
// We explicitly want this.
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4355)
#endif
reader_lock_(*this),
writer_lock_(*this) {
#ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop)
#endif
embb_rwlock_init(&rwlock_);
} }
SharedMutex::~SharedMutex() { RWLock::~RWLock() {
embb_shared_mutex_destroy(&shared_mutex_); embb_rwlock_destroy(&rwlock_);
} }
void SharedMutex::Lock() { void RWLock::LockRead() {
embb_shared_mutex_lock(&shared_mutex_); embb_rwlock_lock_read(&rwlock_);
} }
bool SharedMutex::TryLock() { void RWLock::LockWrite() {
int result = embb_shared_mutex_try_lock(&shared_mutex_); embb_rwlock_lock_write(&rwlock_);
}
bool RWLock::TryLockRead() {
int result = embb_rwlock_try_lock_read(&rwlock_);
return result == EMBB_SUCCESS; return result == EMBB_SUCCESS;
} }
void SharedMutex::Unlock() { bool RWLock::TryLockWrite() {
embb_shared_mutex_unlock(&shared_mutex_); int result = embb_rwlock_try_lock_write(&rwlock_);
return result == EMBB_SUCCESS;
} }
void SharedMutex::LockShared() { void RWLock::UnlockRead() {
embb_shared_mutex_lock_shared(&shared_mutex_); embb_rwlock_unlock_read(&rwlock_);
} }
bool SharedMutex::TryLockShared() { void RWLock::UnlockWrite() {
int result = embb_shared_mutex_try_lock_shared(&shared_mutex_); embb_rwlock_unlock_write(&rwlock_);
return result == EMBB_SUCCESS;
} }
void SharedMutex::UnlockShared() { RWLock::ReaderLock::ReaderLock(RWLock& rwlock) : rwlock_(rwlock) {}
embb_shared_mutex_unlock_shared(&shared_mutex_); void RWLock::ReaderLock::Lock() { rwlock_.LockRead(); }
bool RWLock::ReaderLock::TryLock() { return rwlock_.TryLockRead(); }
void RWLock::ReaderLock::Unlock() { rwlock_.UnlockRead(); }
RWLock::WriterLock::WriterLock(RWLock& rwlock) : rwlock_(rwlock) {}
void RWLock::WriterLock::Lock() { rwlock_.LockWrite(); }
bool RWLock::WriterLock::TryLock() { return rwlock_.TryLockWrite(); }
void RWLock::WriterLock::Unlock() { rwlock_.UnlockWrite(); }
RWLock::ReaderLock& RWLock::GetReaderLock() {
return reader_lock_;
}
RWLock::WriterLock& RWLock::GetWriterLock() {
return writer_lock_;
} }
} // namespace base } // namespace base
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include <thread_specific_storage_test.h> #include <thread_specific_storage_test.h>
#include <atomic_test.h> #include <atomic_test.h>
#include <memory_allocation_test.h> #include <memory_allocation_test.h>
#include <shared_mutex_test.h> #include <rwlock_test.h>
#include <embb/base/c/memory_allocation.h> #include <embb/base/c/memory_allocation.h>
...@@ -46,7 +46,7 @@ using embb::base::test::ThreadSpecificStorageTest; ...@@ -46,7 +46,7 @@ using embb::base::test::ThreadSpecificStorageTest;
using embb::base::test::AtomicTest; using embb::base::test::AtomicTest;
using embb::base::test::MemoryAllocationTest; using embb::base::test::MemoryAllocationTest;
using embb::base::test::ThreadTest; using embb::base::test::ThreadTest;
using embb::base::test::SharedMutexTest; using embb::base::test::RWLockTest;
PT_MAIN("Base C++") { PT_MAIN("Base C++") {
unsigned int max_threads = unsigned int max_threads =
...@@ -60,7 +60,7 @@ PT_MAIN("Base C++") { ...@@ -60,7 +60,7 @@ PT_MAIN("Base C++") {
PT_RUN(AtomicTest); PT_RUN(AtomicTest);
PT_RUN(MemoryAllocationTest); PT_RUN(MemoryAllocationTest);
PT_RUN(ThreadTest); PT_RUN(ThreadTest);
PT_RUN(SharedMutexTest); PT_RUN(RWLockTest);
PT_EXPECT(embb_get_bytes_allocated() == 0); PT_EXPECT(embb_get_bytes_allocated() == 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.
*/
#include <rwlock_test.h>
#include <embb/base/c/errors.h>
#include <embb/base/mutex.h>
namespace embb {
namespace base {
namespace test {
RWLockTest::RWLockTest()
: counter_(0),
num_threads_(partest::TestSuite::GetDefaultNumThreads()),
num_iterations_(partest::TestSuite::GetDefaultNumIterations()) {
CreateUnit("Shared read")
.Add(&RWLockTest::TestSharedRead_ThreadMethod, this,
num_threads_, num_iterations_);
CreateUnit("Exclusive write")
.Pre(&RWLockTest::TestExclusiveWrite_Pre, this)
.Add(&RWLockTest::TestExclusiveWrite_ReaderMethod, this,
num_threads_, num_iterations_)
.Add(&RWLockTest::TestExclusiveWrite_WriterMethod, this,
num_threads_, num_iterations_)
.Post(&RWLockTest::TestExclusiveWrite_Post, this);
}
void RWLockTest::TestSharedRead_ThreadMethod() {
UniqueLock<embb::base::RWLock::ReaderLock> lock(rwlock_.GetReaderLock(),
embb::base::try_lock);
PT_ASSERT_EQ_MSG(lock.OwnsLock(), true, "Failed to lock for reading.");
int spin = 10000;
while (--spin != 0);
}
void RWLockTest::TestExclusiveWrite_Pre() {
counter_ = 0;
}
void RWLockTest::TestExclusiveWrite_ReaderMethod() {
// Just add some contention
UniqueLock<embb::base::RWLock::ReaderLock> lock(rwlock_.GetReaderLock(),
embb::base::try_lock);
int spin = 10000;
while (--spin != 0);
}
void RWLockTest::TestExclusiveWrite_WriterMethod() {
UniqueLock<embb::base::RWLock::WriterLock> lock(rwlock_.GetWriterLock());
++counter_;
}
void RWLockTest::TestExclusiveWrite_Post() {
PT_ASSERT_EQ_MSG(counter_, num_iterations_ * num_threads_,
"Counter value is inconsistent.");
}
} // namespace test
} // namespace base
} // namespace embb
...@@ -24,33 +24,33 @@ ...@@ -24,33 +24,33 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef BASE_CPP_TEST_SHARED_MUTEX_TEST_H_ #ifndef BASE_CPP_TEST_RWLOCK_TEST_H_
#define BASE_CPP_TEST_SHARED_MUTEX_TEST_H_ #define BASE_CPP_TEST_RWLOCK_TEST_H_
#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/rwlock.h>
namespace embb { namespace embb {
namespace base { namespace base {
namespace test { namespace test {
class SharedMutexTest : public partest::TestCase { class RWLockTest : public partest::TestCase {
public: public:
SharedMutexTest(); RWLockTest();
private: private:
void TestSharedRead_Pre();
void TestSharedRead_ThreadMethod(); void TestSharedRead_ThreadMethod();
void TestSharedRead_Post();
void TestExclusiveWrite_Pre(); void TestExclusiveWrite_Pre();
void TestExclusiveWrite_ReaderMethod(); void TestExclusiveWrite_ReaderMethod();
void TestExclusiveWrite_WriterMethod(); void TestExclusiveWrite_WriterMethod();
void TestExclusiveWrite_Post(); void TestExclusiveWrite_Post();
void TestSharedLock_ThreadMethod(); RWLock rwlock_;
SharedMutex shared_mutex_;
size_t counter_; size_t counter_;
size_t num_threads_; size_t num_threads_;
size_t num_iterations_; size_t num_iterations_;
...@@ -60,4 +60,4 @@ class SharedMutexTest : public partest::TestCase { ...@@ -60,4 +60,4 @@ class SharedMutexTest : public partest::TestCase {
} // namespace base } // namespace base
} // namespace embb } // namespace embb
#endif // BASE_CPP_TEST_SHARED_MUTEX_TEST_H_ #endif // BASE_CPP_TEST_RWLOCK_TEST_H_
/*
* 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.
*/
#include <shared_mutex_test.h>
#include <embb/base/c/errors.h>
#include <embb/base/mutex.h>
namespace embb {
namespace base {
namespace test {
SharedMutexTest::SharedMutexTest()
: shared_mutex_(),
counter_(0),
num_threads_(partest::TestSuite::GetDefaultNumThreads()),
num_iterations_(partest::TestSuite::GetDefaultNumIterations()) {
CreateUnit("Shared read")
.Add(&SharedMutexTest::TestSharedRead_ThreadMethod, this,
num_threads_, num_iterations_);
CreateUnit("Exclusive write")
.Pre(&SharedMutexTest::TestExclusiveWrite_Pre, this)
.Add(&SharedMutexTest::TestExclusiveWrite_ReaderMethod, this,
num_threads_ / 2, num_iterations_)
.Add(&SharedMutexTest::TestExclusiveWrite_WriterMethod, this,
num_threads_ / 2, num_iterations_)
.Post(&SharedMutexTest::TestExclusiveWrite_Post, this);
CreateUnit("SharedLock")
.Add(&SharedMutexTest::TestSharedLock_ThreadMethod, this,
num_threads_, num_iterations_);
}
void SharedMutexTest::TestSharedRead_ThreadMethod() {
SharedLock<embb::base::SharedMutex> lock(shared_mutex_, embb::base::try_lock);
PT_ASSERT_EQ_MSG(lock.OwnsLock(), true, "Failed to lock for reading.");
int spin = 10000;
while (--spin != 0);
}
void SharedMutexTest::TestExclusiveWrite_Pre() {
counter_ = 0;
}
void SharedMutexTest::TestExclusiveWrite_ReaderMethod() {
// Just add some contention
SharedLock<embb::base::SharedMutex> lock(shared_mutex_, embb::base::try_lock);
if (lock.OwnsLock()) {
int spin = 10000;
while (--spin != 0);
}
}
void SharedMutexTest::TestExclusiveWrite_WriterMethod() {
UniqueLock<embb::base::SharedMutex> lock(shared_mutex_);
++counter_;
}
void SharedMutexTest::TestExclusiveWrite_Post() {
PT_ASSERT_EQ_MSG(counter_, num_iterations_ * (num_threads_ / 2),
"Counter value is inconsistent.");
}
void SharedMutexTest::TestSharedLock_ThreadMethod() {
// Test basic usage
{
SharedLock<> lock(shared_mutex_);
PT_EXPECT_EQ(lock.OwnsLock(), true);
lock.UnlockShared();
PT_EXPECT_EQ(lock.OwnsLock(), false);
lock.LockShared();
PT_EXPECT_EQ(lock.OwnsLock(), true);
lock.UnlockShared();
PT_EXPECT_EQ(lock.OwnsLock(), false);
bool locked_after_try = lock.TryLockShared();
PT_EXPECT_EQ(locked_after_try, true);
PT_EXPECT_EQ(lock.OwnsLock(), true);
lock.Release()->UnlockShared();
PT_EXPECT_EQ(lock.OwnsLock(), false);
}
// Test deferred lock construction
{
SharedLock<> lock(shared_mutex_, embb::base::defer_lock);
PT_EXPECT_EQ(lock.OwnsLock(), false);
}
// Test try-lock construction
{
SharedLock<> lock(shared_mutex_, embb::base::try_lock);
PT_EXPECT_EQ(lock.OwnsLock(), true);
}
// Test adopt lock construction
{
shared_mutex_.LockShared();
SharedLock<> lock(shared_mutex_, embb::base::adopt_lock);
PT_EXPECT_EQ(lock.OwnsLock(), true);
}
// Test lock swapping
{
SharedMutex another_mutex;
SharedLock<> lock1(another_mutex);
PT_EXPECT_EQ(lock1.OwnsLock(), true);
{
SharedLock<> lock2(shared_mutex_);
PT_EXPECT_EQ(lock2.OwnsLock(), true);
lock1.Swap(lock2);
PT_EXPECT_EQ(lock1.OwnsLock(), true);
PT_EXPECT_EQ(lock2.OwnsLock(), true);
}
// At this point, lock2 was destroyed and "another_mutex" must be unlocked
SharedLock<> lock3(another_mutex, embb::base::try_lock);
PT_EXPECT_EQ(lock3.OwnsLock(), true);
// But lock1 must still be locking "shared_mutex_"
PT_EXPECT_EQ(lock1.OwnsLock(), true);
lock1.Release()->UnlockShared();
PT_EXPECT_EQ(lock1.OwnsLock(), false);
}
}
} // namespace test
} // namespace base
} // namespace embb
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