From fc660db00e2d24d063c2fc0b66d8bb100694c938 Mon Sep 17 00:00:00 2001 From: bernhard-gatzhammer Date: Tue, 3 Nov 2015 15:43:11 +0100 Subject: [PATCH] Worked in review comments. EMBB-453 --- base_c/include/embb/base/c/base.h | 1 - base_c/include/embb/base/c/mutex.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/shared_mutex.h | 196 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- base_c/src/shared_mutex.c | 2 +- base_c/test/shared_mutex_test.cc | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------- base_c/test/shared_mutex_test.h | 43 ++++++++++++++++++++++++++++++++++--------- 6 files changed, 230 insertions(+), 226 deletions(-) delete mode 100644 base_c/include/embb/base/c/shared_mutex.h diff --git a/base_c/include/embb/base/c/base.h b/base_c/include/embb/base/c/base.h index 0f697de..0251a43 100644 --- a/base_c/include/embb/base/c/base.h +++ b/base_c/include/embb/base/c/base.h @@ -55,6 +55,5 @@ #include #include #include -#include #endif /* EMBB_BASE_C_BASE_H_ */ diff --git a/base_c/include/embb/base/c/mutex.h b/base_c/include/embb/base/c/mutex.h index ffd2c7f..e2cc648 100644 --- a/base_c/include/embb/base/c/mutex.h +++ b/base_c/include/embb/base/c/mutex.h @@ -147,6 +147,146 @@ void embb_mutex_destroy( /**< [IN/OUT] Pointer to mutex */ ); + + +#ifdef DOXYGEN +/** + * Opaque type representing a shared mutex. + */ +typedef opaque_type embb_shared_mutex_t; +#endif // DOXYGEN + +/** + * Initializes a shared mutex + * + * \pre \c shared_mutex is uninitialized + * \post \c shared_mutex is initialized + * + * \param shared_mutex Pointer to shared mutex + * \return EMBB_SUCCESS if shared mutex could be initialized \n + * EMBB_ERROR otherwise + * + * \memory (Potentially) allocates dynamic memory + * + * \notthreadsafe + * + * \see embb_shared_mutex_destroy() + */ +int embb_shared_mutex_init(embb_shared_mutex_t* shared_mutex); + +/** + * 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 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_ERROR otherwise + * + * \threadsafe + * + * \see embb_shared_mutex_try_lock(), embb_shared_mutex_unlock() + */ +int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex); + +/** + * Tries to lock the shared mutex for writing and returns immediately. + * + * \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_ERROR if an error occurred + * + * \threadsafe + * + * \see embb_shared_mutex_lock(), embb_shared_mutex_unlock() + */ +int embb_shared_mutex_try_lock(embb_shared_mutex_t* shared_mutex); + +/** + * Unlocks the shared mutex locked for writing. + * + * \pre \c shared_mutex has been locked for writing by the current thread. + * \post If successful, \c shared_mutex is unlocked. + * + * \param shared_mutex Pointer to shared mutex + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_shared_mutex_lock(), embb_shared_mutex_try_lock() + */ +int embb_shared_mutex_unlock(embb_shared_mutex_t* shared_mutex); + +/** + * Waits until the shared mutex can be locked for reading and locks it. + * + * \pre \c shared_mutex is initialized and not locked by the current thread. + * \post If successful, \c shared_mutex is locked for reading. + * + * \param shared_mutex Pointer to shared mutex + * \return EMBB_SUCCESS if shared_mutex could be locked for reading \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_shared_mutex_try_lock_shared(), embb_shared_mutex_unlock_shared() + */ +int embb_shared_mutex_lock_shared(embb_shared_mutex_t* shared_mutex); + +/** + * Tries to lock the shared mutex for reading and returns immediately. + * + * \pre \c shared_mutex is initialized + * \post If successful, \c shared_mutex is locked for reading + * + * \param shared_mutex Pointer to shared mutex + * \return EMBB_SUCCESS if shared_mutex could be locked for reading \n + * EMBB_BUSY if shared_mutex could not be locked for reading \n + * EMBB_ERROR if an error occurred + * + * \threadsafe + * + * \see embb_shared_mutex_lock_shared(), embb_shared_mutex_unlock_shared() + */ +int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex); + +/** + * Unlocks the shared mutex locked for reading. + * + * \pre \c shared_mutex has been locked for reading by the current thread. + * \post If successful, \c shared_mutex is unlocked. + * + * \param shared_mutex Pointer to shared mutex + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_shared_mutex_lock_shared(), embb_shared_mutex_try_lock_shared() + */ +int embb_shared_mutex_unlock_shared(embb_shared_mutex_t* shared_mutex); + +/** + * Destroys the shared mutex and frees its resources. + * + * \pre \c shared_mutex has been initialized + * \post \c shared_mutex is uninitialized + * + * \param shared_mutex Pointer to shared mutex + * + * \notthreadsafe + * + * \see embb_shared_mutex_init() + */ +void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex); + + #ifdef __cplusplus } /* Close extern "C" { */ #endif diff --git a/base_c/include/embb/base/c/shared_mutex.h b/base_c/include/embb/base/c/shared_mutex.h deleted file mode 100644 index 5855dfc..0000000 --- a/base_c/include/embb/base/c/shared_mutex.h +++ /dev/null @@ -1,196 +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_C_SHARED_MUTEX_H_ -#define EMBB_BASE_C_SHARED_MUTEX_H_ - -/** - * \defgroup C_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 C_BASE - * \{ - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#ifdef DOXYGEN -/** - * Opaque type representing a shared mutex. - */ -typedef opaque_type embb_shared_mutex_t; -#endif // DOXYGEN - -/** - * Initializes a shared mutex - * - * \post \c shared_mutex is initialized - * - * \param shared_mutex Pointer to shared mutex - * \return EMBB_SUCCESS if shared mutex could be initialized \n - * EMBB_ERROR otherwise - * - * \memory (Potentially) allocates dynamic memory - * - * \notthreadsafe - * - * \see embb_shared_mutex_destroy() - */ -int embb_shared_mutex_init(embb_shared_mutex_t* shared_mutex); - -/** - * 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 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_ERROR otherwise - * - * \threadsafe - * - * \see embb_shared_mutex_try_lock(), embb_shared_mutex_unlock() - */ -int embb_shared_mutex_lock(embb_shared_mutex_t* shared_mutex); - -/** - * Tries to lock the shared mutex for writing and returns immediately. - * - * \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_ERROR if an error occurred - * - * \threadsafe - * - * \see embb_shared_mutex_lock(), embb_shared_mutex_unlock() - */ -int embb_shared_mutex_try_lock(embb_shared_mutex_t* shared_mutex); - -/** - * Unlocks the shared mutex locked for writing. - * - * \pre \c shared_mutex has been locked for writing by the current thread. - * \post If successful, \c shared_mutex is unlocked. - * - * \param shared_mutex Pointer to shared mutex - * \return EMBB_SUCCESS if the operation was successful \n - * EMBB_ERROR otherwise - * - * \threadsafe - * - * \see embb_shared_mutex_lock(), embb_shared_mutex_try_lock() - */ -int embb_shared_mutex_unlock(embb_shared_mutex_t* shared_mutex); - -/** - * Waits until the shared mutex can be locked for reading and locks it. - * - * \pre \c shared_mutex is initialized and not locked by the current thread. - * \post If successful, \c shared_mutex is locked for reading. - * - * \param shared_mutex Pointer to shared mutex - * \return EMBB_SUCCESS if shared_mutex could be locked for reading \n - * EMBB_ERROR otherwise - * - * \threadsafe - * - * \see embb_shared_mutex_try_lock_shared(), embb_shared_mutex_unlock_shared() - */ -int embb_shared_mutex_lock_shared(embb_shared_mutex_t* shared_mutex); - -/** - * Tries to lock the shared mutex for reading and returns immediately. - * - * \pre \c shared_mutex is initialized - * \post If successful, \c shared_mutex is locked for reading - * - * \param shared_mutex Pointer to shared mutex - * \return EMBB_SUCCESS if shared_mutex could be locked for reading \n - * EMBB_BUSY if shared_mutex could not be locked for reading \n - * EMBB_ERROR if an error occurred - * - * \threadsafe - * - * \see embb_shared_mutex_lock_shared(), embb_shared_mutex_unlock_shared() - */ -int embb_shared_mutex_try_lock_shared(embb_shared_mutex_t* shared_mutex); - -/** - * Unlocks the shared mutex locked for reading. - * - * \pre \c shared_mutex has been locked for reading by the current thread. - * \post If successful, \c shared_mutex is unlocked. - * - * \param shared_mutex Pointer to shared mutex - * \return EMBB_SUCCESS if the operation was successful \n - * EMBB_ERROR otherwise - * - * \threadsafe - * - * \see embb_shared_mutex_lock_shared(), embb_shared_mutex_try_lock_shared() - */ -int embb_shared_mutex_unlock_shared(embb_shared_mutex_t* shared_mutex); - -/** - * Destroys the shared mutex and frees its resources. - * - * \pre \c shared_mutex has been initialized - * \post \c shared_mutex is uninitialized - * - * \param shared_mutex Pointer to shared mutex - * - * \notthreadsafe - * - * \see embb_shared_mutex_init() - */ -void embb_shared_mutex_destroy(embb_shared_mutex_t* shared_mutex); - -#ifdef __cplusplus -} // Close extern "C" -#endif - -/** - * \} - */ - -#endif // EMBB_BASE_C_SHARED_MUTEX_H_ diff --git a/base_c/src/shared_mutex.c b/base_c/src/shared_mutex.c index 0709d26..c569b72 100644 --- a/base_c/src/shared_mutex.c +++ b/base_c/src/shared_mutex.c @@ -24,7 +24,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #include #include diff --git a/base_c/test/shared_mutex_test.cc b/base_c/test/shared_mutex_test.cc index 9ec1005..fd9986c 100644 --- a/base_c/test/shared_mutex_test.cc +++ b/base_c/test/shared_mutex_test.cc @@ -36,25 +36,32 @@ SharedMutexTest::SharedMutexTest() num_threads_(partest::TestSuite::GetDefaultNumThreads()), num_iterations_(partest::TestSuite::GetDefaultNumIterations()) { CreateUnit("Shared read") - .Pre(&SharedMutexTest::TestSharedRead_Pre, this) - .Add(&SharedMutexTest::TestSharedRead_ThreadMethod, this, + .Pre(&SharedMutexTest::TestSharedReadPre, this) + .Add(&SharedMutexTest::TestSharedReadThreadMethod, this, num_threads_, num_iterations_) - .Post(&SharedMutexTest::TestSharedRead_Post, this); - CreateUnit("Exclusive write") - .Pre(&SharedMutexTest::TestExclusiveWrite_Pre, this) - .Add(&SharedMutexTest::TestExclusiveWrite_ReaderMethod, this, + .Post(&SharedMutexTest::TestSharedReadPost, this); + CreateUnit("Multiple writer") + .Pre(&SharedMutexTest::TestMultipleWriterPre, this) + .Add(&SharedMutexTest::TestMultipleWriterReaderMethod, this, num_threads_ / 2, num_iterations_) - .Add(&SharedMutexTest::TestExclusiveWrite_WriterMethod, this, + .Add(&SharedMutexTest::TestMultipleWriterWriterMethod, this, num_threads_ / 2, num_iterations_) - .Post(&SharedMutexTest::TestExclusiveWrite_Post, this); + .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); } -void SharedMutexTest::TestSharedRead_Pre() { +void SharedMutexTest::TestSharedReadPre() { int success = embb_shared_mutex_init(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); } -void SharedMutexTest::TestSharedRead_ThreadMethod() { +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."); @@ -65,31 +72,28 @@ void SharedMutexTest::TestSharedRead_ThreadMethod() { PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); } -void SharedMutexTest::TestSharedRead_Post() { +void SharedMutexTest::TestSharedReadPost() { embb_shared_mutex_destroy(&shared_mutex_); } -void SharedMutexTest::TestExclusiveWrite_Pre() { +void SharedMutexTest::TestMultipleWriterPre() { int success = embb_shared_mutex_init(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize shared mutex."); counter_ = 0; } -void SharedMutexTest::TestExclusiveWrite_ReaderMethod() { +void SharedMutexTest::TestMultipleWriterReaderMethod() { // Just add some contention - int success = embb_shared_mutex_try_lock_shared(&shared_mutex_); + int success = embb_shared_mutex_lock_shared(&shared_mutex_); if (success != EMBB_SUCCESS) return; - int spin = 10000; - while (--spin != 0); - success = embb_shared_mutex_unlock_shared(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); } -void SharedMutexTest::TestExclusiveWrite_WriterMethod() { +void SharedMutexTest::TestMultipleWriterWriterMethod() { int success = embb_shared_mutex_lock(&shared_mutex_); PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); @@ -99,12 +103,44 @@ void SharedMutexTest::TestExclusiveWrite_WriterMethod() { PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); } -void SharedMutexTest::TestExclusiveWrite_Post() { +void SharedMutexTest::TestMultipleWriterPost() { 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_); +} + } // namespace test } // namespace base } // namespace embb diff --git a/base_c/test/shared_mutex_test.h b/base_c/test/shared_mutex_test.h index 67f4a31..43e29d7 100644 --- a/base_c/test/shared_mutex_test.h +++ b/base_c/test/shared_mutex_test.h @@ -29,7 +29,7 @@ #include #include -#include +#include namespace embb { @@ -41,14 +41,39 @@ class SharedMutexTest : public partest::TestCase { SharedMutexTest(); private: - void TestSharedRead_Pre(); - void TestSharedRead_ThreadMethod(); - void TestSharedRead_Post(); - - void TestExclusiveWrite_Pre(); - void TestExclusiveWrite_ReaderMethod(); - void TestExclusiveWrite_WriterMethod(); - void TestExclusiveWrite_Post(); + /** + * Only readers. + * + * The readers use try_lock_shared() and test if that always obtains the + * shared lock. + */ + void TestSharedReadPre(); + void TestSharedReadThreadMethod(); + void TestSharedReadPost(); + + /** + * Multiple readers and writers. + * + * The writers use lock() and test if that always obtains the lock (after + * 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(); + + /** + * 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_; -- libgit2 0.26.0