Commit 6ec6101f by Danila Klimenko

* Initial commit for the Chromatic binary search tree. Currently uses a…

* Initial commit for the Chromatic binary search tree. Currently uses a readers-writers lock to synchronize access to the tree.
* Basic tests for inserting, updating, retrieving and deleting elements (both single and multi threaded).
parent 5abeb065
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <embb/base/internal/platform.h> #include <embb/base/internal/platform.h>
#include <embb/base/exceptions.h> #include <embb/base/exceptions.h>
#include <embb/base/c/counter.h>
namespace embb { namespace embb {
namespace base { namespace base {
...@@ -483,6 +484,71 @@ class UniqueLock { ...@@ -483,6 +484,71 @@ class UniqueLock {
friend class embb::base::ConditionVariable; friend class embb::base::ConditionVariable;
}; };
class ReadWriteLock {
public:
ReadWriteLock() : read_lock_(), write_lock_(),
reader_(self()), writer_(self()) {
embb_counter_init(&active_readers_);
}
void ReadLock() {
LockGuard<Mutex> read_guard(read_lock_);
unsigned int me = embb_counter_increment(&active_readers_);
if (me == 0) {
write_lock_.Lock();
}
}
void ReadUnlock() {
unsigned int me = embb_counter_decrement(&active_readers_);
if (me == 1) {
write_lock_.Unlock();
}
}
void WriteLock() {
read_lock_.Lock();
write_lock_.Lock();
}
void WriteUnlock() {
write_lock_.Unlock();
read_lock_.Unlock();
}
~ReadWriteLock() {
embb_counter_destroy(&active_readers_);
}
class Reader {
public:
explicit Reader(ReadWriteLock& rwlock) : readwrite_lock_(rwlock) {}
void Lock() { readwrite_lock_.ReadLock(); }
void Unlock() { readwrite_lock_.ReadUnlock(); }
private:
ReadWriteLock& readwrite_lock_;
};
class Writer {
public:
explicit Writer(ReadWriteLock& rwlock) : readwrite_lock_(rwlock) {}
void Lock() { readwrite_lock_.WriteLock(); }
void Unlock() { readwrite_lock_.WriteUnlock(); }
private:
ReadWriteLock& readwrite_lock_;
};
Reader& GetReader() { return reader_; }
Writer& GetWriter() { return writer_; }
private:
ReadWriteLock(const ReadWriteLock&);
ReadWriteLock& operator=(const ReadWriteLock&);
ReadWriteLock& self() { return *this; }
Mutex read_lock_;
Mutex write_lock_;
embb_counter_t active_readers_;
Reader reader_;
Writer writer_;
};
} // namespace base } // namespace base
} // namespace embb } // namespace embb
......
...@@ -187,6 +187,20 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate( ...@@ -187,6 +187,20 @@ Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
} }
template<class Type, typename ValuePool, class ObjectAllocator> template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1, typename Param2, typename Param3, typename Param4,
typename Param5>
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4,
Param5 const& param5) {
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)Type(param1, param2, param3, param4, param5);
return rawObject;
}
template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() { ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() {
// Deallocate the objects // Deallocate the objects
objectAllocator.deallocate(objects, capacity); objectAllocator.deallocate(objects, capacity);
......
...@@ -180,6 +180,11 @@ class ObjectPool { ...@@ -180,6 +180,11 @@ class ObjectPool {
Type* Allocate(Param1 const& param1, Param2 const& param2, Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4); Param3 const& param3, Param4 const& param4);
template<typename Param1, typename Param2, typename Param3, typename Param4,
typename Param5>
Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4, Param5 const& param5);
#endif #endif
}; };
} // namespace containers } // namespace containers
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <embb/containers/object_pool.h> #include <embb/containers/object_pool.h>
#include <embb/containers/lock_free_stack.h> #include <embb/containers/lock_free_stack.h>
#include <embb/containers/lock_free_mpmc_queue.h> #include <embb/containers/lock_free_mpmc_queue.h>
#include <embb/containers/lock_free_chromatic_tree.h>
#include <embb/base/c/memory_allocation.h> #include <embb/base/c/memory_allocation.h>
#include <partest/partest.h> #include <partest/partest.h>
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
#include "./stack_test.h" #include "./stack_test.h"
#include "./hazard_pointer_test.h" #include "./hazard_pointer_test.h"
#include "./object_pool_test.h" #include "./object_pool_test.h"
#include "./tree_test.h"
#define COMMA , #define COMMA ,
...@@ -50,11 +52,13 @@ using embb::containers::LockFreeMPMCQueue; ...@@ -50,11 +52,13 @@ using embb::containers::LockFreeMPMCQueue;
using embb::containers::LockFreeStack; using embb::containers::LockFreeStack;
using embb::containers::LockFreeTreeValuePool; using embb::containers::LockFreeTreeValuePool;
using embb::containers::WaitFreeArrayValuePool; using embb::containers::WaitFreeArrayValuePool;
using embb::containers::ChromaticTree;
using embb::containers::test::PoolTest; using embb::containers::test::PoolTest;
using embb::containers::test::HazardPointerTest; using embb::containers::test::HazardPointerTest;
using embb::containers::test::QueueTest; using embb::containers::test::QueueTest;
using embb::containers::test::StackTest; using embb::containers::test::StackTest;
using embb::containers::test::ObjectPoolTest; using embb::containers::test::ObjectPoolTest;
using embb::containers::test::TreeTest;
PT_MAIN("Data Structures C++") { PT_MAIN("Data Structures C++") {
unsigned int max_threads = static_cast<unsigned int>( unsigned int max_threads = static_cast<unsigned int>(
...@@ -70,6 +74,7 @@ PT_MAIN("Data Structures C++") { ...@@ -70,6 +74,7 @@ PT_MAIN("Data Structures C++") {
PT_RUN(StackTest< LockFreeStack<int> >); PT_RUN(StackTest< LockFreeStack<int> >);
PT_RUN(ObjectPoolTest< LockFreeTreeValuePool<bool COMMA false > >); PT_RUN(ObjectPoolTest< LockFreeTreeValuePool<bool COMMA false > >);
PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool<bool COMMA false> >); PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool<bool COMMA false> >);
PT_RUN(TreeTest< ChromaticTree<size_t COMMA int> >);
PT_EXPECT(embb_get_bytes_allocated() == 0); PT_EXPECT(embb_get_bytes_allocated() == 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.
*/
#ifndef CONTAINERS_CPP_TEST_TREE_TEST_INL_H_
#define CONTAINERS_CPP_TEST_TREE_TEST_INL_H_
#include <utility>
#include <vector>
#include <algorithm>
#include "tree_test.h"
namespace embb {
namespace containers {
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
CreateUnit("TreeTestSingleThreadInsertDelete").
Pre(&TreeTest::TreeTestSingleThreadInsertDelete_Pre, this).
Add(&TreeTest::TreeTestSingleThreadInsertDelete_ThreadMethod, this, 1, 2).
Post(&TreeTest::TreeTestSingleThreadInsertDelete_Post, this);
CreateUnit("TreeTestMultiThreadInsertDelete").
Pre(&TreeTest::TreeTestSingleThreadInsertDelete_Pre, this).
Add(&TreeTest::TreeTestMultiThreadInsertDelete_ThreadMethod, this,
NUM_TEST_THREADS, 2).
Post(&TreeTest::TreeTestSingleThreadInsertDelete_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) {}
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));
}
// 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);
PT_ASSERT_MSG(success, "Failed to insert element into 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) {
Value 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.");
}
// 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) {
it->second *= 13;
bool success = tree_.TryInsert(it->first, it->second);
PT_ASSERT_MSG(success, "Failed to insert element into 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) {
Value 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.");
}
// 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);
PT_ASSERT_MSG(success, "Failed to delete element from the tree.");
PT_ASSERT_MSG(it->second == value, "Wrong value deleted from the tree.");
}
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestSingleThreadInsertDelete_Pre() {
tree_ = new Tree(TREE_CAPACITY);
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestSingleThreadInsertDelete_ThreadMethod() {
size_t thread_id = partest::TestSuite::GetCurrentThreadID();
Worker worker(*tree_, thread_id, TREE_CAPACITY);
worker.Run();
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestMultiThreadInsertDelete_ThreadMethod() {
size_t thread_id = partest::TestSuite::GetCurrentThreadID();
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();
}
template<typename Tree>
void TreeTest<Tree>::
TreeTestSingleThreadInsertDelete_Post() {
PT_ASSERT_MSG((tree_->IsEmpty()), "The tree must be empty at this point.");
delete tree_;
}
} // namespace test
} // namespace containers
} // namespace embb
#endif // CONTAINERS_CPP_TEST_TREE_TEST_INL_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.
*/
#ifndef CONTAINERS_CPP_TEST_TREE_TEST_H_
#define CONTAINERS_CPP_TEST_TREE_TEST_H_
namespace embb {
namespace containers {
namespace test {
template<typename Tree>
class TreeTest : public partest::TestCase {
public:
TreeTest();
private:
class Worker {
public:
Worker(Tree& tree, size_t thread_id, int num_elements);
void Run();
private:
Tree& tree_;
size_t thread_id_;
int num_elements_;
};
typedef int Key;
typedef int Value;
typedef ::std::pair<Key, Value> Element;
typedef ::std::vector<Element> ElementVector;
typedef ElementVector::iterator ElementIterator;
static const int TREE_CAPACITY = 2000;
static const int NUM_TEST_THREADS = 3;
void TreeTestSingleThreadInsertDelete_Pre();
void TreeTestSingleThreadInsertDelete_ThreadMethod();
void TreeTestMultiThreadInsertDelete_ThreadMethod();
void TreeTestSingleThreadInsertDelete_Post();
Tree *tree_;
};
} // namespace test
} // namespace containers
} // namespace embb
#include "./tree_test-inl.h"
#endif // CONTAINERS_CPP_TEST_TREE_TEST_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