Commit 3fdf419d by bernhard-gatzhammer

Revert "Merge remote-tracking branch 'origin/development' into embb453_rwlock"

This reverts commit 554c76f7, reversing
changes made to fc660db0.
parent 554c76f7
Embedded Multicore Building Blocks (EMB²) Embedded Multicore Building Blocks (EMB²)
========================================= =========================================
Version 0.3.1
-------------
### Features:
- None
### Changes and improvements:
- Removed one function argument from algorithms::Invoke
- Added "explicit" specifier to base type constructor of Atomic<BaseType*>
- Added "const" qualifier to dereference operator and member access operator of AtomicPointer<>
- Changed AtomicBase<>::CompareAndSwap to atomically return expected value
- Replaced constant in dataflow_cpp_test_simple.cc with corresponding macro
- Added initialization of atomic variable in hazard_pointer_test.cc to avoid warning with GCC 5.1
- Changed initial value of allocated_object_from_different_thread
- Added tests for ID Pool and check for memory leaks
- Updated unit test for the UniqueLock::Swap
### Bug fixes:
- Fixed implementation of ID pool (provided fewer elements than specified by capacity)
- Fixed unsigned overflow bug in timed wait function of condition variables
- Fixed implementation of UniqueLock::Swap
### Build system:
- Improved CMake output for automatic initialization option
- Fixed cpplint and unsigned/signed warnings
### Documentation:
- Fixed documentation of UniqueLock class
- Updated README file
Version 0.3.0 Version 0.3.0
------------- -------------
......
...@@ -28,7 +28,7 @@ cmake_minimum_required (VERSION 2.8.9) ...@@ -28,7 +28,7 @@ cmake_minimum_required (VERSION 2.8.9)
# Version number # Version number
set (EMBB_BASE_VERSION_MAJOR 0) set (EMBB_BASE_VERSION_MAJOR 0)
set (EMBB_BASE_VERSION_MINOR 3) set (EMBB_BASE_VERSION_MINOR 3)
set (EMBB_BASE_VERSION_PATCH 1) set (EMBB_BASE_VERSION_PATCH 0)
# Fix compilation for CMake versions >= 3.1 # Fix compilation for CMake versions >= 3.1
# #
...@@ -59,9 +59,7 @@ IF(NOT OpenCL_FOUND) ...@@ -59,9 +59,7 @@ IF(NOT OpenCL_FOUND)
MESSAGE( STATUS "OpenCL is not there, will build without MTAPI OpenCL Plugin." ) MESSAGE( STATUS "OpenCL is not there, will build without MTAPI OpenCL Plugin." )
ENDIF() ENDIF()
# give the user the possibility, to append compiler flags
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CMAKE_CXX_FLAGS}")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CMAKE_C_FLAGS}")
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING set(CMAKE_BUILD_TYPE "Release" CACHE STRING
...@@ -102,13 +100,6 @@ else() ...@@ -102,13 +100,6 @@ else()
endif() endif()
message(" (set with command line option -DWARNINGS_ARE_ERRORS=ON/OFF)") message(" (set with command line option -DWARNINGS_ARE_ERRORS=ON/OFF)")
if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON)
message("-- MTAPI/Tasks automatic initialization enabled (default)")
else()
message("-- MTAPI/Tasks automatic initialization disabled")
endif()
message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)")
include(CMakeCommon/SetCompilerFlags.cmake) include(CMakeCommon/SetCompilerFlags.cmake)
SetGNUCompilerFlags(compiler_libs compiler_flags) SetGNUCompilerFlags(compiler_libs compiler_flags)
SetVisualStudioCompilerFlags(compiler_libs compiler_flags) SetVisualStudioCompilerFlags(compiler_libs compiler_flags)
......
...@@ -270,8 +270,8 @@ If you want to use the C++ functionalities of EMB², you have to link the ...@@ -270,8 +270,8 @@ If you want to use the C++ functionalities of EMB², you have to link the
following libraries (names will be different on Windows and on Linux) in the following libraries (names will be different on Windows and on Linux) in the
given order: given order:
embb_dataflow_cpp, embb_algorithms_cpp, embb_containers_cpp, embb_base, embb_base_cpp, embb_mtapi_c, embb_mtapi_cpp, embb_containers_cpp,
embb_mtapi_cpp, embb_mtapi_c, embb_base_cpp, embb_base_c embb_algorithms_cpp, embb_dataflow_cpp
The C++ header files can be included as follows: The C++ header files can be included as follows:
...@@ -284,7 +284,7 @@ The C++ header files can be included as follows: ...@@ -284,7 +284,7 @@ The C++ header files can be included as follows:
The following libraries have to be linked in the given order: The following libraries have to be linked in the given order:
embb_mtapi_c, embb_base_c embb_base_c, mtapi_c
The C header files can be included as follows: The C header files can be included as follows:
...@@ -323,8 +323,6 @@ Known Bugs and Limitations ...@@ -323,8 +323,6 @@ Known Bugs and Limitations
is bounded by a predefined but modifiable constant (see functions is bounded by a predefined but modifiable constant (see functions
embb_thread_get_max_count() / embb_thread_set_max_count() and class embb_thread_get_max_count() / embb_thread_set_max_count() and class
embb::base::Thread). embb::base::Thread).
- While MTAPI fully supports heterogeneous systems, the algorithms and
dataflow components are currently limited to homogeneous systems.
Development and Contribution Development and Contribution
......
...@@ -49,37 +49,33 @@ typedef embb::base::Function<void> InvokeFunctionType; ...@@ -49,37 +49,33 @@ typedef embb::base::Function<void> InvokeFunctionType;
#ifdef DOXYGEN #ifdef DOXYGEN
/** /**
* Spawns two to ten function objects at once and runs them in parallel. * Spawns one to ten function objects at once and runs them in parallel.
* *
* Blocks until all of them are done. * Blocks until all of them are done.
* *
* \ingroup CPP_ALGORITHMS_INVOKE * \ingroup CPP_ALGORITHMS_INVOKE
*/ */
template<typename Function1, typename Function2, ...> template<typename Function1, ...>
void Invoke( void Invoke(
Function1 func1, Function1 func1,
/**< [in] First function object to invoke */ /**< [in] First function object to invoke */
Function2 func2,
/**< [in] Second function object to invoke */
...); ...);
/** /**
* Spawns two to ten function objects at once and runs them in parallel using the * Spawns one to ten function objects at once and runs them in parallel using the
* given embb::mtapi::ExecutionPolicy. * given embb::mtapi::ExecutionPolicy.
* *
* Blocks until all of them are done. * Blocks until all of them are done.
* *
* \ingroup CPP_ALGORITHMS_INVOKE * \ingroup CPP_ALGORITHMS_INVOKE
*/ */
template<typename Function1, typename Function2, ...> template<typename Function1, ...>
void Invoke( void Invoke(
Function1 func1, Function1 func1,
/**< [in] Function object to invoke */ /**< [in] Function object to invoke */
Function2 func2,
/**< [in] Second function object to invoke */
..., ...,
const embb::tasks::ExecutionPolicy & policy const embb::mtapi::ExecutionPolicy & policy
/**< [in] embb::tasks::ExecutionPolicy to use */ /**< [in] embb::mtapi::ExecutionPolicy to use */
); );
#else // DOXYGEN #else // DOXYGEN
...@@ -122,6 +118,13 @@ class TaskWrapper { ...@@ -122,6 +118,13 @@ class TaskWrapper {
}; };
} // namespace internal } // namespace internal
template<typename Function1>
void Invoke(
Function1 func1,
const embb::tasks::ExecutionPolicy& policy) {
internal::TaskWrapper<Function1> wrap1(func1, policy);
}
template<typename Function1, typename Function2> template<typename Function1, typename Function2>
void Invoke( void Invoke(
Function1 func1, Function1 func1,
...@@ -287,6 +290,12 @@ template<typename Function1, typename Function2, typename Function3, ...@@ -287,6 +290,12 @@ template<typename Function1, typename Function2, typename Function3,
internal::TaskWrapper<Function10> wrap10(func10, policy); internal::TaskWrapper<Function10> wrap10(func10, policy);
} }
template<typename Function1>
void Invoke(
Function1 func1) {
Invoke(func1, embb::tasks::ExecutionPolicy());
}
template<typename Function1, typename Function2> template<typename Function1, typename Function2>
void Invoke( void Invoke(
Function1 func1, Function1 func1,
......
...@@ -44,6 +44,7 @@ static void Invocable10() {} ...@@ -44,6 +44,7 @@ static void Invocable10() {}
void InvokeTest::Test() { void InvokeTest::Test() {
using embb::algorithms::Invoke; using embb::algorithms::Invoke;
Invoke(&Invocable1);
Invoke(&Invocable1, &Invocable2); Invoke(&Invocable1, &Invocable2);
Invoke(&Invocable1, &Invocable2, &Invocable3); Invoke(&Invocable1, &Invocable2, &Invocable3);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4); Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4);
...@@ -60,24 +61,4 @@ void InvokeTest::Test() { ...@@ -60,24 +61,4 @@ void InvokeTest::Test() {
&Invocable6, &Invocable7, &Invocable8, &Invocable9); &Invocable6, &Invocable7, &Invocable8, &Invocable9);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10); &Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10);
embb::tasks::ExecutionPolicy policy;
Invoke(&Invocable1, &Invocable2, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10,
policy);
} }
...@@ -83,8 +83,8 @@ int embb_condition_wait_until(embb_condition_t* condition_var, ...@@ -83,8 +83,8 @@ int embb_condition_wait_until(embb_condition_t* condition_var,
embb_time_t now; embb_time_t now;
embb_time_now(&now); embb_time_now(&now);
/* Check if absolute timepoint (in milliseconds) still is in the future */ /* Check if absolute timepoint (in milliseconds) still is in the future */
if ((time->seconds * 1000 + time->nanoseconds / 1000000) if (time->seconds * 1000 + time->nanoseconds / 1000000
> (now.seconds * 1000 + now.nanoseconds / 1000000)) { - now.seconds * 1000 - now.nanoseconds / 1000000 > 0) {
/* Convert to (unsigned type) milliseconds and round up */ /* Convert to (unsigned type) milliseconds and round up */
DWORD time_diff = (DWORD) ( DWORD time_diff = (DWORD) (
time->seconds * 1000 + time->nanoseconds / 1000000 time->seconds * 1000 + time->nanoseconds / 1000000
......
...@@ -128,20 +128,6 @@ void embb_internal_thread_index_set_max(unsigned int max) { ...@@ -128,20 +128,6 @@ void embb_internal_thread_index_set_max(unsigned int max) {
*embb_max_number_thread_indices() = max; *embb_max_number_thread_indices() = max;
} }
/**
* \pre the calling thread is the only active thread
*
* \post the thread indices count and calling thread index is reset
*/
void embb_internal_thread_index_reset() { void embb_internal_thread_index_reset() {
/** This function is only called in tests, usually when all other threads
* except the main thread have terminated. However, the main thread still has
* potentially stored its old index value in its thread local storage,
* which might be assigned additionally to another thread (as the counter is
* reset), which may lead to hard to detect bugs. Therefore, reset the thread
* local thread id here.
*/
embb_internal_thread_index_var = UINT_MAX;
embb_counter_init(embb_thread_index_counter()); embb_counter_init(embb_thread_index_counter());
} }
\ No newline at end of file
...@@ -38,7 +38,7 @@ ConditionVarTest::ConditionVarTest() ...@@ -38,7 +38,7 @@ ConditionVarTest::ConditionVarTest()
embb_condition_init(&cond_wait_); embb_condition_init(&cond_wait_);
embb_mutex_init(&mutex_cond_wait_, EMBB_MUTEX_PLAIN); embb_mutex_init(&mutex_cond_wait_, EMBB_MUTEX_PLAIN);
CreateUnit("Timed wait timeouts") CreateUnit("Timed wait timouts")
.Add(&ConditionVarTest::TestTimedWaitTimeouts, this); .Add(&ConditionVarTest::TestTimedWaitTimeouts, this);
if (num_threads_ >= 2) { if (num_threads_ >= 2) {
CreateUnit("Condition Notify Test") CreateUnit("Condition Notify Test")
...@@ -64,10 +64,10 @@ void ConditionVarTest::TestNotify() { ...@@ -64,10 +64,10 @@ void ConditionVarTest::TestNotify() {
while (embb_counter_get(&counter_) while (embb_counter_get(&counter_)
< static_cast<unsigned int>(num_threads_-1)) < static_cast<unsigned int>(num_threads_-1))
{} // All threads entered critical section {} // all threads entered critical section
embb_mutex_lock(&mutex_cond_notify_); embb_mutex_lock(&mutex_cond_notify_);
embb_mutex_unlock(&mutex_cond_notify_); embb_mutex_unlock(&mutex_cond_notify_);
// All threads called wait on the condition (even last thread) // All threads called wait on the condition (Even last thread)
embb_counter_init(&counter_); embb_counter_init(&counter_);
...@@ -75,7 +75,7 @@ void ConditionVarTest::TestNotify() { ...@@ -75,7 +75,7 @@ void ConditionVarTest::TestNotify() {
embb_mutex_lock(&mutex_cond_wait_); embb_mutex_lock(&mutex_cond_wait_);
embb_condition_wait_for(&cond_wait_, &mutex_cond_wait_, &duration); embb_condition_wait_for(&cond_wait_, &mutex_cond_wait_, &duration);
while (embb_counter_get(&counter_) == 0) while (embb_counter_get(&counter_) == 0)
{} // If test hangs here, signalling has not succeeded {} //if hangs here signal has not succeded
PT_ASSERT_EQ_MSG(embb_counter_get(&counter_), static_cast<unsigned int>(1), PT_ASSERT_EQ_MSG(embb_counter_get(&counter_), static_cast<unsigned int>(1),
"Only one thread notified"); "Only one thread notified");
...@@ -85,7 +85,7 @@ void ConditionVarTest::TestNotify() { ...@@ -85,7 +85,7 @@ void ConditionVarTest::TestNotify() {
while (embb_counter_get(&counter_) != while (embb_counter_get(&counter_) !=
static_cast<unsigned int>(num_threads_-1)) static_cast<unsigned int>(num_threads_-1))
{} // If test hangs here, not all threads were notified {} // If this hangs then not all threads were notified.
embb_mutex_unlock(&mutex_cond_wait_); embb_mutex_unlock(&mutex_cond_wait_);
embb_mutex_destroy(&mutex_cond_wait_); embb_mutex_destroy(&mutex_cond_wait_);
...@@ -105,13 +105,13 @@ void ConditionVarTest::TestTimedWaitTimeouts() { ...@@ -105,13 +105,13 @@ void ConditionVarTest::TestTimedWaitTimeouts() {
embb_time_t time; embb_time_t time;
embb_duration_t duration = EMBB_DURATION_INIT; embb_duration_t duration = EMBB_DURATION_INIT;
// Wait for "now" tests already passed time point // Wait for now tests already passed time point
embb_time_now(&time); embb_time_now(&time);
embb_mutex_lock(&mutex); embb_mutex_lock(&mutex);
int status = embb_condition_wait_until(&cond, &mutex, &time); int status = embb_condition_wait_until(&cond, &mutex, &time);
PT_EXPECT_EQ(status, EMBB_TIMEDOUT); PT_EXPECT_EQ(status, EMBB_TIMEDOUT);
// Wait for a future time point // Wait for a future timepoint
status = embb_duration_set_milliseconds(&duration, 1); status = embb_duration_set_milliseconds(&duration, 1);
PT_EXPECT_EQ(status, EMBB_SUCCESS); PT_EXPECT_EQ(status, EMBB_SUCCESS);
status = embb_time_in(&time, &duration); // Time now status = embb_time_in(&time, &duration); // Time now
......
...@@ -36,9 +36,6 @@ namespace test { ...@@ -36,9 +36,6 @@ namespace test {
TimeTest::TimeTest() { TimeTest::TimeTest() {
CreateUnit("Time in duration").Add(&TimeTest::TestTimeInDuration, this); CreateUnit("Time in duration").Add(&TimeTest::TestTimeInDuration, this);
CreateUnit("Monotonicity").Add(
&TimeTest::TestMonotonicity, this,
1, partest::TestSuite::GetDefaultNumIterations() * 10);
} }
void TimeTest::TestTimeInDuration() { void TimeTest::TestTimeInDuration() {
...@@ -51,20 +48,6 @@ void TimeTest::TestTimeInDuration() { ...@@ -51,20 +48,6 @@ void TimeTest::TestTimeInDuration() {
PT_EXPECT_EQ(status, EMBB_SUCCESS); PT_EXPECT_EQ(status, EMBB_SUCCESS);
} }
void TimeTest::TestMonotonicity() {
embb_time_t first;
embb_time_t second;
int status1 = embb_time_in(&first, embb_duration_zero());
int status2 = embb_time_in(&second, embb_duration_zero());
PT_EXPECT_EQ(status1, EMBB_SUCCESS);
PT_EXPECT_EQ(status2, EMBB_SUCCESS);
unsigned long long first_abs = first.seconds * 1000 +
first.nanoseconds / 1000000;
unsigned long long second_abs = second.seconds * 1000 +
second.nanoseconds / 1000000;
PT_EXPECT_GE(second_abs, first_abs);
}
} // namespace test } // namespace test
} // namespace base } // namespace base
} // namespace embb } // namespace embb
...@@ -42,14 +42,9 @@ class TimeTest : public partest::TestCase { ...@@ -42,14 +42,9 @@ class TimeTest : public partest::TestCase {
private: private:
/** /**
* Tests time-in-duration method. * Tests time in duration method.
*/ */
void TestTimeInDuration(); void TestTimeInDuration();
/**
* Tests that succeedingly taken times are monotonously increasing.
*/
void TestMonotonicity();
}; };
} // namespace test } // namespace test
......
...@@ -478,7 +478,7 @@ class Atomic<BaseType*> : public embb::base::internal::atomic:: ...@@ -478,7 +478,7 @@ class Atomic<BaseType*> : public embb::base::internal::atomic::
public: public:
Atomic() : embb::base::internal::atomic:: Atomic() : embb::base::internal::atomic::
AtomicPointer<BaseType, ptrdiff_t, sizeof(BaseType*)>() {} AtomicPointer<BaseType, ptrdiff_t, sizeof(BaseType*)>() {}
explicit Atomic(BaseType* p) : embb::base::internal::atomic:: Atomic(BaseType* p) : embb::base::internal::atomic::
AtomicPointer<BaseType, ptrdiff_t, sizeof(BaseType*)>(p) {} AtomicPointer<BaseType, ptrdiff_t, sizeof(BaseType*)>(p) {}
BaseType* operator=(BaseType* p) { BaseType* operator=(BaseType* p) {
......
...@@ -177,7 +177,8 @@ CompareAndSwap(BaseType& expected, BaseType desired) { ...@@ -177,7 +177,8 @@ CompareAndSwap(BaseType& expected, BaseType desired) {
compare_and_swap(&AtomicValue, &native_expected, native_desired)) !=0 compare_and_swap(&AtomicValue, &native_expected, native_desired)) !=0
? true : false; ? true : false;
memcpy(&expected, &native_expected, sizeof(expected)); if (!return_val)
expected = Load();
return return_val; return return_val;
} }
......
...@@ -65,8 +65,8 @@ class AtomicPointer : public AtomicArithmetic<BaseType*, DifferenceType, S> { ...@@ -65,8 +65,8 @@ class AtomicPointer : public AtomicArithmetic<BaseType*, DifferenceType, S> {
bool IsPointer() const; bool IsPointer() const;
// The methods below are documented in atomic.h // The methods below are documented in atomic.h
BaseType* operator->() const; BaseType* operator->();
BaseType& operator*() const; BaseType& operator*();
}; };
template<typename BaseType, typename DifferenceType, size_t S> template<typename BaseType, typename DifferenceType, size_t S>
...@@ -93,13 +93,13 @@ inline bool AtomicPointer<BaseType, DifferenceType, S>:: ...@@ -93,13 +93,13 @@ inline bool AtomicPointer<BaseType, DifferenceType, S>::
template<typename BaseType, typename DifferenceType, size_t S> template<typename BaseType, typename DifferenceType, size_t S>
inline BaseType* AtomicPointer<BaseType, DifferenceType, S>:: inline BaseType* AtomicPointer<BaseType, DifferenceType, S>::
operator->() const { operator->() {
return this->Load(); return this->Load();
} }
template<typename BaseType, typename DifferenceType, size_t S> template<typename BaseType, typename DifferenceType, size_t S>
inline BaseType& AtomicPointer<BaseType, DifferenceType, S>:: inline BaseType& AtomicPointer<BaseType, DifferenceType, S>::
operator*() const { operator*() {
return *(this->Load()); return *(this->Load());
} }
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#define EMBB_BASE_INTERNAL_MUTEX_INL_H_ #define EMBB_BASE_INTERNAL_MUTEX_INL_H_
#include <cassert> #include <cassert>
#include <algorithm>
namespace embb { namespace embb {
namespace base { namespace base {
...@@ -96,8 +95,8 @@ void UniqueLock<Mutex>::Unlock() { ...@@ -96,8 +95,8 @@ void UniqueLock<Mutex>::Unlock() {
template<typename Mutex> template<typename Mutex>
void UniqueLock<Mutex>::Swap(UniqueLock<Mutex>& other) { void UniqueLock<Mutex>::Swap(UniqueLock<Mutex>& other) {
std::swap(mutex_, other.mutex_); locked_ = other.locked_;
std::swap(locked_, other.locked_); mutex_ = other.Release();
} }
template<typename Mutex> template<typename Mutex>
......
...@@ -439,11 +439,11 @@ class UniqueLock { ...@@ -439,11 +439,11 @@ class UniqueLock {
void Unlock(); void Unlock();
/** /**
* Exchanges ownership of the wrapped mutex with another lock. * Transfers ownership of a mutex to this lock.
*/ */
void Swap( void Swap(
UniqueLock<Mutex>& other UniqueLock<Mutex>& other
/**< [IN/OUT] The lock to exchange ownership with */ /**< [IN/OUT] Lock from which ownership shall be transferred */
); );
/** /**
......
...@@ -191,21 +191,13 @@ void MutexTest::TestUniqueLock() { ...@@ -191,21 +191,13 @@ void MutexTest::TestUniqueLock() {
} }
{ // Test lock swapping { // Test lock swapping
UniqueLock<> lock1(mutex_); UniqueLock<> lock1;
UniqueLock<> lock2(mutex_);
PT_EXPECT_EQ(lock1.OwnsLock(), false);
PT_EXPECT_EQ(lock2.OwnsLock(), true);
lock1.Swap(lock2);
PT_EXPECT_EQ(lock1.OwnsLock(), true); PT_EXPECT_EQ(lock1.OwnsLock(), true);
PT_EXPECT_EQ(lock2.OwnsLock(), false);
{
UniqueLock<> lock2;
PT_EXPECT_EQ(lock2.OwnsLock(), false);
lock1.Swap(lock2);
PT_EXPECT_EQ(lock1.OwnsLock(), false);
PT_EXPECT_EQ(lock2.OwnsLock(), true);
}
// At this point, "lock2" was destroyed and "mutex_" must be unlocked.
UniqueLock<> lock3(mutex_, embb::base::try_lock);
PT_EXPECT_EQ(lock3.OwnsLock(), true);
} }
} }
......
...@@ -77,12 +77,7 @@ LockFreeMPMCQueue<Type, ValuePool>::~LockFreeMPMCQueue() { ...@@ -77,12 +77,7 @@ LockFreeMPMCQueue<Type, ValuePool>::~LockFreeMPMCQueue() {
template< typename Type, typename ValuePool > template< typename Type, typename ValuePool >
LockFreeMPMCQueue<Type, ValuePool>::LockFreeMPMCQueue(size_t capacity) : LockFreeMPMCQueue<Type, ValuePool>::LockFreeMPMCQueue(size_t capacity) :
capacity(capacity), capacity(capacity),
// Object pool, size with respect to the maximum number of retired nodes not
// eligible for reuse. +1 for dummy node.
objectPool(
MPMCQueueNodeHazardPointer_t::ComputeMaximumRetiredObjectCount(2) +
capacity + 1),
// Disable "this is used in base member initializer" warning. // Disable "this is used in base member initializer" warning.
// We explicitly want this. // We explicitly want this.
#ifdef EMBB_PLATFORM_COMPILER_MSVC #ifdef EMBB_PLATFORM_COMPILER_MSVC
...@@ -94,7 +89,13 @@ delete_pointer_callback(*this, ...@@ -94,7 +89,13 @@ delete_pointer_callback(*this,
#ifdef EMBB_PLATFORM_COMPILER_MSVC #ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop) #pragma warning(pop)
#endif #endif
hazardPointer(delete_pointer_callback, NULL, 2) { hazardPointer(delete_pointer_callback, NULL, 2),
// Object pool, size with respect to the maximum number of retired nodes not
// eligible for reuse. +1 for dummy node.
objectPool(
hazardPointer.GetRetiredListMaxSize()*
embb::base::Thread::GetThreadsMaxCount() +
capacity + 1) {
// Allocate dummy node to reduce the number of special cases to consider. // Allocate dummy node to reduce the number of special cases to consider.
internal::LockFreeMPMCQueueNode<Type>* dummyNode = objectPool.Allocate(); internal::LockFreeMPMCQueueNode<Type>* dummyNode = objectPool.Allocate();
// Initially, head and tail point to the dummy node. // Initially, head and tail point to the dummy node.
...@@ -119,7 +120,7 @@ bool LockFreeMPMCQueue<Type, ValuePool>::TryEnqueue(Type const& element) { ...@@ -119,7 +120,7 @@ bool LockFreeMPMCQueue<Type, ValuePool>::TryEnqueue(Type const& element) {
for (;;) { for (;;) {
my_tail = tail; my_tail = tail;
hazardPointer.Guard(0, my_tail); hazardPointer.GuardPointer(0, my_tail);
// Check if pointer is still valid after guarding. // Check if pointer is still valid after guarding.
if (my_tail != tail) { if (my_tail != tail) {
...@@ -162,12 +163,12 @@ bool LockFreeMPMCQueue<Type, ValuePool>::TryDequeue(Type & element) { ...@@ -162,12 +163,12 @@ bool LockFreeMPMCQueue<Type, ValuePool>::TryDequeue(Type & element) {
Type data; Type data;
for (;;) { for (;;) {
my_head = head; my_head = head;
hazardPointer.Guard(0, my_head); hazardPointer.GuardPointer(0, my_head);
if (my_head != head) continue; if (my_head != head) continue;
my_tail = tail; my_tail = tail;
my_next = my_head->GetNext(); my_next = my_head->GetNext();
hazardPointer.Guard(1, my_next); hazardPointer.GuardPointer(1, my_next);
if (head != my_head) continue; if (head != my_head) continue;
if (my_next == NULL) if (my_next == NULL)
...@@ -186,7 +187,7 @@ bool LockFreeMPMCQueue<Type, ValuePool>::TryDequeue(Type & element) { ...@@ -186,7 +187,7 @@ bool LockFreeMPMCQueue<Type, ValuePool>::TryDequeue(Type & element) {
break; break;
} }
hazardPointer.EnqueueForDeletion(my_head); hazardPointer.EnqueuePointerForDeletion(my_head);
element = data; element = data;
return true; return true;
} }
......
...@@ -81,12 +81,13 @@ capacity(capacity), ...@@ -81,12 +81,13 @@ capacity(capacity),
#ifdef EMBB_PLATFORM_COMPILER_MSVC #ifdef EMBB_PLATFORM_COMPILER_MSVC
#pragma warning(pop) #pragma warning(pop)
#endif #endif
hazardPointer(delete_pointer_callback, NULL, 1),
// Object pool, size with respect to the maximum number of retired nodes not // Object pool, size with respect to the maximum number of retired nodes not
// eligible for reuse: // eligible for reuse:
objectPool( objectPool(
StackNodeHazardPointer_t::ComputeMaximumRetiredObjectCount(1) + hazardPointer.GetRetiredListMaxSize()*
capacity), embb::base::Thread::GetThreadsMaxCount() +
hazardPointer(delete_pointer_callback, NULL, 1) { capacity) {
} }
template< typename Type, typename ValuePool > template< typename Type, typename ValuePool >
...@@ -127,7 +128,7 @@ bool LockFreeStack< Type, ValuePool >::TryPop(Type & element) { ...@@ -127,7 +128,7 @@ bool LockFreeStack< Type, ValuePool >::TryPop(Type & element) {
return false; return false;
// Guard top_cached // Guard top_cached
hazardPointer.Guard(0, top_cached); hazardPointer.GuardPointer(0, top_cached);
// Check if top is still top. If this is the case, it has not been // Check if top is still top. If this is the case, it has not been
// retired yet (because before retiring that thing, the retiring thread // retired yet (because before retiring that thing, the retiring thread
...@@ -143,16 +144,16 @@ bool LockFreeStack< Type, ValuePool >::TryPop(Type & element) { ...@@ -143,16 +144,16 @@ bool LockFreeStack< Type, ValuePool >::TryPop(Type & element) {
break; break;
} else { } else {
// We continue with the next and can unguard top_cached // We continue with the next and can unguard top_cached
hazardPointer.Guard(0, NULL); hazardPointer.GuardPointer(0, NULL);
} }
} }
Type data = top_cached->GetElement(); Type data = top_cached->GetElement();
// We don't need to read from this reference anymore, unguard it // We don't need to read from this reference anymore, unguard it
hazardPointer.Guard(0, NULL); hazardPointer.GuardPointer(0, NULL);
hazardPointer.EnqueueForDeletion(top_cached); hazardPointer.EnqueuePointerForDeletion(top_cached);
element = data; element = data;
return true; return true;
......
...@@ -42,7 +42,7 @@ template<typename Type, Type Undefined, class PoolAllocator, ...@@ -42,7 +42,7 @@ template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator > class TreeAllocator >
bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>:: bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
IsLeaf(int node) { IsLeaf(int node) {
if (node >= size_ - 1 && node <= 2 * size_ - 1) { if (node >= size - 1 && node <= 2 * size - 1) {
return true; return true;
} }
return false; return false;
...@@ -52,7 +52,7 @@ template<typename Type, Type Undefined, class PoolAllocator, ...@@ -52,7 +52,7 @@ template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator > class TreeAllocator >
bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>:: bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
IsValid(int node) { IsValid(int node) {
return (node >= 0 && node <= 2 * size_ - 1); return (node >= 0 && node <= 2 * size - 1);
} }
template<typename Type, Type Undefined, class PoolAllocator, template<typename Type, Type Undefined, class PoolAllocator,
...@@ -77,14 +77,14 @@ template<typename T, T Undefined, class PoolAllocator, class TreeAllocator > ...@@ -77,14 +77,14 @@ template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>:: int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
NodeIndexToPoolIndex(int node) { NodeIndexToPoolIndex(int node) {
assert(IsLeaf(node)); assert(IsLeaf(node));
return(node - (size_ - 1)); return(node - (size - 1));
} }
template<typename Type, Type Undefined, class PoolAllocator, template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator > class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>:: int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
PoolIndexToNodeIndex(int index) { PoolIndexToNodeIndex(int index) {
int node = index + (size_ - 1); int node = index + (size - 1);
assert(IsLeaf(node)); assert(IsLeaf(node));
return node; return node;
} }
...@@ -100,7 +100,7 @@ template<typename T, T Undefined, class PoolAllocator, class TreeAllocator > ...@@ -100,7 +100,7 @@ template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>:: int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
GetParentNode(int node) { GetParentNode(int node) {
int parent = (node - 1) / 2; int parent = (node - 1) / 2;
assert(parent >= 0 && parent < size_ - 1); assert(parent >= 0 && parent < size - 1);
return parent; return parent;
} }
...@@ -112,11 +112,11 @@ allocate_rec(int node, Type& element) { ...@@ -112,11 +112,11 @@ allocate_rec(int node, Type& element) {
if (IsLeaf(node)) { if (IsLeaf(node)) {
int pool_index = NodeIndexToPoolIndex(node); int pool_index = NodeIndexToPoolIndex(node);
Type expected = pool_[pool_index]; Type expected = pool[pool_index];
if (expected == Undefined) if (expected == Undefined)
return -1; return -1;
if (pool_[pool_index].CompareAndSwap(expected, Undefined)) { if (pool[pool_index].CompareAndSwap(expected, Undefined)) {
element = expected; element = expected;
return pool_index; return pool_index;
} }
...@@ -131,11 +131,11 @@ allocate_rec(int node, Type& element) { ...@@ -131,11 +131,11 @@ allocate_rec(int node, Type& element) {
// atomically decrement the value in the node if the result is greater than // atomically decrement the value in the node if the result is greater than
// or equal to zero. This cannot be done atomically. // or equal to zero. This cannot be done atomically.
do { do {
current = tree_[node]; current = tree[node];
desired = current - 1; desired = current - 1;
if (desired < 0) if (desired < 0)
return -1; return -1;
} while (!tree_[node].CompareAndSwap(current, desired)); } while (!tree[node].CompareAndSwap(current, desired));
int leftResult = allocate_rec(GetLeftChildIndex(node), element); int leftResult = allocate_rec(GetLeftChildIndex(node), element);
if (leftResult != -1) { if (leftResult != -1) {
...@@ -156,7 +156,7 @@ Fill(int node, int elementsToStore, int power2Value) { ...@@ -156,7 +156,7 @@ Fill(int node, int elementsToStore, int power2Value) {
if (IsLeaf(node)) if (IsLeaf(node))
return; return;
tree_[node] = elementsToStore; tree[node] = elementsToStore;
int postPower2Value = power2Value >> 1; int postPower2Value = power2Value >> 1;
...@@ -188,14 +188,14 @@ Free(Type element, int index) { ...@@ -188,14 +188,14 @@ Free(Type element, int index) {
assert(element != Undefined); assert(element != Undefined);
// Put the element back // Put the element back
pool_[index].Store(element); pool[index].Store(element);
assert(index >= 0 && index < size_); assert(index >= 0 && index < size);
int node = PoolIndexToNodeIndex(index); int node = PoolIndexToNodeIndex(index);
while (!IsRoot(node)) { while (!IsRoot(node)) {
node = GetParentNode(node); node = GetParentNode(node);
tree_[node].FetchAndAdd(1); tree[node].FetchAndAdd(1);
} }
} }
...@@ -205,76 +205,37 @@ template< typename ForwardIterator > ...@@ -205,76 +205,37 @@ template< typename ForwardIterator >
LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>:: LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
LockFreeTreeValuePool(ForwardIterator first, ForwardIterator last) { LockFreeTreeValuePool(ForwardIterator first, ForwardIterator last) {
// Number of elements to store // Number of elements to store
real_size_ = static_cast<int>(::std::distance(first, last)); real_size = static_cast<int>(::std::distance(first, last));
// Let k be smallest number so that real_size <= 2^k, size = 2^k // Let k be smallest number so that real_size <= 2^k, size = 2^k
size_ = GetSmallestPowerByTwoValue(real_size_); size = GetSmallestPowerByTwoValue(real_size);
// Size of binary tree without the leaves // Size of binary tree without the leaves
tree_size_ = size_ - 1; tree_size = size - 1;
// make sure, signed values are not negative
assert(tree_size_ >= 0);
assert(real_size_ >= 0);
size_t tree_size_unsigned = static_cast<size_t>(tree_size_);
size_t real_size_unsigned = static_cast<size_t>(real_size_);
// Pool stores elements of type T // Pool stores elements of type T
pool_ = pool_allocator_.allocate(real_size_unsigned); pool = poolAllocator.allocate(static_cast<size_t>(real_size));
// invoke inplace new for each pool element
for (size_t i = 0; i != real_size_unsigned; ++i) {
new (&pool_[i]) embb::base::Atomic<Type>();
}
// Tree holds the counter of not allocated elements // Tree holds the counter of not allocated elements
tree_ = tree_allocator_.allocate(tree_size_unsigned); tree = treeAllocator.allocate(static_cast<size_t>(tree_size));
// invoke inplace new for each tree element
for (size_t i = 0; i != tree_size_unsigned; ++i) {
new (&tree_[i]) embb::base::Atomic<int>();
}
int i = 0; int i = 0;
// Store the elements from the range // Store the elements from the range
for (ForwardIterator curIter(first); curIter != last; ++curIter) { for (ForwardIterator curIter(first); curIter != last; ++curIter) {
pool_[i++] = *curIter; pool[i++] = *curIter;
} }
// Initialize the binary tree without leaves (counters) // Initialize the binary tree without leaves (counters)
Fill(0, static_cast<int>(::std::distance(first, last)), size_); Fill(0, static_cast<int>(::std::distance(first, last)), size);
} }
template<typename Type, Type Undefined, class PoolAllocator, template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator > class TreeAllocator >
LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>:: LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
~LockFreeTreeValuePool() { ~LockFreeTreeValuePool() {
size_t tree_size_unsigned = static_cast<size_t>(tree_size_); poolAllocator.deallocate(pool, static_cast<size_t>(real_size));
size_t real_size_unsigned = static_cast<size_t>(real_size_); treeAllocator.deallocate(tree, static_cast<size_t>(tree_size));
// invoke destructor for each pool element
for (size_t i = 0; i != real_size_unsigned; ++i) {
pool_[i].~Atomic();
}
pool_allocator_.deallocate(pool_, real_size_unsigned);
// invoke destructor for each tree element
for (size_t i = 0; i != tree_size_unsigned; ++i) {
tree_[i].~Atomic();
}
tree_allocator_.deallocate(tree_, tree_size_unsigned);
}
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
size_t LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
GetMinimumElementCountForGuaranteedCapacity(size_t capacity) {
// for this value pool, this is just capacity...
return capacity;
} }
} // namespace containers } // namespace containers
......
...@@ -83,8 +83,7 @@ ReturningTrueIterator::operator!=(const self_type& rhs) { ...@@ -83,8 +83,7 @@ ReturningTrueIterator::operator!=(const self_type& rhs) {
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
bool ObjectPool<Type, ValuePool, ObjectAllocator>:: bool ObjectPool<Type, ValuePool, ObjectAllocator>::
IsContained(const Type &obj) const { IsContained(const Type &obj) const {
if ((&obj < &objects_array_[0]) || if ((&obj < &objects[0]) || (&obj > &objects[capacity - 1])) {
(&obj > &objects_array_[value_pool_size_ - 1])) {
return false; return false;
} else { } else {
return true; return true;
...@@ -95,17 +94,17 @@ template<class Type, typename ValuePool, class ObjectAllocator> ...@@ -95,17 +94,17 @@ template<class Type, typename ValuePool, class ObjectAllocator>
int ObjectPool<Type, ValuePool, ObjectAllocator>:: int ObjectPool<Type, ValuePool, ObjectAllocator>::
GetIndexOfObject(const Type &obj) const { GetIndexOfObject(const Type &obj) const {
assert(IsContained(obj)); assert(IsContained(obj));
return(static_cast<int>(&obj - &objects_array_[0])); return(static_cast<int>(&obj - &objects[0]));
} }
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::AllocateRaw() { Type* ObjectPool<Type, ValuePool, ObjectAllocator>::AllocateRaw() {
bool val; bool val;
int allocated_index = value_pool_.Allocate(val); int allocated_index = p.Allocate(val);
if (allocated_index == -1) { if (allocated_index == -1) {
return NULL; return NULL;
} else { } else {
Type* ret_pointer = &(objects_array_[allocated_index]); Type* ret_pointer = &(objects[allocated_index]);
return ret_pointer; return ret_pointer;
} }
...@@ -113,17 +112,15 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::AllocateRaw() { ...@@ -113,17 +112,15 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::AllocateRaw() {
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
size_t ObjectPool<Type, ValuePool, ObjectAllocator>::GetCapacity() { size_t ObjectPool<Type, ValuePool, ObjectAllocator>::GetCapacity() {
return capacity_; return capacity;
} }
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::ObjectPool(size_t capacity) : ObjectPool<Type, ValuePool, ObjectAllocator>::ObjectPool(size_t capacity) :
capacity_(capacity), capacity(capacity),
value_pool_size_( p(ReturningTrueIterator(0), ReturningTrueIterator(capacity)) {
ValuePool::GetMinimumElementCountForGuaranteedCapacity(capacity)), // Allocate the objects (without construction, just get the memory)
value_pool_(ReturningTrueIterator(0), ReturningTrueIterator( objects = objectAllocator.allocate(capacity);
value_pool_size_)),
objects_array_(object_allocator_.allocate(value_pool_size_)) {
} }
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
...@@ -131,7 +128,7 @@ void ObjectPool<Type, ValuePool, ObjectAllocator>::Free(Type* obj) { ...@@ -131,7 +128,7 @@ void ObjectPool<Type, ValuePool, ObjectAllocator>::Free(Type* obj) {
int index = GetIndexOfObject(*obj); int index = GetIndexOfObject(*obj);
obj->~Type(); obj->~Type();
value_pool_.Free(true, index); p.Free(true, index);
} }
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
...@@ -192,7 +189,7 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate( ...@@ -192,7 +189,7 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() { ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() {
// Deallocate the objects // Deallocate the objects
object_allocator_.deallocate(objects_array_, value_pool_size_); objectAllocator.deallocate(objects, capacity);
} }
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
......
...@@ -35,21 +35,21 @@ Free(Type element, int index) { ...@@ -35,21 +35,21 @@ Free(Type element, int index) {
assert(element != Undefined); assert(element != Undefined);
// Just put back the element // Just put back the element
pool_array_[index].Store(element); pool[index].Store(element);
} }
template<typename Type, Type Undefined, class Allocator > template<typename Type, Type Undefined, class Allocator >
int WaitFreeArrayValuePool<Type, Undefined, Allocator>:: int WaitFreeArrayValuePool<Type, Undefined, Allocator>::
Allocate(Type & element) { Allocate(Type & element) {
for (int i = 0; i != size_; ++i) { for (int i = 0; i != size; ++i) {
Type expected; Type expected;
// If the memory cell is not available, go ahead // If the memory cell is not available, go ahead
if (Undefined == (expected = pool_array_[i].Load())) if (Undefined == (expected = pool[i].Load()))
continue; continue;
// Try to get the memory cell // Try to get the memory cell
if (pool_array_[i].CompareAndSwap(expected, Undefined)) { if (pool[i].CompareAndSwap(expected, Undefined)) {
// When the CAS was successful, this element is ours // When the CAS was successful, this element is ours
element = expected; element = expected;
return i; return i;
...@@ -64,45 +64,23 @@ WaitFreeArrayValuePool<Type, Undefined, Allocator>:: ...@@ -64,45 +64,23 @@ WaitFreeArrayValuePool<Type, Undefined, Allocator>::
WaitFreeArrayValuePool(ForwardIterator first, ForwardIterator last) { WaitFreeArrayValuePool(ForwardIterator first, ForwardIterator last) {
size_t dist = static_cast<size_t>(std::distance(first, last)); size_t dist = static_cast<size_t>(std::distance(first, last));
size_ = static_cast<int>(dist); size = static_cast<int>(dist);
// conversion may result in negative number. check!
assert(size_ >= 0);
// Use the allocator to allocate an array of size dist // Use the allocator to allocate an array of size dist
pool_array_ = allocator_.allocate(dist); pool = allocator.allocate(dist);
// invoke inplace new for each pool element
for ( size_t i = 0; i != dist; ++i ) {
new (&pool_array_[i]) embb::base::Atomic<Type>();
}
int i = 0; int i = 0;
// Store the elements of the range // Store the elements of the range
for (ForwardIterator curIter(first); curIter != last; ++curIter) { for (ForwardIterator curIter(first); curIter != last; ++curIter) {
pool_array_[i++] = *curIter; pool[i++] = *curIter;
} }
} }
template<typename Type, Type Undefined, class Allocator > template<typename Type, Type Undefined, class Allocator >
WaitFreeArrayValuePool<Type, Undefined, Allocator>::~WaitFreeArrayValuePool() { WaitFreeArrayValuePool<Type, Undefined, Allocator>::~WaitFreeArrayValuePool() {
// invoke destructor for each pool element allocator.deallocate(pool, (size_t)size);
for (int i = 0; i != size_; ++i) {
pool_array_[i].~Atomic();
}
// free memory
allocator_.deallocate(pool_array_, static_cast<size_t>(size_));
} }
template<typename Type, Type Undefined, class Allocator >
size_t WaitFreeArrayValuePool<Type, Undefined, Allocator>::
GetMinimumElementCountForGuaranteedCapacity(size_t capacity) {
// for this value pool, this is just capacity...
return capacity;
}
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
......
...@@ -113,17 +113,8 @@ class LockFreeMPMCQueue { ...@@ -113,17 +113,8 @@ class LockFreeMPMCQueue {
* least as many elements, maybe more. * least as many elements, maybe more.
*/ */
size_t capacity; size_t capacity;
// Do not change the ordering of class local variables.
/** // Important for initialization.
* The object pool, used for lock-free memory allocation.
*
* Warning: the objectPool has to be initialized before the hazardPointer
* object, to be sure that the hazardPointer object is destructed before the
* Pool as the hazardPointer object might return elements to the pool in its
* destructor. So the ordering of the members objectPool and hazardPointer is
* important here!
*/
ObjectPool< internal::LockFreeMPMCQueueNode<Type>, ValuePool > objectPool;
/** /**
* Callback to the method that is called by hazard pointers if a pointer is * Callback to the method that is called by hazard pointers if a pointer is
...@@ -133,17 +124,15 @@ class LockFreeMPMCQueue { ...@@ -133,17 +124,15 @@ class LockFreeMPMCQueue {
delete_pointer_callback; delete_pointer_callback;
/** /**
* Definition of the used hazard pointer type * The hazard pointer object, used for memory management.
*/ */
typedef embb::containers::internal::HazardPointer embb::containers::internal::HazardPointer
< internal::LockFreeMPMCQueueNode<Type>* > < internal::LockFreeMPMCQueueNode<Type>* > hazardPointer;
MPMCQueueNodeHazardPointer_t;
/** /**
* The hazard pointer object, used for memory management. * The object pool, used for lock-free memory allocation.
*/ */
MPMCQueueNodeHazardPointer_t hazardPointer; ObjectPool< internal::LockFreeMPMCQueueNode<Type>, ValuePool > objectPool;
/** /**
* Atomic pointer to the head node of the queue * Atomic pointer to the head node of the queue
......
...@@ -187,6 +187,11 @@ class LockFreeStack { ...@@ -187,6 +187,11 @@ class LockFreeStack {
delete_pointer_callback; delete_pointer_callback;
/** /**
* The hazard pointer object, used for memory management.
*/
internal::HazardPointer<internal::LockFreeStackNode<Type>*> hazardPointer;
/**
* The callback function, used to cleanup non-hazardous pointers. * The callback function, used to cleanup non-hazardous pointers.
* \see delete_pointer_callback * \see delete_pointer_callback
*/ */
...@@ -194,27 +199,10 @@ class LockFreeStack { ...@@ -194,27 +199,10 @@ class LockFreeStack {
/** /**
* The object pool, used for lock-free memory allocation. * The object pool, used for lock-free memory allocation.
*
* Warning: the objectPool has to be initialized before the hazardPointer
* object, to be sure that the hazardPointer object is destructed before the
* Pool as the hazardPointer object might return elements to the pool in its
* destructor. So the ordering of the members objectPool and hazardPointer is
* important here!
*/ */
ObjectPool< internal::LockFreeStackNode<Type>, ValuePool > objectPool; ObjectPool< internal::LockFreeStackNode<Type>, ValuePool > objectPool;
/** /**
* Definition of the used hazard pointer type
*/
typedef internal::HazardPointer < internal::LockFreeStackNode<Type>* >
StackNodeHazardPointer_t;
/**
* The hazard pointer object, used for memory management.
*/
StackNodeHazardPointer_t hazardPointer;
/**
* Atomic pointer to the top node of the stack (element that is popped next) * Atomic pointer to the top node of the stack (element that is popped next)
*/ */
embb::base::Atomic<internal::LockFreeStackNode<Type>*> top; embb::base::Atomic<internal::LockFreeStackNode<Type>*> top;
......
...@@ -123,25 +123,22 @@ class LockFreeTreeValuePool { ...@@ -123,25 +123,22 @@ class LockFreeTreeValuePool {
LockFreeTreeValuePool& operator=(const LockFreeTreeValuePool&); LockFreeTreeValuePool& operator=(const LockFreeTreeValuePool&);
// See algorithm description above // See algorithm description above
int size_; int size;
// See algorithm description above // See algorithm description above
int tree_size_; int tree_size;
// See algorithm description above // See algorithm description above
int real_size_; int real_size;
// The tree above the pool // The tree above the pool
embb::base::Atomic<int>* tree_; embb::base::Atomic<int>* tree;
// The actual pool // The actual pool
embb::base::Atomic<Type>* pool_; embb::base::Atomic<Type>* pool;
// respective allocator PoolAllocator poolAllocator;
PoolAllocator pool_allocator_; TreeAllocator treeAllocator;
// respective allocator
TreeAllocator tree_allocator_;
/** /**
* Computes smallest power of two fitting the specified value * Computes smallest power of two fitting the specified value
...@@ -281,18 +278,6 @@ class LockFreeTreeValuePool { ...@@ -281,18 +278,6 @@ class LockFreeTreeValuePool {
); );
/** /**
* Due to concurrency effects, a pool might provide less elements than managed
* by it. However, usually one wants to guarantee a minimal capacity. The
* count of elements, that must be given to the pool when to guarantee \c
* capacity elements is computed using this function.
*
* \return count of indices the pool has to be initialized with
*/
static size_t GetMinimumElementCountForGuaranteedCapacity(
size_t capacity
/**< [IN] count of indices that shall be guaranteed */);
/**
* Destructs the pool. * Destructs the pool.
* *
* \notthreadsafe * \notthreadsafe
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
namespace embb { namespace embb {
namespace containers { namespace containers {
/** /**
* \defgroup CPP_CONTAINERS_POOLS Pools * \defgroup CPP_CONTAINERS_POOLS Pools
* Concurrent pools * Concurrent pools
...@@ -61,29 +62,22 @@ class ObjectPool { ...@@ -61,29 +62,22 @@ class ObjectPool {
/** /**
* Allocator used to allocate elements of the object pool * Allocator used to allocate elements of the object pool
*/ */
ObjectAllocator object_allocator_; ObjectAllocator objectAllocator;
/** /**
* Capacity of the object pool * Array holding the allocated object
*/ */
size_t capacity_; Type* objects;
/** /**
* The size of the underlying value pool. This is also the size of the object * Capacity of the object pool
* array in this class. It is assumed, that the valuepool manages indices in
* range [0;value_pool_size_-1].
*/ */
size_t value_pool_size_; size_t capacity;
/** /**
* Underlying value pool * Underlying value pool
*/ */
ValuePool value_pool_; ValuePool p;
/**
* Array holding the allocated object
*/
Type* objects_array_;
/** /**
* Helper providing a virtual iterator that just returns true in each * Helper providing a virtual iterator that just returns true in each
......
...@@ -39,30 +39,12 @@ namespace containers { ...@@ -39,30 +39,12 @@ namespace containers {
* \ingroup CPP_CONCEPT * \ingroup CPP_CONCEPT
* \{ * \{
* \par Description * \par Description
* A value pool is a multi-set of elements, where each element has a unique, * A value pool is a fixed-size multiset of elements, where each element has a
* continuous (starting with 0) index. The elements cannot be modified and are * unique index. The elements cannot be modified and are given at construction
* given at construction time by providing first/last iterators. * time (by providing first/last iterators). A value pool provides two
* * operations: \c Allocate and \c Free. \c Allocate removes an element from the
* \par * pool, and \c Free returns an element to the pool. It is only allowed to
* A value pool provides two primary operations: \c Allocate and \c Free. \c * free elements that have previously been allocated.
* Allocate allocates an element/index "pair" (index via return, element via
* reference parameter) from the pool, and \c Free returns an element/index pair
* to the pool. To guarantee linearizability, \c element is not allowed to be
* modified between \c Allocate and \c Free. It is only allowed to free elements
* that have previously been allocated. The \c Allocate function does not
* guarantee an order on which indices are allocated. The count of elements that
* can be allocated with \c Allocate might be smaller than the count of
* elements, the pool is initialized with. This might be because of
* implementation details and respective concurrency effects: for example, if
* indices are managed within a queue, one has to protect queue elements from
* concurrency effects (reuse and access). As long as a thread potentially
* accesses a node (and with that an index), the respective index cannot not be
* given out to the user, even if being logically not part of the pool anymore.
* However, the user might want to guarantee a certain amount of indices to the
* user. Therefore, the static \c GetMinimumElementCountForGuaranteedCapacity
* method is used. The user passes the count of indices to this method, that
* shall be guaranteed by the pool. The method returns the count on indices, the
* pool has to be initialized with in order to guarantee this count on indices.
* *
* \par Requirements * \par Requirements
* - Let \c Pool be the pool class * - Let \c Pool be the pool class
...@@ -72,7 +54,6 @@ namespace containers { ...@@ -72,7 +54,6 @@ namespace containers {
* - Let \c i, j be forward iterators supporting \c std::distance. * - Let \c i, j be forward iterators supporting \c std::distance.
* - Let \c c be an object of type \c Type& * - Let \c c be an object of type \c Type&
* - Let \c e be a value of type \c int * - Let \c e be a value of type \c int
* - Let \c f be a value of type \c int
* *
* \par Valid Expressions * \par Valid Expressions
* *
...@@ -91,7 +72,7 @@ namespace containers { ...@@ -91,7 +72,7 @@ namespace containers {
* the bottom element. The bottom element cannot be stored in the pool, it * the bottom element. The bottom element cannot be stored in the pool, it
* is exclusively used to mark empty cells. The pool initially contains * is exclusively used to mark empty cells. The pool initially contains
* \c std::distance(i, j) elements which are copied during construction from * \c std::distance(i, j) elements which are copied during construction from
* the range \c [i, j]. A concrete class satisfying the value pool concept * the range \c [i, j). A concrete class satisfying the value pool concept
* might provide additional template parameters for specifying allocators. * might provide additional template parameters for specifying allocators.
* </td> * </td>
* </tr> * </tr>
...@@ -99,10 +80,9 @@ namespace containers { ...@@ -99,10 +80,9 @@ namespace containers {
* <td>\code{.cpp} Allocate(c) \endcode</td> * <td>\code{.cpp} Allocate(c) \endcode</td>
* <td>\c int</td> * <td>\c int</td>
* <td> * <td>
* Allocates an element/index "pair" from the pool. Returns -1, if no * Gets an element from the pool. Returns -1, if no element is available,
* element is available, i.e., the pool is empty. Otherwise, returns the * i.e., the pool is empty. Otherwise, returns the index of the element in
* index of the element in the pool. The value of the pool element is * the pool. The value of the pool element is written into reference \c c.
* written into parameter reference \c c.
* </td> * </td>
* </tr> * </tr>
* <tr> * <tr>
...@@ -113,15 +93,6 @@ namespace containers { ...@@ -113,15 +93,6 @@ namespace containers {
* \c Allocate. For each allocated element, \c Free must be called exactly * \c Allocate. For each allocated element, \c Free must be called exactly
* once.</td> * once.</td>
* </tr> * </tr>
* <tr>
* <td>\code{.cpp} GetMinimumElementCountForGuaranteedCapacity(f)
* \endcode</td>
* <td>\c void</td>
* <td>Static method, returns the count of indices, the user has to
* initialize the pool with in order to guarantee a count of \c f elements
* (irrespective of concurrency effects).
* </td>
* </tr>
* </table> * </table>
* *
* \} * \}
...@@ -145,10 +116,10 @@ template<typename Type, ...@@ -145,10 +116,10 @@ template<typename Type,
class Allocator = embb::base::Allocator< embb::base::Atomic<Type> > > class Allocator = embb::base::Allocator< embb::base::Atomic<Type> > >
class WaitFreeArrayValuePool { class WaitFreeArrayValuePool {
private: private:
int size_; int size;
embb::base::Atomic<Type>* pool_array_; embb::base::Atomic<Type>* pool;
WaitFreeArrayValuePool(); WaitFreeArrayValuePool();
Allocator allocator_; Allocator allocator;
// Prevent copy-construction // Prevent copy-construction
WaitFreeArrayValuePool(const WaitFreeArrayValuePool&); WaitFreeArrayValuePool(const WaitFreeArrayValuePool&);
...@@ -179,18 +150,6 @@ class WaitFreeArrayValuePool { ...@@ -179,18 +150,6 @@ class WaitFreeArrayValuePool {
); );
/** /**
* Due to concurrency effects, a pool might provide less elements than managed
* by it. However, usually one wants to guarantee a minimal capacity. The
* count of elements, that must be given to the pool when to guarantee \c
* capacity elements is computed using this function.
*
* \return count of indices the pool has to be initialized with
*/
static size_t GetMinimumElementCountForGuaranteedCapacity(
size_t capacity
/**< [IN] count of indices that shall be guaranteed */);
/**
* Destructs the pool. * Destructs the pool.
* *
* \notthreadsafe * \notthreadsafe
...@@ -216,7 +175,7 @@ class WaitFreeArrayValuePool { ...@@ -216,7 +175,7 @@ class WaitFreeArrayValuePool {
* Returns an element to the pool. * Returns an element to the pool.
* *
* \note The element must have been allocated with Allocate(). * \note The element must have been allocated with Allocate().
* *
* \waitfree * \waitfree
* *
* \see CPP_CONCEPTS_VALUE_POOL * \see CPP_CONCEPTS_VALUE_POOL
......
...@@ -36,112 +36,32 @@ ...@@ -36,112 +36,32 @@
namespace embb { namespace embb {
namespace containers { namespace containers {
namespace test { namespace test {
/** class HazardPointerTest : public partest::TestCase {
* @brief a very simple wait-free object pool implementation to have tests
* being independent of the EMBB object pool implementation.
*/
class IntObjectTestPool {
private: private:
int* simplePoolObjects; embb::base::Function<void, embb::base::Atomic<int>*> delete_pointer_callback;
embb::base::Atomic<int>* simplePool;
public:
static const int ALLOCATED_MARKER = 1;
static const int FREE_MARKER = 0;
unsigned int poolSize;
explicit IntObjectTestPool(unsigned int pool_size);
~IntObjectTestPool(); //used to allocate random stuff, we will just use the pointers, not the
//contents
/** embb::containers::ObjectPool< embb::base::Atomic<int> >* object_pool;
* Allocate object from the pool
*
* @return the allocated object
*/
int* Allocate();
/** //used to move pointer between threads
* Return an element to the pool embb::containers::LockFreeStack< embb::base::Atomic<int>* >* stack;
* embb::base::Mutex vector_mutex;
* @param objectPointer the object to be freed embb::containers::internal::HazardPointer<embb::base::Atomic<int>*>* hp;
*/ std::vector< embb::base::Atomic<int>* > deleted_vector;
void Release(int* object_pointer); int n_threads;
}; int n_elements_per_thread;
int n_elements;
class HazardPointerTest : public partest::TestCase {
public: public:
/** /**
* Adds test methods. * Adds test methods.
*/ */
HazardPointerTest(); HazardPointerTest();
void HazardPointerTest1Pre(); void HazardPointerTest1_Pre();
void HazardPointerTest1Post(); void HazardPointerTest1_Post();
void HazardPointerTest1ThreadMethod(); void HazardPointerTest1_ThreadMethod();
void DeletePointerCallback(embb::base::Atomic<int>* to_delete); void DeletePointerCallback(embb::base::Atomic<int>* to_delete);
private:
embb::base::Function<void, embb::base::Atomic<int>*> delete_pointer_callback_;
//used to allocate random stuff, we will just use the pointers, not the
//contents
embb::containers::ObjectPool< embb::base::Atomic<int> >* object_pool_;
//used to move pointer between threads
embb::containers::LockFreeStack< embb::base::Atomic<int>* >* stack_;
embb::base::Mutex vector_mutex_;
embb::containers::internal::HazardPointer<embb::base::Atomic<int>*>*
hazard_pointer_;
std::vector< embb::base::Atomic<int>* > deleted_vector_;
int n_threads_;
int n_elements_per_thread_;
int n_elements_;
};
class HazardPointerTest2 : public partest::TestCase {
public:
void DeletePointerCallback(int* to_delete);
bool SetRelativeGuards();
void HazardPointerTest2Master();
void HazardPointerTest2Slave();
void HazardPointerTest2Pre();
void HazardPointerTest2Post();
void HazardPointerTest2ThreadMethod();
HazardPointerTest2();
private:
// number of threads, participating in that test
int n_threads;
embb::base::Function<void, int*> delete_pointer_callback_;
// the thread id of the master
embb::base::Atomic<unsigned int> current_master_;
// variables, to synchronize threads. At each point in time, one master,
// the master changes each round until each thread was assigned master once.
embb::base::Atomic<int> sync1_;
embb::base::Atomic<unsigned int> sync2_;
unsigned int guards_per_phread_count_;
unsigned int guaranteed_capacity_pool_;
unsigned int pool_size_using_hazard_pointer_;
// The threads write here, if they guarded an object successfully. Used to
// determine when all allocated objects were guarded successfully.
embb::base::Atomic<int*>* shared_guarded_;
// This array is used by the master, to communicate and share what he has
// allocated with the slaves.
embb::base::Atomic<int*>* shared_allocated_;
// Reference to the object pool
IntObjectTestPool* test_pool_;
embb::containers::internal::HazardPointer<int*>* hazard_pointer_;
static const int FINISH_MARKER = -1;
}; };
} // namespace test } // namespace test
} // namespace containers } // namespace containers
......
...@@ -55,7 +55,6 @@ using embb::containers::test::HazardPointerTest; ...@@ -55,7 +55,6 @@ using embb::containers::test::HazardPointerTest;
using embb::containers::test::QueueTest; using embb::containers::test::QueueTest;
using embb::containers::test::StackTest; using embb::containers::test::StackTest;
using embb::containers::test::ObjectPoolTest; using embb::containers::test::ObjectPoolTest;
using embb::containers::test::HazardPointerTest2;
PT_MAIN("Data Structures C++") { PT_MAIN("Data Structures C++") {
unsigned int max_threads = static_cast<unsigned int>( unsigned int max_threads = static_cast<unsigned int>(
...@@ -65,7 +64,6 @@ PT_MAIN("Data Structures C++") { ...@@ -65,7 +64,6 @@ PT_MAIN("Data Structures C++") {
PT_RUN(PoolTest< WaitFreeArrayValuePool<int COMMA -1> >); PT_RUN(PoolTest< WaitFreeArrayValuePool<int COMMA -1> >);
PT_RUN(PoolTest< LockFreeTreeValuePool<int COMMA -1> >); PT_RUN(PoolTest< LockFreeTreeValuePool<int COMMA -1> >);
PT_RUN(HazardPointerTest); PT_RUN(HazardPointerTest);
PT_RUN(HazardPointerTest2);
PT_RUN(QueueTest< WaitFreeSPSCQueue< ::std::pair<size_t COMMA int> > >); PT_RUN(QueueTest< WaitFreeSPSCQueue< ::std::pair<size_t COMMA int> > >);
PT_RUN(QueueTest< LockFreeMPMCQueue< ::std::pair<size_t COMMA int> > PT_RUN(QueueTest< LockFreeMPMCQueue< ::std::pair<size_t COMMA int> >
COMMA true COMMA true >); COMMA true COMMA true >);
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#define NUM_SLICES 8 #define NUM_SLICES 8
#define TEST_COUNT 12 #define TEST_COUNT 12
typedef embb::dataflow::Network<NUM_SLICES> MyNetwork; typedef embb::dataflow::Network<8> MyNetwork;
typedef MyNetwork::ConstantSource< int > MyConstantSource; typedef MyNetwork::ConstantSource< int > MyConstantSource;
typedef MyNetwork::Source< int > MySource; typedef MyNetwork::Source< int > MySource;
typedef MyNetwork::SerialProcess< MyNetwork::Inputs<int>::Type, typedef MyNetwork::SerialProcess< MyNetwork::Inputs<int>::Type,
...@@ -156,7 +156,9 @@ void SimpleTest::TestBasic() { ...@@ -156,7 +156,9 @@ void SimpleTest::TestBasic() {
core_set, core_set,
1024, // max tasks (default: 1024) 1024, // max tasks (default: 1024)
128, // max groups (default: 128) 128, // max groups (default: 128)
num_cores, // max queues (default: 16) // Currently needs to be initialized
// with (max_queues + 1), see defect embb449
num_cores + 1, // max queues (default: 16)
1024, // queue capacity (default: 1024) 1024, // queue capacity (default: 1024)
4); // num priorities (default: 4) 4); // num priorities (default: 4)
......
...@@ -71,7 +71,7 @@ mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that) { ...@@ -71,7 +71,7 @@ mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that) {
/* acquire position to fetch id from */ /* acquire position to fetch id from */
mtapi_uint_t id_position = that->get_id_position; mtapi_uint_t id_position = that->get_id_position;
that->get_id_position++; that->get_id_position++;
if (that->capacity < that->get_id_position) { if (that->capacity <= that->get_id_position) {
that->get_id_position = 0; that->get_id_position = 0;
} }
...@@ -97,7 +97,7 @@ void embb_mtapi_id_pool_deallocate( ...@@ -97,7 +97,7 @@ void embb_mtapi_id_pool_deallocate(
/* acquire position to put id to */ /* acquire position to put id to */
mtapi_uint_t id_position = that->put_id_position; mtapi_uint_t id_position = that->put_id_position;
that->put_id_position++; that->put_id_position++;
if (that->capacity < that->put_id_position) { if (that->capacity <= that->put_id_position) {
that->put_id_position = 0; that->put_id_position = 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 <embb_mtapi_test_id_pool.h>
#include <vector>
IdPoolTest::IdPoolTest() {
CreateUnit("mtapi id pool test single threaded").
Add(&IdPoolTest::TestBasic, this, 1, 1000).
Pre(&IdPoolTest::TestBasicPre, this).
Post(&IdPoolTest::TestBasicPost, this);
CreateUnit("mtapi id pool test concurrent").
Add(&IdPoolTest::TestParallel, this, concurrent_accessors_id_pool_2
, 20).
Post(&IdPoolTest::TestParallelPost, this).
Pre(&IdPoolTest::TestParallelPre, this);
}
void IdPoolTest::TestParallel() {
// allocate ID_ELEMENTS_PER_ACCESSOR elements. Each test thread is
// guaranteed to be able to allocate this amount of elements.
TestAllocateDeallocateNElementsFromPool(id_pool_parallel,
id_elements_per_accessor);
}
void IdPoolTest::TestParallelPre() {
// create second id pool with CONCURRENT_ACCESSORS_ID_POOL_2*
// ID_ELEMENTS_PER_ACCESSOR elements
embb_mtapi_id_pool_initialize(&id_pool_parallel,
concurrent_accessors_id_pool_2*id_elements_per_accessor);
}
void IdPoolTest::TestParallelPost() {
// after the parallel tests, try to again allocate and deallocate all
// elements sequentially.
TestAllocateDeallocateNElementsFromPool(id_pool_parallel,
concurrent_accessors_id_pool_2*id_elements_per_accessor, true);
// finalize pool
embb_mtapi_id_pool_finalize(&id_pool_parallel);
}
void IdPoolTest::TestBasic() {
TestAllocateDeallocateNElementsFromPool(id_pool, id_pool_size_1, true);
}
void IdPoolTest::TestBasicPre() {
// create id pool with ID_POOL_SIZE_1 elements
embb_mtapi_id_pool_initialize(&id_pool, id_pool_size_1);
}
void IdPoolTest::TestBasicPost() {
// finalize pool
embb_mtapi_id_pool_finalize(&id_pool);
}
void IdPoolTest::TestAllocateDeallocateNElementsFromPool(
embb_mtapi_id_pool_t &pool,
int count_elements,
bool empty_check) {
std::vector<unsigned int> allocated;
for (int i = 0; i != count_elements; ++i) {
allocated.push_back(embb_mtapi_id_pool_allocate(&pool));
}
// the allocated elements should be disjunctive, and never invalid element
for (unsigned int x = 0; x != allocated.size(); ++x) {
PT_ASSERT(allocated[x] != EMBB_MTAPI_IDPOOL_INVALID_ID);
for (unsigned int y = 0; y != allocated.size(); ++y) {
if (x == y) {
continue;
}
PT_ASSERT(allocated[x] != allocated[y]);
}
}
// now the id pool should be empty... try ten times to get an id,
// we should always get the invalid element
if (empty_check) {
for (int i = 0; i != 10; ++i) {
PT_ASSERT_EQ(embb_mtapi_id_pool_allocate(&pool),
static_cast<unsigned int>(EMBB_MTAPI_IDPOOL_INVALID_ID)
)
}
}
// now return allocated elements in a shuffled manner.
::std::random_shuffle(allocated.begin(), allocated.end());
for (int i = 0; i != count_elements; ++i) {
embb_mtapi_id_pool_deallocate(&pool,
allocated[static_cast<unsigned int>(i)]);
}
}
/*
* 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 MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
#define MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
#include <partest/partest.h>
#include <embb_mtapi_id_pool_t.h>
// for shuffling a vector
#include <algorithm>
class IdPoolTest : public partest::TestCase {
public:
embb_mtapi_id_pool_t id_pool;
embb_mtapi_id_pool_t id_pool_parallel;
IdPoolTest();
private:
static const unsigned int id_pool_size_1 = 100;
static const unsigned int concurrent_accessors_id_pool_2 = 10;
static const unsigned int id_elements_per_accessor = 10;
/**
* We create a pool of size number_accessors*elements_per_accessor, so
* at each time we can guarantee each thread to be able to allocate
* elements_per_accessor elements.
* We create number_accessor threads, where each thread iteratively
* allocates and frees elements_per_accessor elements, which in each case
* has to be successful. Additionally, the sanity checks from the basic tests
* are repeated. The TestParallelPost function also repeats all
* sequential tests.
*/
void TestParallel();
void TestParallelPre();
void TestParallelPost();
/**
* Create a pool of size N. We repeatedly allocate and free N elements, check
* if the pool always returns disjunctive ids and check that the pool never
* returns the invalid element, if the pool is not empty. Check that the
* invalid element is returned if the pool is empty.
*/
void TestBasic();
void TestBasicPre();
void TestBasicPost();
static void TestAllocateDeallocateNElementsFromPool(
embb_mtapi_id_pool_t &pool,
int count_elements,
bool empty_check = false);
};
#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
...@@ -37,9 +37,6 @@ ...@@ -37,9 +37,6 @@
#include <embb_mtapi_test_group.h> #include <embb_mtapi_test_group.h>
#include <embb_mtapi_test_queue.h> #include <embb_mtapi_test_queue.h>
#include <embb_mtapi_test_error.h> #include <embb_mtapi_test_error.h>
#include <embb_mtapi_test_id_pool.h>
#include <embb/base/c/memory_allocation.h>
PT_MAIN("MTAPI C") { PT_MAIN("MTAPI C") {
embb_log_set_log_level(EMBB_LOG_LEVEL_NONE); embb_log_set_log_level(EMBB_LOG_LEVEL_NONE);
...@@ -51,7 +48,4 @@ PT_MAIN("MTAPI C") { ...@@ -51,7 +48,4 @@ PT_MAIN("MTAPI C") {
PT_RUN(InitFinalizeTest); PT_RUN(InitFinalizeTest);
PT_RUN(GroupTest); PT_RUN(GroupTest);
PT_RUN(QueueTest); PT_RUN(QueueTest);
PT_RUN(IdPoolTest);
PT_EXPECT(embb_get_bytes_allocated() == 0);
} }
...@@ -5,10 +5,14 @@ file(GLOB_RECURSE EMBB_MTAPI_CPP_HEADERS "include/*.h") ...@@ -5,10 +5,14 @@ file(GLOB_RECURSE EMBB_MTAPI_CPP_HEADERS "include/*.h")
file(GLOB_RECURSE EMBB_MTAPI_CPP_TEST_SOURCES "test/*.cc" "test/*.h") file(GLOB_RECURSE EMBB_MTAPI_CPP_TEST_SOURCES "test/*.cc" "test/*.h")
if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON)
message("-- Automatic initialization enabled (default)")
set(MTAPI_CPP_AUTOMATIC_INITIALIZE 1) set(MTAPI_CPP_AUTOMATIC_INITIALIZE 1)
else() else()
set(MTAPI_CPP_AUTOMATIC_INITIALIZE 0) set(MTAPI_CPP_AUTOMATIC_INITIALIZE 0)
message("-- Automatic initialization disabled")
endif() endif()
message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)")
# Execute the GroupSources macro # Execute the GroupSources macro
include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake)
......
...@@ -5,10 +5,13 @@ file(GLOB_RECURSE EMBB_TASKS_CPP_HEADERS "include/*.h") ...@@ -5,10 +5,13 @@ file(GLOB_RECURSE EMBB_TASKS_CPP_HEADERS "include/*.h")
file(GLOB_RECURSE EMBB_TASKS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") file(GLOB_RECURSE EMBB_TASKS_CPP_TEST_SOURCES "test/*.cc" "test/*.h")
if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON)
message("-- Automatic initialization enabled (default)")
set(TASKS_CPP_AUTOMATIC_INITIALIZE 1) set(TASKS_CPP_AUTOMATIC_INITIALIZE 1)
else() else()
set(TASKS_CPP_AUTOMATIC_INITIALIZE 0) set(TASKS_CPP_AUTOMATIC_INITIALIZE 0)
message("-- Automatic initialization disabled")
endif() endif()
message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)")
configure_file("include/embb/tasks/internal/cmake_config.h.in" configure_file("include/embb/tasks/internal/cmake_config.h.in"
"include/embb/tasks/internal/cmake_config.h") "include/embb/tasks/internal/cmake_config.h")
......
...@@ -78,19 +78,13 @@ void TaskTest::TestBasic() { ...@@ -78,19 +78,13 @@ void TaskTest::TestBasic() {
PT_EXPECT_EQ(policy.GetPriority(), 0u); PT_EXPECT_EQ(policy.GetPriority(), 0u);
policy.AddWorker(0u); policy.AddWorker(0u);
PT_EXPECT_EQ(policy.GetAffinity(), 1u); PT_EXPECT_EQ(policy.GetAffinity(), 1u);
policy.AddWorker(1u);
if (policy.GetCoreCount() > 1) { PT_EXPECT_EQ(policy.GetAffinity(), 3u);
policy.AddWorker(1u);
PT_EXPECT_EQ(policy.GetAffinity(), 3u);
}
policy.RemoveWorker(0u); policy.RemoveWorker(0u);
PT_EXPECT_EQ(policy.GetAffinity(), 2u);
PT_EXPECT_EQ(policy.IsSetWorker(0), false); PT_EXPECT_EQ(policy.IsSetWorker(0), false);
PT_EXPECT_EQ(policy.IsSetWorker(1), true);
if (policy.GetCoreCount() > 1) {
PT_EXPECT_EQ(policy.GetAffinity(), 2u);
PT_EXPECT_EQ(policy.IsSetWorker(1), true);
}
std::string test; std::string test;
embb::tasks::Task task = node.Spawn( embb::tasks::Task task = node.Spawn(
embb::base::Bind( embb::base::Bind(
......
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