diff --git a/CHANGELOG.md b/CHANGELOG.md index b4fa4ed..6a0946d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,37 @@ 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 +- 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 ------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index ed453d0..3f43e85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ cmake_minimum_required (VERSION 2.8.9) # Version number set (EMBB_BASE_VERSION_MAJOR 0) 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 # @@ -100,6 +100,13 @@ else() endif() 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) SetGNUCompilerFlags(compiler_libs compiler_flags) SetVisualStudioCompilerFlags(compiler_libs compiler_flags) diff --git a/README.md b/README.md index 7996d94..bf3b609 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,8 @@ Known Bugs and Limitations is bounded by a predefined but modifiable constant (see functions embb_thread_get_max_count() / embb_thread_set_max_count() and class embb::base::Thread). +- While MTAPI fully supports heterogeneous systems, the algorithms and + dataflow components are currently limited to homogeneous systems. Development and Contribution diff --git a/algorithms_cpp/include/embb/algorithms/invoke.h b/algorithms_cpp/include/embb/algorithms/invoke.h index 10dd818..a3884a4 100644 --- a/algorithms_cpp/include/embb/algorithms/invoke.h +++ b/algorithms_cpp/include/embb/algorithms/invoke.h @@ -49,33 +49,37 @@ typedef embb::base::Function InvokeFunctionType; #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. * * \ingroup CPP_ALGORITHMS_INVOKE */ -template +template void Invoke( Function1 func1, /**< [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. * * Blocks until all of them are done. * * \ingroup CPP_ALGORITHMS_INVOKE */ -template +template void Invoke( Function1 func1, /**< [in] Function object to invoke */ + Function2 func2, + /**< [in] Second function object to invoke */ ..., - const embb::mtapi::ExecutionPolicy & policy - /**< [in] embb::mtapi::ExecutionPolicy to use */ + const embb::tasks::ExecutionPolicy & policy + /**< [in] embb::tasks::ExecutionPolicy to use */ ); #else // DOXYGEN @@ -118,13 +122,6 @@ class TaskWrapper { }; } // namespace internal -template -void Invoke( - Function1 func1, - const embb::tasks::ExecutionPolicy& policy) { - internal::TaskWrapper wrap1(func1, policy); -} - template void Invoke( Function1 func1, @@ -290,12 +287,6 @@ template wrap10(func10, policy); } -template -void Invoke( - Function1 func1) { - Invoke(func1, embb::tasks::ExecutionPolicy()); -} - template void Invoke( Function1 func1, diff --git a/algorithms_cpp/test/invoke_test.cc b/algorithms_cpp/test/invoke_test.cc index 361e73a..1481747 100644 --- a/algorithms_cpp/test/invoke_test.cc +++ b/algorithms_cpp/test/invoke_test.cc @@ -44,7 +44,6 @@ static void Invocable10() {} void InvokeTest::Test() { using embb::algorithms::Invoke; - Invoke(&Invocable1); Invoke(&Invocable1, &Invocable2); Invoke(&Invocable1, &Invocable2, &Invocable3); Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4); @@ -61,4 +60,24 @@ void InvokeTest::Test() { &Invocable6, &Invocable7, &Invocable8, &Invocable9); Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, &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); } diff --git a/base_c/src/condition_variable.c b/base_c/src/condition_variable.c index 81f8c3b..575bb33 100644 --- a/base_c/src/condition_variable.c +++ b/base_c/src/condition_variable.c @@ -83,8 +83,8 @@ int embb_condition_wait_until(embb_condition_t* condition_var, embb_time_t now; embb_time_now(&now); /* Check if absolute timepoint (in milliseconds) still is in the future */ - if (time->seconds * 1000 + time->nanoseconds / 1000000 - - now.seconds * 1000 - now.nanoseconds / 1000000 > 0) { + if ((time->seconds * 1000 + time->nanoseconds / 1000000) + > (now.seconds * 1000 + now.nanoseconds / 1000000)) { /* Convert to (unsigned type) milliseconds and round up */ DWORD time_diff = (DWORD) ( time->seconds * 1000 + time->nanoseconds / 1000000 diff --git a/base_c/test/condition_var_test.cc b/base_c/test/condition_var_test.cc index f292d3c..ad0fe32 100644 --- a/base_c/test/condition_var_test.cc +++ b/base_c/test/condition_var_test.cc @@ -105,7 +105,7 @@ void ConditionVarTest::TestTimedWaitTimeouts() { embb_time_t time; 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_mutex_lock(&mutex); int status = embb_condition_wait_until(&cond, &mutex, &time); diff --git a/base_c/test/time_test.cc b/base_c/test/time_test.cc index 94d6d1c..9797350 100644 --- a/base_c/test/time_test.cc +++ b/base_c/test/time_test.cc @@ -36,6 +36,9 @@ namespace test { TimeTest::TimeTest() { CreateUnit("Time in duration").Add(&TimeTest::TestTimeInDuration, this); + CreateUnit("Monotonicity").Add( + &TimeTest::TestMonotonicity, this, + 1, partest::TestSuite::GetDefaultNumIterations() * 10); } void TimeTest::TestTimeInDuration() { @@ -48,6 +51,20 @@ void TimeTest::TestTimeInDuration() { 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 base } // namespace embb diff --git a/base_c/test/time_test.h b/base_c/test/time_test.h index 629befc..419d26d 100644 --- a/base_c/test/time_test.h +++ b/base_c/test/time_test.h @@ -42,9 +42,14 @@ class TimeTest : public partest::TestCase { private: /** - * Tests time in duration method. + * Tests time-in-duration method. */ void TestTimeInDuration(); + + /** + * Tests that succeedingly taken times are monotonously increasing. + */ + void TestMonotonicity(); }; } // namespace test diff --git a/base_cpp/include/embb/base/atomic.h b/base_cpp/include/embb/base/atomic.h index 990b2e0..d39f494 100644 --- a/base_cpp/include/embb/base/atomic.h +++ b/base_cpp/include/embb/base/atomic.h @@ -478,7 +478,7 @@ class Atomic : public embb::base::internal::atomic:: public: Atomic() : embb::base::internal::atomic:: AtomicPointer() {} - Atomic(BaseType* p) : embb::base::internal::atomic:: + explicit Atomic(BaseType* p) : embb::base::internal::atomic:: AtomicPointer(p) {} BaseType* operator=(BaseType* p) { diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_base.h b/base_cpp/include/embb/base/internal/atomic/atomic_base.h index 258a686..1be01e5 100644 --- a/base_cpp/include/embb/base/internal/atomic/atomic_base.h +++ b/base_cpp/include/embb/base/internal/atomic/atomic_base.h @@ -177,8 +177,7 @@ CompareAndSwap(BaseType& expected, BaseType desired) { compare_and_swap(&AtomicValue, &native_expected, native_desired)) !=0 ? true : false; - if (!return_val) - expected = Load(); + memcpy(&expected, &native_expected, sizeof(expected)); return return_val; } diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h b/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h index d7fd970..a689054 100644 --- a/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h +++ b/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h @@ -65,8 +65,8 @@ class AtomicPointer : public AtomicArithmetic { bool IsPointer() const; // The methods below are documented in atomic.h - BaseType* operator->(); - BaseType& operator*(); + BaseType* operator->() const; + BaseType& operator*() const; }; template @@ -93,13 +93,13 @@ inline bool AtomicPointer:: template inline BaseType* AtomicPointer:: - operator->() { + operator->() const { return this->Load(); } template inline BaseType& AtomicPointer:: - operator*() { + operator*() const { return *(this->Load()); } diff --git a/base_cpp/include/embb/base/internal/mutex-inl.h b/base_cpp/include/embb/base/internal/mutex-inl.h index 0d9b336..86f66ac 100644 --- a/base_cpp/include/embb/base/internal/mutex-inl.h +++ b/base_cpp/include/embb/base/internal/mutex-inl.h @@ -28,6 +28,7 @@ #define EMBB_BASE_INTERNAL_MUTEX_INL_H_ #include +#include namespace embb { namespace base { @@ -95,8 +96,8 @@ void UniqueLock::Unlock() { template void UniqueLock::Swap(UniqueLock& other) { - locked_ = other.locked_; - mutex_ = other.Release(); + std::swap(mutex_, other.mutex_); + std::swap(locked_, other.locked_); } template diff --git a/base_cpp/include/embb/base/mutex.h b/base_cpp/include/embb/base/mutex.h index fe51706..1d63027 100644 --- a/base_cpp/include/embb/base/mutex.h +++ b/base_cpp/include/embb/base/mutex.h @@ -29,7 +29,6 @@ #include #include -#include namespace embb { namespace base { @@ -440,11 +439,11 @@ class UniqueLock { void Unlock(); /** - * Transfers ownership of a mutex to this lock. + * Exchanges ownership of the wrapped mutex with another lock. */ void Swap( UniqueLock& other - /**< [IN/OUT] Lock from which ownership shall be transferred */ + /**< [IN/OUT] The lock to exchange ownership with */ ); /** @@ -484,71 +483,6 @@ class UniqueLock { 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 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 embb diff --git a/base_cpp/test/mutex_test.cc b/base_cpp/test/mutex_test.cc index 17e5c9e..48cc0a9 100644 --- a/base_cpp/test/mutex_test.cc +++ b/base_cpp/test/mutex_test.cc @@ -191,13 +191,21 @@ void MutexTest::TestUniqueLock() { } { // Test lock swapping - UniqueLock<> lock1; - UniqueLock<> lock2(mutex_); - PT_EXPECT_EQ(lock1.OwnsLock(), false); - PT_EXPECT_EQ(lock2.OwnsLock(), true); - lock1.Swap(lock2); + UniqueLock<> lock1(mutex_); 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); } } diff --git a/containers_cpp/CMakeLists.txt b/containers_cpp/CMakeLists.txt index 014d2d3..f21408e 100644 --- a/containers_cpp/CMakeLists.txt +++ b/containers_cpp/CMakeLists.txt @@ -12,12 +12,25 @@ GroupSourcesMSVC(test) set (EMBB_CONTAINERS_CPP_INCLUDE_DIRS "include" "src" "test") include_directories(${EMBB_CONTAINERS_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include ${CMAKE_CURRENT_SOURCE_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) if (BUILD_TESTS STREQUAL ON) @@ -30,4 +43,5 @@ endif() install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb DESTINATION include FILES_MATCHING PATTERN "*.h") +install(FILES ${CHROMATIC_TREE_GENERATED_HEADER} DESTINATION include/embb/containers/internal) install(TARGETS embb_containers_cpp DESTINATION lib) diff --git a/containers_cpp/generator/CMakeLists.txt b/containers_cpp/generator/CMakeLists.txt new file mode 100644 index 0000000..1e3bf2f --- /dev/null +++ b/containers_cpp/generator/CMakeLists.txt @@ -0,0 +1 @@ +add_executable(chromatic_tree_generator chromatic_tree_generator.cc chromatic_tree_operations.h) diff --git a/containers_cpp/generator/chromatic_tree_generator.cc b/containers_cpp/generator/chromatic_tree_generator.cc new file mode 100644 index 0000000..5a0abe4 --- /dev/null +++ b/containers_cpp/generator/chromatic_tree_generator.cc @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2014-2015, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#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 \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(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"); +}; diff --git a/containers_cpp/generator/chromatic_tree_operations.h b/containers_cpp/generator/chromatic_tree_operations.h new file mode 100644 index 0000000..bf61726 --- /dev/null +++ b/containers_cpp/generator/chromatic_tree_operations.h @@ -0,0 +1,269 @@ +#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 + diff --git a/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h b/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h index 3873dba..edb026e 100644 --- a/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h +++ b/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h @@ -182,7 +182,9 @@ HazardPointerThreadEntry::~HazardPointerThreadEntry() { } template< typename GuardType > -GuardType HazardPointerThreadEntry::GetGuard(int pos) const { +typename HazardPointerThreadEntry::AtomicGuard& +HazardPointerThreadEntry::GetGuard(int pos) const { + assert(pos >= 0 && pos < guards_per_thread); return guarded_pointers[pos]; } @@ -393,6 +395,12 @@ void HazardPointer< GuardType >::GuardPointer(int guardPosition, } template< typename GuardType > +typename HazardPointer< GuardType >::AtomicGuard& +HazardPointer< GuardType >::GetGuardedPointer(int guardPosition) { + return GetHazardPointerElementForCurrentThread().GetGuard(guardPosition); +} + +template< typename GuardType > void HazardPointer< GuardType >::EnqueuePointerForDeletion( GuardType guardedElement) { GetHazardPointerElementForCurrentThread().AddRetired(guardedElement); @@ -410,6 +418,124 @@ void HazardPointer< GuardType >::EnqueuePointerForDeletion( template const double embb::containers::internal::HazardPointer:: RETIRE_THRESHOLD = 1.25f; + +template +UniqueHazardPointer:: +UniqueHazardPointer() + : hazard_guard_(NULL), + local_ptr_value_(NULL), + undefined_guard_(NULL), + active_(false) {} + +template +UniqueHazardPointer:: +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 +UniqueHazardPointer::~UniqueHazardPointer() { + if (IsActive()) ClearHazard(); +} + +template +bool UniqueHazardPointer::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 +void UniqueHazardPointer::ProtectSafe(Type* safe_ptr) { + assert(OwnsHazardGuard()); + StoreGuardedPointer(safe_ptr); + SetActive(true); +} + +template +UniqueHazardPointer::operator Type* () const { + assert(IsActive()); + return LoadGuardedPointer(); +} + +template +Type* UniqueHazardPointer::operator->() const { + assert(IsActive()); + return LoadGuardedPointer(); +} + +template +Type& UniqueHazardPointer::operator*() const { + assert(IsActive()); + return *(LoadGuardedPointer()); +} + +template +void UniqueHazardPointer::AdoptHazard(const UniqueHazardPointer& other) { + assert(OwnsHazardGuard()); + StoreGuardedPointer(other.LoadGuardedPointer()); + SetActive(other.active_); +} + +template +void UniqueHazardPointer::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 +Type* UniqueHazardPointer::ReleaseHazard() { + assert(IsActive()); + Type* released_hazard = LoadGuardedPointer(); + ClearHazard(); + SetActive(false); + return released_hazard; +} + +template +bool UniqueHazardPointer::IsActive() const { + return active_; +} + +template +void UniqueHazardPointer::SetActive(bool active) { + active_ = active; +} + +template +void UniqueHazardPointer::ClearHazard() { + StoreGuardedPointer(undefined_guard_); +} + +template +Type* UniqueHazardPointer::LoadGuardedPointer() const { + return local_ptr_value_; +} + +template +void UniqueHazardPointer::StoreGuardedPointer(Type* ptr) { + hazard_guard_->Store(ptr); + local_ptr_value_ = ptr; +} + +template +bool UniqueHazardPointer::OwnsHazardGuard() const { + return hazard_guard_ != NULL; +} + } // namespace internal } // namespace containers } // namespace embb diff --git a/containers_cpp/include/embb/containers/internal/hazard_pointer.h b/containers_cpp/include/embb/containers/internal/hazard_pointer.h index e1e5f49..ccd3fe3 100644 --- a/containers_cpp/include/embb/containers/internal/hazard_pointer.h +++ b/containers_cpp/include/embb/containers/internal/hazard_pointer.h @@ -238,6 +238,8 @@ class HazardPointerThreadEntry { HazardPointerThreadEntry & operator= (const HazardPointerThreadEntry&); public: + typedef embb::base::Atomic< GuardType > AtomicGuard; + /** * Checks if current thread is active (with respect to participating in hazard * pointer management) @@ -318,7 +320,7 @@ class HazardPointerThreadEntry { * Gets the guard at the specified position. * Positions are numbered, beginning with 0. */ - GuardType GetGuard( + AtomicGuard& GetGuard( int pos /**< [IN] Position of the guard */) const; @@ -467,6 +469,8 @@ class HazardPointer { can be deleted*/); public: + typedef typename HazardPointerThreadEntry_t::AtomicGuard AtomicGuard; + /** * Gets the capacity of one retired list * @@ -516,12 +520,186 @@ class HazardPointer { * Guards \c guardedElement with the guard at position \c guardPosition */ void GuardPointer(int guardPosition, GuardType guardedElement); + + AtomicGuard& GetGuardedPointer(int guardPosition); + /** * Enqueue a pointer for deletion. It is added to the retired list and * deleted when no thread accesses it anymore. */ 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 +class UniqueHazardPointer { + public: + /** Typedef for a atomic pointer to the guarded object. */ + typedef embb::base::Atomic 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 containers } // namespace embb diff --git a/containers_cpp/include/embb/containers/internal/lock_free_chromatic_tree-inl.h b/containers_cpp/include/embb/containers/internal/lock_free_chromatic_tree-inl.h index 5d5cca2..931be27 100644 --- a/containers_cpp/include/embb/containers/internal/lock_free_chromatic_tree-inl.h +++ b/containers_cpp/include/embb/containers/internal/lock_free_chromatic_tree-inl.h @@ -30,406 +30,1089 @@ #include #include +#ifdef EMBB_DEBUG +static const size_t INVALID_POINTER = static_cast(-1); +# define VERIFY_ADDRESS(addr) assert(reinterpret_cast((addr)) != \ + INVALID_POINTER) +#else +# define VERIFY_ADDRESS(address) ((void)0) +#endif + namespace embb { namespace containers { namespace internal { template ChromaticTreeNode:: -ChromaticTreeNode(const Key& key, const Value& value, const int& weight, - ChromaticTreeNode* const & left, - ChromaticTreeNode* const & right) +ChromaticTreeNode(const Key& key, const Value& value, int weight, + Node* left, Node* right, Operation* operation) : key_(key), value_(value), - weight_(weight), + weight_(weight < 0 ? -weight : weight), + is_leaf_(left == NULL), + is_sentinel_(weight < 0), left_(left), - right_(right) {} + right_(right), + retired_(false), + operation_(operation) {} template ChromaticTreeNode:: -ChromaticTreeNode(const Key& key, const Value& value) +ChromaticTreeNode(const Key& key, const Value& value, int weight, + Operation* operation) : key_(key), value_(value), - weight_(1), + weight_(weight < 0 ? -weight : weight), + is_leaf_(true), + is_sentinel_(weight < 0), left_(NULL), - right_(NULL) {} - -template -ChromaticTreeNode:: -ChromaticTreeNode(const ChromaticTreeNode& other) - : key_(other.key_), - value_(other.value_), - weight_(other.weight_), - left_(other.left_), - right_(other.right_) {} + right_(NULL), + retired_(false), + operation_(operation) {} template -const Key& ChromaticTreeNode::GetKey() const { +inline const Key& ChromaticTreeNode::GetKey() const { return key_; } template -const Value& ChromaticTreeNode::GetValue() const { +inline const Value& ChromaticTreeNode::GetValue() const { return value_; } template -const int& ChromaticTreeNode::GetWeight() const { +inline int ChromaticTreeNode::GetWeight() const { return weight_; } template -ChromaticTreeNode*& ChromaticTreeNode::GetLeft() { +inline typename ChromaticTreeNode::AtomicNodePtr& +ChromaticTreeNode::GetLeft() { return left_; } template -ChromaticTreeNode*& ChromaticTreeNode::GetRight() { +inline typename ChromaticTreeNode::Node* +ChromaticTreeNode::GetLeft() const { + return left_.Load(); +} + +template +inline typename ChromaticTreeNode::AtomicNodePtr& +ChromaticTreeNode::GetRight() { return right_; } +template +inline typename ChromaticTreeNode::Node* +ChromaticTreeNode::GetRight() const { + return right_.Load(); +} + +template +inline bool ChromaticTreeNode::IsLeaf() const { + return is_leaf_; +} + +template +inline bool ChromaticTreeNode::IsSentinel() const { + return is_sentinel_; +} + +template +bool ChromaticTreeNode:: +ReplaceChild(Node* old_child, Node* new_child) { + bool replaced = false; + + if (left_ == old_child) { + replaced = left_.CompareAndSwap(old_child, new_child); + } else if (right_ == old_child) { + replaced = right_.CompareAndSwap(old_child, new_child); + } + + return replaced; +} + +template +inline void ChromaticTreeNode::Retire() { + retired_ = true; +} + +template +inline bool ChromaticTreeNode::IsRetired() const { + return retired_; +} + +template +inline typename ChromaticTreeNode::AtomicOperationPtr& +ChromaticTreeNode::GetOperation() { + return operation_; +} + + + +template +ChromaticTreeOperation::ChromaticTreeOperation(bool is_dummy) + : state_(is_dummy ? STATE_COMMITTED : STATE_FREEZING), + root_(NULL), + root_operation_(NULL), + num_old_nodes_(0), + new_child_(NULL) +#ifdef EMBB_DEBUG + , deleted_(false) +#endif +{ + for (size_t i = 0; i < MAX_NODES; ++i) { + old_nodes_[i] = NULL; + old_operations_[i] = NULL; + } +} + +template +void ChromaticTreeOperation:: +SetRoot(Node* root, Operation* root_operation) { + root_ = root; + root_operation_ = root_operation; +} + +template +void ChromaticTreeOperation:: +SetOldNodes(Node* node1, Operation* operation1) { + num_old_nodes_ = 1; + old_nodes_[0] = node1; + old_operations_[0] = operation1; +} + +template +void ChromaticTreeOperation:: +SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2) { + num_old_nodes_ = 2; + old_nodes_[0] = node1; + old_operations_[0] = operation1; + old_nodes_[1] = node2; + old_operations_[1] = operation2; +} + +template +void ChromaticTreeOperation:: +SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2, + Node* node3, Operation* operation3) { + num_old_nodes_ = 3; + old_nodes_[0] = node1; + old_operations_[0] = operation1; + old_nodes_[1] = node2; + old_operations_[1] = operation2; + old_nodes_[2] = node3; + old_operations_[2] = operation3; +} + +template +void ChromaticTreeOperation:: +SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2, + Node* node3, Operation* operation3, + Node* node4, Operation* operation4) { + num_old_nodes_ = 4; + old_nodes_[0] = node1; + old_operations_[0] = operation1; + old_nodes_[1] = node2; + old_operations_[1] = operation2; + old_nodes_[2] = node3; + old_operations_[2] = operation3; + old_nodes_[3] = node4; + old_operations_[3] = operation4; +} + +template +void ChromaticTreeOperation:: +SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2, + Node* node3, Operation* operation3, + Node* node4, Operation* operation4, + Node* node5, Operation* operation5) { + num_old_nodes_ = 5; + old_nodes_[0] = node1; + old_operations_[0] = operation1; + old_nodes_[1] = node2; + old_operations_[1] = operation2; + old_nodes_[2] = node3; + old_operations_[2] = operation3; + old_nodes_[3] = node4; + old_operations_[3] = operation4; + old_nodes_[4] = node5; + old_operations_[4] = operation5; +} + +template +void ChromaticTreeOperation::SetNewChild(Node* new_child) { + new_child_ = new_child; +} + +template +bool ChromaticTreeOperation:: +Help(AtomicNodePtr& node_guard, AtomicOperationPtr& oper_guard) { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + // Freezing step + if (!FreezeAll(node_guard, oper_guard)) { + return IsCommitted(); + } + + // All frozen step + if (!SwitchState(STATE_FREEZING, STATE_ALL_FROZEN)) { + return IsCommitted(); + } + + // At this point operation may no longer fail - complete it + HelpCommit(node_guard); + + return true; +} + +template +void ChromaticTreeOperation::HelpCommit(AtomicNodePtr& node_guard) { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + HazardNodePtr node_hp(node_guard); + + // Mark step (retire old nodes) + for (size_t i = 0; i < num_old_nodes_; ++i) { + node_hp.ProtectSafe(old_nodes_[i]); + if (IsCommitted()) return; + old_nodes_[i]->Retire(); + } + + // Update step + node_hp.ProtectSafe(root_); + if (IsCommitted()) return; + root_->ReplaceChild(old_nodes_[0], new_child_); + + // Commit step + SwitchState(STATE_ALL_FROZEN, STATE_COMMITTED); +} + +template +void ChromaticTreeOperation::HelpAbort(Node* node) { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + for (size_t i = 0; i < num_old_nodes_; ++i) { + if (old_nodes_[i] == node) { + Unfreeze(old_nodes_[i], old_operations_[i]); + break; + } + } +} + +template +inline bool ChromaticTreeOperation::IsAborted() { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + return state_ == STATE_ABORTED; +} + +template +inline bool ChromaticTreeOperation::IsInProgress() { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + State state = state_.Load(); + return (state != STATE_ABORTED && state != STATE_COMMITTED); +} + +template +inline bool ChromaticTreeOperation::IsCommitted() { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + return state_ == STATE_COMMITTED; +} + +template +void ChromaticTreeOperation::CleanUp(Operation* retired_dummy) { +#ifdef EMBB_DEBUG + assert(!deleted_); +#endif + assert(!IsInProgress()); + + if (IsCommitted()) { + for (size_t i = 0; i < num_old_nodes_; ++i) { + assert(old_nodes_[i]->GetOperation() == this); + old_nodes_[i]->GetOperation() = retired_dummy; + } + } +} + +#ifdef EMBB_DEBUG +template +void ChromaticTreeOperation::SetDeleted() { + deleted_ = true; +} +#endif + +template +bool ChromaticTreeOperation::IsRollingBack() { + State state = state_.Load(); + return (state == STATE_ROLLBACK || state == STATE_ABORTED); +} + +template +bool ChromaticTreeOperation::IsFreezing() { + return state_ == STATE_FREEZING; +} + +template +bool ChromaticTreeOperation::IsAllFrozen() { + State state = state_.Load(); + return (state == STATE_ALL_FROZEN || state == STATE_COMMITTED); +} + +template +bool ChromaticTreeOperation:: +FreezeAll(AtomicNodePtr& node_guard, AtomicOperationPtr& oper_guard) { + if (IsFreezing()) { + HazardNodePtr node_hp(node_guard); + HazardOperationPtr oper_hp(oper_guard); + + node_hp.ProtectSafe(root_); + oper_hp.ProtectSafe(root_operation_); + + if (IsFreezing()) { + Freeze(root_, root_operation_); + } + + for (size_t i = 0; i < num_old_nodes_; ++i) { + node_hp.ProtectSafe(old_nodes_[i]); + oper_hp.ProtectSafe(old_operations_[i]); + if (!IsFreezing()) break; + + Freeze(old_nodes_[i], old_operations_[i]); + } + } + + if (IsRollingBack()) { + UnfreezeAll(node_guard); + return false; + } + + return true; +} + +template +bool ChromaticTreeOperation:: +Freeze(Node* node, Operation* operation) { + bool freezed = node->GetOperation().CompareAndSwap(operation, this); + + if (!freezed && operation != this && !IsAllFrozen()) { + // Node is frozen for another operation - abort "this" + SwitchState(STATE_FREEZING, STATE_ROLLBACK); + + // If the "operation" was aborted and rolled back, some other thread could + // have helped "this" to freeze all nodes, and the previous "SwitchState" + // would fail + return IsAllFrozen(); + } + + if (freezed && IsRollingBack()) { + // "False-positive" CAS: "this" was aborted and the "operation" might have + // been rolled back; While "node" is hazard protected, unfreeze it again + Unfreeze(node, operation); + return false; + } + + return true; +} + +template +void ChromaticTreeOperation:: +UnfreezeAll(AtomicNodePtr& node_guard) { + HazardNodePtr node_hp(node_guard); + + node_hp.ProtectSafe(root_); + if (IsAborted()) return; + + Unfreeze(root_, root_operation_); + + for (size_t i = 0; i < num_old_nodes_; ++i) { + node_hp.ProtectSafe(old_nodes_[i]); + if (IsAborted()) return; + + Unfreeze(old_nodes_[i], old_operations_[i]); + } + + SwitchState(STATE_ROLLBACK, STATE_ABORTED); +} + +template +void ChromaticTreeOperation:: +Unfreeze(Node* node, Operation* operation) { + Operation* expected = this; + node->GetOperation().CompareAndSwap(expected, operation); +} + +template +bool ChromaticTreeOperation:: +SwitchState(State old_state, State new_state) { + if (state_ != new_state) { + bool switched = state_.CompareAndSwap(old_state, new_state); + + if (!switched && old_state != new_state) { + return false; + } + } + + return true; +} + } // namespace internal -template -ChromaticTree:: +template +ChromaticTree:: ChromaticTree(size_t capacity, Key undefined_key, Value undefined_value, Compare compare) - : undefined_key_(undefined_key), +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:4355) +#endif + : node_hazard_manager_( + embb::base::Function(*this, &ChromaticTree::FreeNode), + NULL, HIDX_MAX), + operation_hazard_manager_( + embb::base::Function(*this, + &ChromaticTree::FreeOperation), + NULL, HIDX_MAX), +#ifdef EMBB_PLATFORM_COMPILER_MSVC +#pragma warning(pop) +#endif + undefined_key_(undefined_key), undefined_value_(undefined_value), compare_(compare), capacity_(capacity), - node_pool_(2 + 5 + 2 * capacity_) { - entry_ = node_pool_.Allocate(undefined_key_, undefined_value_); - NodePtr sentinel = node_pool_.Allocate(undefined_key_, undefined_value_); - entry_->GetLeft() = sentinel; + node_pool_(2 + 5 + 2 * capacity_ + + node_hazard_manager_.GetRetiredListMaxSize() * + embb::base::Thread::GetThreadsMaxCount()), + operation_pool_(2 + 5 + 2 * capacity_ + + operation_hazard_manager_.GetRetiredListMaxSize() * + embb::base::Thread::GetThreadsMaxCount()), + initial_operation_dummy_(true), + retired_operation_dummy_(true), + entry_(node_pool_.Allocate(undefined_key_, undefined_value_, -1, + node_pool_.Allocate(undefined_key_, + undefined_value_, + -1, + &initial_operation_dummy_), + static_cast(NULL), + &initial_operation_dummy_)) { + assert(entry_ != NULL); + assert(entry_->GetLeft() != NULL); } -template -ChromaticTree:: +template +ChromaticTree:: ~ChromaticTree() { Destruct(entry_->GetLeft()); - node_pool_.Free(entry_); + FreeNode(entry_); } -template -bool ChromaticTree:: +template +bool ChromaticTree:: Get(const Key& key, Value& value) { - ReaderLockGuard guard(readwrite_lock_.GetReader()); - - NodePtr leaf; - Search(key, leaf); + HazardNodePtr grandparent(GetNodeGuard(HIDX_GRANDPARENT)); + HazardNodePtr parent(GetNodeGuard(HIDX_PARENT)); + HazardNodePtr leaf(GetNodeGuard(HIDX_LEAF)); + Search(key, leaf, parent, grandparent); - bool keys_are_equal = !(compare_(key, leaf->GetKey()) || + bool keys_are_equal = !leaf->IsSentinel() && + !(compare_(key, leaf->GetKey()) || compare_(leaf->GetKey(), key)); - if (!IsSentinel(leaf) && keys_are_equal) { + + if (keys_are_equal) { value = leaf->GetValue(); - return true; - } else { - return false; } + + return keys_are_equal; } -template -bool ChromaticTree:: +template +bool ChromaticTree:: TryInsert(const Key& key, const Value& value) { Value old_value; return TryInsert(key, value, old_value); } -template -bool ChromaticTree:: +template +bool ChromaticTree:: TryInsert(const Key& key, const Value& value, Value& old_value) { - WriterLockGuard guard(readwrite_lock_.GetWriter()); - - NodePtr leaf, parent; - Search(key, leaf, parent); - - assert(HasChild(parent, leaf)); - - NodePtr new_parent; + Node* new_leaf = NULL; + Node* new_sibling = NULL; + Node* new_parent = NULL; + bool insertion_succeeded = false; bool added_violation = false; - NodePtr new_leaf = node_pool_.Allocate(key, value); - if (new_leaf == NULL) { - return false; - } - - bool keys_are_equal = !(compare_(key, leaf->GetKey()) || - compare_(leaf->GetKey(), key)); - if (!IsSentinel(leaf) && keys_are_equal) { - old_value = leaf->GetValue(); - new_parent = new_leaf; - } else { - old_value = undefined_value_; - - NodePtr new_sibling = node_pool_.Allocate(*leaf); - if (new_sibling == NULL) { - node_pool_.Free(new_leaf); - return false; - } - - int new_weight = (HasFixedWeight(leaf)) ? 1 : (leaf->GetWeight() - 1); - if (IsSentinel(leaf) || compare_(key, leaf->GetKey())) { - new_parent = node_pool_.Allocate( - leaf->GetKey(), undefined_value_, new_weight, new_leaf, new_sibling); + while (!insertion_succeeded) { + HazardNodePtr grandparent(GetNodeGuard(HIDX_GRANDPARENT)); + HazardNodePtr parent(GetNodeGuard(HIDX_PARENT)); + HazardNodePtr leaf(GetNodeGuard(HIDX_LEAF)); + Search(key, leaf, parent, grandparent); + + // Protect the parent + HazardOperationPtr parent_op(GetOperationGuard(HIDX_PARENT)); + if (!WeakLLX(parent, parent_op)) continue; + // Verify that the leaf is still the parent's child + if (!HasChild(parent, leaf)) continue; + + // Protect the leaf + HazardOperationPtr leaf_op(GetOperationGuard(HIDX_LEAF)); + if (!WeakLLX(leaf, leaf_op)) continue; + + bool keys_are_equal = !leaf->IsSentinel() && + !(compare_(key, leaf->GetKey()) || + compare_(leaf->GetKey(), key)); + if (keys_are_equal) { + // Reached leaf has a matching key: replace it with a new copy + old_value = leaf->GetValue(); + new_parent = node_pool_.Allocate(key, value, leaf->GetWeight(), + &initial_operation_dummy_); + if (new_parent == NULL) break; + + // Reached leaf has a different key: add a new leaf } else { - new_parent = node_pool_.Allocate( - key, undefined_value_, new_weight, new_sibling, new_leaf); + old_value = undefined_value_; + + new_leaf = node_pool_.Allocate(key, value, 1, + &initial_operation_dummy_); + if (new_leaf == NULL) break; + new_sibling = node_pool_.Allocate(leaf->GetKey(), leaf->GetValue(), 1, + &initial_operation_dummy_); + if (new_sibling == NULL) break; + + int new_weight = + leaf->IsSentinel() ? -1 : + parent->IsSentinel() ? 1 : + (leaf->GetWeight() - 1); + if (leaf->IsSentinel() || compare_(key, leaf->GetKey())) { + new_parent = node_pool_.Allocate(leaf->GetKey(), undefined_value_, + new_weight, new_leaf, new_sibling, + &initial_operation_dummy_); + } else { + new_parent = node_pool_.Allocate(key, undefined_value_, + new_weight, new_sibling, new_leaf, + &initial_operation_dummy_); + } + if (new_parent == NULL) break; } - if (new_parent == NULL) { - node_pool_.Free(new_leaf); - node_pool_.Free(new_sibling); - return false; + // Create and fill the operation object + HazardOperationPtr insert_op(GetOperationGuard(HIDX_CURRENT_OP)); + insert_op.ProtectSafe(operation_pool_.Allocate()); + if (insert_op == NULL) break; + insert_op->SetRoot(parent, parent_op); + insert_op->SetOldNodes(leaf, leaf_op); + insert_op->SetNewChild(new_parent); + + // Execute operation + insertion_succeeded = insert_op->Help(GetNodeGuard(HIDX_HELPING), + GetOperationGuard(HIDX_HELPING)); + insert_op->CleanUp(&retired_operation_dummy_); + + // If operation failed + if (!insertion_succeeded) { + // Retire failed operation + RetireOperation(insert_op); + // Delete new nodes + FreeNode(new_parent); new_parent = NULL; + if (!keys_are_equal) { + FreeNode(new_leaf); new_leaf = NULL; + FreeNode(new_sibling); new_sibling = NULL; + } + // Restart from scratch + continue; } - added_violation = (parent->GetWeight() == 0 && new_weight == 0); - } - - NodePtr& new_subtree_ptr = GetPointerToChild(parent, leaf); - new_subtree_ptr = new_parent; + // Retire old operation objects + RetireOperation(parent_op); + RetireOperation(leaf_op); + // Retire old nodes + RetireNode(leaf); - node_pool_.Free(leaf); + added_violation = (parent->GetWeight() == 0 && + new_parent->GetWeight() == 0); + } - if (added_violation) { - CleanUp(key); + if (insertion_succeeded) { + if (added_violation) CleanUp(key); + } else { + if (new_leaf != NULL) FreeNode(new_leaf); + if (new_sibling != NULL) FreeNode(new_sibling); + if (new_parent != NULL) FreeNode(new_parent); } - return true; + return insertion_succeeded; } -template -bool ChromaticTree:: +template +bool ChromaticTree:: TryDelete(const Key& key) { Value old_value; return TryDelete(key, old_value); } -template -bool ChromaticTree:: +template +bool ChromaticTree:: TryDelete(const Key& key, Value& old_value) { - WriterLockGuard guard(readwrite_lock_.GetWriter()); + Node* new_leaf = NULL; + bool deletion_succeeded = false; + bool added_violation = false; - NodePtr leaf, parent, grandparent; - Search(key, leaf, parent, grandparent); + while (!deletion_succeeded) { + HazardNodePtr grandparent(GetNodeGuard(HIDX_GRANDPARENT)); + HazardNodePtr parent(GetNodeGuard(HIDX_PARENT)); + HazardNodePtr leaf(GetNodeGuard(HIDX_LEAF)); + Search(key, leaf, parent, grandparent); + + // Reached leaf has a different key - nothing to delete + if (leaf->IsSentinel() || (compare_(key, leaf->GetKey()) || + compare_(leaf->GetKey(), key))) { + old_value = undefined_value_; + deletion_succeeded = true; + break; + } - bool keys_are_equal = !(compare_(key, leaf->GetKey()) || - compare_(leaf->GetKey(), key)); - if (IsSentinel(leaf) || !keys_are_equal) { - old_value = undefined_value_; - return false; - } + // Protect the grandparent + HazardOperationPtr grandparent_op(GetOperationGuard(HIDX_GRANDPARENT)); + if (!WeakLLX(grandparent, grandparent_op)) continue; + // Verify that the parent is still the child of grandparent + if (!HasChild(grandparent, parent)) continue; - assert(HasChild(grandparent, parent)); - assert(HasChild(parent, leaf)); + // Protect the parent + HazardOperationPtr parent_op(GetOperationGuard(HIDX_PARENT)); + if (!WeakLLX(parent, parent_op)) continue; - NodePtr sibling = ((parent->GetLeft() == leaf) ? - parent->GetRight() : parent->GetLeft()); + // Get the sibling (and protect it with hazard pointer) + HazardNodePtr sibling(GetNodeGuard(HIDX_SIBLING)); + sibling.ProtectHazard((parent->GetLeft() == leaf) ? + parent->GetRight() : parent->GetLeft()); + if (parent->IsRetired() || !sibling.IsActive()) continue; + VERIFY_ADDRESS(static_cast(sibling)); - int new_weight = (HasFixedWeight(parent)) ? - 1 : (parent->GetWeight() + sibling->GetWeight()); - bool added_violation = (new_weight > 1); + // Verify that the leaf is still the parent's child + if (!HasChild(parent, leaf)) continue; - NodePtr new_leaf = node_pool_.Allocate( - sibling->GetKey(), sibling->GetValue(), new_weight, - sibling->GetLeft(), sibling->GetRight()); - assert((new_leaf != NULL) && "No nodes available for replacement!"); + // Protect the sibling + HazardOperationPtr sibling_op(GetOperationGuard(HIDX_SIBLING)); + if (!WeakLLX(sibling, sibling_op)) continue; - NodePtr& new_subtree_ptr = GetPointerToChild(grandparent, parent); - new_subtree_ptr = new_leaf; + // Protect the leaf + HazardOperationPtr leaf_op(GetOperationGuard(HIDX_LEAF)); + if (!WeakLLX(leaf, leaf_op)) continue; - old_value = leaf->GetValue(); + int new_weight = + parent->IsSentinel() ? -1 : + grandparent->IsSentinel() ? 1 : + (parent->GetWeight() + sibling->GetWeight()); - node_pool_.Free(leaf); - node_pool_.Free(sibling); - node_pool_.Free(parent); + new_leaf = node_pool_.Allocate( + sibling->GetKey(), sibling->GetValue(), new_weight, + sibling->GetLeft(), sibling->GetRight(), &initial_operation_dummy_); + if (new_leaf == NULL) break; - if (added_violation) { - CleanUp(key); + old_value = leaf->GetValue(); + + // Create and fill the operation object + HazardOperationPtr delete_op(GetOperationGuard(HIDX_CURRENT_OP)); + delete_op.ProtectSafe(operation_pool_.Allocate()); + if (delete_op == NULL) break; + delete_op->SetRoot(grandparent, grandparent_op); + delete_op->SetOldNodes(parent, parent_op, leaf, leaf_op, sibling, sibling_op); + delete_op->SetNewChild(new_leaf); + + // Execute operation + deletion_succeeded = delete_op->Help(GetNodeGuard(HIDX_HELPING), + GetOperationGuard(HIDX_HELPING)); + delete_op->CleanUp(&retired_operation_dummy_); + + // If operation failed + if (!deletion_succeeded) { + // Retire failed operation + RetireOperation(delete_op); + // Delete new nodes + FreeNode(new_leaf); + // Restart from scratch + continue; + } + + // Retire old operation objects + RetireOperation(grandparent_op); + RetireOperation(parent_op); + RetireOperation(leaf_op); + RetireOperation(sibling_op); + // Retire old nodes + RetireNode(parent); + RetireNode(leaf); + RetireNode(sibling); + + added_violation = (new_weight > 1); } - return true; + if (deletion_succeeded) { + if (added_violation) CleanUp(key); + } else { + if (new_leaf != NULL) FreeNode(new_leaf); + } + + return deletion_succeeded; } -template -size_t ChromaticTree:: -GetCapacity() { +template +size_t ChromaticTree:: +GetCapacity() const { return capacity_; } -template -const Value& ChromaticTree:: -GetUndefinedValue() { +template +const Value& ChromaticTree:: +GetUndefinedValue() const { return undefined_value_; } -template -bool ChromaticTree:: -IsEmpty() { - return IsLeaf(entry_->GetLeft()); +template +inline bool ChromaticTree:: +IsEmpty() const { + return entry_->GetLeft()->IsLeaf(); +} + +template +void ChromaticTree:: +Search(const Key& key, HazardNodePtr& leaf, HazardNodePtr& parent, + HazardNodePtr& grandparent) { + bool reached_leaf = false; + + while (!reached_leaf) { + grandparent.ProtectSafe(entry_); + parent.ProtectSafe(entry_); + leaf.ProtectSafe(entry_); + + reached_leaf = leaf->IsLeaf(); + while (!reached_leaf) { + grandparent.AdoptHazard(parent); + parent.AdoptHazard(leaf); + + AtomicNodePtr& next_leaf = + (leaf->IsSentinel() || compare_(key, leaf->GetKey())) ? + leaf->GetLeft() : leaf->GetRight(); + + // Parent is protected, so we can tolerate a changing child pointer + while(!leaf.ProtectHazard(next_leaf)); + + // Parent is retired - make sure it is actually removed from the tree + if (parent->IsRetired()) { + HazardOperationPtr op(GetOperationGuard(HIDX_HELPING)); + if (op.ProtectHazard(parent->GetOperation())) { + op->HelpCommit(GetNodeGuard(HIDX_HELPING)); + } + // Can't follow a child pointer in a retired node - restart from root + break; + } + + VERIFY_ADDRESS(static_cast(leaf)); + + reached_leaf = leaf->IsLeaf(); + } + } +} + +template +inline bool ChromaticTree:: +HasChild(const Node* parent, const Node* child) const { + return (parent->GetLeft() == child || parent->GetRight() == child); } -template -void ChromaticTree:: -Search(const Key& key, NodePtr& leaf) const { - NodePtr parent; - Search(key, leaf, parent); +template +void ChromaticTree:: +Destruct(Node* node) { + if (!node->IsLeaf()) { + Destruct(node->GetLeft()); + Destruct(node->GetRight()); + } + FreeNode(node); } -template -void ChromaticTree:: -Search(const Key& key, NodePtr& leaf, NodePtr& parent) const { - NodePtr grandparent; - Search(key, leaf, parent, grandparent); +template +int ChromaticTree:: +GetHeight(const Node* node) const { + int height = 0; + if (node != NULL) { + height = 1 + ::std::max(GetHeight(node->GetLeft()), + GetHeight(node->GetRight())); + } + return height; +} + +template +bool ChromaticTree:: +IsBalanced() const { + return IsBalanced(entry_->GetLeft()); } -template -void ChromaticTree:: -Search(const Key& key, NodePtr& leaf, NodePtr& parent, - NodePtr& grandparent) const { - grandparent = NULL; - parent = entry_; - leaf = entry_->GetLeft(); +template +bool ChromaticTree:: +IsBalanced(const Node* node) const { + // Overweight violation + bool has_violation = node->GetWeight() > 1; - while (!IsLeaf(leaf)) { - grandparent = parent; - parent = leaf; - leaf = (IsSentinel(leaf) || compare_(key, leaf->GetKey())) ? - leaf->GetLeft() : leaf->GetRight(); + if (!has_violation && !node->IsLeaf()) { + const Node* left = node->GetLeft(); + const Node* right = node->GetRight(); + + // Red-red violation + has_violation = node->GetWeight() == 0 && + (left->GetWeight() == 0 || right->GetWeight() == 0); + + // Check children + if (!has_violation) { + has_violation = !IsBalanced(left) || !IsBalanced(right); + } } + + return !has_violation; } -template -bool ChromaticTree:: -IsLeaf(const NodePtr& node) const { - return node->GetLeft() == NULL; +template +bool ChromaticTree:: +IsDummyOperation(const Operation* operation) const { + return (operation == &initial_operation_dummy_ || + operation == &retired_operation_dummy_); } -template -bool ChromaticTree:: -IsSentinel(const NodePtr& node) const { - return (node == entry_) || (node == entry_->GetLeft()); +template +void ChromaticTree:: +RetireNode(HazardNodePtr& node) { + node_hazard_manager_.EnqueuePointerForDeletion(node.ReleaseHazard()); } -template -bool ChromaticTree:: -HasFixedWeight(const NodePtr& node) const { - return (IsSentinel(node)) || (node == entry_->GetLeft()->GetLeft()); +template +void ChromaticTree:: +RetireOperation(HazardOperationPtr& operation) { + Operation* op = operation.ReleaseHazard(); + // Make sure we don't return the static dummy-operations to the pool + if (!IsDummyOperation(op)) { + operation_hazard_manager_.EnqueuePointerForDeletion(op); + } } -template -bool ChromaticTree:: -HasChild(const NodePtr& parent, const NodePtr& child) const { - return (parent->GetLeft() == child || parent->GetRight() == child); +template +typename ChromaticTree::AtomicNodePtr& +ChromaticTree:: +GetNodeGuard(HazardIndex index) { + return node_hazard_manager_.GetGuardedPointer(static_cast(index)); } -template -typename ChromaticTree::NodePtr& -ChromaticTree:: -GetPointerToChild(const NodePtr& parent, const NodePtr& child) const { - assert(HasChild(parent, child)); - return (parent->GetLeft() == child) ? parent->GetLeft() : parent->GetRight(); +template +typename ChromaticTree::AtomicOperationPtr& +ChromaticTree:: +GetOperationGuard(HazardIndex index) { + return operation_hazard_manager_.GetGuardedPointer(static_cast(index)); } -template -void ChromaticTree:: -Destruct(const NodePtr& node) { - if (!IsLeaf(node)) { - Destruct(node->GetLeft()); - Destruct(node->GetRight()); +template +bool ChromaticTree:: +WeakLLX(HazardNodePtr& node, HazardOperationPtr& operation) { + // Make sure we have a protected operation pointer + while (!operation.ProtectHazard(node->GetOperation())); + + // Node is not retired and operation is committed - node is available + if (!node->IsRetired() && operation->IsCommitted()) { + return true; } + + if (operation->IsAborted()) { + // Operation is aborted, but the node is still frozen - unfreeze it + operation->HelpAbort(node); + + } else if (operation->IsInProgress()) { + // Operation is still in progress - help it + operation->Help(GetNodeGuard(HIDX_HELPING), + GetOperationGuard(HIDX_HELPING)); + } + + // LLX failed - operation pointer should not be exposed + operation.ReleaseHazard(); + + return false; +} + +template +void ChromaticTree:: +FreeNode(Node* node) { +#ifdef EMBB_DEBUG + node->GetLeft() = reinterpret_cast(INVALID_POINTER); + node->GetRight() = reinterpret_cast(INVALID_POINTER); +#endif node_pool_.Free(node); } -template -bool ChromaticTree:: +template +void ChromaticTree:: +FreeOperation(Operation* operation) { +#ifdef EMBB_DEBUG + operation->SetDeleted(); +#endif + operation_pool_.Free(operation); +} + +template +bool ChromaticTree:: CleanUp(const Key& key) { - while (true) { - NodePtr grandgrandparent = NULL; - NodePtr grandparent = NULL; - NodePtr parent = entry_; - NodePtr leaf = entry_->GetLeft(); - - while (!IsLeaf(leaf) && (leaf->GetWeight() <= 1) && - (leaf->GetWeight() != 0 || parent->GetWeight() != 0)) { - grandgrandparent = grandparent; - grandparent = parent; - parent = leaf; - leaf = (IsSentinel(leaf) || compare_(key, leaf->GetKey())) ? - leaf->GetLeft() : leaf->GetRight(); - } + HazardNodePtr grandgrandparent(GetNodeGuard(HIDX_GRANDGRANDPARENT)); + HazardNodePtr grandparent(GetNodeGuard(HIDX_GRANDPARENT)); + HazardNodePtr parent(GetNodeGuard(HIDX_PARENT)); + HazardNodePtr leaf(GetNodeGuard(HIDX_LEAF)); + bool reached_leaf = false; + + while (!reached_leaf) { + bool found_violation = false; + + grandgrandparent.ProtectSafe(entry_); + grandparent.ProtectSafe(entry_); + parent.ProtectSafe(entry_); + leaf.ProtectSafe(entry_); + + reached_leaf = leaf->IsLeaf(); + while (!reached_leaf && !found_violation) { + grandgrandparent.AdoptHazard(grandparent); + grandparent.AdoptHazard(parent); + parent.AdoptHazard(leaf); + + AtomicNodePtr& next_leaf = + (leaf->IsSentinel() || compare_(key, leaf->GetKey())) ? + leaf->GetLeft() : leaf->GetRight(); + + // Parent is protected, so we can tolerate a changing child pointer + while(!leaf.ProtectHazard(next_leaf)); + + // Parent is retired - make sure it is actually removed from the tree + if (parent->IsRetired()) { + HazardOperationPtr op(GetOperationGuard(HIDX_HELPING)); + if (op.ProtectHazard(parent->GetOperation())) { + op->HelpCommit(GetNodeGuard(HIDX_HELPING)); + } + // Can't follow a child pointer in a retired node - restart from root + break; + } - if (leaf->GetWeight() == 1) { - break; - } + VERIFY_ADDRESS(static_cast(leaf)); - if (!Rebalance(grandgrandparent, grandparent, parent, leaf)) { - return false; + // Check for violations + if ((leaf->GetWeight() > 1) || (leaf->GetWeight() == 0 && + parent->GetWeight() == 0)) { + if (Rebalance(grandgrandparent, grandparent, parent, leaf) == + EMBB_NOMEM) { + assert(false && "No memory for rebalancing!"); + return false; + } + break; + } + + reached_leaf = leaf->IsLeaf(); } } return true; } -template -bool ChromaticTree:: -Rebalance(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, - const NodePtr& uxxx) { - //TODO: weakLLX(u); - if (!HasChild(u, ux)) return false; - - //TODO: weakLLX(ux); - NodePtr uxl = ux->GetLeft(); - NodePtr uxr = ux->GetRight(); - bool uxx_is_left = (uxx == uxl); - if (!HasChild(ux, uxx)) return false; - - //TODO: weakLLX(uxx); - NodePtr uxxl = uxx->GetLeft(); - NodePtr uxxr = uxx->GetRight(); +#define PROTECT_NODE_WITH_LLX(h_idx, node, op_name) \ + HazardOperationPtr op_name(GetOperationGuard(h_idx)); \ + if (!WeakLLX(node, op_name)) return EMBB_BUSY + +#define DEFINE_HAZARDOUS_NODE(h_idx, node, parent, method) \ + HazardNodePtr node(GetNodeGuard(h_idx)); \ + node.ProtectHazard(parent->method()); \ + if (parent->IsRetired() || !node.IsActive()) return EMBB_BUSY; \ + VERIFY_ADDRESS(static_cast(node)) + +template +embb_errors_t ChromaticTree:: +Rebalance(HazardNodePtr& u, HazardNodePtr& ux, HazardNodePtr& uxx, + HazardNodePtr& uxxx) { + // Protect node 'u' + PROTECT_NODE_WITH_LLX(HIDX_U, u, u_op); + // Verify that ux is still a child of u + if (!HasChild(u, ux)) return EMBB_BUSY; + + // Protect node 'ux' + PROTECT_NODE_WITH_LLX(HIDX_UX, ux, ux_op); + // Get children of 'ux' + DEFINE_HAZARDOUS_NODE(HIDX_UXL, uxl, ux, GetLeft); + DEFINE_HAZARDOUS_NODE(HIDX_UXR, uxr, ux, GetRight); + // Verify that 'uxx' is still a child of 'ux' + bool uxx_is_left = (uxx == uxl); (void)uxx_is_left; + if (!HasChild(ux, uxx)) return EMBB_BUSY; + + // Protect node 'uxx' + PROTECT_NODE_WITH_LLX(HIDX_UXX, uxx, uxx_op); + // Get children of 'uxx' + DEFINE_HAZARDOUS_NODE(HIDX_UXXL, uxxl, uxx, GetLeft); + DEFINE_HAZARDOUS_NODE(HIDX_UXXR, uxxr, uxx, GetRight); + // Verify that 'uxxx' is still a child of 'uxx' bool uxxx_is_left = (uxxx == uxxl); - if (!HasChild(uxx, uxxx)) return false; + if (!HasChild(uxx, uxxx)) return EMBB_BUSY; if (uxxx->GetWeight() > 1) { if (uxxx_is_left) { - //TODO: weakLLX(uxxl); - return OverweightLeft(u, ux, uxx, uxl, uxr, uxxl, uxxr, uxx_is_left); + // Protect node 'uxxl' + PROTECT_NODE_WITH_LLX(HIDX_UXXL, uxxl, uxxl_op); + return OverweightLeft(u, u_op, ux, ux_op, uxx, uxx_op, uxl, uxr, + uxxl, uxxl_op, uxxr, uxx_is_left); } else { - //TODO: weakLLX(uxxr); - return OverweightRight(u, ux, uxx, uxl, uxr, uxxl, uxxr, !uxx_is_left); + // Protect node 'uxxr' + PROTECT_NODE_WITH_LLX(HIDX_UXXR, uxxr, uxxr_op); + return OverweightRight(u, u_op, ux, ux_op, uxx, uxx_op, uxl, uxr, + uxxl, uxxr, uxxr_op, !uxx_is_left); } } else { + assert(uxxx->GetWeight() == 0 && uxx->GetWeight() == 0); //Red-red violation if (uxx_is_left) { if (uxr->GetWeight() == 0) { - //TODO: weakLLX(uxr); - return BLK(u, ux, uxx, uxr); + // Protect node 'uxr' + PROTECT_NODE_WITH_LLX(HIDX_UXR, uxr, uxr_op); + return BLK(u, u_op, ux, ux_op, uxx, uxx_op, uxr, uxr_op); } else if (uxxx_is_left) { - return RB1_L(u, ux, uxx); + return RB1_L(u, u_op, ux, ux_op, uxx, uxx_op); } else { - //TODO: weakLLX(uxxr); - return RB2_L(u, ux, uxx, uxxr); + // Protect node 'uxxr' + PROTECT_NODE_WITH_LLX(HIDX_UXXR, uxxr, uxxr_op); + return RB2_L(u, u_op, ux, ux_op, uxx, uxx_op, uxxr, uxxr_op); } } else { if (uxl->GetWeight() == 0) { - //TODO: weakLLX(uxl); - return BLK(u, ux, uxl, uxx); + // Protect node 'uxl' + PROTECT_NODE_WITH_LLX(HIDX_UXL, uxl, uxl_op); + return BLK(u, u_op, ux, ux_op, uxl, uxl_op, uxx, uxx_op); } else if (!uxxx_is_left) { - return RB1_R(u, ux, uxx); + return RB1_R(u, u_op, ux, ux_op, uxx, uxx_op); } else { - //TODO: weakLLX(uxxl); - return RB2_R(u, ux, uxx, uxxl); + // Protect node 'uxxl' + PROTECT_NODE_WITH_LLX(HIDX_UXXL, uxxl, uxxl_op); + return RB2_R(u, u_op, ux, ux_op, uxx, uxx_op, uxxl, uxxl_op); } } } } -template -bool ChromaticTree:: -OverweightLeft(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxxl, const NodePtr& uxxr, - const bool& uxx_is_left) { +template +embb_errors_t ChromaticTree:: +OverweightLeft(HazardNodePtr& u, HazardOperationPtr& u_op, + HazardNodePtr& ux, HazardOperationPtr& ux_op, + HazardNodePtr& uxx, HazardOperationPtr& uxx_op, + HazardNodePtr& uxl, HazardNodePtr& uxr, + HazardNodePtr& uxxl, HazardOperationPtr& uxxl_op, + HazardNodePtr& uxxr, bool uxx_is_left) { // Let "Root" be the top of the overweight violation decision tree (see p.30) // Root -> Middle if (uxxr->GetWeight() == 0) { @@ -439,14 +1122,16 @@ OverweightLeft(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, if (uxx_is_left) { // Root -> Middle -> Left -> Left -> Left if (uxr->GetWeight() == 0) { - //TODO: weakLLX(uxr); - return BLK(u, ux, uxx, uxr); + // Protect node 'uxr' + PROTECT_NODE_WITH_LLX(HIDX_UXR, uxr, uxr_op); + return BLK(u, u_op, ux, ux_op, uxx, uxx_op, uxr, uxr_op); // Root -> Middle -> Left -> Left -> Right } else { assert(uxr->GetWeight() > 0); - //TODO: weakLLX(uxxr); - return RB2_L(u, ux, uxx, uxxr); + // Protect node 'uxxr' + PROTECT_NODE_WITH_LLX(HIDX_UXXR, uxxr, uxxr_op); + return RB2_L(u, u_op, ux, ux_op, uxx, uxx_op, uxxr, uxxr_op); } // Root -> Middle -> Left -> Right @@ -454,104 +1139,126 @@ OverweightLeft(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, assert(!uxx_is_left); // Root -> Middle -> Left -> Right -> Left if (uxl->GetWeight() == 0) { - //TODO: weakLLX(uxl); - return BLK(u, ux, uxl, uxx); + // Protect node 'uxl' + PROTECT_NODE_WITH_LLX(HIDX_UXL, uxl, uxl_op); + return BLK(u, u_op, ux, ux_op, uxl, uxl_op, uxx, uxx_op); // Root -> Middle -> Left -> Right -> Right } else { assert(uxl->GetWeight() > 0); - return RB1_R(u, ux, uxx); + return RB1_R(u, u_op, ux, ux_op, uxx, uxx_op); } } // Root -> Middle -> Right } else { assert(uxx->GetWeight() > 0); - //TODO: weakLLX(uxxr); + // Protect node 'uxxr' + PROTECT_NODE_WITH_LLX(HIDX_UXXR, uxxr, uxxr_op); + + // Get left child of 'uxxr' // Note: we know that 'uxxr' is not a leaf because it has weight 0. - NodePtr uxxrl = uxxr->GetLeft(); - //TODO: weakLLX(uxxrl); + DEFINE_HAZARDOUS_NODE(HIDX_UXXRL, uxxrl, uxxr, GetLeft); + + // Protect node 'uxxrl' + PROTECT_NODE_WITH_LLX(HIDX_UXXRL, uxxrl, uxxrl_op); // Root -> Middle -> Right -> Left if (uxxrl->GetWeight() == 0) { - return RB2_R(ux, uxx, uxxr, uxxrl); + return RB2_R(ux, ux_op, uxx, uxx_op, + uxxr, uxxr_op, uxxrl, uxxrl_op); // Root -> Middle -> Right -> Middle } else if (uxxrl->GetWeight() == 1) { - NodePtr uxxrll = uxxrl->GetLeft(); - NodePtr uxxrlr = uxxrl->GetRight(); - if (uxxrlr == NULL) return false; + DEFINE_HAZARDOUS_NODE(HIDX_UXXRLR, uxxrlr, uxxrl, GetRight); + if (uxxrlr == NULL) return EMBB_BUSY; // Root -> Middle -> Right -> Middle -> Left if (uxxrlr->GetWeight() == 0) { - //TODO: weakLLX(uxxrlr); - return W4_L(ux, uxx, uxxl, uxxr, uxxrl, uxxrlr); + // Protect node 'uxxrlr' + PROTECT_NODE_WITH_LLX(HIDX_UXXRLR, uxxrlr, uxxrlr_op); + return W4_L(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxrl, uxxrl_op, uxxrlr, uxxrlr_op); // Root -> Middle -> Right -> Middle -> Right } else { assert(uxxrlr->GetWeight() > 0); // Root -> Middle -> Right -> Middle -> Right -> Left + // Node: reusing hazard of node 'uxxrlr' as it is no longer used + DEFINE_HAZARDOUS_NODE(HIDX_UXXRLL, uxxrll, uxxrl, GetLeft); if (uxxrll->GetWeight() == 0) { - //TODO: weakLLX(uxxrll); - return W3_L(ux, uxx, uxxl, uxxr, uxxrl, uxxrll); + // Protect node 'uxxrll' + PROTECT_NODE_WITH_LLX(HIDX_UXXRLL, uxxrll, uxxrll_op); + return W3_L(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, uxxr, + uxxr_op, uxxrl, uxxrl_op, uxxrll, uxxrll_op); // Root -> Middle -> Right -> Middle -> Right -> Right } else { assert(uxxrll->GetWeight() > 0); - return W2_L(ux, uxx, uxxl, uxxr, uxxrl); + return W2_L(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxrl, uxxrl_op); } } // Root -> Middle -> Right -> Right } else { assert(uxxrl->GetWeight() > 1); - return W1_L(ux, uxx, uxxl, uxxr, uxxrl); + return W1_L(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxrl, uxxrl_op); } } // Root -> Right } else if (uxxr->GetWeight() == 1) { - //TODO: weakLLX(uxxr); - NodePtr uxxrl = uxxr->GetLeft(); - NodePtr uxxrr = uxxr->GetRight(); - if (uxxrl == NULL) return false; + // Protect node 'uxxr' + PROTECT_NODE_WITH_LLX(HIDX_UXXR, uxxr, uxxr_op); + // Get children of 'uxxr' + DEFINE_HAZARDOUS_NODE(HIDX_UXXRL, uxxrl, uxxr, GetLeft); + DEFINE_HAZARDOUS_NODE(HIDX_UXXRR, uxxrr, uxxr, GetRight); + if (uxxrl == NULL) return EMBB_BUSY; // Root -> Right -> Left if (uxxrr->GetWeight() == 0) { - //TODO: weakLLX(uxxrr); - return W5_L(ux, uxx, uxxl, uxxr, uxxrr); + // Protect node 'uxxrr' + PROTECT_NODE_WITH_LLX(HIDX_UXXRR, uxxrr, uxxrr_op); + return W5_L(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxrr, uxxrr_op); // Root -> Right -> Right } else { assert(uxxrr->GetWeight() > 0); // Root -> Right -> Right -> Left if (uxxrl->GetWeight() == 0) { - //TODO: weakLLX(uxxrl); - return W6_L(ux, uxx, uxxl, uxxr, uxxrl); + // Protect node 'uxxrl' + PROTECT_NODE_WITH_LLX(HIDX_UXXRL, uxxrl, uxxrl_op); + return W6_L(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxrl, uxxrl_op); // Root -> Right -> Right -> Right } else { assert(uxxrl->GetWeight() > 0); - return PUSH_L(ux, uxx, uxxl, uxxr); + return PUSH_L(ux, ux_op, uxx, uxx_op, + uxxl, uxxl_op, uxxr, uxxr_op); } } // Root -> Left } else { assert(uxxr->GetWeight() > 1); - //TODO: weakLLX(uxxr); - return W7(ux, uxx, uxxl, uxxr); + // Protect node 'uxxr' + PROTECT_NODE_WITH_LLX(HIDX_UXXR, uxxr, uxxr_op); + return W7(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, uxxr, uxxr_op); } - - return true; } -template -bool ChromaticTree:: -OverweightRight(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxxl, const NodePtr& uxxr, - const bool& uxx_is_right) { +template +embb_errors_t ChromaticTree:: +OverweightRight(HazardNodePtr& u, HazardOperationPtr& u_op, + HazardNodePtr& ux, HazardOperationPtr& ux_op, + HazardNodePtr& uxx, HazardOperationPtr& uxx_op, + HazardNodePtr& uxl, HazardNodePtr& uxr, + HazardNodePtr& uxxl, HazardNodePtr& uxxr, + HazardOperationPtr& uxxr_op, bool uxx_is_right) { // Let "Root" be the top of the overweight violation decision tree (see p.30) // Root -> Middle if (uxxl->GetWeight() == 0) { @@ -561,14 +1268,16 @@ OverweightRight(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, if (uxx_is_right) { // Root -> Middle -> Left -> Left -> Left if (uxl->GetWeight() == 0) { - //TODO: weakLLX(uxl); - return BLK(u, ux, uxl, uxx); + // Protect node 'uxl' + PROTECT_NODE_WITH_LLX(HIDX_UXL, uxl, uxl_op); + return BLK(u, u_op, ux, ux_op, uxl, uxl_op, uxx, uxx_op); // Root -> Middle -> Left -> Left -> Right } else { assert(uxl->GetWeight() > 0); - //TODO: weakLLX(uxxl); - return RB2_R(u, ux, uxx, uxxl); + // Protect node 'uxxl' + PROTECT_NODE_WITH_LLX(HIDX_UXXL, uxxl, uxxl_op); + return RB2_R(u, u_op, ux, ux_op, uxx, uxx_op, uxxl, uxxl_op); } // Root -> Middle -> Left -> Right @@ -576,983 +1285,118 @@ OverweightRight(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, assert(!uxx_is_right); // Root -> Middle -> Left -> Right -> Left if (uxr->GetWeight() == 0) { - //TODO: weakLLX(uxr); - return BLK(u, ux, uxx, uxr); + // Protect node 'uxr' + PROTECT_NODE_WITH_LLX(HIDX_UXR, uxr, uxr_op); + return BLK(u, u_op, ux, ux_op, uxx, uxx_op, uxr, uxr_op); // Root -> Middle -> Left -> Right -> Right } else { assert(uxr->GetWeight() > 0); - return RB1_L(u, ux, uxx); + return RB1_L(u, u_op, ux, ux_op, uxx, uxx_op); } } // Root -> Middle -> Right } else { assert(uxx->GetWeight() > 0); - //TODO: weakLLX(uxxl); + // Protect node 'uxxl' + PROTECT_NODE_WITH_LLX(HIDX_UXXL, uxxl, uxxl_op); + + // Get left child of 'uxxl' // Note: we know that 'uxxl' is not a leaf because it has weight 0. - NodePtr uxxlr = uxxl->GetRight(); - //TODO: weakLLX(uxxlr); + DEFINE_HAZARDOUS_NODE(HIDX_UXXLR, uxxlr, uxxl, GetRight); + + // Protect node 'uxxlr' + PROTECT_NODE_WITH_LLX(HIDX_UXXLR, uxxlr, uxxlr_op); // Root -> Middle -> Right -> Left if (uxxlr->GetWeight() == 0) { - return RB2_L(ux, uxx, uxxl, uxxlr); + return RB2_L(ux, ux_op, uxx, uxx_op, + uxxl, uxxl_op, uxxlr, uxxlr_op); // Root -> Middle -> Right -> Middle } else if (uxxlr->GetWeight() == 1) { - NodePtr uxxlrl = uxxlr->GetLeft(); - NodePtr uxxlrr = uxxlr->GetRight(); - if (uxxlrl == NULL) return false; + DEFINE_HAZARDOUS_NODE(HIDX_UXXLRL, uxxlrl, uxxlr, GetLeft); + if (uxxlrl == NULL) return EMBB_BUSY; // Root -> Middle -> Right -> Middle -> Left if (uxxlrl->GetWeight() == 0) { - //TODO: weakLLX(uxxlrl); - return W4_R(ux, uxx, uxxl, uxxr, uxxlr, uxxlrl); + // Protect node 'uxxlrl' + PROTECT_NODE_WITH_LLX(HIDX_UXXLRL, uxxlrl, uxxlrl_op); + return W4_R(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxlr, uxxlr_op, uxxlrl, uxxlrl_op); // Root -> Middle -> Right -> Middle -> Right } else { assert(uxxlrl->GetWeight() > 0); // Root -> Middle -> Right -> Middle -> Right -> Left + // Node: reusing hazard of node 'uxxlrl' as it is no longer used + DEFINE_HAZARDOUS_NODE(HIDX_UXXLRR, uxxlrr, uxxlr, GetRight); if (uxxlrr->GetWeight() == 0) { - //TODO: weakLLX(uxxlrr); - return W3_R(ux, uxx, uxxl, uxxr, uxxlr, uxxlrr); + // Protect node 'uxxlrr' + PROTECT_NODE_WITH_LLX(HIDX_UXXLRR, uxxlrr, uxxlrr_op); + return W3_R(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, uxxr, + uxxr_op, uxxlr, uxxlr_op, uxxlrr, uxxlrr_op); // Root -> Middle -> Right -> Middle -> Right -> Right } else { assert(uxxlrr->GetWeight() > 0); - return W2_R(ux, uxx, uxxl, uxxr, uxxlr); + return W2_R(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxlr, uxxlr_op); } } // Root -> Middle -> Right -> Right } else { assert(uxxlr->GetWeight() > 1); - return W1_R(ux, uxx, uxxl, uxxr, uxxlr); + return W1_R(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxlr, uxxlr_op); } } // Root -> Right } else if (uxxl->GetWeight() == 1) { - //TODO: weakLLX(uxxl); - NodePtr uxxll = uxxl->GetLeft(); - NodePtr uxxlr = uxxl->GetRight(); - if (uxxll == NULL) return false; + // Protect node 'uxxl' + PROTECT_NODE_WITH_LLX(HIDX_UXXL, uxxl, uxxl_op); + // Get children of 'uxxl' + DEFINE_HAZARDOUS_NODE(HIDX_UXXLL, uxxll, uxxl, GetLeft); + DEFINE_HAZARDOUS_NODE(HIDX_UXXLR, uxxlr, uxxl, GetRight); + if (uxxll == NULL) return EMBB_BUSY; // Root -> Right -> Left if (uxxll->GetWeight() == 0) { - //TODO: weakLLX(uxxll); - return W5_R(ux, uxx, uxxl, uxxr, uxxll); + // Protect node 'uxxll' + PROTECT_NODE_WITH_LLX(HIDX_UXXLL, uxxll, uxxll_op); + return W5_R(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxll, uxxll_op); // Root -> Right -> Right } else { assert(uxxll->GetWeight() > 0); // Root -> Right -> Right -> Left if (uxxlr->GetWeight() == 0) { - //TODO: weakLLX(uxxlr); - return W6_R(ux, uxx, uxxl, uxxr, uxxlr); + // Protect node 'uxxlr' + PROTECT_NODE_WITH_LLX(HIDX_UXXLR, uxxlr, uxxlr_op); + return W6_R(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, + uxxr, uxxr_op, uxxlr, uxxlr_op); // Root -> Right -> Right -> Right } else { assert(uxxlr->GetWeight() > 0); - return PUSH_R(ux, uxx, uxxl, uxxr); + return PUSH_R(ux, ux_op, uxx, uxx_op, + uxxl, uxxl_op, uxxr, uxxr_op); } } // Root -> Left } else { assert(uxxl->GetWeight() > 1); - //TODO: weakLLX(uxxl); - return W7(ux, uxx, uxxl, uxxr); - } - - return true; -} - -template -int ChromaticTree:: -GetHeight(const NodePtr& node) const { - int height = 0; - if (node != NULL) { - height = 1 + ::std::max(GetHeight(node->GetLeft()), - GetHeight(node->GetRight())); - } - return height; -} - - -template -bool ChromaticTree:: -BLK(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr) { - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nx = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - HasFixedWeight(ux) ? 1 : ux->GetWeight() - 1, - nxl, nxr); - - if (nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - - return true; -} - -template -bool ChromaticTree:: -PUSH_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr) { - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - 0, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nx = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - HasFixedWeight(ux) ? 1 : ux->GetWeight() + 1, - nxl, nxr); - - if (nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - - return true; -} - -template -bool ChromaticTree:: -PUSH_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr) { - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - 0, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nx = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - HasFixedWeight(ux) ? 1 : ux->GetWeight() + 1, - nxl, nxr); - - if (nxr == NULL || - nxl == NULL || - nx == NULL) { - if (nxr) node_pool_.Free(nxr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - - return true; -} - -template -bool ChromaticTree:: -RB1_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl) { - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 0, - uxl->GetRight(), ux->GetRight()); - NodePtr nx = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - ux->GetWeight(), - uxl->GetLeft(), nxr); - - if (nxr == NULL || - nx == NULL) { - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - - return true; -} - -template -bool ChromaticTree:: -RB1_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxr) { - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 0, - ux->GetLeft(), uxr->GetLeft()); - NodePtr nx = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - ux->GetWeight(), - nxl, uxr->GetRight()); - - if (nxl == NULL || - nx == NULL) { - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxr); - - return true; -} - -template -bool ChromaticTree:: -RB2_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxlr) { - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - 0, - uxl->GetLeft(), uxlr->GetLeft()); - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 0, - uxlr->GetRight(), ux->GetRight()); - NodePtr nx = node_pool_.Allocate( - uxlr->GetKey(), uxlr->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxlr); - - return true; -} - -template -bool ChromaticTree:: -RB2_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxr, - const NodePtr& uxrl) { - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - 0, - uxrl->GetRight(), uxr->GetRight()); - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 0, - ux->GetLeft(), uxrl->GetLeft()); - NodePtr nx = node_pool_.Allocate( - uxrl->GetKey(), uxrl->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxr == NULL || - nxl == NULL || - nx == NULL) { - if (nxr) node_pool_.Free(nxr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxr); - node_pool_.Free(uxrl); - - return true; -} - -template -bool ChromaticTree:: -W1_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxrl) { - NodePtr nxll = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxlr = node_pool_.Allocate( - uxrl->GetKey(), uxrl->GetValue(), - uxrl->GetWeight() - 1, - uxrl->GetLeft(), uxrl->GetRight()); - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxll, nxlr); - NodePtr nx = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - ux->GetWeight(), - nxl, uxr->GetRight()); - - if (nxll == NULL || - nxlr == NULL || - nxl == NULL || - nx == NULL) { - if (nxll) node_pool_.Free(nxll); - if (nxlr) node_pool_.Free(nxlr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxrl); - - return true; -} - -template -bool ChromaticTree:: -W1_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxlr) { - NodePtr nxrr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxrl = node_pool_.Allocate( - uxlr->GetKey(), uxlr->GetValue(), - uxlr->GetWeight() - 1, - uxlr->GetLeft(), uxlr->GetRight()); - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxrl, nxrr); - NodePtr nx = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - ux->GetWeight(), - uxl->GetLeft(), nxr); - - if (nxrr == NULL || - nxrl == NULL || - nxr == NULL || - nx == NULL) { - if (nxrr) node_pool_.Free(nxrr); - if (nxrl) node_pool_.Free(nxrl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxlr); - - return true; -} - -template -bool ChromaticTree:: -W2_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxrl) { - NodePtr nxll = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxlr = node_pool_.Allocate( - uxrl->GetKey(), uxrl->GetValue(), - 0, - uxrl->GetLeft(), uxrl->GetRight()); - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxll, nxlr); - NodePtr nx = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - ux->GetWeight(), - nxl, uxr->GetRight()); - - if (nxll == NULL || - nxlr == NULL || - nxl == NULL || - nx == NULL) { - if (nxll) node_pool_.Free(nxll); - if (nxlr) node_pool_.Free(nxlr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxrl); - - return true; -} - -template -bool ChromaticTree:: -W2_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxlr) { - NodePtr nxrr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxrl = node_pool_.Allocate( - uxlr->GetKey(), uxlr->GetValue(), - 0, - uxlr->GetLeft(), uxlr->GetRight()); - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxrl, nxrr); - NodePtr nx = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - ux->GetWeight(), - uxl->GetLeft(), nxr); - - if (nxrr == NULL || - nxrl == NULL || - nxr == NULL || - nx == NULL) { - if (nxrr) node_pool_.Free(nxrr); - if (nxrl) node_pool_.Free(nxrl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxlr); - - return true; -} - -template -bool ChromaticTree:: -W3_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxrl, - const NodePtr& uxrll) { - NodePtr nxlll = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxll = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxlll, uxrll->GetLeft()); - NodePtr nxlr = node_pool_.Allocate( - uxrl->GetKey(), uxrl->GetValue(), - 1, - uxrll->GetRight(), uxrl->GetRight()); - NodePtr nxl = node_pool_.Allocate( - uxrll->GetKey(), uxrll->GetValue(), - 0, - nxll, nxlr); - NodePtr nx = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - ux->GetWeight(), - nxl, uxr->GetRight()); - - if (nxlll == NULL || - nxll == NULL || - nxlr == NULL || - nxl == NULL || - nx == NULL) { - if (nxlll) node_pool_.Free(nxlll); - if (nxll) node_pool_.Free(nxll); - if (nxlr) node_pool_.Free(nxlr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxrl); - node_pool_.Free(uxrll); - - return true; -} - -template -bool ChromaticTree:: -W3_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxlr, - const NodePtr& uxlrr) { - NodePtr nxrrr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxrr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - uxlrr->GetRight(), nxrrr); - NodePtr nxrl = node_pool_.Allocate( - uxlr->GetKey(), uxlr->GetValue(), - 1, - uxlr->GetLeft(), uxlrr->GetLeft()); - NodePtr nxr = node_pool_.Allocate( - uxlrr->GetKey(), uxlrr->GetValue(), - 0, - nxrl, nxrr); - NodePtr nx = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - ux->GetWeight(), - uxl->GetLeft(), nxr); - - if (nxrrr == NULL || - nxrr == NULL || - nxrl == NULL || - nxr == NULL || - nx == NULL) { - if (nxrrr) node_pool_.Free(nxrrr); - if (nxrr) node_pool_.Free(nxrr); - if (nxrl) node_pool_.Free(nxrl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxlr); - node_pool_.Free(uxlrr); - - return true; -} - -template -bool ChromaticTree:: -W4_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxrl, - const NodePtr& uxrlr) { - NodePtr nxll = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxrl = node_pool_.Allocate( - uxrlr->GetKey(), uxrlr->GetValue(), - 1, - uxrlr->GetLeft(), uxrlr->GetRight()); - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxll, uxrl->GetLeft()); - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - 0, - nxrl, uxr->GetRight()); - NodePtr nx = node_pool_.Allocate( - uxrl->GetKey(), uxrl->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxll == NULL || - nxrl == NULL || - nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxll) node_pool_.Free(nxll); - if (nxrl) node_pool_.Free(nxrl); - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; + // Protect node 'uxxl' + PROTECT_NODE_WITH_LLX(HIDX_UXXL, uxxl, uxxl_op); + return W7(ux, ux_op, uxx, uxx_op, uxxl, uxxl_op, uxxr, uxxr_op); } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxrl); - node_pool_.Free(uxrlr); - - return true; -} - -template -bool ChromaticTree:: -W4_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxlr, - const NodePtr& uxlrl) { - NodePtr nxrr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxlr = node_pool_.Allocate( - uxlrl->GetKey(), uxlrl->GetValue(), - 1, - uxlrl->GetLeft(), uxlrl->GetRight()); - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - uxlr->GetRight(), nxrr); - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - 0, - uxl->GetLeft(), nxlr); - NodePtr nx = node_pool_.Allocate( - uxlr->GetKey(), uxlr->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxrr == NULL || - nxlr == NULL || - nxr == NULL || - nxl == NULL || - nx == NULL) { - if (nxrr) node_pool_.Free(nxrr); - if (nxlr) node_pool_.Free(nxlr); - if (nxr) node_pool_.Free(nxr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxlr); - node_pool_.Free(uxlrl); - - return true; } -template -bool ChromaticTree:: -W5_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxrr) { - NodePtr nxll = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxll, uxr->GetLeft()); - NodePtr nxr = node_pool_.Allocate( - uxrr->GetKey(), uxrr->GetValue(), - 1, - uxrr->GetLeft(), uxrr->GetRight()); - NodePtr nx = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxll == NULL || - nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxll) node_pool_.Free(nxll); - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxrr); - - return true; -} - -template -bool ChromaticTree:: -W5_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxll) { - NodePtr nxrr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - uxl->GetRight(), nxrr); - NodePtr nxl = node_pool_.Allocate( - uxll->GetKey(), uxll->GetValue(), - 1, - uxll->GetLeft(), uxll->GetRight()); - NodePtr nx = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxrr == NULL || - nxr == NULL || - nxl == NULL || - nx == NULL) { - if (nxrr) node_pool_.Free(nxrr); - if (nxr) node_pool_.Free(nxr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxll); - - return true; -} - -template -bool ChromaticTree:: -W6_L(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxrl) { - NodePtr nxll = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxl = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - nxll, uxrl->GetLeft()); - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - 1, - uxrl->GetRight(), uxr->GetRight()); - NodePtr nx = node_pool_.Allocate( - uxrl->GetKey(), uxrl->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxll == NULL || - nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxll) node_pool_.Free(nxll); - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxrl); - - return true; -} - -template -bool ChromaticTree:: -W6_R(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr, - const NodePtr& uxlr) { - NodePtr nxrr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nxr = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - 1, - uxlr->GetRight(), nxrr); - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - 1, - uxl->GetLeft(), uxlr->GetLeft()); - NodePtr nx = node_pool_.Allocate( - uxlr->GetKey(), uxlr->GetValue(), - ux->GetWeight(), - nxl, nxr); - - if (nxrr == NULL || - nxr == NULL || - nxl == NULL || - nx == NULL) { - if (nxrr) node_pool_.Free(nxrr); - if (nxr) node_pool_.Free(nxr); - if (nxl) node_pool_.Free(nxl); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - node_pool_.Free(uxlr); - - return true; -} - -template -bool ChromaticTree:: -W7(const NodePtr& u, - const NodePtr& ux, - const NodePtr& uxl, - const NodePtr& uxr) { - NodePtr nxl = node_pool_.Allocate( - uxl->GetKey(), uxl->GetValue(), - uxl->GetWeight() - 1, - uxl->GetLeft(), uxl->GetRight()); - NodePtr nxr = node_pool_.Allocate( - uxr->GetKey(), uxr->GetValue(), - uxr->GetWeight() - 1, - uxr->GetLeft(), uxr->GetRight()); - NodePtr nx = node_pool_.Allocate( - ux->GetKey(), ux->GetValue(), - HasFixedWeight(ux) ? 1 : ux->GetWeight() + 1, - nxl, nxr); - - if (nxl == NULL || - nxr == NULL || - nx == NULL) { - if (nxl) node_pool_.Free(nxl); - if (nxr) node_pool_.Free(nxr); - if (nx) node_pool_.Free(nx); - return false; - } - - NodePtr& new_subtree_ptr = GetPointerToChild(u, ux); - new_subtree_ptr = nx; - - node_pool_.Free(ux); - node_pool_.Free(uxl); - node_pool_.Free(uxr); - - return true; -} - - } // namespace containers } // namespace embb diff --git a/containers_cpp/include/embb/containers/internal/object_pool-inl.h b/containers_cpp/include/embb/containers/internal/object_pool-inl.h index cef483c..7179da2 100644 --- a/containers_cpp/include/embb/containers/internal/object_pool-inl.h +++ b/containers_cpp/include/embb/containers/internal/object_pool-inl.h @@ -201,6 +201,20 @@ Type* ObjectPool::Allocate( } template +template +Type* ObjectPool::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 ObjectPool::~ObjectPool() { // Deallocate the objects objectAllocator.deallocate(objects, capacity); diff --git a/containers_cpp/include/embb/containers/lock_free_chromatic_tree.h b/containers_cpp/include/embb/containers/lock_free_chromatic_tree.h index 9b191a7..544a5b1 100644 --- a/containers_cpp/include/embb/containers/lock_free_chromatic_tree.h +++ b/containers_cpp/include/embb/containers/lock_free_chromatic_tree.h @@ -30,14 +30,21 @@ #include #include +#include #include +#include +#include +#include namespace embb { namespace containers { namespace internal { +template +class ChromaticTreeOperation; + /** - * Tree node + * Chromatic tree node. * * Stores the key-value pair, as well as the weight value (used for rebalancing) * and two pointers to child nodes (left and right). @@ -48,35 +55,38 @@ namespace internal { template class ChromaticTreeNode { public: + /** Node of the tree (self type). */ + typedef ChromaticTreeNode Node; + /** Atomic pointer to a node. */ + typedef embb::base::Atomic AtomicNodePtr; + /** Chromatic tree operation. */ + typedef ChromaticTreeOperation Operation; + /** Atomic pointer to a tree operation. */ + typedef embb::base::Atomic AtomicOperationPtr; + /** * Creates a node with given parameters. * - * \param[IN] key Key of the new node - * \param[IN] value Value of the new node - * \param[IN] weight Weight of the new node - * \param[IN] left Pointer to the left child node - * \param[IN] right Pointer to the right child node - */ - ChromaticTreeNode(const Key& key, const Value& value, const int& weight, - ChromaticTreeNode* const & left, - ChromaticTreeNode* const & right); - - /** - * Creates a node given only a key-value pair. Node will have no child nodes - * and a default weight (1). - * - * \param[IN] key Key of the new node - * \param[IN] value Value of the new node + * \param[IN] key Key of the new node + * \param[IN] value Value of the new node + * \param[IN] weight Weight of the new node + * \param[IN] left Pointer to the left child node + * \param[IN] right Pointer to the right child node + * \param[IN] operation Pointer to an operation object */ - ChromaticTreeNode(const Key& key, const Value& value); + ChromaticTreeNode(const Key& key, const Value& value, int weight, + Node* left, Node* right, Operation* operation); /** - * Creates a copy of a given node. + * Creates a node with given parameters and no child nodes. * - * \param[IN] other Node to be copied + * \param[IN] key Key of the new node + * \param[IN] value Value of the new node + * \param[IN] weight Weight of the new node + * \param[IN] operation Pointer to an operation object */ - ChromaticTreeNode(const ChromaticTreeNode& other); - + ChromaticTreeNode(const Key& key, const Value& value, int weight, + Operation* operation); /** * Accessor for the stored key. @@ -97,57 +107,408 @@ class ChromaticTreeNode { * * \return Weight of the node */ - const int& GetWeight() const; + int GetWeight() const; /** * Accessor for the left child pointer. * * \return Reference to the left child pointer */ - ChromaticTreeNode*& GetLeft(); + AtomicNodePtr& GetLeft(); + Node* GetLeft() const; /** * Accessor for the right child pointer. * * \return Reference to the right child pointer */ - ChromaticTreeNode*& GetRight(); + AtomicNodePtr& GetRight(); + Node* GetRight() const; + + /** + * Checks if the node is a leaf. + * + * @return \c true if node is a leaf, \c false otherwise + */ + bool IsLeaf() const; + + /** + * Checks if the node is a sentinel. + * + * @return \c true if node is a sentinel, \c false otherwise + */ + bool IsSentinel() const; + + /** + * Tries to replace one of the child pointers that compares equal to + * \c old_child with the \c new_child using an atomic compare-and-swap + * operation. If neither left nor right child pointer is pointing to + * \c old_child, returns \c false. + * + * \param old_child[IN] Pointer to an old child node to compare against + * \param new_child[IN] Pointer to the new child node + * + * \return \c true if one of the child pointers is now pointing to + * \c new_child, \c false otherwise + */ + bool ReplaceChild(Node* old_child, Node* new_child); + + /** + * Marks node for deletion from the tree + */ + void Retire(); + + /** + * Checks whether the node is marked for deletion from the tree. + * + * \return \c true if node is retired, \c false otherwise + */ + bool IsRetired() const; + + /** + * Accessor for the operation pointer of the node. + * + * \return Reference to this node's operation pointer + */ + AtomicOperationPtr& GetOperation(); + + private: + /** Atomic boolean flag. */ + typedef embb::base::Atomic AtomicFlag; + + /** + * Disable copy construction and assignment. + */ + ChromaticTreeNode(const ChromaticTreeNode&); + ChromaticTreeNode& operator=(const ChromaticTreeNode&); + + const Key key_; /**< Stored key. */ + const Value value_; /**< Stored value. */ + const int weight_; /**< Weight of the node. */ + const bool is_leaf_; /**< True if node is a leaf. */ + const bool is_sentinel_; /**< True if node is a sentinel. */ + AtomicNodePtr left_; /**< Pointer to left child node. */ + AtomicNodePtr right_; /**< Pointer to right child node. */ + AtomicFlag retired_; /**< Retired (marked for deletion) flag. */ + AtomicOperationPtr operation_; /**< Pointer to a tree operation object. */ +}; + +/** + * Tree operation + * + * Describes a chromatic tree operation (insertion, deletion or rotation). + * Contains pointers to the root node of the operation, nodes that are part of + * the operation window, and the newly created node to become a new child of the + * root node. For all existing nodes, the original operation pointers acquired + * through a previous WeakLLXs are stored. Methods for initialization, helping + * and status enquiries are provided. + * + * \tparam Key Key type + * \tparam Value Value type + */ +template +class ChromaticTreeOperation { + public: + /** Node of the tree. */ + typedef ChromaticTreeNode Node; + /** Atomic pointer to a node. */ + typedef embb::base::Atomic AtomicNodePtr; + /** Hazard-protected pointer to a node. */ + typedef UniqueHazardPointer HazardNodePtr; + /** Chromatic tree operation (self type). */ + typedef ChromaticTreeOperation Operation; + /** Atomic pointer to a tree operation. */ + typedef embb::base::Atomic AtomicOperationPtr; + /** Hazard-protected pointer to a tree operation. */ + typedef UniqueHazardPointer HazardOperationPtr; + + /** + * Creates an empty operation object with an "in progress" state. + * + * \param is_dummy Boolean flag for creation of dummy operation objects. + * + */ + ChromaticTreeOperation(bool is_dummy = false); + + /** + * Set the root node of this operation together with its original operation + * pointer. + * + * \param root The root node of the operation + * \param root_operation The original operation pointer of the root node + */ + void SetRoot(Node* root, Operation* root_operation); + + /** + * Sets the nodes of this operation window together with their original + * operation pointers. + * + * \param node1 The node from the operation window + * \param operation1 The original operation pointer of \c node1 + */ + void SetOldNodes(Node* node1, Operation* operation1); + + void SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2); + + void SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2, + Node* node3, Operation* operation3); + + void SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2, + Node* node3, Operation* operation3, + Node* node4, Operation* operation4); + + void SetOldNodes(Node* node1, Operation* operation1, + Node* node2, Operation* operation2, + Node* node3, Operation* operation3, + Node* node4, Operation* operation4, + Node* node5, Operation* operation5); + + /** + * Set the node that is to become the new child of the operation root. + * + * \param new_child Node to become the new child of the operation root + */ + void SetNewChild(Node* new_child); + + /** + * Help execute the operation. First tries to freeze all the nodes in the + * operation window, and if succeeds - retires those nodes and injects the new + * window into the tree. If the freezing step fails, rolls back the operation + * by unfreezing all the nodes of the original window. + * + * \param node_guard Node hazard guard used to protect the helped nodes + * \param oper_guard Operation hazard guard to protect the original operation + * pointers of the helped nodes + * + * \return \c true is the operation successfully commits, \c false if it + * aborts + */ + bool Help(AtomicNodePtr& node_guard, AtomicOperationPtr& oper_guard); + + /** + * Help an operation that has successfully frozen all the nodes in its window + * to complete. This operation may no longer fail or abort. + * + * \param node_guard Node hazard guard used to protect the helped nodes + */ + void HelpCommit(AtomicNodePtr& node_guard); + + /** + * Help an aborted operation by unfreezing the given \c node. + * + * \param node The node to be unfrozen + */ + void HelpAbort(Node* node); + + /** + * Check whether the operation is aborted. + * + * \return \c true if operation is aborted, \c false otherwise + */ + bool IsAborted(); + + /** + * Check whether the operation is in progress. + * + * \return \c true if operation is in progress, \c false otherwise + */ + bool IsInProgress(); + + /** + * Check whether the operation is committed. + * + * \return \c true if operation is committed, \c false otherwise + */ + bool IsCommitted(); + + /** + * Performs the necessary post-processing steps for the operation, i.e. if the + * operation commits, resets all the operation pointers of the nodes of the + * removed operation window to point to the retired dummy-operation. Must be + * called only once after the operation either commits or aborts. + * + * \param retired_dummy Dummy operation object used to reset operation + * pointers in the retired nodes. + */ + void CleanUp(Operation* retired_dummy); + +#ifdef EMBB_DEBUG + /** + * Set the deleted flag for this operation. No other method of this operation + * is allowed to be called after this method returns. + */ + void SetDeleted(); +#endif private: - const Key key_; /**< Stored key */ - const Value value_; /**< Stored value */ - const int weight_; /**< Weight of the node */ - ChromaticTreeNode* left_; /**< Pointer to left child node */ - ChromaticTreeNode* right_; /**< Pointer to right child node */ + /** Enumeration of possible operation states. */ + typedef enum { + STATE_ABORTED, + STATE_ROLLBACK, + STATE_FREEZING, + STATE_ALL_FROZEN, + STATE_COMMITTED + } State; + /** Atomic wrapper for the operation state. */ + typedef embb::base::Atomic AtomicState; + + /** Maximal possible number of nodes in an operation window. */ + static const size_t MAX_NODES = 5; + + /** + * Check whether the operation is rolling back. + * + * \return \c true if operation is rolling back, \c false otherwise + */ + bool IsRollingBack(); + + /** + * Check whether the operation is freezing. + * + * \return \c true if operation is freezing, \c false otherwise + */ + bool IsFreezing(); + + /** + * Check whether all the nodes from the operation window were successfully + * frozen for this operation. + * + * \return \c true if all nodes of the window were frozen, \c false otherwise + */ + bool IsAllFrozen(); + + /** + * Tries to freeze all the nodes from the operation window for this operation. + * If the current operation encounters a conflict and fails, this method helps + * to roll back the operation by unfreezing all the nodes. + * + * \param node_guard Node hazard guard used to protect the freezing nodes + * \param oper_guard Operation hazard guard to protect the original operation + * pointers of the freezing nodes + * + * \return \c true if all nodes of this operation window were successfully + * frozen, \c false otherwise + */ + bool FreezeAll(AtomicNodePtr& node_guard, AtomicOperationPtr& oper_guard); + + /** + * Tries to freeze the given \c node for the current operation by using a CAS + * to set the node's operation pointer to point to this operation. If the CAS + * fails and the node is not frozen for this operation, tries to switch to the + * "rolling back" state. + * + * \param node The node to be frozen + * \param operation The original operation pointer of the \c node + * + * \return \c true if \c node was successfully frozen, \c false otherwise + */ + bool Freeze(Node* node, Operation* operation); + + /** + * Rolls back the current operation by unfreezing all the nodes of the + * operation window, i.e. changing the operation pointers of those nodes to + * their original values. + * + * \param node_guard Node hazard guard used to protect the nodes being + * unfrozen + */ + void UnfreezeAll(AtomicNodePtr& node_guard); + + /** + * Unfreezes the given \c node by changing its operation pointer back to the + * original value. + * + * \param node The node to be unfrozen + * \param operation The original operation pointer of the \c node + */ + void Unfreeze(Node* node, Operation* operation); + + /** + * Tries to switch from one operation state to another. If current state is + * already equal to \c new_state, returns \c true. Otherwise, tries to switch + * from \c old_state to \c new_state atomically using a CAS operation, and + * returns \c true if this CAS succeeds. + * + * \param old_state Currently expected state of the operation + * \param new_state New desired state of the operation + * + * \return \c true is state was successfully changed to \c new_state, \c false + * otherwise + */ + bool SwitchState(State old_state, State new_state); + + /** + * Disable copy construction and assignment. + */ + ChromaticTreeOperation(const ChromaticTreeOperation&); + ChromaticTreeOperation& operator=(const ChromaticTreeOperation&); + + /** Current state of the operation. */ + AtomicState state_; + /** Root node of the operation. */ + Node* root_; + /** Original operation pointer of the root node. */ + Operation* root_operation_; + /** Number of nodes in the operation window. */ + size_t num_old_nodes_; + /** Nodes of the operation window. */ + Node* old_nodes_[MAX_NODES]; + /** Original operation pointers for the nodes of the window. */ + Operation* old_operations_[MAX_NODES]; + /** Pointer to the new node to become the new child of the root. */ + Node* new_child_; +#ifdef EMBB_DEBUG + /** Debug flag for memory management control (is set when node is deleted). */ + embb::base::Atomic deleted_; +#endif }; } // namespace internal +namespace test { +/** + * Forward declaration of the test class. + */ +template +class TreeTest; + +} // namespace test /** - * Chromatic balanced binary search tree + * Chromatic balanced binary search tree. * * Implements a balanced BST with support for \c Get, \c Insert and \c Delete * operations. * - * \tparam Key Key type - * \tparam Value Value type - * \tparam Compare Custom comparator type for the keys. An object of the - * type \c Compare must must be a functor taking two - * arguments \c rhs and \c lhs of type \c Key and - * returning \c true if and only if (rhs < lhs) holds - * \tparam NodePool The object pool type used for allocation/deallocation - * of tree nodes. + * \tparam Key Key type + * \tparam Value Value type + * \tparam Compare Custom comparator type for the keys. An object of the + * type \c Compare must must be a functor taking two + * arguments \c rhs and \c lhs of type \c Key and + * returning \c true if and only if (rhs < lhs) holds + * \tparam ValuePool Type of the value pool to be used inside object pools for + * tree nodes and operation objects */ template, - typename NodePool = ObjectPool, - LockFreeTreeValuePool > + typename ValuePool = LockFreeTreeValuePool > class ChromaticTree { public: /** + * Exposing the \c Key template parameter back to the user. + */ + typedef Key KeyType; + /** + * Exposing the \c Value template parameter back to the user. + */ + typedef Value ValueType; + + /** * Creates a new tree with given capacity. * * \memory Allocates (2 * capacity + 7) tree nodes each of size @@ -198,7 +559,7 @@ class ChromaticTree { * \param[IN] value New value to be inserted * * \return \c true if the given key-value pair was successfully inserted into - * the tree, \c false if tree has reached its capacity + * the tree, \c false if the tree has reached its capacity */ bool TryInsert(const Key& key, const Value& value); @@ -215,7 +576,7 @@ class ChromaticTree { * tree for the given key * * \return \c true if the given key-value pair was successfully inserted into - * the tree, \c false if tree has reached its capacity + * the tree, \c false if the tree has reached its capacity */ bool TryInsert(const Key& key, const Value& value, Value& old_value); @@ -225,7 +586,7 @@ class ChromaticTree { * \param[IN] key Key to be removed * * \return \c true if the given key-value pair was successfully deleted from - * the tree, \c false if the given key was not stored in the tree + * the tree, \c false if there is not enough memory */ bool TryDelete(const Key& key); @@ -239,7 +600,7 @@ class ChromaticTree { * tree for the given key * * \return \c true if the given key-value pair was successfully deleted from - * the tree, \c false if the given key was not stored in the tree + * the tree, \c false if there is not enough memory */ bool TryDelete(const Key& key, Value& old_value); @@ -249,54 +610,74 @@ class ChromaticTree { * * \return Number of key-value pairs the tree can store */ - size_t GetCapacity(); + size_t GetCapacity() const; /** * Accessor for the dummy value used by the tree * * \return Object of type \c Value that is used by the tree as a dummy value */ - const Value& GetUndefinedValue(); + const Value& GetUndefinedValue() const; /** * Checks whether the tree is currently empty. * * \return \c true if the tree stores no key-value pairs, \c false otherwise */ - bool IsEmpty(); + bool IsEmpty() const; private: - /** - * Typedef for a node of the tree. - */ + /** Node of the tree. */ typedef internal::ChromaticTreeNode Node; - /** - * Typedef for a pointer to a node of the tree. - */ - typedef internal::ChromaticTreeNode* NodePtr; - - - /** - * Follows a path from the root of the tree to some leaf searching for the - * given key (the leaf found by this method may or may not contain the given - * key). Returns the reached leaf. - * - * \param[IN] key Key to be searched for - * \param[IN,OUT] leaf Reference to the reached leaf - */ - void Search(const Key& key, NodePtr& leaf) const; - - /** - * Follows a path from the root of the tree to some leaf searching for the - * given key (the leaf found by this method may or may not contain the given - * key). Returns the reached leaf together with its ancestors. - * - * \param[IN] key Key to be searched for - * \param[IN,OUT] leaf Reference to the reached leaf - * \param[IN,OUT] parent Reference to the parent of the reached leaf - */ - void Search(const Key& key, NodePtr& leaf, NodePtr& parent) const; - + /** Atomic pointer to a node. */ + typedef embb::base::Atomic AtomicNodePtr; + /** Hazard-protected pointer to a node. */ + typedef internal::UniqueHazardPointer HazardNodePtr; + /** Chromatic tree operation. */ + typedef internal::ChromaticTreeOperation Operation; + /** Atomic pointer to a tree operation. */ + typedef embb::base::Atomic AtomicOperationPtr; + /** Hazard-protected pointer to a tree operation. */ + typedef internal::UniqueHazardPointer HazardOperationPtr; + /** Object pool for tree nodes. */ + typedef ObjectPool NodePool; + /** Object pool for tree operations. */ + typedef ObjectPool OperationPool; + + /** Enumeration of used hazard pointer indexes. */ + typedef enum { + // Node/operation used for helping + HIDX_HELPING = 0, + // Common shared nodes/operations + HIDX_GRANDGRANDPARENT, + HIDX_GRANDPARENT, + HIDX_PARENT, + HIDX_LEAF, + HIDX_SIBLING = HIDX_GRANDGRANDPARENT, // Never occur in the same scope + // Rebalancing nodes/operations + HIDX_U = HIDX_GRANDGRANDPARENT, // Renamed when passed to "Rebalance" + HIDX_UX = HIDX_GRANDPARENT, // Renamed when passed to "Rebalance" + HIDX_UXX = HIDX_PARENT, // Renamed when passed to "Rebalance" + HIDX_UXXX = HIDX_LEAF, // Renamed when passed to "Rebalance" + HIDX_UXL = HIDX_LEAF + 1, // Restoring sequence + HIDX_UXR, + HIDX_UXXL, + HIDX_UXXR, + // Left overweight + HIDX_UXXRL = HIDX_U, // Reusing hazard guard that is no longer used + HIDX_UXXRR = HIDX_UXR, // Reusing hazard guard that is no longer used + HIDX_UXXRLR = HIDX_UXR, // Reusing hazard guard that is no longer used + HIDX_UXXRLL = HIDX_UXL, // Reusing hazard guard that is no longer used + // Right overweight + HIDX_UXXLR = HIDX_UXXRL, // Symmetric rotation + HIDX_UXXLL = HIDX_UXXRR, // Symmetric rotation + HIDX_UXXLRL = HIDX_UXXRLR, // Symmetric rotation + HIDX_UXXLRR = HIDX_UXXRLL, // Symmetric rotation + // Current operation object + HIDX_CURRENT_OP = HIDX_UXXR + 1, // Restoring sequence + HIDX_MAX + } HazardIndex; + /** * Follows a path from the root of the tree to some leaf searching for the * given key (the leaf found by this method may or may not contain the given @@ -307,35 +688,8 @@ class ChromaticTree { * \param[IN,OUT] parent Reference to the parent of the reached leaf * \param[IN,OUT] grandparent Reference to the grandparent of the reached leaf */ - void Search(const Key& key, NodePtr& leaf, NodePtr& parent, - NodePtr& grandparent) const; - - /** - * Checks whether the given node is a leaf. - * - * \param[IN] node Node to be checked - * - * \return \c true if the given node is a leaf, \c false otherwise - */ - bool IsLeaf(const NodePtr& node) const; - - /** - * Checks whether the given node is a sentinel node. - * - * \param[IN] node Node to be checked - * - * \return \c true if the given node is a sentinel node, \c false otherwise - */ - bool IsSentinel(const NodePtr& node) const; - - /** - * Checks whether the given node has to maintain the constant weight of \c 1. - * - * \param[IN] node Node to be checked - * - * \return \c true if the given node has constant weight, \c false otherwise - */ - bool HasFixedWeight(const NodePtr& node) const; + void Search(const Key& key, HazardNodePtr& leaf, HazardNodePtr& parent, + HazardNodePtr& grandparent); /** * Checks whether the given node has a specified child node. @@ -346,21 +700,8 @@ class ChromaticTree { * \return \c true if \c child is a child node of \c parent, \c false * otherwise */ - bool HasChild(const NodePtr& parent, const NodePtr& child) const; - - /** - * Accessor for the child pointer of a given parent to the specified child. - * - * \pre The \c child has to be an actual child of the \c parent. - * - * \param[IN] parent Parent node - * \param[IN] child Child node of the \c parent - * - * \return Reference to a member pointer of the \c parent that points to - * the \c child - */ - NodePtr& GetPointerToChild(const NodePtr& parent, const NodePtr& child) const; - + bool HasChild(const Node* parent, const Node* child) const; + /** * Destroys all the nodes of a subtree rooted at the given node, including the * node itself. @@ -369,14 +710,117 @@ class ChromaticTree { * * \param node Root of the subtree to be destroyed */ - void Destruct(const NodePtr& node); - + void Destruct(Node* node); + + /** + * Computes the hight of the subtree rooted at the given node. + * + * \notthreadsafe + * + * \param[IN] node Root of the subtree for which the height is requested + * + * \return The height of a subtree rooted at node \c node. (The height of a + * leaf node is defined to be zero). + */ + int GetHeight(const Node* node) const; + + /** + * Check whether the tree is currently in a balanced state (if it is a valid + * red-black tree). + * + * \return \c true if the tree is balanced, \c false otherwise + */ + bool IsBalanced() const; + + /** + * Check whether a subtree rooted at the given node is balanced. + * + * \param[IN] node Root of the subtree for which the balance is checked + * + * \return \c true if the tree is balanced, \c false otherwise + */ + bool IsBalanced(const Node* node) const; + + /** + * Checks whether a given operation is a dummy operation. + * + * \param[IN] operation Operation to be checked + * + * \return \c true if the given operation is a dummy, \c false otherwise + */ + bool IsDummyOperation(const Operation* operation) const; + + /** + * Retire a hazardous node using the node hazard manager. + * + * \param node A hazardous node to be retired + */ + void RetireNode(HazardNodePtr& node); + + /** + * Retire a hazardous operation object using the operation hazard manager. + * + * \param operation A hazardous operation to be retired + */ + void RetireOperation(HazardOperationPtr& operation); + + /** + * Get a node hazard guard with the specified \c index from the node hazard + * manager. + * + * \param index Index of requested guard + * + * \return Hazard guard with the specified index + */ + AtomicNodePtr& GetNodeGuard(HazardIndex index); + + /** + * Get an operation hazard guard with the specified \c index from the + * operation hazard manager. + * + * \param index Index of requested guard + * + * \return Hazard guard with the specified index + */ + AtomicOperationPtr& GetOperationGuard(HazardIndex index); + + /** + * Performs a WeakLLX operation according to the Tree Update template, i.e. + * reads the operation pointer of the given \c node and check whether this + * operation is completed or in progress. If it is completed and the \c node + * is not retired, returns \c true and sets the \c operation variable to point + * to the read operation. If the read operation is in progress, helps it and + * returns \c false. + * + * \param node The node + * \param operation Reference to the current operation pointer value of the + * \c node to be used in the following SCX + * + * \return \c true if the \c node is not reserved by another operation and the + * \c operation was successfully read, \c false if the \c node is busy + */ + bool WeakLLX(HazardNodePtr& node, HazardOperationPtr& operation); + + /** + * Free a tree node by returning it to the node pool. + * + * \param[IN] node A node to be freed. + */ + void FreeNode(Node* node); + + /** + * Free a tree operation by returning it to the operation pool. + * + * \param[IN] operation An operation to be freed. + */ + void FreeOperation(Operation* operation); + /** * Follows the path from the root to some leaf (directed by the given key) and * checks for any tree balancing violations. If a violation is found, tries * to fix it by using a set of rebalancing rotations. * - * \param key Key to be searched for + * \param[IN] key Key to be searched for * * \return \c true if the tree was successfully rebalanced, \c false otherwise */ @@ -385,90 +829,56 @@ class ChromaticTree { /** * Next block of methods is used internally to keep the balance of the tree. */ - bool Rebalance(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, - const NodePtr& uxxx); - bool OverweightLeft(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxxl, const NodePtr& uxxr, - const bool& uxx_is_left); - bool OverweightRight(const NodePtr& u, const NodePtr& ux, const NodePtr& uxx, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxxl, const NodePtr& uxxr, - const bool& uxx_is_right); - bool BLK(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr); - bool PUSH_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr); - bool PUSH_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr); - bool RB1_L(const NodePtr& u, const NodePtr& ux, const NodePtr& uxl); - bool RB1_R(const NodePtr& u, const NodePtr& ux, const NodePtr& uxr); - bool RB2_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxlr); - bool RB2_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxr, const NodePtr& uxrl); - bool W1_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxrl); - bool W1_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxlr); - bool W2_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxrl); - bool W2_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxlr); - bool W3_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxrl, const NodePtr& uxrll); - bool W3_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxlr, const NodePtr& uxlrr); - bool W4_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxrl, const NodePtr& uxrlr); - bool W4_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxlr, const NodePtr& uxlrl); - bool W5_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxrr); - bool W5_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxll); - bool W6_L(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxrl); - bool W6_R(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr, - const NodePtr& uxlr); - bool W7(const NodePtr& u, const NodePtr& ux, - const NodePtr& uxl, const NodePtr& uxr); - + embb_errors_t Rebalance(HazardNodePtr& u, HazardNodePtr& ux, + HazardNodePtr& uxx, HazardNodePtr& uxxx); + + embb_errors_t OverweightLeft(HazardNodePtr& u, HazardOperationPtr& u_op, + HazardNodePtr& ux, HazardOperationPtr& ux_op, + HazardNodePtr& uxx, HazardOperationPtr& uxx_op, + HazardNodePtr& uxl, HazardNodePtr& uxr, + HazardNodePtr& uxxl, HazardOperationPtr& uxxl_op, + HazardNodePtr& uxxr, bool uxx_is_left); + + embb_errors_t OverweightRight(HazardNodePtr& u, HazardOperationPtr& u_op, + HazardNodePtr& ux, HazardOperationPtr& ux_op, + HazardNodePtr& uxx, HazardOperationPtr& uxx_op, + HazardNodePtr& uxl, HazardNodePtr& uxr, + HazardNodePtr& uxxl, HazardNodePtr& uxxr, + HazardOperationPtr& uxxr_op, bool uxx_is_right); + + // The following included header contains the class methods implementing + // tree rotations. It is generated automatically and must be included + // directly inside the class definition. +# include + + /** Hazard pointer manager for protecting node pointers. */ + internal::HazardPointer node_hazard_manager_; + /** Hazard pointer manager for protecting operation pointers. */ + internal::HazardPointer operation_hazard_manager_; + + /** A dummy key used by the tree. */ + const Key undefined_key_; + /** A dummy value used by the tree. */ + const Value undefined_value_; + /** Comparator object for the keys. */ + const Compare compare_; + /** User-requested capacity of the tree. */ + size_t capacity_; + /** Pool of tree nodes. */ + NodePool node_pool_; + /** Pool of operation objects. */ + OperationPool operation_pool_; + /** Dummy operation used in newly created nodes. */ + Operation initial_operation_dummy_; + /** Dummy operation used in retired nodes. */ + Operation retired_operation_dummy_; + /** Pointer to the sentinel node used as the entry point into the tree. */ + Node* const entry_; + /** - * Computes the hight of the subtree rooted at the given node. - * - * \param[IN] node Root of the subtree for which the height is requested - * - * \return The height of a subtree rooted at node \c node. (The height of a - * leaf node is defined to be zero). + * Friending the test class for white-box testing */ - int GetHeight(const NodePtr& node) const; - - const Key undefined_key_; /**< A dummy key used by the tree */ - const Value undefined_value_; /**< A dummy value used by the tree */ - const Compare compare_; /**< Comparator object for the keys */ - size_t capacity_; /**< User-requested capacity of the tree */ - NodePool node_pool_; /**< Comparator object for the keys */ - NodePtr entry_; /**< Pointer to the sentinel node used as - * the entry point into the tree */ - - typedef embb::base::ReadWriteLock::Reader ReadWriteLockReader; - typedef embb::base::ReadWriteLock::Writer ReadWriteLockWriter; - typedef embb::base::LockGuard ReaderLockGuard; - typedef embb::base::LockGuard WriterLockGuard; - embb::base::ReadWriteLock readwrite_lock_; + friend class test::TreeTest; }; } // namespace containers diff --git a/containers_cpp/include/embb/containers/object_pool.h b/containers_cpp/include/embb/containers/object_pool.h index c4f6114..3dae564 100644 --- a/containers_cpp/include/embb/containers/object_pool.h +++ b/containers_cpp/include/embb/containers/object_pool.h @@ -185,6 +185,12 @@ class ObjectPool { Type* Allocate(Param1 const& param1, Param2 const& param2, Param3 const& param3, Param4 const& param4, Param5 const& param5); + template + Type* Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3, Param4 const& param4, Param5 const& param5, + Param6 const& param6); + #endif }; } // namespace containers diff --git a/containers_cpp/test/hazard_pointer_test.cc b/containers_cpp/test/hazard_pointer_test.cc index 17a4c38..ab2666c 100644 --- a/containers_cpp/test/hazard_pointer_test.cc +++ b/containers_cpp/test/hazard_pointer_test.cc @@ -97,7 +97,7 @@ void HazardPointerTest::HazardPointerTest1_ThreadMethod() { PT_ASSERT(success == true); - embb::base::Atomic* allocated_object_from_different_thread; + embb::base::Atomic* allocated_object_from_different_thread(0); int diff_count = 0; diff --git a/containers_cpp/test/tree_test-inl.h b/containers_cpp/test/tree_test-inl.h index 42e1252..78913c4 100644 --- a/containers_cpp/test/tree_test-inl.h +++ b/containers_cpp/test/tree_test-inl.h @@ -39,116 +39,298 @@ namespace test { template TreeTest::TreeTest() - : tree_(NULL) { - // Repeat twice to ensure that the tree remains operational after all the - // elements were removed from it + : tree_(NULL), + bad_key_(static_cast(-1)), + bad_value_(static_cast(-1)) { CreateUnit("TreeTestSingleThreadInsertDelete"). - Pre(&TreeTest::TreeTestSingleThreadInsertDelete_Pre, this). - Add(&TreeTest::TreeTestSingleThreadInsertDelete_ThreadMethod, this, 1, 2). - Post(&TreeTest::TreeTestSingleThreadInsertDelete_Post, this); + Pre(&TreeTest::TreeTestInsertDelete_Pre, this). + Add(&TreeTest::TreeTestInsertDeleteSingleThread_ThreadMethod, + this, 1, NUM_ITERATIONS). + Post(&TreeTest::TreeTestInsertDelete_Post, this); CreateUnit("TreeTestMultiThreadInsertDelete"). - Pre(&TreeTest::TreeTestSingleThreadInsertDelete_Pre, this). - Add(&TreeTest::TreeTestMultiThreadInsertDelete_ThreadMethod, this, - NUM_TEST_THREADS, 2). - Post(&TreeTest::TreeTestSingleThreadInsertDelete_Post, this); + Pre(&TreeTest::TreeTestInsertDelete_Pre, this). + Add(&TreeTest::TreeTestInsertDeleteMultiThread_ThreadMethod, this, + NUM_TEST_THREADS, NUM_ITERATIONS). + Post(&TreeTest::TreeTestInsertDelete_Post, this); + CreateUnit("TreeTestConcurrentGet"). + Pre(&TreeTest::TreeTestConcurrentGet_Pre, this). + Add(&TreeTest::TreeTestConcurrentGet_WriterMethod, this, + NUM_TEST_THREADS / 2, NUM_ITERATIONS). + Add(&TreeTest::TreeTestConcurrentGet_ReaderMethod, this, + NUM_TEST_THREADS / 2, NUM_ITERATIONS). + Post(&TreeTest::TreeTestConcurrentGet_Post, this); + CreateUnit("TreeTestConcurrentGetMinimal"). + Pre(&TreeTest::TreeTestConcurrentGetMinimal_Pre, this). + Add(&TreeTest::TreeTestConcurrentGet_WriterMethod, this, + NUM_TEST_THREADS / 2, NUM_ITERATIONS). + Add(&TreeTest::TreeTestConcurrentGet_ReaderMethod, this, + NUM_TEST_THREADS / 2, NUM_ITERATIONS). + Post(&TreeTest::TreeTestConcurrentGet_Post, this); + CreateUnit("TreeTestBalance"). + Pre(&TreeTest::TreeTestBalance_Pre, this). + Add(&TreeTest::TreeTestBalance_ThreadMethod, this, + NUM_TEST_THREADS, 1). + Post(&TreeTest::TreeTestBalance_Post, this); } template TreeTest::Worker:: -Worker(Tree& tree, size_t thread_id, int num_elements) - : tree_(tree), thread_id_(thread_id), num_elements_(num_elements) {} +Worker(TreePtr tree, int thread_id) + : tree_(tree), thread_id_(thread_id) {} template void TreeTest::Worker:: -Run() { - ElementVector elements; - for (int i = 0; i < num_elements_; ++i) { - Key key = i * 133 * 100 + thread_id_; - Value value = i * 133 * 100 + thread_id_; - elements.push_back(std::make_pair(key, value)); +InsertReplaceDelete(int num_elements) { + PrepareElements(num_elements); + InsertAll(); + ReplaceHalf(); + DeleteAll(); +} + +template +void TreeTest::Worker:: +PrepareElements(int num_elements) { + // Fill the "elements_" vector + elements_.clear(); + for (int i = 0; i < num_elements; ++i) { + Key key = static_cast(i * 100 + thread_id_); + Value value = static_cast(i * 100 + thread_id_); + elements_.push_back(std::make_pair(key, value)); } +} +template +void TreeTest::Worker:: +InsertAll() { // Insert elements into the tree - ::std::random_shuffle(elements.begin(), elements.end()); - for (ElementIterator it = elements.begin(); it != elements.end(); ++it) { - bool success = tree_.TryInsert(it->first, it->second); + ::std::random_shuffle(elements_.begin(), elements_.end()); + for (ElementIterator it = elements_.begin(); it != elements_.end(); ++it) { + Value old_value; + Value bad_value = tree_->GetUndefinedValue(); + bool success = tree_->TryInsert(it->first, it->second, old_value); PT_ASSERT_MSG(success, "Failed to insert element into the tree."); + PT_ASSERT_EQ_MSG(old_value, bad_value, "A key was already in the tree."); } // Verify that all inserted elements are available in the tree - ::std::random_shuffle(elements.begin(), elements.end()); - for (ElementIterator it = elements.begin(); it != elements.end(); ++it) { + ::std::random_shuffle(elements_.begin(), elements_.end()); + for (ElementIterator it = elements_.begin(); it != elements_.end(); ++it) { Value value; - bool success = tree_.Get(it->first, value); + bool success = tree_->Get(it->first, value); PT_ASSERT_MSG(success, "Failed to get an element from the tree."); - PT_ASSERT_MSG(it->second == value, "Wrong value retrieved from the tree."); + PT_ASSERT_EQ_MSG(it->second, value, "Wrong value retrieved from the tree."); } +} +template +void TreeTest::Worker:: +ReplaceHalf() { // Replace some of the elements that were inserted earlier - ::std::random_shuffle(elements.begin(), elements.end()); - ElementIterator elements_middle = elements.begin() + num_elements_ / 2; - for (ElementIterator it = elements.begin(); it != elements_middle; ++it) { + ::std::random_shuffle(elements_.begin(), elements_.end()); + ElementIterator elements_middle = elements_.begin() + + static_cast(elements_.size() / 2); + for (ElementIterator it = elements_.begin(); it != elements_middle; ++it) { + Value old_value; + Value expected = it->second; it->second *= 13; - bool success = tree_.TryInsert(it->first, it->second); + bool success = tree_->TryInsert(it->first, it->second, old_value); PT_ASSERT_MSG(success, "Failed to insert element into the tree."); + PT_ASSERT_EQ_MSG(old_value, expected, "Wrong value replaced in the tree."); } // Verify again that all elements are in the tree and have correct values - ::std::random_shuffle(elements.begin(), elements.end()); - for (ElementIterator it = elements.begin(); it != elements.end(); ++it) { + ::std::random_shuffle(elements_.begin(), elements_.end()); + for (ElementIterator it = elements_.begin(); it != elements_.end(); ++it) { Value value; - bool success = tree_.Get(it->first, value); + bool success = tree_->Get(it->first, value); PT_ASSERT_MSG(success, "Failed to get an element from the tree."); - PT_ASSERT_MSG(it->second == value, "Wrong value retrieved from the tree."); + PT_ASSERT_EQ_MSG(it->second, value, "Wrong value retrieved from the tree."); } +} +template +void TreeTest::Worker:: +DeleteAll() { // Delete elements from the tree - ::std::random_shuffle(elements.begin(), elements.end()); - for (ElementIterator it = elements.begin(); it != elements.end(); ++it) { - Value value; - bool success = tree_.TryDelete(it->first, value); + ::std::random_shuffle(elements_.begin(), elements_.end()); + for (ElementIterator it = elements_.begin(); it != elements_.end(); ++it) { + Value old_value; + Value expected = it->second; + bool success = tree_->TryDelete(it->first, old_value); PT_ASSERT_MSG(success, "Failed to delete element from the tree."); - PT_ASSERT_MSG(it->second == value, "Wrong value deleted from the tree."); + PT_ASSERT_EQ_MSG(expected, old_value, "Wrong value deleted from the tree."); } } template -void TreeTest:: -TreeTestSingleThreadInsertDelete_Pre() { - tree_ = new Tree(TREE_CAPACITY); +void TreeTest::Worker:: +DeleteHalf() { + // Delete half of the elements from the tree + ::std::random_shuffle(elements_.begin(), elements_.end()); + ElementIterator elements_middle = elements_.begin() + + static_cast(elements_.size() / 2); + for (ElementIterator it = elements_.begin(); it != elements_middle; ++it) { + Value old_value; + Value expected = it->second; + bool success = tree_->TryDelete(it->first, old_value); + PT_ASSERT_MSG(success, "Failed to delete element from the tree."); + PT_ASSERT_EQ_MSG(expected, old_value, "Wrong value deleted from the tree."); + } } template void TreeTest:: -TreeTestSingleThreadInsertDelete_ThreadMethod() { - size_t thread_id = partest::TestSuite::GetCurrentThreadID(); +TreeTestInsertDelete_Pre() { + embb_internal_thread_index_reset(); + tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_); +} - Worker worker(*tree_, thread_id, TREE_CAPACITY); +template +void TreeTest:: +TreeTestInsertDeleteSingleThread_ThreadMethod() { + Worker worker(tree_, 0); - worker.Run(); + for (int i = 1; i <= 10; ++i) { + worker.InsertReplaceDelete(i * (TREE_CAPACITY / 10)); + } } template void TreeTest:: -TreeTestMultiThreadInsertDelete_ThreadMethod() { - size_t thread_id = partest::TestSuite::GetCurrentThreadID(); +TreeTestInsertDeleteMultiThread_ThreadMethod() { + int thread_id = static_cast(partest::TestSuite::GetCurrentThreadID()); + thread_id %= NUM_TEST_THREADS; int num_elements = TREE_CAPACITY / (NUM_TEST_THREADS + 1); if (thread_id == 0) { num_elements *= 2; } - Worker worker(*tree_, thread_id, num_elements); - worker.Run(); + Worker worker(tree_, thread_id); + + for (int i = 1; i <= 10; ++i) { + worker.InsertReplaceDelete(i * (num_elements / 10)); + } } template void TreeTest:: -TreeTestSingleThreadInsertDelete_Post() { +TreeTestInsertDelete_Post() { PT_ASSERT_MSG((tree_->IsEmpty()), "The tree must be empty at this point."); delete tree_; } +template +void TreeTest:: +TreeTestConcurrentGet_Pre() { + embb_internal_thread_index_reset(); + tree_ = new Tree(TREE_CAPACITY / 2, bad_key_, bad_value_); + + ElementVector elements; + for (int i = 0; i < TREE_CAPACITY / 2; ++i) { + Key key = static_cast(i); + Value value = static_cast(i); + elements.push_back(std::make_pair(key, value)); + } + + ::std::random_shuffle(elements.begin(), elements.end()); + for (ElementIterator it = elements.begin(); it != elements.end(); ++it) { + Value old_value; + Value bad_value = tree_->GetUndefinedValue(); + bool success = tree_->TryInsert(it->first, it->second, old_value); + PT_ASSERT_MSG(success, "Failed to insert element into the tree."); + PT_ASSERT_EQ_MSG(old_value, bad_value, "A key was already in the tree."); + } +} + +template +void TreeTest:: +TreeTestConcurrentGetMinimal_Pre() { + embb_internal_thread_index_reset(); + tree_ = new Tree(NUM_TEST_THREADS / 2, bad_key_, bad_value_); + + for (int i = 0; i < NUM_TEST_THREADS / 2; ++i) { + Key key = static_cast(TREE_CAPACITY / 4 + i); + Value value = static_cast(TREE_CAPACITY / 4 + i); + Value old_value; + Value bad_value = tree_->GetUndefinedValue(); + bool success = tree_->TryInsert(key, value, old_value); + PT_ASSERT_MSG(success, "Failed to insert element into the tree."); + PT_ASSERT_EQ_MSG(old_value, bad_value, "A key was already in the tree."); + } +} + +template +void TreeTest:: +TreeTestConcurrentGet_WriterMethod() { + int idx = static_cast(partest::TestSuite::GetCurrentThreadID()); + idx %= (NUM_TEST_THREADS / 2); + + Key key = static_cast(TREE_CAPACITY / 4 + idx); + Value value = static_cast(TREE_CAPACITY / 4 + idx); + + for (int i = 0; i < 1000; ++i) { + Value old_value; + bool success = tree_->TryDelete(key, old_value); + PT_ASSERT_MSG(success, "Failed to delete element from the tree."); + PT_ASSERT_EQ_MSG(old_value, value, "Wrong value deleted from the tree."); + + Value bad_value = tree_->GetUndefinedValue(); + success = tree_->TryInsert(key, value, old_value); + PT_ASSERT_MSG(success, "Failed to insert element into the tree."); + PT_ASSERT_EQ_MSG(old_value, bad_value, "A key was already in the tree."); + } +} + +template +void TreeTest:: +TreeTestConcurrentGet_ReaderMethod() { + int idx = static_cast(partest::TestSuite::GetCurrentThreadID()); + idx %= (NUM_TEST_THREADS / 2); + + Key key = static_cast(TREE_CAPACITY / 4 + idx); + + for (int i = 0; i < 1000; ++i) { + Value value; + tree_->Get(key, value); + } +} + +template +void TreeTest:: +TreeTestConcurrentGet_Post() { + delete tree_; +} + +template +void TreeTest:: +TreeTestBalance_Pre() { + embb_internal_thread_index_reset(); + tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_); +} + +template +void TreeTest:: +TreeTestBalance_ThreadMethod() { + int thread_id = static_cast(partest::TestSuite::GetCurrentThreadID()); + thread_id %= NUM_TEST_THREADS; + + int num_elements = TREE_CAPACITY / NUM_TEST_THREADS; + + Worker worker(tree_, thread_id); + + worker.PrepareElements(num_elements); + worker.InsertAll(); + worker.DeleteHalf(); +} + +template +void TreeTest:: +TreeTestBalance_Post() { + PT_ASSERT_MSG((tree_->IsBalanced()), "The tree must balanced at this point."); + delete tree_; +} + } // namespace test } // namespace containers } // namespace embb diff --git a/containers_cpp/test/tree_test.h b/containers_cpp/test/tree_test.h index 1c42805..9fac203 100644 --- a/containers_cpp/test/tree_test.h +++ b/containers_cpp/test/tree_test.h @@ -37,32 +37,52 @@ class TreeTest : public partest::TestCase { TreeTest(); private: + typedef Tree* TreePtr; + typedef typename Tree::KeyType Key; + typedef typename Tree::ValueType Value; + typedef ::std::pair Element; + typedef ::std::vector ElementVector; + typedef typename ElementVector::iterator ElementIterator; + class Worker { public: - Worker(Tree& tree, size_t thread_id, int num_elements); - void Run(); + Worker(TreePtr tree, int thread_id); + void InsertReplaceDelete(int num_elements); + void PrepareElements(int num_elements); + void InsertAll(); + void ReplaceHalf(); + void DeleteAll(); + void DeleteHalf(); private: - Tree& tree_; - size_t thread_id_; - int num_elements_; - }; + Worker(const Worker&); + Worker& operator=(const Worker&); - typedef int Key; - typedef int Value; - typedef ::std::pair Element; - typedef ::std::vector ElementVector; - typedef ElementVector::iterator ElementIterator; + TreePtr tree_; + int thread_id_; + ElementVector elements_; + }; - static const int TREE_CAPACITY = 2000; - static const int NUM_TEST_THREADS = 3; + static const int TREE_CAPACITY = 100; + static const int NUM_TEST_THREADS = 4; + static const int NUM_ITERATIONS = 100; - void TreeTestSingleThreadInsertDelete_Pre(); - void TreeTestSingleThreadInsertDelete_ThreadMethod(); - void TreeTestMultiThreadInsertDelete_ThreadMethod(); - void TreeTestSingleThreadInsertDelete_Post(); + void TreeTestInsertDelete_Pre(); + void TreeTestInsertDeleteSingleThread_ThreadMethod(); + void TreeTestInsertDeleteMultiThread_ThreadMethod(); + 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 diff --git a/dataflow_cpp/test/dataflow_cpp_test_simple.cc b/dataflow_cpp/test/dataflow_cpp_test_simple.cc index 78af07c..b434625 100644 --- a/dataflow_cpp/test/dataflow_cpp_test_simple.cc +++ b/dataflow_cpp/test/dataflow_cpp_test_simple.cc @@ -39,7 +39,7 @@ #define NUM_SLICES 8 #define TEST_COUNT 12 -typedef embb::dataflow::Network<8> MyNetwork; +typedef embb::dataflow::Network MyNetwork; typedef MyNetwork::ConstantSource< int > MyConstantSource; typedef MyNetwork::Source< int > MySource; typedef MyNetwork::SerialProcess< MyNetwork::Inputs::Type, @@ -156,9 +156,7 @@ void SimpleTest::TestBasic() { core_set, 1024, // max tasks (default: 1024) 128, // max groups (default: 128) - // Currently needs to be initialized - // with (max_queues + 1), see defect embb449 - num_cores + 1, // max queues (default: 16) + num_cores, // max queues (default: 16) 1024, // queue capacity (default: 1024) 4); // num priorities (default: 4) diff --git a/mtapi_c/src/embb_mtapi_id_pool_t.c b/mtapi_c/src/embb_mtapi_id_pool_t.c index 570fa98..184a9f6 100644 --- a/mtapi_c/src/embb_mtapi_id_pool_t.c +++ b/mtapi_c/src/embb_mtapi_id_pool_t.c @@ -71,7 +71,7 @@ mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that) { /* acquire position to fetch id from */ mtapi_uint_t 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; } @@ -97,7 +97,7 @@ void embb_mtapi_id_pool_deallocate( /* acquire position to put id to */ mtapi_uint_t 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; } diff --git a/mtapi_c/test/embb_mtapi_test_id_pool.cc b/mtapi_c/test/embb_mtapi_test_id_pool.cc new file mode 100644 index 0000000..f7c7855 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_id_pool.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014-2015, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +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 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(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(i)]); + } +} + diff --git a/mtapi_c/test/embb_mtapi_test_id_pool.h b/mtapi_c/test/embb_mtapi_test_id_pool.h new file mode 100644 index 0000000..a85a284 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_id_pool.h @@ -0,0 +1,78 @@ +/* + * 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 +#include + +// for shuffling a vector +#include + +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_ diff --git a/mtapi_c/test/main.cc b/mtapi_c/test/main.cc index a92b25d..85b2e57 100644 --- a/mtapi_c/test/main.cc +++ b/mtapi_c/test/main.cc @@ -37,6 +37,9 @@ #include #include #include +#include + +#include PT_MAIN("MTAPI C") { embb_log_set_log_level(EMBB_LOG_LEVEL_NONE); @@ -48,4 +51,7 @@ PT_MAIN("MTAPI C") { PT_RUN(InitFinalizeTest); PT_RUN(GroupTest); PT_RUN(QueueTest); + PT_RUN(IdPoolTest); + + PT_EXPECT(embb_get_bytes_allocated() == 0); } diff --git a/mtapi_cpp/CMakeLists.txt b/mtapi_cpp/CMakeLists.txt index 3652d63..3a343f2 100644 --- a/mtapi_cpp/CMakeLists.txt +++ b/mtapi_cpp/CMakeLists.txt @@ -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") if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) - message("-- Automatic initialization enabled (default)") set(MTAPI_CPP_AUTOMATIC_INITIALIZE 1) else() set(MTAPI_CPP_AUTOMATIC_INITIALIZE 0) - message("-- Automatic initialization disabled") endif() -message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)") - # Execute the GroupSources macro include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake) diff --git a/tasks_cpp/CMakeLists.txt b/tasks_cpp/CMakeLists.txt index 31effbf..397be86 100644 --- a/tasks_cpp/CMakeLists.txt +++ b/tasks_cpp/CMakeLists.txt @@ -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") if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON) - message("-- Automatic initialization enabled (default)") set(TASKS_CPP_AUTOMATIC_INITIALIZE 1) else() set(TASKS_CPP_AUTOMATIC_INITIALIZE 0) - message("-- Automatic initialization disabled") endif() -message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)") configure_file("include/embb/tasks/internal/cmake_config.h.in" "include/embb/tasks/internal/cmake_config.h")