Commit 26594733 by christian-kern

Merge pull request #8 from danklmn/embb334_lockfree_tree

Merging the lock-free chromatic tree back to Siemens/embb
parents 6ec6101f d4549427
Embedded Multicore Building Blocks (EMB²)
=========================================
Version 0.3.1
-------------
### Features:
- None
### Changes and improvements:
- Removed one function argument from algorithms::Invoke
- Added "explicit" specifier to base type constructor of Atomic<BaseType*>
- Added "const" qualifier to dereference operator and member access operator of AtomicPointer<>
- Changed AtomicBase<>::CompareAndSwap to atomically return expected value
- Replaced constant in dataflow_cpp_test_simple.cc with corresponding macro
- Added initialization of atomic variable in hazard_pointer_test.cc to avoid warning with GCC 5.1
- Changed initial value of allocated_object_from_different_thread
- Added tests for ID Pool and check for memory leaks
- Updated unit test for the UniqueLock::Swap
### Bug fixes:
- Fixed implementation of ID pool (provided fewer elements than specified by capacity)
- Fixed unsigned overflow bug in timed wait function of condition variables
- Fixed implementation of UniqueLock::Swap
### Build system:
- Improved CMake output for automatic initialization option
- Fixed cpplint and unsigned/signed warnings
### Documentation:
- Fixed documentation of UniqueLock class
- Updated README file
Version 0.3.0
-------------
......
......@@ -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)
......
......@@ -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
......
......@@ -49,33 +49,37 @@ typedef embb::base::Function<void> 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<typename Function1, ...>
template<typename Function1, typename Function2, ...>
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<typename Function1, ...>
template<typename Function1, typename Function2, ...>
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<typename Function1>
void Invoke(
Function1 func1,
const embb::tasks::ExecutionPolicy& policy) {
internal::TaskWrapper<Function1> wrap1(func1, policy);
}
template<typename Function1, typename Function2>
void Invoke(
Function1 func1,
......@@ -290,12 +287,6 @@ template<typename Function1, typename Function2, typename Function3,
internal::TaskWrapper<Function10> wrap10(func10, policy);
}
template<typename Function1>
void Invoke(
Function1 func1) {
Invoke(func1, embb::tasks::ExecutionPolicy());
}
template<typename Function1, typename Function2>
void Invoke(
Function1 func1,
......
......@@ -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);
}
......@@ -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
......
......@@ -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);
......
......@@ -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
......@@ -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
......
......@@ -478,7 +478,7 @@ class Atomic<BaseType*> : public embb::base::internal::atomic::
public:
Atomic() : embb::base::internal::atomic::
AtomicPointer<BaseType, ptrdiff_t, sizeof(BaseType*)>() {}
Atomic(BaseType* p) : embb::base::internal::atomic::
explicit Atomic(BaseType* p) : embb::base::internal::atomic::
AtomicPointer<BaseType, ptrdiff_t, sizeof(BaseType*)>(p) {}
BaseType* operator=(BaseType* p) {
......
......@@ -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;
}
......
......@@ -65,8 +65,8 @@ class AtomicPointer : public AtomicArithmetic<BaseType*, DifferenceType, S> {
bool IsPointer() const;
// The methods below are documented in atomic.h
BaseType* operator->();
BaseType& operator*();
BaseType* operator->() const;
BaseType& operator*() const;
};
template<typename BaseType, typename DifferenceType, size_t S>
......@@ -93,13 +93,13 @@ inline bool AtomicPointer<BaseType, DifferenceType, S>::
template<typename BaseType, typename DifferenceType, size_t S>
inline BaseType* AtomicPointer<BaseType, DifferenceType, S>::
operator->() {
operator->() const {
return this->Load();
}
template<typename BaseType, typename DifferenceType, size_t S>
inline BaseType& AtomicPointer<BaseType, DifferenceType, S>::
operator*() {
operator*() const {
return *(this->Load());
}
......
......@@ -28,6 +28,7 @@
#define EMBB_BASE_INTERNAL_MUTEX_INL_H_
#include <cassert>
#include <algorithm>
namespace embb {
namespace base {
......@@ -95,8 +96,8 @@ void UniqueLock<Mutex>::Unlock() {
template<typename Mutex>
void UniqueLock<Mutex>::Swap(UniqueLock<Mutex>& other) {
locked_ = other.locked_;
mutex_ = other.Release();
std::swap(mutex_, other.mutex_);
std::swap(locked_, other.locked_);
}
template<typename Mutex>
......
......@@ -29,7 +29,6 @@
#include <embb/base/internal/platform.h>
#include <embb/base/exceptions.h>
#include <embb/base/c/counter.h>
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<Mutex>& other
/**< [IN/OUT] Lock from which ownership shall be transferred */
/**< [IN/OUT] The lock to exchange ownership with */
);
/**
......@@ -484,71 +483,6 @@ class UniqueLock {
friend class embb::base::ConditionVariable;
};
class ReadWriteLock {
public:
ReadWriteLock() : read_lock_(), write_lock_(),
reader_(self()), writer_(self()) {
embb_counter_init(&active_readers_);
}
void ReadLock() {
LockGuard<Mutex> read_guard(read_lock_);
unsigned int me = embb_counter_increment(&active_readers_);
if (me == 0) {
write_lock_.Lock();
}
}
void ReadUnlock() {
unsigned int me = embb_counter_decrement(&active_readers_);
if (me == 1) {
write_lock_.Unlock();
}
}
void WriteLock() {
read_lock_.Lock();
write_lock_.Lock();
}
void WriteUnlock() {
write_lock_.Unlock();
read_lock_.Unlock();
}
~ReadWriteLock() {
embb_counter_destroy(&active_readers_);
}
class Reader {
public:
explicit Reader(ReadWriteLock& rwlock) : readwrite_lock_(rwlock) {}
void Lock() { readwrite_lock_.ReadLock(); }
void Unlock() { readwrite_lock_.ReadUnlock(); }
private:
ReadWriteLock& readwrite_lock_;
};
class Writer {
public:
explicit Writer(ReadWriteLock& rwlock) : readwrite_lock_(rwlock) {}
void Lock() { readwrite_lock_.WriteLock(); }
void Unlock() { readwrite_lock_.WriteUnlock(); }
private:
ReadWriteLock& readwrite_lock_;
};
Reader& GetReader() { return reader_; }
Writer& GetWriter() { return writer_; }
private:
ReadWriteLock(const ReadWriteLock&);
ReadWriteLock& operator=(const ReadWriteLock&);
ReadWriteLock& self() { return *this; }
Mutex read_lock_;
Mutex write_lock_;
embb_counter_t active_readers_;
Reader reader_;
Writer writer_;
};
} // namespace base
} // namespace embb
......
......@@ -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);
}
}
......
......@@ -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)
add_executable(chromatic_tree_generator chromatic_tree_generator.cc chromatic_tree_operations.h)
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "chromatic_tree_operations.h"
static const char INCLUDE_GUARD[] =
"EMBB_CONTAINERS_INTERNAL_LOCK_FREE_CHROMATIC_TREE_REBALANCE_H_";
static const char GENERATOR_NOTICE[] =
"//\n"
"// This file was created automatically by a code generator.\n"
"// Any direct changes will be lost after rebuild of the project.\n"
"//";
static const char RETURN_TYPE[] = "embb_errors_t";
static const char NODEARG_TYPE[] = "HazardNodePtr";
static const char LOCKARG_TYPE[] = "HazardOperationPtr";
static const char NEWNODE_TYPE[] = "Node*";
void PrintOperationSourceCode(FILE* file, const RebalancingOperation& op);
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "USAGE:\n %s <output_file>\n", argv[0]);
return 1;
}
const char* filename = argv[1];
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4996)
#endif
FILE *file = fopen(filename, "w");
#ifdef _MSC_VER
#pragma warning(pop)
#endif
if (file == NULL) {
fprintf(stderr, "Error: Cannot open file '%s' for writing!\n", filename);
return 1;
}
// Printing header
fprintf(file, "#ifndef %s\n#define %s\n\n", INCLUDE_GUARD, INCLUDE_GUARD);
fprintf(file, "%s\n\n", GENERATOR_NOTICE);
// Printing methods code
int num_operations = (sizeof(REBALANCING_OPERATIONS) /
sizeof(REBALANCING_OPERATIONS[0]));
for (int i = 0; i < num_operations; ++i) {
PrintOperationSourceCode(file, REBALANCING_OPERATIONS[i]);
}
// Printing trailer
fprintf(file, "#endif // %s\n", INCLUDE_GUARD);
fclose(file);
return 0;
}
void PrintOperationSourceCode(FILE* file, const RebalancingOperation& op) {
// Method signature
fprintf(file, "%s %s(", RETURN_TYPE, op.name);
// Method arguments
fprintf(file, "%s& u, %s& u_op", NODEARG_TYPE, LOCKARG_TYPE);
int offset = static_cast<int>(strlen(NODEARG_TYPE) + strlen(op.name) + 2);
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, ",\n%*s%s& %s, %s& %s_op", offset, "",
NODEARG_TYPE, op.old_nodes[i].name,
LOCKARG_TYPE, op.old_nodes[i].name);
}
fprintf(file, ") {\n"
" embb_errors_t result = EMBB_NOMEM;\n");
// Define nodes
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " %s %s = NULL;\n", NEWNODE_TYPE, op.new_nodes[i].name);
}
fprintf(file, "\n"
" while (result != EMBB_SUCCESS) {\n");
// Construct new nodes
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " %s = node_pool_.Allocate(\n"
" %s->GetKey(), %s->GetValue(), %s,\n"
" %s, %s, &initial_operation_dummy_);\n"
" if (%s == NULL) break;\n",
op.new_nodes[i].name,
op.new_nodes[i].orig_node, op.new_nodes[i].orig_node,
op.new_nodes[i].weight,
op.new_nodes[i].left, op.new_nodes[i].right,
op.new_nodes[i].name);
}
fprintf(file, "\n"
" HazardOperationPtr op(GetOperationGuard(HIDX_CURRENT_OP));\n"
" op.ProtectSafe(operation_pool_.Allocate());\n"
" if (op == NULL) break;\n");
// Create and fill the operation object
fprintf(file, "\n"
" op->SetRoot(u, u_op);\n"
" op->SetNewChild(nx);\n"
" op->SetOldNodes(%s, %s_op",
op.old_nodes[0].name, op.old_nodes[0].name);
for (int i = 1; i < op.num_nodes; ++i) {
fprintf(file, ",\n %s, %s_op",
op.old_nodes[i].name, op.old_nodes[i].name);
}
fprintf(file, ");\n");
// Execute operation
fprintf(file, "\n"
" bool succeeded = op->Help(GetNodeGuard(HIDX_HELPING),\n"
" GetOperationGuard(HIDX_HELPING));\n"
" op->CleanUp(&retired_operation_dummy_);\n"
" \n"
" if (!succeeded) {\n"
" RetireOperation(op);\n"
" result = EMBB_BUSY;\n"
" break;\n"
" }\n");
// Release original nodes and operations
fprintf(file, "\n"
" RetireOperation(u_op);\n");
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " RetireNode(%s); RetireOperation(%s_op);\n",
op.old_nodes[i].name, op.old_nodes[i].name);
}
fprintf(file, "\n"
" result = EMBB_SUCCESS;\n"
" }\n");
// Delete new nodes if operation failed
fprintf(file, "\n"
" if (result != EMBB_SUCCESS) {\n");
for (int i = 0; i < op.num_nodes; ++i) {
fprintf(file, " if (%s) FreeNode(%s);\n",
op.new_nodes[i].name, op.new_nodes[i].name);
}
fprintf(file, " }\n");
// Return statement
fprintf(file, "\n"
" return result;\n"
"}\n"
"\n");
};
#ifndef OPERATIONS_H
#define OPERATIONS_H
static const int MAX_NODES = 5;
typedef struct {
const char *name;
} OldNode;
typedef struct {
const char *name;
const char *orig_node;
const char *weight;
const char *left;
const char *right;
} NewNode;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4510 4512 4610)
#endif
typedef struct {
const char *name;
const int num_nodes;
const OldNode old_nodes[MAX_NODES];
const NewNode new_nodes[MAX_NODES];
} RebalancingOperation;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
static const RebalancingOperation REBALANCING_OPERATIONS[] = {
{
"BLK",
3,
{"ux", "uxl", "uxr"},
{
{"nxl", "uxl", "1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxr", "uxr", "1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() - 1", "nxl", "nxr"}
}
},
{
"PUSH_L",
3,
{"ux", "uxl", "uxr"},
{
{"nxl", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxr", "uxr", "0", "uxr->GetLeft()", "uxr->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() + 1", "nxl", "nxr"}
}
},
{
"PUSH_R",
3,
{"ux", "uxl", "uxr"},
{
{"nxr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxl", "uxl", "0", "uxl->GetLeft()", "uxl->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() + 1", "nxl", "nxr"}
}
},
{
"RB1_L",
2,
{"ux", "uxl"},
{
{"nxr", "ux", "0", "uxl->GetRight()", "ux->GetRight()"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"RB1_R",
2,
{"ux", "uxr"},
{
{"nxl", "ux", "0", "ux->GetLeft()", "uxr->GetLeft()"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"RB2_L",
3,
{"ux", "uxl", "uxlr"},
{
{"nxl", "uxl", "0", "uxl->GetLeft()", "uxlr->GetLeft()"},
{"nxr", "ux", "0", "uxlr->GetRight()", "ux->GetRight()"},
{"nx", "uxlr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"RB2_R",
3,
{"ux", "uxr", "uxrl"},
{
{"nxr", "uxr", "0", "uxrl->GetRight()", "uxr->GetRight()"},
{"nxl", "ux", "0", "ux->GetLeft()", "uxrl->GetLeft()"},
{"nx", "uxrl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W1_L",
4,
{"ux", "uxl", "uxr", "uxrl"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxlr", "uxrl", "uxrl->GetWeight() - 1", "uxrl->GetLeft()", "uxrl->GetRight()"},
{"nxl", "ux", "1", "nxll", "nxlr"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"W1_R",
4,
{"ux", "uxl", "uxr", "uxlr"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxrl", "uxlr", "uxlr->GetWeight() - 1", "uxlr->GetLeft()", "uxlr->GetRight()"},
{"nxr", "ux", "1", "nxrl", "nxrr"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"W2_L",
4,
{"ux", "uxl", "uxr", "uxrl"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxlr", "uxrl", "0", "uxrl->GetLeft()", "uxrl->GetRight()"},
{"nxl", "ux", "1", "nxll", "nxlr"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"W2_R",
4,
{"ux", "uxl", "uxr", "uxlr"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxrl", "uxlr", "0", "uxlr->GetLeft()", "uxlr->GetRight()"},
{"nxr", "ux", "1", "nxrl", "nxrr"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"W3_L",
5,
{"ux", "uxl", "uxr", "uxrl", "uxrll"},
{
{"nxlll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxll", "ux", "1", "nxlll", "uxrll->GetLeft()"},
{"nxlr", "uxrl", "1", "uxrll->GetRight()", "uxrl->GetRight()"},
{"nxl", "uxrll", "0", "nxll", "nxlr"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "uxr->GetRight()"}
}
},
{
"W3_R",
5,
{"ux", "uxl", "uxr", "uxlr", "uxlrr"},
{
{"nxrrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxrr", "ux", "1", "uxlrr->GetRight()", "nxrrr"},
{"nxrl", "uxlr", "1", "uxlr->GetLeft()", "uxlrr->GetLeft()"},
{"nxr", "uxlrr", "0", "nxrl", "nxrr"},
{"nx", "uxl", "ux->GetWeight()", "uxl->GetLeft()", "nxr"}
}
},
{
"W4_L",
5,
{"ux", "uxl", "uxr", "uxrl", "uxrlr"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxrl", "uxrlr", "1", "uxrlr->GetLeft()", "uxrlr->GetRight()"},
{"nxl", "ux", "1", "nxll", "uxrl->GetLeft()"},
{"nxr", "uxr", "0", "nxrl", "uxr->GetRight()"},
{"nx", "uxrl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W4_R",
5,
{"ux", "uxl", "uxr", "uxlr", "uxlrl"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxlr", "uxlrl", "1", "uxlrl->GetLeft()", "uxlrl->GetRight()"},
{"nxr", "ux", "1", "uxlr->GetRight()", "nxrr"},
{"nxl", "uxl", "0", "uxl->GetLeft()", "nxlr"},
{"nx", "uxlr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W5_L",
4,
{"ux", "uxl", "uxr", "uxrr"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxl", "ux", "1", "nxll", "uxr->GetLeft()"},
{"nxr", "uxrr", "1", "uxrr->GetLeft()", "uxrr->GetRight()"},
{"nx", "uxr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W5_R",
4,
{"ux", "uxl", "uxr", "uxll"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxr", "ux", "1", "uxl->GetRight()", "nxrr"},
{"nxl", "uxll", "1", "uxll->GetLeft()", "uxll->GetRight()"},
{"nx", "uxl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W6_L",
4,
{"ux", "uxl", "uxr", "uxrl"},
{
{"nxll", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxl", "ux", "1", "nxll", "uxrl->GetLeft()"},
{"nxr", "uxr", "1", "uxrl->GetRight()", "uxr->GetRight()"},
{"nx", "uxrl", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W6_R",
4,
{"ux", "uxl", "uxr", "uxlr"},
{
{"nxrr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nxr", "ux", "1", "uxlr->GetRight()", "nxrr"},
{"nxl", "uxl", "1", "uxl->GetLeft()", "uxlr->GetLeft()"},
{"nx", "uxlr", "ux->GetWeight()", "nxl", "nxr"}
}
},
{
"W7",
3,
{"ux", "uxl", "uxr"},
{
{"nxl", "uxl", "uxl->GetWeight() - 1", "uxl->GetLeft()", "uxl->GetRight()"},
{"nxr", "uxr", "uxr->GetWeight() - 1", "uxr->GetLeft()", "uxr->GetRight()"},
{"nx", "ux", "u->IsSentinel() ? 1 : ux->GetWeight() + 1", "nxl", "nxr"}
}
}
};
#endif // OPERATIONS_H
......@@ -182,7 +182,9 @@ HazardPointerThreadEntry<GuardType>::~HazardPointerThreadEntry() {
}
template< typename GuardType >
GuardType HazardPointerThreadEntry<GuardType>::GetGuard(int pos) const {
typename HazardPointerThreadEntry<GuardType>::AtomicGuard&
HazardPointerThreadEntry<GuardType>::GetGuard(int pos) const {
assert(pos >= 0 && pos < guards_per_thread);
return guarded_pointers[pos];
}
......@@ -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<typename GuardType>
const double embb::containers::internal::HazardPointer<GuardType>::
RETIRE_THRESHOLD = 1.25f;
template<typename Type>
UniqueHazardPointer<Type>::
UniqueHazardPointer()
: hazard_guard_(NULL),
local_ptr_value_(NULL),
undefined_guard_(NULL),
active_(false) {}
template<typename Type>
UniqueHazardPointer<Type>::
UniqueHazardPointer(AtomicTypePtr& hazard_guard, Type* undefined_guard)
: hazard_guard_(&hazard_guard),
local_ptr_value_(hazard_guard_->Load()),
undefined_guard_(undefined_guard),
active_(LoadGuardedPointer() == undefined_guard_) {}
template<typename Type>
UniqueHazardPointer<Type>::~UniqueHazardPointer() {
if (IsActive()) ClearHazard();
}
template<typename Type>
bool UniqueHazardPointer<Type>::ProtectHazard(const AtomicTypePtr& hazard) {
assert(OwnsHazardGuard());
// Read the hazard and store it into the guard
StoreGuardedPointer(hazard.Load());
// Check whether the guard is valid
SetActive(LoadGuardedPointer() == hazard.Load());
// Clear the guard if it is invalid
if (!IsActive()) ClearHazard();
return IsActive();
}
template<typename Type>
void UniqueHazardPointer<Type>::ProtectSafe(Type* safe_ptr) {
assert(OwnsHazardGuard());
StoreGuardedPointer(safe_ptr);
SetActive(true);
}
template<typename Type>
UniqueHazardPointer<Type>::operator Type* () const {
assert(IsActive());
return LoadGuardedPointer();
}
template<typename Type>
Type* UniqueHazardPointer<Type>::operator->() const {
assert(IsActive());
return LoadGuardedPointer();
}
template<typename Type>
Type& UniqueHazardPointer<Type>::operator*() const {
assert(IsActive());
return *(LoadGuardedPointer());
}
template<typename Type>
void UniqueHazardPointer<Type>::AdoptHazard(const UniqueHazardPointer& other) {
assert(OwnsHazardGuard());
StoreGuardedPointer(other.LoadGuardedPointer());
SetActive(other.active_);
}
template<typename Type>
void UniqueHazardPointer<Type>::Swap(UniqueHazardPointer& other) {
std::swap(hazard_guard_, other.hazard_guard_);
std::swap(local_ptr_value_, other.local_ptr_value_);
std::swap(undefined_guard_, other.undefined_guard_);
std::swap(active_, other.active_);
}
template<typename Type>
Type* UniqueHazardPointer<Type>::ReleaseHazard() {
assert(IsActive());
Type* released_hazard = LoadGuardedPointer();
ClearHazard();
SetActive(false);
return released_hazard;
}
template<typename Type>
bool UniqueHazardPointer<Type>::IsActive() const {
return active_;
}
template<typename Type>
void UniqueHazardPointer<Type>::SetActive(bool active) {
active_ = active;
}
template<typename Type>
void UniqueHazardPointer<Type>::ClearHazard() {
StoreGuardedPointer(undefined_guard_);
}
template<typename Type>
Type* UniqueHazardPointer<Type>::LoadGuardedPointer() const {
return local_ptr_value_;
}
template<typename Type>
void UniqueHazardPointer<Type>::StoreGuardedPointer(Type* ptr) {
hazard_guard_->Store(ptr);
local_ptr_value_ = ptr;
}
template<typename Type>
bool UniqueHazardPointer<Type>::OwnsHazardGuard() const {
return hazard_guard_ != NULL;
}
} // namespace internal
} // namespace containers
} // namespace embb
......
......@@ -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<typename Type>
class UniqueHazardPointer {
public:
/** Typedef for a atomic pointer to the guarded object. */
typedef embb::base::Atomic<Type*> AtomicTypePtr;
/**
* Creates an uninitialized, empty wrapper.
*
* An uninitialized wrapper may only be swapped with another wrapper (using
* \c Swap() method) or checked for being active (using 'IsActive()' method,
* which should always return /c false for an uninitialized wrapper).
*/
UniqueHazardPointer();
/**
* Creates a wrapper that uses the given hazard table entry (referred to as
* "guard") to protect hazardous pointers.
*
* \param[IN] hazard_guard Reference to a hazard table entry
* \param[IN] undefined_guard Dummy value used to clear the hazard table entry
*/
explicit
UniqueHazardPointer(AtomicTypePtr& hazard_guard,
Type* undefined_guard = NULL);
/**
* If initialized and active, clears the hazard table entry.
*/
~UniqueHazardPointer();
/**
* Tries to protect the given hazard using the wrapped guard.
* If it succeeds, the hazard may be safely dereferenced as long as the guard
* is not destroyed or reset to protect another hazard.
*
* \param hazard The hazard to be protected
* \return \c true if the specified hazard is now protected by the guard,
* \c false if the hazard was modified by a concurrent thread
*/
bool ProtectHazard(const AtomicTypePtr& hazard);
/**
* Uses the wrapped guard to protect a pointer that is not hazardous yet.
*
* \param safe_ptr The pointer to be protected
*/
void ProtectSafe(Type* safe_ptr);
/**
* Type cast operator.
*
* \return The hazardous pointer protected by this wrapper
*/
operator Type* () const;
/**
* Pointer member access operator.
*
* \return The hazardous pointer protected by this wrapper
*/
Type* operator->() const;
/**
* Pointer dereference operator.
*
* \return Reference to the object pointed to by the protected pointer
*/
Type& operator*() const;
/**
* Protects the hazard that is currently protected by another wrapper (so it
* becomes protected by two guards simultaneously). The other wrapper remains
* unmodified.
*
* \param other Another wrapper those protected pointer is to be protected by
* the calling wrapper
*/
void AdoptHazard(const UniqueHazardPointer& other);
/**
* Swaps the guard ownership with another wrapper. Swaps not just the
* protected hazards, but the hazard guards themselves.
*
* \param other Another wrapper to swap guards with
*/
void Swap(UniqueHazardPointer& other);
/**
* Clears the hazard guard and returns the hazard previously protected by that
* guard.
*
* \return The hazardous pointer previously protected by this wrapper
*/
Type* ReleaseHazard();
/**
* Check whether the wrapper is active.
*
* \return \c true if the wrapper is initialized and currently protecting some
* hazard, \c false otherwise
*/
bool IsActive() const;
private:
/**
* Sets the 'active' flag of this wrapper.
*
* \param active The new value for the flag
*/
void SetActive(bool active);
/**
* Reset the wrapped hazard guard to a state when it is not protecting any
* hazards.
*/
void ClearHazard();
/**
* Retrieves the hazardous pointer currently protected by the wrapped guard.
*
* \return The hazardous pointer protected by this wrapper
*/
Type* LoadGuardedPointer() const;
/**
* Updates the wrapped guard to protect the specified hazardous pointer.
*
* \param ptr Hazardous pointer to be protected
*/
void StoreGuardedPointer(Type* ptr);
/**
* Check whether the wrapper is initialized (i.e. it wraps some hazard guard)
*
* \return \c true if this wrapper is initialized, \c false otherwise
*/
bool OwnsHazardGuard() const;
/**
* Disable copy construction and assignment.
*/
UniqueHazardPointer(const UniqueHazardPointer&);
UniqueHazardPointer& operator=(const UniqueHazardPointer&);
/**
* Pointer to a hazard table entry (the guard) that is used to store the
* hazardous pointers
*/
AtomicTypePtr* hazard_guard_;
/** Local copy of the guarded pointer value (used for optimization) */
Type* local_ptr_value_;
/** Dummy value used to clear the hazard guard from any hazards */
Type* undefined_guard_;
/** Flag set to true when the guard is protecting some hazardous pointer */
bool active_;
};
} // namespace internal
} // namespace containers
} // namespace embb
......
......@@ -30,406 +30,1089 @@
#include <assert.h>
#include <algorithm>
#ifdef EMBB_DEBUG
static const size_t INVALID_POINTER = static_cast<size_t>(-1);
# define VERIFY_ADDRESS(addr) assert(reinterpret_cast<size_t>((addr)) != \
INVALID_POINTER)
#else
# define VERIFY_ADDRESS(address) ((void)0)
#endif
namespace embb {
namespace containers {
namespace internal {
template<typename Key, typename Value>
ChromaticTreeNode<Key, Value>::
ChromaticTreeNode(const Key& key, const Value& value, const int& weight,
ChromaticTreeNode<Key, Value>* const & left,
ChromaticTreeNode<Key, Value>* 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<typename Key, typename Value>
ChromaticTreeNode<Key, Value>::
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<typename Key, typename Value>
ChromaticTreeNode<Key, Value>::
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<typename Key, typename Value>
const Key& ChromaticTreeNode<Key, Value>::GetKey() const {
inline const Key& ChromaticTreeNode<Key, Value>::GetKey() const {
return key_;
}
template<typename Key, typename Value>
const Value& ChromaticTreeNode<Key, Value>::GetValue() const {
inline const Value& ChromaticTreeNode<Key, Value>::GetValue() const {
return value_;
}
template<typename Key, typename Value>
const int& ChromaticTreeNode<Key, Value>::GetWeight() const {
inline int ChromaticTreeNode<Key, Value>::GetWeight() const {
return weight_;
}
template<typename Key, typename Value>
ChromaticTreeNode<Key, Value>*& ChromaticTreeNode<Key, Value>::GetLeft() {
inline typename ChromaticTreeNode<Key, Value>::AtomicNodePtr&
ChromaticTreeNode<Key, Value>::GetLeft() {
return left_;
}
template<typename Key, typename Value>
ChromaticTreeNode<Key, Value>*& ChromaticTreeNode<Key, Value>::GetRight() {
inline typename ChromaticTreeNode<Key, Value>::Node*
ChromaticTreeNode<Key, Value>::GetLeft() const {
return left_.Load();
}
template<typename Key, typename Value>
inline typename ChromaticTreeNode<Key, Value>::AtomicNodePtr&
ChromaticTreeNode<Key, Value>::GetRight() {
return right_;
}
template<typename Key, typename Value>
inline typename ChromaticTreeNode<Key, Value>::Node*
ChromaticTreeNode<Key, Value>::GetRight() const {
return right_.Load();
}
template<typename Key, typename Value>
inline bool ChromaticTreeNode<Key, Value>::IsLeaf() const {
return is_leaf_;
}
template<typename Key, typename Value>
inline bool ChromaticTreeNode<Key, Value>::IsSentinel() const {
return is_sentinel_;
}
template<typename Key, typename Value>
bool ChromaticTreeNode<Key, Value>::
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<typename Key, typename Value>
inline void ChromaticTreeNode<Key, Value>::Retire() {
retired_ = true;
}
template<typename Key, typename Value>
inline bool ChromaticTreeNode<Key, Value>::IsRetired() const {
return retired_;
}
template<typename Key, typename Value>
inline typename ChromaticTreeNode<Key, Value>::AtomicOperationPtr&
ChromaticTreeNode<Key, Value>::GetOperation() {
return operation_;
}
template<typename Key, typename Value>
ChromaticTreeOperation<Key, Value>::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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
SetRoot(Node* root, Operation* root_operation) {
root_ = root;
root_operation_ = root_operation;
}
template<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
SetOldNodes(Node* node1, Operation* operation1) {
num_old_nodes_ = 1;
old_nodes_[0] = node1;
old_operations_[0] = operation1;
}
template<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::SetNewChild(Node* new_child) {
new_child_ = new_child;
}
template<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::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<typename Key, typename Value>
inline bool ChromaticTreeOperation<Key, Value>::IsAborted() {
#ifdef EMBB_DEBUG
assert(!deleted_);
#endif
return state_ == STATE_ABORTED;
}
template<typename Key, typename Value>
inline bool ChromaticTreeOperation<Key, Value>::IsInProgress() {
#ifdef EMBB_DEBUG
assert(!deleted_);
#endif
State state = state_.Load();
return (state != STATE_ABORTED && state != STATE_COMMITTED);
}
template<typename Key, typename Value>
inline bool ChromaticTreeOperation<Key, Value>::IsCommitted() {
#ifdef EMBB_DEBUG
assert(!deleted_);
#endif
return state_ == STATE_COMMITTED;
}
template<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::SetDeleted() {
deleted_ = true;
}
#endif
template<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::IsRollingBack() {
State state = state_.Load();
return (state == STATE_ROLLBACK || state == STATE_ABORTED);
}
template<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::IsFreezing() {
return state_ == STATE_FREEZING;
}
template<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::IsAllFrozen() {
State state = state_.Load();
return (state == STATE_ALL_FROZEN || state == STATE_COMMITTED);
}
template<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value>
void ChromaticTreeOperation<Key, Value>::
Unfreeze(Node* node, Operation* operation) {
Operation* expected = this;
node->GetOperation().CompareAndSwap(expected, operation);
}
template<typename Key, typename Value>
bool ChromaticTreeOperation<Key, Value>::
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<typename Key, typename Value, typename Compare, typename NodePool>
ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
ChromaticTree<Key, Value, Compare, ValuePool>::
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<void, Node*>(*this, &ChromaticTree::FreeNode),
NULL, HIDX_MAX),
operation_hazard_manager_(
embb::base::Function<void, Operation*>(*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<Node*>(NULL),
&initial_operation_dummy_)) {
assert(entry_ != NULL);
assert(entry_->GetLeft() != NULL);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
ChromaticTree<Key, Value, Compare, ValuePool>::
~ChromaticTree() {
Destruct(entry_->GetLeft());
node_pool_.Free(entry_);
FreeNode(entry_);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
TryInsert(const Key& key, const Value& value) {
Value old_value;
return TryInsert(key, value, old_value);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
TryDelete(const Key& key) {
Value old_value;
return TryDelete(key, old_value);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
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<Node*>(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<typename Key, typename Value, typename Compare, typename NodePool>
size_t ChromaticTree<Key, Value, Compare, NodePool>::
GetCapacity() {
template<typename Key, typename Value, typename Compare, typename ValuePool>
size_t ChromaticTree<Key, Value, Compare, ValuePool>::
GetCapacity() const {
return capacity_;
}
template<typename Key, typename Value, typename Compare, typename NodePool>
const Value& ChromaticTree<Key, Value, Compare, NodePool>::
GetUndefinedValue() {
template<typename Key, typename Value, typename Compare, typename ValuePool>
const Value& ChromaticTree<Key, Value, Compare, ValuePool>::
GetUndefinedValue() const {
return undefined_value_;
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
IsEmpty() {
return IsLeaf(entry_->GetLeft());
template<typename Key, typename Value, typename Compare, typename ValuePool>
inline bool ChromaticTree<Key, Value, Compare, ValuePool>::
IsEmpty() const {
return entry_->GetLeft()->IsLeaf();
}
template<typename Key, typename Value, typename Compare, typename ValuePool>
void ChromaticTree<Key, Value, Compare, ValuePool>::
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<Node*>(leaf));
reached_leaf = leaf->IsLeaf();
}
}
}
template<typename Key, typename Value, typename Compare, typename ValuePool>
inline bool ChromaticTree<Key, Value, Compare, ValuePool>::
HasChild(const Node* parent, const Node* child) const {
return (parent->GetLeft() == child || parent->GetRight() == child);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
void ChromaticTree<Key, Value, Compare, NodePool>::
Search(const Key& key, NodePtr& leaf) const {
NodePtr parent;
Search(key, leaf, parent);
template<typename Key, typename Value, typename Compare, typename ValuePool>
void ChromaticTree<Key, Value, Compare, ValuePool>::
Destruct(Node* node) {
if (!node->IsLeaf()) {
Destruct(node->GetLeft());
Destruct(node->GetRight());
}
FreeNode(node);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
void ChromaticTree<Key, Value, Compare, NodePool>::
Search(const Key& key, NodePtr& leaf, NodePtr& parent) const {
NodePtr grandparent;
Search(key, leaf, parent, grandparent);
template<typename Key, typename Value, typename Compare, typename ValuePool>
int ChromaticTree<Key, Value, Compare, ValuePool>::
GetHeight(const Node* node) const {
int height = 0;
if (node != NULL) {
height = 1 + ::std::max(GetHeight(node->GetLeft()),
GetHeight(node->GetRight()));
}
return height;
}
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
IsBalanced() const {
return IsBalanced(entry_->GetLeft());
}
template<typename Key, typename Value, typename Compare, typename NodePool>
void ChromaticTree<Key, Value, Compare, NodePool>::
Search(const Key& key, NodePtr& leaf, NodePtr& parent,
NodePtr& grandparent) const {
grandparent = NULL;
parent = entry_;
leaf = entry_->GetLeft();
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
IsLeaf(const NodePtr& node) const {
return node->GetLeft() == NULL;
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
IsDummyOperation(const Operation* operation) const {
return (operation == &initial_operation_dummy_ ||
operation == &retired_operation_dummy_);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
IsSentinel(const NodePtr& node) const {
return (node == entry_) || (node == entry_->GetLeft());
template<typename Key, typename Value, typename Compare, typename ValuePool>
void ChromaticTree<Key, Value, Compare, ValuePool>::
RetireNode(HazardNodePtr& node) {
node_hazard_manager_.EnqueuePointerForDeletion(node.ReleaseHazard());
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
HasFixedWeight(const NodePtr& node) const {
return (IsSentinel(node)) || (node == entry_->GetLeft()->GetLeft());
template<typename Key, typename Value, typename Compare, typename ValuePool>
void ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
HasChild(const NodePtr& parent, const NodePtr& child) const {
return (parent->GetLeft() == child || parent->GetRight() == child);
template<typename Key, typename Value, typename Compare, typename ValuePool>
typename ChromaticTree<Key, Value, Compare, ValuePool>::AtomicNodePtr&
ChromaticTree<Key, Value, Compare, ValuePool>::
GetNodeGuard(HazardIndex index) {
return node_hazard_manager_.GetGuardedPointer(static_cast<int>(index));
}
template<typename Key, typename Value, typename Compare, typename NodePool>
typename ChromaticTree<Key, Value, Compare, NodePool>::NodePtr&
ChromaticTree<Key, Value, Compare, NodePool>::
GetPointerToChild(const NodePtr& parent, const NodePtr& child) const {
assert(HasChild(parent, child));
return (parent->GetLeft() == child) ? parent->GetLeft() : parent->GetRight();
template<typename Key, typename Value, typename Compare, typename ValuePool>
typename ChromaticTree<Key, Value, Compare, ValuePool>::AtomicOperationPtr&
ChromaticTree<Key, Value, Compare, ValuePool>::
GetOperationGuard(HazardIndex index) {
return operation_hazard_manager_.GetGuardedPointer(static_cast<int>(index));
}
template<typename Key, typename Value, typename Compare, typename NodePool>
void ChromaticTree<Key, Value, Compare, NodePool>::
Destruct(const NodePtr& node) {
if (!IsLeaf(node)) {
Destruct(node->GetLeft());
Destruct(node->GetRight());
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename ValuePool>
void ChromaticTree<Key, Value, Compare, ValuePool>::
FreeNode(Node* node) {
#ifdef EMBB_DEBUG
node->GetLeft() = reinterpret_cast<Node*>(INVALID_POINTER);
node->GetRight() = reinterpret_cast<Node*>(INVALID_POINTER);
#endif
node_pool_.Free(node);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
template<typename Key, typename Value, typename Compare, typename ValuePool>
void ChromaticTree<Key, Value, Compare, ValuePool>::
FreeOperation(Operation* operation) {
#ifdef EMBB_DEBUG
operation->SetDeleted();
#endif
operation_pool_.Free(operation);
}
template<typename Key, typename Value, typename Compare, typename ValuePool>
bool ChromaticTree<Key, Value, Compare, ValuePool>::
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<Node*>(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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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*>(node))
template<typename Key, typename Value, typename Compare, typename ValuePool>
embb_errors_t ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename ValuePool>
embb_errors_t ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename ValuePool>
embb_errors_t ChromaticTree<Key, Value, Compare, ValuePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
int ChromaticTree<Key, Value, Compare, NodePool>::
GetHeight(const NodePtr& node) const {
int height = 0;
if (node != NULL) {
height = 1 + ::std::max(GetHeight(node->GetLeft()),
GetHeight(node->GetRight()));
}
return height;
}
template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>::
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
......
......@@ -201,6 +201,20 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
}
template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1, typename Param2, typename Param3, typename Param4,
typename Param5, typename Param6>
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4,
Param5 const& param5, Param6 const& param6) {
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)Type(param1, param2, param3, param4, param5, param6);
return rawObject;
}
template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() {
// Deallocate the objects
objectAllocator.deallocate(objects, capacity);
......
......@@ -30,14 +30,21 @@
#include <stddef.h>
#include <functional>
#include <embb/base/c/errors.h>
#include <embb/base/mutex.h>
#include <embb/containers/internal/hazard_pointer.h>
#include <embb/containers/lock_free_tree_value_pool.h>
#include <embb/containers/object_pool.h>
namespace embb {
namespace containers {
namespace internal {
template<typename Key, typename Value>
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<typename Key, typename Value>
class ChromaticTreeNode {
public:
/** Node of the tree (self type). */
typedef ChromaticTreeNode Node;
/** Atomic pointer to a node. */
typedef embb::base::Atomic<Node*> AtomicNodePtr;
/** Chromatic tree operation. */
typedef ChromaticTreeOperation<Key, Value> Operation;
/** Atomic pointer to a tree operation. */
typedef embb::base::Atomic<Operation*> 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<Key, Value>* const & left,
ChromaticTreeNode<Key, Value>* 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<Key, Value>*& GetLeft();
AtomicNodePtr& GetLeft();
Node* GetLeft() const;
/**
* Accessor for the right child pointer.
*
* \return Reference to the right child pointer
*/
ChromaticTreeNode<Key, Value>*& 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<bool> 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<typename Key, typename Value>
class ChromaticTreeOperation {
public:
/** Node of the tree. */
typedef ChromaticTreeNode<Key, Value> Node;
/** Atomic pointer to a node. */
typedef embb::base::Atomic<Node*> AtomicNodePtr;
/** Hazard-protected pointer to a node. */
typedef UniqueHazardPointer<Node> HazardNodePtr;
/** Chromatic tree operation (self type). */
typedef ChromaticTreeOperation Operation;
/** Atomic pointer to a tree operation. */
typedef embb::base::Atomic<Operation*> AtomicOperationPtr;
/** Hazard-protected pointer to a tree operation. */
typedef UniqueHazardPointer<Operation> 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<Key, Value>* left_; /**< Pointer to left child node */
ChromaticTreeNode<Key, Value>* 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<State> 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<bool> deleted_;
#endif
};
} // namespace internal
namespace test {
/**
* Forward declaration of the test class.
*/
template<typename Tree>
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 <tt>(rhs < lhs)</tt> 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 <tt>(rhs < lhs)</tt> holds
* \tparam ValuePool Type of the value pool to be used inside object pools for
* tree nodes and operation objects
*/
template<typename Key,
typename Value,
typename Compare = ::std::less<Key>,
typename NodePool = ObjectPool<internal::ChromaticTreeNode<Key, Value>,
LockFreeTreeValuePool<bool, false> >
typename ValuePool = LockFreeTreeValuePool<bool, false>
>
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 <tt>(2 * capacity + 7)</tt> 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<Key, Value> Node;
/**
* Typedef for a pointer to a node of the tree.
*/
typedef internal::ChromaticTreeNode<Key, Value>* 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<Node*> AtomicNodePtr;
/** Hazard-protected pointer to a node. */
typedef internal::UniqueHazardPointer<Node> HazardNodePtr;
/** Chromatic tree operation. */
typedef internal::ChromaticTreeOperation<Key, Value> Operation;
/** Atomic pointer to a tree operation. */
typedef embb::base::Atomic<Operation*> AtomicOperationPtr;
/** Hazard-protected pointer to a tree operation. */
typedef internal::UniqueHazardPointer<Operation> HazardOperationPtr;
/** Object pool for tree nodes. */
typedef ObjectPool<Node, ValuePool> NodePool;
/** Object pool for tree operations. */
typedef ObjectPool<Operation, ValuePool> 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 <embb/containers/internal/lock_free_chromatic_tree-rebalance.h>
/** Hazard pointer manager for protecting node pointers. */
internal::HazardPointer<Node*> node_hazard_manager_;
/** Hazard pointer manager for protecting operation pointers. */
internal::HazardPointer<Operation*> 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<ReadWriteLockReader> ReaderLockGuard;
typedef embb::base::LockGuard<ReadWriteLockWriter> WriterLockGuard;
embb::base::ReadWriteLock readwrite_lock_;
friend class test::TreeTest<ChromaticTree>;
};
} // namespace containers
......
......@@ -185,6 +185,12 @@ class ObjectPool {
Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4, Param5 const& param5);
template<typename Param1, typename Param2, typename Param3, typename Param4,
typename Param5, typename Param6>
Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4, Param5 const& param5,
Param6 const& param6);
#endif
};
} // namespace containers
......
......@@ -97,7 +97,7 @@ void HazardPointerTest::HazardPointerTest1_ThreadMethod() {
PT_ASSERT(success == true);
embb::base::Atomic<int>* allocated_object_from_different_thread;
embb::base::Atomic<int>* allocated_object_from_different_thread(0);
int diff_count = 0;
......
......@@ -39,116 +39,298 @@ namespace test {
template<typename Tree>
TreeTest<Tree>::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<Key>(-1)),
bad_value_(static_cast<Value>(-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<typename Tree>
TreeTest<Tree>::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<typename Tree>
void TreeTest<Tree>::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<typename Tree>
void TreeTest<Tree>::Worker::
PrepareElements(int num_elements) {
// Fill the "elements_" vector
elements_.clear();
for (int i = 0; i < num_elements; ++i) {
Key key = static_cast<Key >(i * 100 + thread_id_);
Value value = static_cast<Value>(i * 100 + thread_id_);
elements_.push_back(std::make_pair(key, value));
}
}
template<typename Tree>
void TreeTest<Tree>::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<typename Tree>
void TreeTest<Tree>::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<ptrdiff_t>(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<typename Tree>
void TreeTest<Tree>::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<typename Tree>
void TreeTest<Tree>::
TreeTestSingleThreadInsertDelete_Pre() {
tree_ = new Tree(TREE_CAPACITY);
void TreeTest<Tree>::Worker::
DeleteHalf() {
// Delete half of the elements from the tree
::std::random_shuffle(elements_.begin(), elements_.end());
ElementIterator elements_middle = elements_.begin() +
static_cast<ptrdiff_t>(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<typename Tree>
void TreeTest<Tree>::
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<typename Tree>
void TreeTest<Tree>::
TreeTestInsertDeleteSingleThread_ThreadMethod() {
Worker worker(tree_, 0);
worker.Run();
for (int i = 1; i <= 10; ++i) {
worker.InsertReplaceDelete(i * (TREE_CAPACITY / 10));
}
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestMultiThreadInsertDelete_ThreadMethod() {
size_t thread_id = partest::TestSuite::GetCurrentThreadID();
TreeTestInsertDeleteMultiThread_ThreadMethod() {
int thread_id = static_cast<int>(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<typename Tree>
void TreeTest<Tree>::
TreeTestSingleThreadInsertDelete_Post() {
TreeTestInsertDelete_Post() {
PT_ASSERT_MSG((tree_->IsEmpty()), "The tree must be empty at this point.");
delete tree_;
}
template<typename Tree>
void TreeTest<Tree>::
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<Key >(i);
Value value = static_cast<Value>(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<typename Tree>
void TreeTest<Tree>::
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<Key >(TREE_CAPACITY / 4 + i);
Value value = static_cast<Value>(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<typename Tree>
void TreeTest<Tree>::
TreeTestConcurrentGet_WriterMethod() {
int idx = static_cast<int>(partest::TestSuite::GetCurrentThreadID());
idx %= (NUM_TEST_THREADS / 2);
Key key = static_cast<Key >(TREE_CAPACITY / 4 + idx);
Value value = static_cast<Value>(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<typename Tree>
void TreeTest<Tree>::
TreeTestConcurrentGet_ReaderMethod() {
int idx = static_cast<int>(partest::TestSuite::GetCurrentThreadID());
idx %= (NUM_TEST_THREADS / 2);
Key key = static_cast<Key>(TREE_CAPACITY / 4 + idx);
for (int i = 0; i < 1000; ++i) {
Value value;
tree_->Get(key, value);
}
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestConcurrentGet_Post() {
delete tree_;
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestBalance_Pre() {
embb_internal_thread_index_reset();
tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_);
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestBalance_ThreadMethod() {
int thread_id = static_cast<int>(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<typename Tree>
void TreeTest<Tree>::
TreeTestBalance_Post() {
PT_ASSERT_MSG((tree_->IsBalanced()), "The tree must balanced at this point.");
delete tree_;
}
} // namespace test
} // namespace containers
} // namespace embb
......
......@@ -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<Key, Value> Element;
typedef ::std::vector<Element> 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<Key, Value> Element;
typedef ::std::vector<Element> 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
......
......@@ -39,7 +39,7 @@
#define NUM_SLICES 8
#define TEST_COUNT 12
typedef embb::dataflow::Network<8> MyNetwork;
typedef embb::dataflow::Network<NUM_SLICES> MyNetwork;
typedef MyNetwork::ConstantSource< int > MyConstantSource;
typedef MyNetwork::Source< int > MySource;
typedef MyNetwork::SerialProcess< MyNetwork::Inputs<int>::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)
......
......@@ -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;
}
......
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <embb_mtapi_test_id_pool.h>
#include <vector>
IdPoolTest::IdPoolTest() {
CreateUnit("mtapi id pool test single threaded").
Add(&IdPoolTest::TestBasic, this, 1, 1000).
Pre(&IdPoolTest::TestBasicPre, this).
Post(&IdPoolTest::TestBasicPost, this);
CreateUnit("mtapi id pool test concurrent").
Add(&IdPoolTest::TestParallel, this, concurrent_accessors_id_pool_2
, 20).
Post(&IdPoolTest::TestParallelPost, this).
Pre(&IdPoolTest::TestParallelPre, this);
}
void IdPoolTest::TestParallel() {
// allocate ID_ELEMENTS_PER_ACCESSOR elements. Each test thread is
// guaranteed to be able to allocate this amount of elements.
TestAllocateDeallocateNElementsFromPool(id_pool_parallel,
id_elements_per_accessor);
}
void IdPoolTest::TestParallelPre() {
// create second id pool with CONCURRENT_ACCESSORS_ID_POOL_2*
// ID_ELEMENTS_PER_ACCESSOR elements
embb_mtapi_id_pool_initialize(&id_pool_parallel,
concurrent_accessors_id_pool_2*id_elements_per_accessor);
}
void IdPoolTest::TestParallelPost() {
// after the parallel tests, try to again allocate and deallocate all
// elements sequentially.
TestAllocateDeallocateNElementsFromPool(id_pool_parallel,
concurrent_accessors_id_pool_2*id_elements_per_accessor, true);
// finalize pool
embb_mtapi_id_pool_finalize(&id_pool_parallel);
}
void IdPoolTest::TestBasic() {
TestAllocateDeallocateNElementsFromPool(id_pool, id_pool_size_1, true);
}
void IdPoolTest::TestBasicPre() {
// create id pool with ID_POOL_SIZE_1 elements
embb_mtapi_id_pool_initialize(&id_pool, id_pool_size_1);
}
void IdPoolTest::TestBasicPost() {
// finalize pool
embb_mtapi_id_pool_finalize(&id_pool);
}
void IdPoolTest::TestAllocateDeallocateNElementsFromPool(
embb_mtapi_id_pool_t &pool,
int count_elements,
bool empty_check) {
std::vector<unsigned int> allocated;
for (int i = 0; i != count_elements; ++i) {
allocated.push_back(embb_mtapi_id_pool_allocate(&pool));
}
// the allocated elements should be disjunctive, and never invalid element
for (unsigned int x = 0; x != allocated.size(); ++x) {
PT_ASSERT(allocated[x] != EMBB_MTAPI_IDPOOL_INVALID_ID);
for (unsigned int y = 0; y != allocated.size(); ++y) {
if (x == y) {
continue;
}
PT_ASSERT(allocated[x] != allocated[y]);
}
}
// now the id pool should be empty... try ten times to get an id,
// we should always get the invalid element
if (empty_check) {
for (int i = 0; i != 10; ++i) {
PT_ASSERT_EQ(embb_mtapi_id_pool_allocate(&pool),
static_cast<unsigned int>(EMBB_MTAPI_IDPOOL_INVALID_ID)
)
}
}
// now return allocated elements in a shuffled manner.
::std::random_shuffle(allocated.begin(), allocated.end());
for (int i = 0; i != count_elements; ++i) {
embb_mtapi_id_pool_deallocate(&pool,
allocated[static_cast<unsigned int>(i)]);
}
}
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
#define MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
#include <partest/partest.h>
#include <embb_mtapi_id_pool_t.h>
// for shuffling a vector
#include <algorithm>
class IdPoolTest : public partest::TestCase {
public:
embb_mtapi_id_pool_t id_pool;
embb_mtapi_id_pool_t id_pool_parallel;
IdPoolTest();
private:
static const unsigned int id_pool_size_1 = 100;
static const unsigned int concurrent_accessors_id_pool_2 = 10;
static const unsigned int id_elements_per_accessor = 10;
/**
* We create a pool of size number_accessors*elements_per_accessor, so
* at each time we can guarantee each thread to be able to allocate
* elements_per_accessor elements.
* We create number_accessor threads, where each thread iteratively
* allocates and frees elements_per_accessor elements, which in each case
* has to be successful. Additionally, the sanity checks from the basic tests
* are repeated. The TestParallelPost function also repeats all
* sequential tests.
*/
void TestParallel();
void TestParallelPre();
void TestParallelPost();
/**
* Create a pool of size N. We repeatedly allocate and free N elements, check
* if the pool always returns disjunctive ids and check that the pool never
* returns the invalid element, if the pool is not empty. Check that the
* invalid element is returned if the pool is empty.
*/
void TestBasic();
void TestBasicPre();
void TestBasicPost();
static void TestAllocateDeallocateNElementsFromPool(
embb_mtapi_id_pool_t &pool,
int count_elements,
bool empty_check = false);
};
#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
......@@ -37,6 +37,9 @@
#include <embb_mtapi_test_group.h>
#include <embb_mtapi_test_queue.h>
#include <embb_mtapi_test_error.h>
#include <embb_mtapi_test_id_pool.h>
#include <embb/base/c/memory_allocation.h>
PT_MAIN("MTAPI C") {
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);
}
......@@ -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)
......
......@@ -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")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment