diff --git a/base_c/include/embb/base/c/base.h b/base_c/include/embb/base/c/base.h index 0251a43..d16e355 100644 --- a/base_c/include/embb/base/c/base.h +++ b/base_c/include/embb/base/c/base.h @@ -55,5 +55,6 @@ #include #include #include +#include #endif /* EMBB_BASE_C_BASE_H_ */ diff --git a/base_c/include/embb/base/c/internal/platform.h b/base_c/include/embb/base/c/internal/platform.h index 7f025fc..358b435 100644 --- a/base_c/include/embb/base/c/internal/platform.h +++ b/base_c/include/embb/base/c/internal/platform.h @@ -66,6 +66,7 @@ typedef struct embb_thread_t { typedef DWORD embb_thread_id_t; typedef CRITICAL_SECTION embb_mutex_t; typedef CONDITION_VARIABLE embb_condition_t; +typedef SRWLOCK embb_rwlock_t; #define EMBB_DURATION_MIN_NANOSECONDS 1000 @@ -90,6 +91,7 @@ typedef struct embb_thread_t { typedef pthread_t embb_thread_id_t; typedef pthread_mutex_t embb_mutex_t; typedef pthread_cond_t embb_condition_t; +typedef pthread_rwlock_t embb_rwlock_t; #define EMBB_DURATION_MIN_NANOSECONDS 1 diff --git a/base_c/include/embb/base/c/rwlock.h b/base_c/include/embb/base/c/rwlock.h new file mode 100644 index 0000000..ef62d5b --- /dev/null +++ b/base_c/include/embb/base/c/rwlock.h @@ -0,0 +1,193 @@ +/* + * 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_RWLOCK_H_ +#define EMBB_BASE_C_RWLOCK_H_ + +/** + * \defgroup C_BASE_RWLOCK Readers-Writers Lock + * + * Shared-read/exclusive-write lock for thread synchronization + * + * Provides an abstraction from platform-specific readers-writers lock + * implementations. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing an rwlock. + */ +typedef opaque_type embb_rwlock_t; +#endif // DOXYGEN + +/** + * Initializes an rwlock + * + * \post \c rwlock is initialized + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if rwlock could be initialized \n + * EMBB_ERROR otherwise + * + * \memory (Potentially) allocates dynamic memory + * + * \notthreadsafe + * + * \see embb_rwlock_destroy() + */ +int embb_rwlock_init(embb_rwlock_t* rwlock); + +/** + * Waits until the rwlock can be locked for reading and locks it. + * + * \pre \c rwlock is initialized and not locked by the current thread. + * \post If successful, \c rwlock is locked for reading. + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if rwlock could be locked for reading \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_rwlock_try_lock_read(), embb_rwlock_unlock_read() + */ +int embb_rwlock_lock_read(embb_rwlock_t* rwlock); + +/** + * Waits until the rwlock can be locked for writing and locks it. + * + * \pre \c rwlock is initialized and not locked by the current thread. + * \post If successful, \c rwlock is locked for writing. + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if rwlock could be locked for writing \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_rwlock_try_lock_write(), embb_rwlock_unlock_write() + */ +int embb_rwlock_lock_write(embb_rwlock_t* rwlock); + +/** + * Tries to lock the rwlock for reading and returns immediately. + * + * \pre \c rwlock is initialized + * \post If successful, \c rwlock is locked for reading + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if rwlock could be locked for reading \n + * EMBB_BUSY if rwlock could not be locked for reading \n + * EMBB_ERROR if an error occurred + * + * \threadsafe + * + * \see embb_rwlock_lock_read(), embb_rwlock_unlock_read() + */ +int embb_rwlock_try_lock_read(embb_rwlock_t* rwlock); + +/** + * Tries to lock the rwlock for writing and returns immediately. + * + * \pre \c rwlock is initialized + * \post If successful, \c rwlock is locked for writing + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if rwlock could be locked for writing \n + * EMBB_BUSY if rwlock could not be locked for writing \n + * EMBB_ERROR if an error occurred + * + * \threadsafe + * + * \see embb_rwlock_lock_write(), embb_rwlock_unlock_write() + */ +int embb_rwlock_try_lock_write(embb_rwlock_t* rwlock); + +/** + * Unlocks an rwlock locked for reading. + * + * \pre \c rwlock has been locked for reading by the current thread. + * \post If successful, \c rwlock is unlocked. + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_rwlock_lock_read(), embb_rwlock_try_lock_read() + */ +int embb_rwlock_unlock_read(embb_rwlock_t* rwlock); + +/** + * Unlocks an rwlock locked for writing. + * + * \pre \c rwlock has been locked for writing by the current thread. + * \post If successful, \c rwlock is unlocked. + * + * \param rwlock Pointer to rwlock + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * + * \threadsafe + * + * \see embb_rwlock_lock_write(), embb_rwlock_try_lock_write() + */ +int embb_rwlock_unlock_write(embb_rwlock_t* rwlock); + +/** + * Destroys an rwlock and frees its resources. + * + * \pre \c rwlock has been initialized + * \post \c rwlock is uninitialized + * + * \param rwlock Pointer to rwlock + * + * \notthreadsafe + * + * \see embb_rwlock_init() + */ +void embb_rwlock_destroy(embb_rwlock_t* rwlock); + +#ifdef __cplusplus +} // Close extern "C" +#endif + +/** + * \} + */ + +#endif // EMBB_BASE_C_RWLOCK_H_ diff --git a/base_c/src/rwlock.c b/base_c/src/rwlock.c new file mode 100644 index 0000000..a9bbbf5 --- /dev/null +++ b/base_c/src/rwlock.c @@ -0,0 +1,148 @@ +/* + * 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 +#include + +#include + +#ifdef EMBB_PLATFORM_THREADING_WINTHREADS + +int embb_rwlock_init(embb_rwlock_t* rwlock) { + InitializeSRWLock(rwlock); + return EMBB_SUCCESS; +} + +int embb_rwlock_lock_read(embb_rwlock_t* rwlock) { + AcquireSRWLockShared(rwlock); + return EMBB_SUCCESS; +} + +int embb_rwlock_lock_write(embb_rwlock_t* rwlock) { + AcquireSRWLockExclusive(rwlock); + return EMBB_SUCCESS; +} + +int embb_rwlock_try_lock_read(embb_rwlock_t* rwlock) { + BOOLEAN success; + success = TryAcquireSRWLockShared(rwlock); + if (success == FALSE) return EMBB_ERROR; + return EMBB_SUCCESS; +} + +int embb_rwlock_try_lock_write(embb_rwlock_t* rwlock) { + BOOLEAN success; + success = TryAcquireSRWLockExclusive(rwlock); + if (success == FALSE) return EMBB_ERROR; + return EMBB_SUCCESS; +} + +int embb_rwlock_unlock_read(embb_rwlock_t* rwlock) { + ReleaseSRWLockShared(rwlock); + return EMBB_SUCCESS; +} + +int embb_rwlock_unlock_write(embb_rwlock_t* rwlock) { + ReleaseSRWLockExclusive(rwlock); + return EMBB_SUCCESS; +} + +void embb_rwlock_destroy(embb_rwlock_t* rwlock) { + // Quoting MSDN: "SRW locks do not need to be explicitly destroyed". + EMBB_UNUSED(rwlock); +} + +#endif /* EMBB_PLATFORM_THREADING_WINTHREADS */ + +#ifdef EMBB_PLATFORM_THREADING_POSIXTHREADS + +int embb_rwlock_init(embb_rwlock_t* rwlock) { + int result = pthread_rwlock_init(rwlock, NULL); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_rwlock_lock_read(embb_rwlock_t* rwlock) { + int result = pthread_rwlock_rdlock(rwlock); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_rwlock_lock_write(embb_rwlock_t* rwlock) { + int result = pthread_rwlock_wrlock(rwlock); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_rwlock_try_lock_read(embb_rwlock_t* rwlock) { + int result = pthread_rwlock_tryrdlock(rwlock); + if (result == 0) { + return EMBB_SUCCESS; + } + if (result == EBUSY) { + return EMBB_BUSY; + } + return EMBB_ERROR; +} + +int embb_rwlock_try_lock_write(embb_rwlock_t* rwlock) { + int result = pthread_rwlock_trywrlock(rwlock); + if (result == 0) { + return EMBB_SUCCESS; + } + if (result == EBUSY) { + return EMBB_BUSY; + } + return EMBB_ERROR; +} + +int embb_rwlock_unlock_read(embb_rwlock_t* rwlock) { + 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) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +void embb_rwlock_destroy(embb_rwlock_t* rwlock) { + pthread_rwlock_destroy(rwlock); +} + +#endif /* EMBB_PLATFORM_THREADING_POSIXTHREADS */ diff --git a/base_c/test/main.cc b/base_c/test/main.cc index 54ce031..faa2430 100644 --- a/base_c/test/main.cc +++ b/base_c/test/main.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ using embb::base::test::CoreSetTest; using embb::base::test::ConditionVarTest; using embb::base::test::ThreadTest; using embb::base::test::ThreadSpecificStorageTest; +using embb::base::test::RWLockTest; PT_MAIN("Base C") { embb_log_set_log_level(EMBB_LOG_LEVEL_WARNING); @@ -68,6 +70,7 @@ PT_MAIN("Base C") { PT_RUN(ConditionVarTest); PT_RUN(ThreadTest); PT_RUN(ThreadSpecificStorageTest); + PT_RUN(RWLockTest); PT_EXPECT(embb_get_bytes_allocated() == 0); } diff --git a/base_c/test/rwlock_test.cc b/base_c/test/rwlock_test.cc new file mode 100644 index 0000000..3950fde --- /dev/null +++ b/base_c/test/rwlock_test.cc @@ -0,0 +1,110 @@ +/* + * 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 +#include + +namespace embb { +namespace base { +namespace test { + +RWLockTest::RWLockTest() + : counter_(0), + num_threads_(partest::TestSuite::GetDefaultNumThreads()), + num_iterations_(partest::TestSuite::GetDefaultNumIterations()) { + CreateUnit("Shared read") + .Pre(&RWLockTest::TestSharedRead_Pre, this) + .Add(&RWLockTest::TestSharedRead_ThreadMethod, this, + num_threads_, num_iterations_) + .Post(&RWLockTest::TestSharedRead_Post, this); + 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_Pre() { + int success = embb_rwlock_init(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize rwlock."); +} + +void RWLockTest::TestSharedRead_ThreadMethod() { + int success = embb_rwlock_try_lock_read(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for reading."); + + int spin = 10000; + while (--spin != 0); + + success = embb_rwlock_unlock_read(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); +} + +void RWLockTest::TestSharedRead_Post() { + embb_rwlock_destroy(&rwlock_); +} + +void RWLockTest::TestExclusiveWrite_Pre() { + int success = embb_rwlock_init(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to initialize rwlock."); + + counter_ = 0; +} + +void RWLockTest::TestExclusiveWrite_ReaderMethod() { + // Just add some contention + + int success = embb_rwlock_try_lock_read(&rwlock_); + if (success != EMBB_SUCCESS) return; + + int spin = 10000; + while (--spin != 0); + + success = embb_rwlock_unlock_read(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (reading)."); +} + +void RWLockTest::TestExclusiveWrite_WriterMethod() { + int success = embb_rwlock_lock_write(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to lock for writing."); + + ++counter_; + + success = embb_rwlock_unlock_write(&rwlock_); + PT_ASSERT_EQ_MSG(success, EMBB_SUCCESS, "Failed to unlock (writing)."); +} + +void RWLockTest::TestExclusiveWrite_Post() { + PT_ASSERT_EQ_MSG(counter_, num_iterations_ * num_threads_, + "Counter value is inconsistent."); + embb_rwlock_destroy(&rwlock_); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/rwlock_test.h b/base_c/test/rwlock_test.h new file mode 100644 index 0000000..8d208cd --- /dev/null +++ b/base_c/test/rwlock_test.h @@ -0,0 +1,63 @@ +/* + * 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 BASE_C_TEST_RWLOCK_TEST_H_ +#define BASE_C_TEST_RWLOCK_TEST_H_ + +#include +#include +#include + + +namespace embb { +namespace base { +namespace test { + +class RWLockTest : public partest::TestCase { + public: + RWLockTest(); + + private: + void TestSharedRead_Pre(); + void TestSharedRead_ThreadMethod(); + void TestSharedRead_Post(); + + void TestExclusiveWrite_Pre(); + void TestExclusiveWrite_ReaderMethod(); + void TestExclusiveWrite_WriterMethod(); + void TestExclusiveWrite_Post(); + + embb_rwlock_t rwlock_; + size_t counter_; + size_t num_threads_; + size_t num_iterations_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_RWLOCK_TEST_H_ diff --git a/base_cpp/include/embb/base/base.h b/base_cpp/include/embb/base/base.h index bf7e903..26d0730 100644 --- a/base_cpp/include/embb/base/base.h +++ b/base_cpp/include/embb/base/base.h @@ -59,5 +59,6 @@ #include #include #include +#include #endif // EMBB_BASE_BASE_H_ diff --git a/base_cpp/include/embb/base/internal/platform.h b/base_cpp/include/embb/base/internal/platform.h index 6258563..04a2b34 100644 --- a/base_cpp/include/embb/base/internal/platform.h +++ b/base_cpp/include/embb/base/internal/platform.h @@ -44,6 +44,7 @@ typedef embb_thread_t ThreadType; typedef DWORD IDType; typedef embb_mutex_t MutexType; typedef embb_condition_t ConditionVariableType; +typedef embb_rwlock_t RWLockType; } // namespace internal } // namespace base @@ -59,6 +60,7 @@ typedef embb_thread_t ThreadType; typedef embb_thread_id_t IDType; typedef embb_mutex_t MutexType; typedef embb_condition_t ConditionVariableType; +typedef embb_rwlock_t RWLockType; } // namespace internal } // namespace base diff --git a/base_cpp/include/embb/base/rwlock.h b/base_cpp/include/embb/base/rwlock.h new file mode 100644 index 0000000..64ecd2a --- /dev/null +++ b/base_cpp/include/embb/base/rwlock.h @@ -0,0 +1,210 @@ +/* + * 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_RWLOCK_H_ +#define EMBB_BASE_RWLOCK_H_ + +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_RWLOCK Readers-Writers Lock + * + * Shared-read/exclusive-write lock for thread synchronization. + * + * \ingroup CPP_BASE + */ + +/** + * Shared-read/exclusive-write (readers-writers) lock. + * + * Allows simultaneous shared access for concurrent readers, but only exclusive + * access for writers. Cannot be locked recursively. Fairness and/or FIFO order + * guaranties are platform specific and should be assumed non-existent. + * + * \ingroup CPP_BASE_RWLOCK + */ +class RWLock { + public: + /** + * Creates an rwlock which is in unlocked state. + * + * \memory Potentially allocates dynamic memory + * \notthreadsafe + */ + RWLock(); + + /** + * Destroys internal representation. + */ + ~RWLock(); + + /** + * Waits until the rwlock can be locked for reading and locks it. + * + * \pre The \c rwlock is not locked by the current thread. + * \post The \c rwlock is locked for reading. + * + * \threadsafe + * + * \see TryLockRead(), UnlockRead() + */ + void LockRead(); + + /** + * Waits until the rwlock can be locked for writing and locks it. + * + * \pre \c rwlock is initialized and not locked by the current thread. + * \post \c rwlock is locked for writing. + * + * \threadsafe + * + * \see TryLockWrite(), UnlockWrite() + */ + void LockWrite(); + + /** + * Tries to lock the rwlock for reading and returns immediately. + * + * \pre \c rwlock is not locked by the current thread. + * \post If successful, \c rwlock is locked for reading. + * + * \return \c true if rwlock was locked for reading \n + * \c false otherwise + * + * \threadsafe + * + * \see LockRead(), UnlockRead() + */ + bool TryLockRead(); + + /** + * Tries to lock the rwlock for writing and returns immediately. + * + * \pre \c rwlock is not locked by the current thread. + * \post If successful, \c rwlock is locked for writing. + * + * \return \c true if rwlock was locked for reading \n + * \c false otherwise + * + * \threadsafe + * + * \see LockWrite(), UnlockWrite() + */ + bool TryLockWrite(); + + /** + * Unlocks an rwlock locked for reading. + * + * \pre \c rwlock has been locked for reading by the current thread. + * \post \c rwlock is unlocked. + * + * \threadsafe + * + * \see LockRead(), TryLockRead() + */ + void UnlockRead(); + + /** + * Unlocks an rwlock locked for writing. + * + * \pre \c rwlock has been locked for writing by the current thread. + * \post \c rwlock is unlocked. + * + * \threadsafe + * + * \see LockWrite(), TryLockWrite() + */ + void UnlockWrite(); + + + /** + * Adaptor class for the readers lock that can be used with "LockGuard" and/or + * "UniqueLock" ownership wrappers. + * + * \see LockGuard, UniqueLock + */ + class ReaderLock { + public: + ReaderLock(RWLock& rwlock); + void Lock(); + bool TryLock(); + void Unlock(); + private: + ReaderLock(const ReaderLock&); + ReaderLock& operator=(const ReaderLock&); + RWLock& rwlock_; + }; + + /** + * Adaptor class for the writers lock that can be used with "LockGuard" and/or + * "UniqueLock" ownership wrappers. + * + * \see LockGuard, UniqueLock + */ + class WriterLock { + public: + WriterLock(RWLock& rwlock); + void Lock(); + bool TryLock(); + void Unlock(); + private: + WriterLock(const WriterLock&); + WriterLock& operator=(const WriterLock&); + RWLock& rwlock_; + }; + + /** + * Returns an adaptor for the readers lock to be used with "LockGuard" and/or + * "UniqueLock" ownership wrappers. + */ + ReaderLock& GetReaderLock(); + + /** + * Returns an adaptor for the writes lock to be used with "LockGuard" and/or + * "UniqueLock" ownership wrappers. + */ + WriterLock& GetWriterLock(); + + private: + /** + * Disables copy construction and assignment. + */ + RWLock(const RWLock&); + RWLock& operator=(const RWLock&); + + internal::RWLockType rwlock_; /**< Actual rwlock implementation from base_c */ + ReaderLock reader_lock_; /**< Lock adaptor for readers */ + WriterLock writer_lock_; /**< Lock adaptor for writers */ +}; + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_RWLOCK_H_ diff --git a/base_cpp/src/rwlock.cc b/base_cpp/src/rwlock.cc new file mode 100644 index 0000000..24dd2c6 --- /dev/null +++ b/base_cpp/src/rwlock.cc @@ -0,0 +1,101 @@ +/* + * 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 +#include + +namespace embb { +namespace base { + +RWLock::RWLock() + : rwlock_(), +// 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_); +} + +RWLock::~RWLock() { + embb_rwlock_destroy(&rwlock_); +} + +void RWLock::LockRead() { + embb_rwlock_lock_read(&rwlock_); +} + +void RWLock::LockWrite() { + embb_rwlock_lock_write(&rwlock_); +} + +bool RWLock::TryLockRead() { + int result = embb_rwlock_try_lock_read(&rwlock_); + return result == EMBB_SUCCESS; +} + +bool RWLock::TryLockWrite() { + int result = embb_rwlock_try_lock_write(&rwlock_); + return result == EMBB_SUCCESS; +} + +void RWLock::UnlockRead() { + embb_rwlock_unlock_read(&rwlock_); +} + +void RWLock::UnlockWrite() { + embb_rwlock_unlock_write(&rwlock_); +} + +RWLock::ReaderLock::ReaderLock(RWLock& rwlock) : rwlock_(rwlock) {} +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 embb + + + + diff --git a/base_cpp/test/main.cc b/base_cpp/test/main.cc index 5d7626d..f25e976 100644 --- a/base_cpp/test/main.cc +++ b/base_cpp/test/main.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -45,6 +46,7 @@ using embb::base::test::ThreadSpecificStorageTest; using embb::base::test::AtomicTest; using embb::base::test::MemoryAllocationTest; using embb::base::test::ThreadTest; +using embb::base::test::RWLockTest; PT_MAIN("Base C++") { unsigned int max_threads = @@ -58,6 +60,7 @@ PT_MAIN("Base C++") { PT_RUN(AtomicTest); PT_RUN(MemoryAllocationTest); PT_RUN(ThreadTest); + PT_RUN(RWLockTest); PT_EXPECT(embb_get_bytes_allocated() == 0); } diff --git a/base_cpp/test/rwlock_test.cc b/base_cpp/test/rwlock_test.cc new file mode 100644 index 0000000..088691b --- /dev/null +++ b/base_cpp/test/rwlock_test.cc @@ -0,0 +1,86 @@ +/* + * 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 +#include +#include + +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 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 lock(rwlock_.GetReaderLock(), + embb::base::try_lock); + + int spin = 10000; + while (--spin != 0); +} + +void RWLockTest::TestExclusiveWrite_WriterMethod() { + UniqueLock 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 diff --git a/base_cpp/test/rwlock_test.h b/base_cpp/test/rwlock_test.h new file mode 100644 index 0000000..47b10e4 --- /dev/null +++ b/base_cpp/test/rwlock_test.h @@ -0,0 +1,63 @@ +/* + * 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 BASE_CPP_TEST_RWLOCK_TEST_H_ +#define BASE_CPP_TEST_RWLOCK_TEST_H_ + +#include +#include +#include + + +namespace embb { +namespace base { +namespace test { + +class RWLockTest : public partest::TestCase { + public: + RWLockTest(); + + private: + void TestSharedRead_Pre(); + void TestSharedRead_ThreadMethod(); + void TestSharedRead_Post(); + + void TestExclusiveWrite_Pre(); + void TestExclusiveWrite_ReaderMethod(); + void TestExclusiveWrite_WriterMethod(); + void TestExclusiveWrite_Post(); + + RWLock rwlock_; + size_t counter_; + size_t num_threads_; + size_t num_iterations_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_RWLOCK_TEST_H_