Commit 26594733 by christian-kern

Merge pull request #8 from danklmn/embb334_lockfree_tree

Merging the lock-free chromatic tree back to Siemens/embb
parents 6ec6101f d4549427
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 0) set (EMBB_BASE_VERSION_PATCH 1)
# Fix compilation for CMake versions >= 3.1 # Fix compilation for CMake versions >= 3.1
# #
...@@ -100,6 +100,13 @@ else() ...@@ -100,6 +100,13 @@ 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)
......
...@@ -323,6 +323,8 @@ Known Bugs and Limitations ...@@ -323,6 +323,8 @@ 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,33 +49,37 @@ typedef embb::base::Function<void> InvokeFunctionType; ...@@ -49,33 +49,37 @@ typedef embb::base::Function<void> InvokeFunctionType;
#ifdef DOXYGEN #ifdef DOXYGEN
/** /**
* Spawns one to ten function objects at once and runs them in parallel. * Spawns two 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, ...> template<typename Function1, typename Function2, ...>
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 one to ten function objects at once and runs them in parallel using the * Spawns two 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, ...> template<typename Function1, typename Function2, ...>
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::mtapi::ExecutionPolicy & policy const embb::tasks::ExecutionPolicy & policy
/**< [in] embb::mtapi::ExecutionPolicy to use */ /**< [in] embb::tasks::ExecutionPolicy to use */
); );
#else // DOXYGEN #else // DOXYGEN
...@@ -118,13 +122,6 @@ class TaskWrapper { ...@@ -118,13 +122,6 @@ 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,
...@@ -290,12 +287,6 @@ template<typename Function1, typename Function2, typename Function3, ...@@ -290,12 +287,6 @@ 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,7 +44,6 @@ static void Invocable10() {} ...@@ -44,7 +44,6 @@ 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);
...@@ -61,4 +60,24 @@ void InvokeTest::Test() { ...@@ -61,4 +60,24 @@ 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 > 0) { > (now.seconds * 1000 + now.nanoseconds / 1000000)) {
/* 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
......
...@@ -105,7 +105,7 @@ void ConditionVarTest::TestTimedWaitTimeouts() { ...@@ -105,7 +105,7 @@ 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);
......
...@@ -36,6 +36,9 @@ namespace test { ...@@ -36,6 +36,9 @@ 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() {
...@@ -48,6 +51,20 @@ void TimeTest::TestTimeInDuration() { ...@@ -48,6 +51,20 @@ 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,9 +42,14 @@ class TimeTest : public partest::TestCase { ...@@ -42,9 +42,14 @@ 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*)>() {}
Atomic(BaseType* p) : embb::base::internal::atomic:: explicit 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,8 +177,7 @@ CompareAndSwap(BaseType& expected, BaseType desired) { ...@@ -177,8 +177,7 @@ 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;
if (!return_val) memcpy(&expected, &native_expected, sizeof(expected));
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->(); BaseType* operator->() const;
BaseType& operator*(); BaseType& operator*() const;
}; };
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->() { operator->() const {
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*() { operator*() const {
return *(this->Load()); return *(this->Load());
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#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 {
...@@ -95,8 +96,8 @@ void UniqueLock<Mutex>::Unlock() { ...@@ -95,8 +96,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) {
locked_ = other.locked_; std::swap(mutex_, other.mutex_);
mutex_ = other.Release(); std::swap(locked_, other.locked_);
} }
template<typename Mutex> template<typename Mutex>
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include <embb/base/internal/platform.h> #include <embb/base/internal/platform.h>
#include <embb/base/exceptions.h> #include <embb/base/exceptions.h>
#include <embb/base/c/counter.h>
namespace embb { namespace embb {
namespace base { namespace base {
...@@ -440,11 +439,11 @@ class UniqueLock { ...@@ -440,11 +439,11 @@ class UniqueLock {
void Unlock(); void Unlock();
/** /**
* Transfers ownership of a mutex to this lock. * Exchanges ownership of the wrapped mutex with another lock.
*/ */
void Swap( void Swap(
UniqueLock<Mutex>& other UniqueLock<Mutex>& other
/**< [IN/OUT] Lock from which ownership shall be transferred */ /**< [IN/OUT] The lock to exchange ownership with */
); );
/** /**
...@@ -484,71 +483,6 @@ class UniqueLock { ...@@ -484,71 +483,6 @@ class UniqueLock {
friend class embb::base::ConditionVariable; friend class embb::base::ConditionVariable;
}; };
class ReadWriteLock {
public:
ReadWriteLock() : read_lock_(), write_lock_(),
reader_(self()), writer_(self()) {
embb_counter_init(&active_readers_);
}
void ReadLock() {
LockGuard<Mutex> read_guard(read_lock_);
unsigned int me = embb_counter_increment(&active_readers_);
if (me == 0) {
write_lock_.Lock();
}
}
void ReadUnlock() {
unsigned int me = embb_counter_decrement(&active_readers_);
if (me == 1) {
write_lock_.Unlock();
}
}
void WriteLock() {
read_lock_.Lock();
write_lock_.Lock();
}
void WriteUnlock() {
write_lock_.Unlock();
read_lock_.Unlock();
}
~ReadWriteLock() {
embb_counter_destroy(&active_readers_);
}
class Reader {
public:
explicit Reader(ReadWriteLock& rwlock) : readwrite_lock_(rwlock) {}
void Lock() { readwrite_lock_.ReadLock(); }
void Unlock() { readwrite_lock_.ReadUnlock(); }
private:
ReadWriteLock& readwrite_lock_;
};
class Writer {
public:
explicit Writer(ReadWriteLock& rwlock) : readwrite_lock_(rwlock) {}
void Lock() { readwrite_lock_.WriteLock(); }
void Unlock() { readwrite_lock_.WriteUnlock(); }
private:
ReadWriteLock& readwrite_lock_;
};
Reader& GetReader() { return reader_; }
Writer& GetWriter() { return writer_; }
private:
ReadWriteLock(const ReadWriteLock&);
ReadWriteLock& operator=(const ReadWriteLock&);
ReadWriteLock& self() { return *this; }
Mutex read_lock_;
Mutex write_lock_;
embb_counter_t active_readers_;
Reader reader_;
Writer writer_;
};
} // namespace base } // namespace base
} // namespace embb } // namespace embb
......
...@@ -191,13 +191,21 @@ void MutexTest::TestUniqueLock() { ...@@ -191,13 +191,21 @@ void MutexTest::TestUniqueLock() {
} }
{ // Test lock swapping { // Test lock swapping
UniqueLock<> lock1; UniqueLock<> lock1(mutex_);
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);
} }
} }
......
...@@ -12,12 +12,25 @@ GroupSourcesMSVC(test) ...@@ -12,12 +12,25 @@ GroupSourcesMSVC(test)
set (EMBB_CONTAINERS_CPP_INCLUDE_DIRS "include" "src" "test") set (EMBB_CONTAINERS_CPP_INCLUDE_DIRS "include" "src" "test")
include_directories(${EMBB_CONTAINERS_CPP_INCLUDE_DIRS} include_directories(${EMBB_CONTAINERS_CPP_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include
${CMAKE_CURRENT_BINARY_DIR}/../base_c/include ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include
${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include
${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include) ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include)
add_library (embb_containers_cpp ${EMBB_CONTAINERS_CPP_SOURCES} ${EMBB_CONTAINERS_CPP_HEADERS}) add_subdirectory(generator)
set(GENERATOR_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/include/embb/containers/internal)
set(CHROMATIC_TREE_GENERATED_HEADER ${GENERATOR_OUTPUT_DIR}/lock_free_chromatic_tree-rebalance.h)
file(MAKE_DIRECTORY ${GENERATOR_OUTPUT_DIR})
add_custom_command (
OUTPUT ${CHROMATIC_TREE_GENERATED_HEADER}
COMMAND chromatic_tree_generator ${CHROMATIC_TREE_GENERATED_HEADER}
DEPENDS chromatic_tree_generator
)
add_library (embb_containers_cpp ${EMBB_CONTAINERS_CPP_SOURCES} ${EMBB_CONTAINERS_CPP_HEADERS}
${CHROMATIC_TREE_GENERATED_HEADER})
target_link_libraries(embb_containers_cpp embb_base_cpp) target_link_libraries(embb_containers_cpp embb_base_cpp)
if (BUILD_TESTS STREQUAL ON) if (BUILD_TESTS STREQUAL ON)
...@@ -30,4 +43,5 @@ endif() ...@@ -30,4 +43,5 @@ endif()
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb
DESTINATION include FILES_MATCHING PATTERN "*.h") DESTINATION include FILES_MATCHING PATTERN "*.h")
install(FILES ${CHROMATIC_TREE_GENERATED_HEADER} DESTINATION include/embb/containers/internal)
install(TARGETS embb_containers_cpp DESTINATION lib) install(TARGETS embb_containers_cpp DESTINATION lib)
add_executable(chromatic_tree_generator chromatic_tree_generator.cc chromatic_tree_operations.h)
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "chromatic_tree_operations.h"
static const char INCLUDE_GUARD[] =
"EMBB_CONTAINERS_INTERNAL_LOCK_FREE_CHROMATIC_TREE_REBALANCE_H_";
static const char GENERATOR_NOTICE[] =
"//\n"
"// This file was created automatically by a code generator.\n"
"// Any direct changes will be lost after rebuild of the project.\n"
"//";
static const char RETURN_TYPE[] = "embb_errors_t";
static const char NODEARG_TYPE[] = "HazardNodePtr";
static const char LOCKARG_TYPE[] = "HazardOperationPtr";
static const char NEWNODE_TYPE[] = "Node*";
void PrintOperationSourceCode(FILE* file, const RebalancingOperation& op);
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "USAGE:\n %s <output_file>\n", argv[0]);
return 1;
}
const char* filename = argv[1];
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
#endif
FILE *file = fopen(filename, "w");
#ifdef _MSC_VER
#pragma warning(pop)
#endif
if (file == NULL) {
fprintf(stderr, "Error: Cannot open file '%s' for writing!\n", filename);
return 1;
}
// Printing header
fprintf(file, "#ifndef %s\n#define %s\n\n", INCLUDE_GUARD, INCLUDE_GUARD);
fprintf(file, "%s\n\n", GENERATOR_NOTICE);
// Printing methods code
int num_operations = (sizeof(REBALANCING_OPERATIONS) /
sizeof(REBALANCING_OPERATIONS[0]));
for (int i = 0; i < num_operations; ++i) {
PrintOperationSourceCode(file, REBALANCING_OPERATIONS[i]);
}
// Printing trailer
fprintf(file, "#endif // %s\n", INCLUDE_GUARD);
fclose(file);
return 0;
}
void PrintOperationSourceCode(FILE* file, const RebalancingOperation& op) {
// Method signature
fprintf(file, "%s %s(", RETURN_TYPE, op.name);
// Method arguments
fprintf(file, "%s& u, %s& u_op", NODEARG_TYPE, LOCKARG_TYPE);
int offset = static_cast<int>(strlen(NODEARG_TYPE) + strlen(op.name) + 2);
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, ",\n%*s%s& %s, %s& %s_op", offset, "",
NODEARG_TYPE, op.old_nodes[i].name,
LOCKARG_TYPE, op.old_nodes[i].name);
}
fprintf(file, ") {\n"
" embb_errors_t result = EMBB_NOMEM;\n");
// Define nodes
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " %s %s = NULL;\n", NEWNODE_TYPE, op.new_nodes[i].name);
}
fprintf(file, "\n"
" while (result != EMBB_SUCCESS) {\n");
// Construct new nodes
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " %s = node_pool_.Allocate(\n"
" %s->GetKey(), %s->GetValue(), %s,\n"
" %s, %s, &initial_operation_dummy_);\n"
" if (%s == NULL) break;\n",
op.new_nodes[i].name,
op.new_nodes[i].orig_node, op.new_nodes[i].orig_node,
op.new_nodes[i].weight,
op.new_nodes[i].left, op.new_nodes[i].right,
op.new_nodes[i].name);
}
fprintf(file, "\n"
" HazardOperationPtr op(GetOperationGuard(HIDX_CURRENT_OP));\n"
" op.ProtectSafe(operation_pool_.Allocate());\n"
" if (op == NULL) break;\n");
// Create and fill the operation object
fprintf(file, "\n"
" op->SetRoot(u, u_op);\n"
" op->SetNewChild(nx);\n"
" op->SetOldNodes(%s, %s_op",
op.old_nodes[0].name, op.old_nodes[0].name);
for (int i = 1; i < op.num_nodes; ++i) {
fprintf(file, ",\n %s, %s_op",
op.old_nodes[i].name, op.old_nodes[i].name);
}
fprintf(file, ");\n");
// Execute operation
fprintf(file, "\n"
" bool succeeded = op->Help(GetNodeGuard(HIDX_HELPING),\n"
" GetOperationGuard(HIDX_HELPING));\n"
" op->CleanUp(&retired_operation_dummy_);\n"
" \n"
" if (!succeeded) {\n"
" RetireOperation(op);\n"
" result = EMBB_BUSY;\n"
" break;\n"
" }\n");
// Release original nodes and operations
fprintf(file, "\n"
" RetireOperation(u_op);\n");
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " RetireNode(%s); RetireOperation(%s_op);\n",
op.old_nodes[i].name, op.old_nodes[i].name);
}
fprintf(file, "\n"
" result = EMBB_SUCCESS;\n"
" }\n");
// Delete new nodes if operation failed
fprintf(file, "\n"
" if (result != EMBB_SUCCESS) {\n");
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " if (%s) FreeNode(%s);\n",
op.new_nodes[i].name, op.new_nodes[i].name);
}
fprintf(file, " }\n");
// Return statement
fprintf(file, "\n"
" return result;\n"
"}\n"
"\n");
};
#ifndef OPERATIONS_H
#define OPERATIONS_H
static const int MAX_NODES = 5;
typedef struct {
const char *name;
} OldNode;
typedef struct {
const char *name;
const char *orig_node;
const char *weight;
const char *left;
const char *right;
} NewNode;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4510 4512 4610)
#endif
typedef struct {
const char *name;
const int num_nodes;
const OldNode old_nodes[MAX_NODES];
const NewNode new_nodes[MAX_NODES];
} RebalancingOperation;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
static const RebalancingOperation REBALANCING_OPERATIONS[] = {
{
"BLK",
3,
{"ux", "uxl", "uxr"},
{
{"nxl", "uxl", "1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxr", "uxr", "1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() - 1", "nxl", "nxr"}
}
},
{
"PUSH_L",
3,
{"ux", "uxl", "uxr"},
{
{"nxl", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxr", "uxr", "0", "uxr->GetLeft()", "uxr->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() + 1", "nxl", "nxr"}
}
},
{
"PUSH_R",
3,
{"ux", "uxl", "uxr"},
{
{"nxr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxl", "uxl", "0", "uxl->GetLeft()", "uxl->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() + 1", "nxl", "nxr"}
}
},
{
"RB1_L",
2,
{"ux", "uxl"},
{
{"nxr", "ux", "0", "uxl->GetRight()", "ux->GetRight()"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"RB1_R",
2,
{"ux", "uxr"},
{
{"nxl", "ux", "0", "ux->GetLeft()", "uxr->GetLeft()"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"RB2_L",
3,
{"ux", "uxl", "uxlr"},
{
{"nxl", "uxl", "0", "uxl->GetLeft()", "uxlr->GetLeft()"},
{"nxr", "ux", "0", "uxlr->GetRight()", "ux->GetRight()"},
{"nx", "uxlr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"RB2_R",
3,
{"ux", "uxr", "uxrl"},
{
{"nxr", "uxr", "0", "uxrl->GetRight()", "uxr->GetRight()"},
{"nxl", "ux", "0", "ux->GetLeft()", "uxrl->GetLeft()"},
{"nx", "uxrl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W1_L",
4,
{"ux", "uxl", "uxr", "uxrl"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxlr", "uxrl", "uxrl->GetWeight() - 1", "uxrl->GetLeft()", "uxrl->GetRight()"},
{"nxl", "ux", "1", "nxll", "nxlr"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"W1_R",
4,
{"ux", "uxl", "uxr", "uxlr"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxrl", "uxlr", "uxlr->GetWeight() - 1", "uxlr->GetLeft()", "uxlr->GetRight()"},
{"nxr", "ux", "1", "nxrl", "nxrr"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"W2_L",
4,
{"ux", "uxl", "uxr", "uxrl"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxlr", "uxrl", "0", "uxrl->GetLeft()", "uxrl->GetRight()"},
{"nxl", "ux", "1", "nxll", "nxlr"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"W2_R",
4,
{"ux", "uxl", "uxr", "uxlr"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxrl", "uxlr", "0", "uxlr->GetLeft()", "uxlr->GetRight()"},
{"nxr", "ux", "1", "nxrl", "nxrr"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"W3_L",
5,
{"ux", "uxl", "uxr", "uxrl", "uxrll"},
{
{"nxlll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxll", "ux", "1", "nxlll", "uxrll->GetLeft()"},
{"nxlr", "uxrl", "1", "uxrll->GetRight()", "uxrl->GetRight()"},
{"nxl", "uxrll", "0", "nxll", "nxlr"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"W3_R",
5,
{"ux", "uxl", "uxr", "uxlr", "uxlrr"},
{
{"nxrrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxrr", "ux", "1", "uxlrr->GetRight()", "nxrrr"},
{"nxrl", "uxlr", "1", "uxlr->GetLeft()", "uxlrr->GetLeft()"},
{"nxr", "uxlrr", "0", "nxrl", "nxrr"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"W4_L",
5,
{"ux", "uxl", "uxr", "uxrl", "uxrlr"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxrl", "uxrlr", "1", "uxrlr->GetLeft()", "uxrlr->GetRight()"},
{"nxl", "ux", "1", "nxll", "uxrl->GetLeft()"},
{"nxr", "uxr", "0", "nxrl", "uxr->GetRight()"},
{"nx", "uxrl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W4_R",
5,
{"ux", "uxl", "uxr", "uxlr", "uxlrl"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxlr", "uxlrl", "1", "uxlrl->GetLeft()", "uxlrl->GetRight()"},
{"nxr", "ux", "1", "uxlr->GetRight()", "nxrr"},
{"nxl", "uxl", "0", "uxl->GetLeft()", "nxlr"},
{"nx", "uxlr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W5_L",
4,
{"ux", "uxl", "uxr", "uxrr"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxl", "ux", "1", "nxll", "uxr->GetLeft()"},
{"nxr", "uxrr", "1", "uxrr->GetLeft()", "uxrr->GetRight()"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W5_R",
4,
{"ux", "uxl", "uxr", "uxll"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxr", "ux", "1", "uxl->GetRight()", "nxrr"},
{"nxl", "uxll", "1", "uxll->GetLeft()", "uxll->GetRight()"},
{"nx", "uxl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W6_L",
4,
{"ux", "uxl", "uxr", "uxrl"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxl", "ux", "1", "nxll", "uxrl->GetLeft()"},
{"nxr", "uxr", "1", "uxrl->GetRight()", "uxr->GetRight()"},
{"nx", "uxrl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W6_R",
4,
{"ux", "uxl", "uxr", "uxlr"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxr", "ux", "1", "uxlr->GetRight()", "nxrr"},
{"nxl", "uxl", "1", "uxl->GetLeft()", "uxlr->GetLeft()"},
{"nx", "uxlr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W7",
3,
{"ux", "uxl", "uxr"},
{
{"nxl", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() + 1", "nxl", "nxr"}
}
}
};
#endif // OPERATIONS_H
...@@ -182,7 +182,9 @@ HazardPointerThreadEntry<GuardType>::~HazardPointerThreadEntry() { ...@@ -182,7 +182,9 @@ HazardPointerThreadEntry<GuardType>::~HazardPointerThreadEntry() {
} }
template< typename GuardType > template< typename GuardType >
GuardType HazardPointerThreadEntry<GuardType>::GetGuard(int pos) const { typename HazardPointerThreadEntry<GuardType>::AtomicGuard&
HazardPointerThreadEntry<GuardType>::GetGuard(int pos) const {
assert(pos >= 0 && pos < guards_per_thread);
return guarded_pointers[pos]; return guarded_pointers[pos];
} }
...@@ -393,6 +395,12 @@ void HazardPointer< GuardType >::GuardPointer(int guardPosition, ...@@ -393,6 +395,12 @@ void HazardPointer< GuardType >::GuardPointer(int guardPosition,
} }
template< typename GuardType > template< typename GuardType >
typename HazardPointer< GuardType >::AtomicGuard&
HazardPointer< GuardType >::GetGuardedPointer(int guardPosition) {
return GetHazardPointerElementForCurrentThread().GetGuard(guardPosition);
}
template< typename GuardType >
void HazardPointer< GuardType >::EnqueuePointerForDeletion( void HazardPointer< GuardType >::EnqueuePointerForDeletion(
GuardType guardedElement) { GuardType guardedElement) {
GetHazardPointerElementForCurrentThread().AddRetired(guardedElement); GetHazardPointerElementForCurrentThread().AddRetired(guardedElement);
...@@ -410,6 +418,124 @@ void HazardPointer< GuardType >::EnqueuePointerForDeletion( ...@@ -410,6 +418,124 @@ void HazardPointer< GuardType >::EnqueuePointerForDeletion(
template<typename GuardType> template<typename GuardType>
const double embb::containers::internal::HazardPointer<GuardType>:: const double embb::containers::internal::HazardPointer<GuardType>::
RETIRE_THRESHOLD = 1.25f; RETIRE_THRESHOLD = 1.25f;
template<typename Type>
UniqueHazardPointer<Type>::
UniqueHazardPointer()
: hazard_guard_(NULL),
local_ptr_value_(NULL),
undefined_guard_(NULL),
active_(false) {}
template<typename Type>
UniqueHazardPointer<Type>::
UniqueHazardPointer(AtomicTypePtr& hazard_guard, Type* undefined_guard)
: hazard_guard_(&hazard_guard),
local_ptr_value_(hazard_guard_->Load()),
undefined_guard_(undefined_guard),
active_(LoadGuardedPointer() == undefined_guard_) {}
template<typename Type>
UniqueHazardPointer<Type>::~UniqueHazardPointer() {
if (IsActive()) ClearHazard();
}
template<typename Type>
bool UniqueHazardPointer<Type>::ProtectHazard(const AtomicTypePtr& hazard) {
assert(OwnsHazardGuard());
// Read the hazard and store it into the guard
StoreGuardedPointer(hazard.Load());
// Check whether the guard is valid
SetActive(LoadGuardedPointer() == hazard.Load());
// Clear the guard if it is invalid
if (!IsActive()) ClearHazard();
return IsActive();
}
template<typename Type>
void UniqueHazardPointer<Type>::ProtectSafe(Type* safe_ptr) {
assert(OwnsHazardGuard());
StoreGuardedPointer(safe_ptr);
SetActive(true);
}
template<typename Type>
UniqueHazardPointer<Type>::operator Type* () const {
assert(IsActive());
return LoadGuardedPointer();
}
template<typename Type>
Type* UniqueHazardPointer<Type>::operator->() const {
assert(IsActive());
return LoadGuardedPointer();
}
template<typename Type>
Type& UniqueHazardPointer<Type>::operator*() const {
assert(IsActive());
return *(LoadGuardedPointer());
}
template<typename Type>
void UniqueHazardPointer<Type>::AdoptHazard(const UniqueHazardPointer& other) {
assert(OwnsHazardGuard());
StoreGuardedPointer(other.LoadGuardedPointer());
SetActive(other.active_);
}
template<typename Type>
void UniqueHazardPointer<Type>::Swap(UniqueHazardPointer& other) {
std::swap(hazard_guard_, other.hazard_guard_);
std::swap(local_ptr_value_, other.local_ptr_value_);
std::swap(undefined_guard_, other.undefined_guard_);
std::swap(active_, other.active_);
}
template<typename Type>
Type* UniqueHazardPointer<Type>::ReleaseHazard() {
assert(IsActive());
Type* released_hazard = LoadGuardedPointer();
ClearHazard();
SetActive(false);
return released_hazard;
}
template<typename Type>
bool UniqueHazardPointer<Type>::IsActive() const {
return active_;
}
template<typename Type>
void UniqueHazardPointer<Type>::SetActive(bool active) {
active_ = active;
}
template<typename Type>
void UniqueHazardPointer<Type>::ClearHazard() {
StoreGuardedPointer(undefined_guard_);
}
template<typename Type>
Type* UniqueHazardPointer<Type>::LoadGuardedPointer() const {
return local_ptr_value_;
}
template<typename Type>
void UniqueHazardPointer<Type>::StoreGuardedPointer(Type* ptr) {
hazard_guard_->Store(ptr);
local_ptr_value_ = ptr;
}
template<typename Type>
bool UniqueHazardPointer<Type>::OwnsHazardGuard() const {
return hazard_guard_ != NULL;
}
} // namespace internal } // namespace internal
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
......
...@@ -238,6 +238,8 @@ class HazardPointerThreadEntry { ...@@ -238,6 +238,8 @@ class HazardPointerThreadEntry {
HazardPointerThreadEntry & operator= (const HazardPointerThreadEntry&); HazardPointerThreadEntry & operator= (const HazardPointerThreadEntry&);
public: public:
typedef embb::base::Atomic< GuardType > AtomicGuard;
/** /**
* Checks if current thread is active (with respect to participating in hazard * Checks if current thread is active (with respect to participating in hazard
* pointer management) * pointer management)
...@@ -318,7 +320,7 @@ class HazardPointerThreadEntry { ...@@ -318,7 +320,7 @@ class HazardPointerThreadEntry {
* Gets the guard at the specified position. * Gets the guard at the specified position.
* Positions are numbered, beginning with 0. * Positions are numbered, beginning with 0.
*/ */
GuardType GetGuard( AtomicGuard& GetGuard(
int pos int pos
/**< [IN] Position of the guard */) const; /**< [IN] Position of the guard */) const;
...@@ -467,6 +469,8 @@ class HazardPointer { ...@@ -467,6 +469,8 @@ class HazardPointer {
can be deleted*/); can be deleted*/);
public: public:
typedef typename HazardPointerThreadEntry_t::AtomicGuard AtomicGuard;
/** /**
* Gets the capacity of one retired list * Gets the capacity of one retired list
* *
...@@ -516,12 +520,186 @@ class HazardPointer { ...@@ -516,12 +520,186 @@ class HazardPointer {
* Guards \c guardedElement with the guard at position \c guardPosition * Guards \c guardedElement with the guard at position \c guardPosition
*/ */
void GuardPointer(int guardPosition, GuardType guardedElement); void GuardPointer(int guardPosition, GuardType guardedElement);
AtomicGuard& GetGuardedPointer(int guardPosition);
/** /**
* Enqueue a pointer for deletion. It is added to the retired list and * Enqueue a pointer for deletion. It is added to the retired list and
* deleted when no thread accesses it anymore. * deleted when no thread accesses it anymore.
*/ */
void EnqueuePointerForDeletion(GuardType guardedElement); void EnqueuePointerForDeletion(GuardType guardedElement);
}; };
/**
* Ownership wrapper for a hazard pointer
*
* Uses an entry of the hazard table (guard) to provide protection for a single
* hazardous pointer. While providing standard pointer dereference and member
* access operators, it requires special care for pointer assignment (realized
* via 'ProtectHazard' method).
* On destruction, it clears the wrapped hazard table entry, releasing the
* protected hazardous pointer (if any).
*
* \tparam Type Type of the object to be protected by the hazard pointer
*/
template<typename Type>
class UniqueHazardPointer {
public:
/** Typedef for a atomic pointer to the guarded object. */
typedef embb::base::Atomic<Type*> AtomicTypePtr;
/**
* Creates an uninitialized, empty wrapper.
*
* An uninitialized wrapper may only be swapped with another wrapper (using
* \c Swap() method) or checked for being active (using 'IsActive()' method,
* which should always return /c false for an uninitialized wrapper).
*/
UniqueHazardPointer();
/**
* Creates a wrapper that uses the given hazard table entry (referred to as
* "guard") to protect hazardous pointers.
*
* \param[IN] hazard_guard Reference to a hazard table entry
* \param[IN] undefined_guard Dummy value used to clear the hazard table entry
*/
explicit
UniqueHazardPointer(AtomicTypePtr& hazard_guard,
Type* undefined_guard = NULL);
/**
* If initialized and active, clears the hazard table entry.
*/
~UniqueHazardPointer();
/**
* Tries to protect the given hazard using the wrapped guard.
* If it succeeds, the hazard may be safely dereferenced as long as the guard
* is not destroyed or reset to protect another hazard.
*
* \param hazard The hazard to be protected
* \return \c true if the specified hazard is now protected by the guard,
* \c false if the hazard was modified by a concurrent thread
*/
bool ProtectHazard(const AtomicTypePtr& hazard);
/**
* Uses the wrapped guard to protect a pointer that is not hazardous yet.
*
* \param safe_ptr The pointer to be protected
*/
void ProtectSafe(Type* safe_ptr);
/**
* Type cast operator.
*
* \return The hazardous pointer protected by this wrapper
*/
operator Type* () const;
/**
* Pointer member access operator.
*
* \return The hazardous pointer protected by this wrapper
*/
Type* operator->() const;
/**
* Pointer dereference operator.
*
* \return Reference to the object pointed to by the protected pointer
*/
Type& operator*() const;
/**
* Protects the hazard that is currently protected by another wrapper (so it
* becomes protected by two guards simultaneously). The other wrapper remains
* unmodified.
*
* \param other Another wrapper those protected pointer is to be protected by
* the calling wrapper
*/
void AdoptHazard(const UniqueHazardPointer& other);
/**
* Swaps the guard ownership with another wrapper. Swaps not just the
* protected hazards, but the hazard guards themselves.
*
* \param other Another wrapper to swap guards with
*/
void Swap(UniqueHazardPointer& other);
/**
* Clears the hazard guard and returns the hazard previously protected by that
* guard.
*
* \return The hazardous pointer previously protected by this wrapper
*/
Type* ReleaseHazard();
/**
* Check whether the wrapper is active.
*
* \return \c true if the wrapper is initialized and currently protecting some
* hazard, \c false otherwise
*/
bool IsActive() const;
private:
/**
* Sets the 'active' flag of this wrapper.
*
* \param active The new value for the flag
*/
void SetActive(bool active);
/**
* Reset the wrapped hazard guard to a state when it is not protecting any
* hazards.
*/
void ClearHazard();
/**
* Retrieves the hazardous pointer currently protected by the wrapped guard.
*
* \return The hazardous pointer protected by this wrapper
*/
Type* LoadGuardedPointer() const;
/**
* Updates the wrapped guard to protect the specified hazardous pointer.
*
* \param ptr Hazardous pointer to be protected
*/
void StoreGuardedPointer(Type* ptr);
/**
* Check whether the wrapper is initialized (i.e. it wraps some hazard guard)
*
* \return \c true if this wrapper is initialized, \c false otherwise
*/
bool OwnsHazardGuard() const;
/**
* Disable copy construction and assignment.
*/
UniqueHazardPointer(const UniqueHazardPointer&);
UniqueHazardPointer& operator=(const UniqueHazardPointer&);
/**
* Pointer to a hazard table entry (the guard) that is used to store the
* hazardous pointers
*/
AtomicTypePtr* hazard_guard_;
/** Local copy of the guarded pointer value (used for optimization) */
Type* local_ptr_value_;
/** Dummy value used to clear the hazard guard from any hazards */
Type* undefined_guard_;
/** Flag set to true when the guard is protecting some hazardous pointer */
bool active_;
};
} // namespace internal } // namespace internal
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
......
...@@ -201,6 +201,20 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate( ...@@ -201,6 +201,20 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
} }
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1, typename Param2, typename Param3, typename Param4,
typename Param5, typename Param6>
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4,
Param5 const& param5, Param6 const& param6) {
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)Type(param1, param2, param3, param4, param5, param6);
return rawObject;
}
template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() { ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() {
// Deallocate the objects // Deallocate the objects
objectAllocator.deallocate(objects, capacity); objectAllocator.deallocate(objects, capacity);
......
...@@ -185,6 +185,12 @@ class ObjectPool { ...@@ -185,6 +185,12 @@ class ObjectPool {
Type* Allocate(Param1 const& param1, Param2 const& param2, Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4, Param5 const& param5); Param3 const& param3, Param4 const& param4, Param5 const& param5);
template<typename Param1, typename Param2, typename Param3, typename Param4,
typename Param5, typename Param6>
Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4, Param5 const& param5,
Param6 const& param6);
#endif #endif
}; };
} // namespace containers } // namespace containers
......
...@@ -97,7 +97,7 @@ void HazardPointerTest::HazardPointerTest1_ThreadMethod() { ...@@ -97,7 +97,7 @@ void HazardPointerTest::HazardPointerTest1_ThreadMethod() {
PT_ASSERT(success == true); PT_ASSERT(success == true);
embb::base::Atomic<int>* allocated_object_from_different_thread; embb::base::Atomic<int>* allocated_object_from_different_thread(0);
int diff_count = 0; int diff_count = 0;
......
...@@ -37,32 +37,52 @@ class TreeTest : public partest::TestCase { ...@@ -37,32 +37,52 @@ class TreeTest : public partest::TestCase {
TreeTest(); TreeTest();
private: private:
typedef Tree* TreePtr;
typedef typename Tree::KeyType Key;
typedef typename Tree::ValueType Value;
typedef ::std::pair<Key, Value> Element;
typedef ::std::vector<Element> ElementVector;
typedef typename ElementVector::iterator ElementIterator;
class Worker { class Worker {
public: public:
Worker(Tree& tree, size_t thread_id, int num_elements); Worker(TreePtr tree, int thread_id);
void Run(); void InsertReplaceDelete(int num_elements);
void PrepareElements(int num_elements);
void InsertAll();
void ReplaceHalf();
void DeleteAll();
void DeleteHalf();
private: private:
Tree& tree_; Worker(const Worker&);
size_t thread_id_; Worker& operator=(const Worker&);
int num_elements_;
};
typedef int Key; TreePtr tree_;
typedef int Value; int thread_id_;
typedef ::std::pair<Key, Value> Element; ElementVector elements_;
typedef ::std::vector<Element> ElementVector; };
typedef ElementVector::iterator ElementIterator;
static const int TREE_CAPACITY = 2000; static const int TREE_CAPACITY = 100;
static const int NUM_TEST_THREADS = 3; static const int NUM_TEST_THREADS = 4;
static const int NUM_ITERATIONS = 100;
void TreeTestSingleThreadInsertDelete_Pre(); void TreeTestInsertDelete_Pre();
void TreeTestSingleThreadInsertDelete_ThreadMethod(); void TreeTestInsertDeleteSingleThread_ThreadMethod();
void TreeTestMultiThreadInsertDelete_ThreadMethod(); void TreeTestInsertDeleteMultiThread_ThreadMethod();
void TreeTestSingleThreadInsertDelete_Post(); void TreeTestInsertDelete_Post();
void TreeTestConcurrentGet_Pre();
void TreeTestConcurrentGetMinimal_Pre();
void TreeTestConcurrentGet_WriterMethod();
void TreeTestConcurrentGet_ReaderMethod();
void TreeTestConcurrentGet_Post();
void TreeTestBalance_Pre();
void TreeTestBalance_ThreadMethod();
void TreeTestBalance_Post();
Tree *tree_; TreePtr tree_;
Key bad_key_;
Value bad_value_;
}; };
} // namespace test } // namespace test
......
...@@ -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<8> MyNetwork; typedef embb::dataflow::Network<NUM_SLICES> 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,9 +156,7 @@ void SimpleTest::TestBasic() { ...@@ -156,9 +156,7 @@ 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)
// Currently needs to be initialized num_cores, // max queues (default: 16)
// 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,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#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);
...@@ -48,4 +51,7 @@ PT_MAIN("MTAPI C") { ...@@ -48,4 +51,7 @@ 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,14 +5,10 @@ file(GLOB_RECURSE EMBB_MTAPI_CPP_HEADERS "include/*.h") ...@@ -5,14 +5,10 @@ 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,13 +5,10 @@ file(GLOB_RECURSE EMBB_TASKS_CPP_HEADERS "include/*.h") ...@@ -5,13 +5,10 @@ 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")
......
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