Commit fc660db0 by bernhard-gatzhammer

Worked in review comments.

EMBB-453
parent ae2f1813
...@@ -55,6 +55,5 @@ ...@@ -55,6 +55,5 @@
#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>
#endif /* EMBB_BASE_C_BASE_H_ */ #endif /* EMBB_BASE_C_BASE_H_ */
...@@ -147,6 +147,146 @@ void embb_mutex_destroy( ...@@ -147,6 +147,146 @@ void embb_mutex_destroy(
/**< [IN/OUT] Pointer to mutex */ /**< [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 #ifdef __cplusplus
} /* Close extern "C" { */ } /* Close extern "C" { */
#endif #endif
......
/*
* 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 <embb/base/c/internal/platform.h>
#include <embb/base/c/errors.h>
#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_
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <embb/base/c/shared_mutex.h> #include <embb/base/c/mutex.h>
#include <assert.h> #include <assert.h>
#include <embb/base/c/internal/unused.h> #include <embb/base/c/internal/unused.h>
......
...@@ -36,25 +36,32 @@ SharedMutexTest::SharedMutexTest() ...@@ -36,25 +36,32 @@ 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")
.Pre(&SharedMutexTest::TestSharedRead_Pre, this) .Pre(&SharedMutexTest::TestSharedReadPre, this)
.Add(&SharedMutexTest::TestSharedRead_ThreadMethod, this, .Add(&SharedMutexTest::TestSharedReadThreadMethod, this,
num_threads_, num_iterations_) num_threads_, num_iterations_)
.Post(&SharedMutexTest::TestSharedRead_Post, this); .Post(&SharedMutexTest::TestSharedReadPost, this);
CreateUnit("Exclusive write") CreateUnit("Multiple writer")
.Pre(&SharedMutexTest::TestExclusiveWrite_Pre, this) .Pre(&SharedMutexTest::TestMultipleWriterPre, this)
.Add(&SharedMutexTest::TestExclusiveWrite_ReaderMethod, this, .Add(&SharedMutexTest::TestMultipleWriterReaderMethod, this,
num_threads_ / 2, num_iterations_) num_threads_ / 2, num_iterations_)
.Add(&SharedMutexTest::TestExclusiveWrite_WriterMethod, this, .Add(&SharedMutexTest::TestMultipleWriterWriterMethod, this,
num_threads_ / 2, num_iterations_) 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_); 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.");
} }
void SharedMutexTest::TestSharedRead_ThreadMethod() { 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 lock for reading.");
...@@ -65,31 +72,28 @@ void SharedMutexTest::TestSharedRead_ThreadMethod() { ...@@ -65,31 +72,28 @@ void SharedMutexTest::TestSharedRead_ThreadMethod() {
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 SharedMutexTest::TestSharedReadPost() {
embb_shared_mutex_destroy(&shared_mutex_); embb_shared_mutex_destroy(&shared_mutex_);
} }
void SharedMutexTest::TestExclusiveWrite_Pre() { void SharedMutexTest::TestMultipleWriterPre() {
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::TestExclusiveWrite_ReaderMethod() { void SharedMutexTest::TestMultipleWriterReaderMethod() {
// Just add some contention // 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; if (success != EMBB_SUCCESS) return;
int spin = 10000;
while (--spin != 0);
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::TestExclusiveWrite_WriterMethod() { void SharedMutexTest::TestMultipleWriterWriterMethod() {
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.");
...@@ -99,12 +103,44 @@ void SharedMutexTest::TestExclusiveWrite_WriterMethod() { ...@@ -99,12 +103,44 @@ void SharedMutexTest::TestExclusiveWrite_WriterMethod() {
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 SharedMutexTest::TestMultipleWriterPost() {
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() {
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 test
} // namespace base } // namespace base
} // namespace embb } // namespace embb
...@@ -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/c/shared_mutex.h> #include <embb/base/c/mutex.h>
namespace embb { namespace embb {
...@@ -41,14 +41,39 @@ class SharedMutexTest : public partest::TestCase { ...@@ -41,14 +41,39 @@ class SharedMutexTest : public partest::TestCase {
SharedMutexTest(); SharedMutexTest();
private: private:
void TestSharedRead_Pre(); /**
void TestSharedRead_ThreadMethod(); * Only readers.
void TestSharedRead_Post(); *
* The readers use try_lock_shared() and test if that always obtains the
void TestExclusiveWrite_Pre(); * shared lock.
void TestExclusiveWrite_ReaderMethod(); */
void TestExclusiveWrite_WriterMethod(); void TestSharedReadPre();
void TestExclusiveWrite_Post(); 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_; embb_shared_mutex_t 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