Commit fd503025 by Marcus Winter

Merge remote-tracking branch 'origin/development' into mtapi_distributed

parents 48baa618 eba09e6d
......@@ -27,15 +27,17 @@ be optimized for non-functional requirements such as minimal latency and
fairness.
Besides the task scheduler, EMB² provides basic parallel algorithms, concurrent
data structures, and skeletons for implementing stream processing
applications. These building blocks are largely implemented in a non-blocking
fashion, thus preventing frequently encountered pitfalls like lock contention,
deadlocks, and priority inversion. As another advantage in real-time systems,
the algorithms and data structures give certain progress guarantees. For
example, wait-free data structures guarantee system-wide progress which means
that every operation completes within a finite number of steps independently
of any other concurrent operations on the same data structure.
data structures, and skeletons for implementing stream processing applications
(see figure below). These building blocks are largely implemented in a
non-blocking fashion, thus preventing frequently encountered pitfalls like
lock contention, deadlocks, and priority inversion. As another advantage in
real-time systems, the algorithms and data structures give certain progress
guarantees. For example, wait-free data structures guarantee system-wide
progress which means that every operation completes within a finite number of
steps independently of any other concurrent operations on the same data
structure.
<img src="doc/images/embb.png" alt="Building blocks of EMB²" width="500"/>
Community and Contact
---------------------
......@@ -73,7 +75,8 @@ This project is based on the standards C99 (for C code) and C++03 (for C++
code) to be usable on a wide range of target systems. It has been tested on
the following OS/compiler/architecture combinations:
- Linux (Ubuntu 12.10) / GCC 4.8.1 / x86, x86_64
- Linux (Ubuntu 12.04) / GCC 4.8.1 / x86, x86_64
- Linux (Ubuntu 12.04) / Clang 3.0.0 / x86_64
- Linux (Ubuntu 14.04) / GCC 4.8.2 / ARMv7
- Windows
* MSVC 12.0.21005.1 REL / x86, x86_64
......@@ -133,7 +136,7 @@ assumed that the project's root directory is now the parent directory.
Choose an appropriate build file generator for your system.
- For Linux, GCC, x86/x86_64/ARM: "Unix Makefiles"
- For Linux, GCC/Clang, x86/x86_64/ARM: "Unix Makefiles"
- For Windows, MSVC of VS 2013, x86: "Visual Studio 12"
- For Windows, MSVC of VS 2013, x86_64: "Visual Studio 12 Win64"
- For Windows, MSVC of VS 2012, x86: "Visual Studio 11"
......@@ -150,6 +153,12 @@ option -DCMAKE_BUILD_TYPE=[Release|Debug]. If no build mode is given on Linux,
the default (Release) is used. The Visual Studio generators create build files
for both modes (the selection is done at build time).
You may choose a custom compiler instead the default one by defining
CMAKE_CXX_COMPILER and/or CMAKE_C_COMPILER. For example, to use Clang on Linux
use:
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang
EMB² can be built with and without C++ exception handling, which has to be
specified on build file generation. When exceptions are turned off, an error
message is emitted and the program aborts in case of an exception within EMB².
......
......@@ -38,11 +38,7 @@ namespace algorithms {
* - the affinity of tasks to MTAPI worker threads (not CPU cores) and
* - the priority of the spawned tasks.
*
* \ingroup CPP_ALGORITHMS_SCAN
* \ingroup CPP_ALGORITHMS_REDUCTION
* \ingroup CPP_ALGORITHMS_FOREACH
* \ingroup CPP_ALGORITHMS_COUNT
* \ingroup CPP_ALGORITHMS_SORTING
* \ingroup CPP_ALGORITHMS
*/
class ExecutionPolicy{
public:
......
......@@ -33,11 +33,7 @@ namespace algorithms {
/**
* Unary identity functor.
*
* \ingroup CPP_ALGORITHMS_SCAN
* \ingroup CPP_ALGORITHMS_REDUCTION
* \ingroup CPP_ALGORITHMS_FOREACH
* \ingroup CPP_ALGORITHMS_COUNT
* \ingroup CPP_ALGORITHMS_SORTING
* \ingroup CPP_ALGORITHMS
*/
struct Identity {
/**
......
......@@ -170,9 +170,6 @@ void MergeSort(
difference_type distance = last - first;
assert(distance >= 0);
assert(block_size == 0); // TODO(Georgios Christodoulou):
// Take block size into account
if (block_size == 0) {
block_size= (static_cast<size_t>(distance) / node.GetCoreCount());
if (block_size == 0)
......
......@@ -32,7 +32,7 @@
#include <functional>
struct IsEven{
bool operator()(int val) {
bool operator()(int val) const {
if (val % 2 == 0)
return true;
else
......
......@@ -38,7 +38,7 @@
*/
struct Square {
template<typename Type>
void operator()(Type& l) {
void operator()(Type& l) const {
l = l * l;
}
};
......
......@@ -45,6 +45,8 @@
#include <time.h>
#include <functional>
#include <embb/base/c/memory_allocation.h>
#define THIS_DOMAIN_ID 1
#define THIS_NODE_ID 1
......@@ -76,6 +78,9 @@ PT_MAIN("Algorithms") {
PT_RUN(InvokeTest);
embb::mtapi::Node::Finalize();
PT_EXPECT(embb_get_bytes_allocated() == 0);
// std::cout << "please press return to continue..." << std::endl;
// std::cin.get();
}
......@@ -64,7 +64,7 @@ void MergeSortTest::TestDataStructures() {
MergeSortAllocate(array, array + kCountSize);
MergeSortAllocate(vector.begin(), vector.end());
MergeSortAllocate(array, array + kCountSize, std::less<int>(),
ExecutionPolicy(), 0);
ExecutionPolicy(), 2);
MergeSortAllocate(deque.begin(), deque.end());
for (size_t i = 0; i < kCountSize; i++) {
PT_EXPECT_EQ(vector_copy[i], array[i]);
......
......@@ -36,7 +36,7 @@
*/
struct Square {
template<typename Type>
Type operator()(Type& l) {
Type operator()(Type& l) const {
return l * l;
}
};
......
......@@ -35,7 +35,7 @@
*/
struct Square {
template<typename Type>
Type operator()(Type& l) {
Type operator()(Type& l) const {
return l * l;
}
};
......@@ -115,13 +115,9 @@ void ScanTest::TestFunctionPointers() {
std::vector<int> vector(kCountSize);
std::vector<int> init(kCountSize);
std::vector<int> outputVector(kCountSize);
int sum = 0;
int sqr_sum = 0;
for (size_t i = 0; i < kCountSize; i++) {
vector[i] = static_cast<int>(i+2);
init[i] = 0;
sum += static_cast<int>(i + 2);
sqr_sum += static_cast<int>((i + 2) * (i + 2));
}
Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction);
......
......@@ -40,16 +40,16 @@ typedef std::vector<int>::const_iterator constVectorIterator;
struct DotProductFunctor {
template<typename TypeA, typename TypeB>
int operator()(embb::algorithms::ZipPair<TypeA, TypeB> pair) {
int operator()(embb::algorithms::ZipPair<TypeA, TypeB> pair) const {
return pair.First() * pair.Second();
}
template<typename TypeA, typename TypeB>
int operator()(int lhs, embb::algorithms::ZipPair<TypeA, TypeB> rhs) {
int operator()(int lhs, embb::algorithms::ZipPair<TypeA, TypeB> rhs) const {
return lhs + rhs.First() * rhs.Second();
}
int operator()(int lhs, int rhs) {
int operator()(int lhs, int rhs) const {
return lhs + rhs;
}
};
......@@ -59,7 +59,7 @@ struct DotProductFunctor {
* The result overwrites the original number.
*/
struct Square {
void operator()(embb::algorithms::ZipPair<int &, int &> pair) {
void operator()(embb::algorithms::ZipPair<int &, int &> pair) const {
pair.First() = pair.First() * pair.First();
pair.Second() = pair.Second() * pair.Second();
}
......@@ -110,14 +110,14 @@ void ZipIteratorTest::TestZipReduce() {
sum += static_cast<long>((i + 2) * (i + 2));
}
std::vector<int>::const_iterator iterA;
std::vector<int>::const_iterator iterB;
embb::algorithms::ZipIterator<constVectorIterator, constVectorIterator>
start_iterator = embb::algorithms::Zip(iterA = vectorA.begin(),
iterB = vectorB.begin());
start_iterator = embb::algorithms::Zip(
std::vector<int>::const_iterator(vectorA.begin()),
std::vector<int>::const_iterator(vectorB.begin()));
embb::algorithms::ZipIterator<constVectorIterator, constVectorIterator>
end_iterator = embb::algorithms::Zip(iterA = vectorA.end(),
iterB = vectorB.end());
end_iterator = embb::algorithms::Zip(
std::vector<int>::const_iterator(vectorA.end()),
std::vector<int>::const_iterator(vectorB.end()));
PT_EXPECT_EQ(embb::algorithms::Reduce(start_iterator, end_iterator, 0,
std::plus<int>(), DotProductFunctor()), sum);
......@@ -189,7 +189,7 @@ void ZipIteratorTest::TestIteratorTypes() {
struct MultiDotProductFunctor{
mtapi_int64_t operator()(
embb::algorithms::ZipPair<embb::algorithms::ZipPair<int&, int&>,
embb::algorithms::ZipPair<int&, int&> > rhs ) {
embb::algorithms::ZipPair<int&, int&> > rhs) const {
return rhs.First().First() * rhs.First().Second() *
rhs.Second().First() * rhs.Second().Second();
}
......
......@@ -78,6 +78,8 @@ endif()
include(CheckIncludeFiles) # Includes module to perform checks
include(CheckSymbolExists) # Includes module to perform symbol checks
check_include_files("sys/sysinfo.h" EMBB_HAS_HEADER_SYSINFO)
check_include_files("sys/types.h;sys/sysctl.h" EMBB_HAS_HEADER_SYSCTL)
check_include_files("sys/param.h;sys/cpuset.h" EMBB_HAS_HEADER_CPUSET)
link_libraries(${link_libraries} ${gnu_libs})
set(CMAKE_EXTRA_INCLUDE_FILES sched.h)
check_type_size(cpu_set_t EMBB_HAS_GLIB_CPU)
......
......@@ -39,6 +39,16 @@
#cmakedefine EMBB_HAS_HEADER_SYSINFO
/**
* Is used to get the number of cores on certain systems.
*/
#cmakedefine EMBB_HAS_HEADER_SYSCTL
/**
* Is used to set thread affinities on certain systems.
*/
#cmakedefine EMBB_HAS_HEADER_CPUSET
/**
* Is used for Linux thread affinities.
*/
#cmakedefine EMBB_HAS_GLIB_CPU
......
......@@ -93,8 +93,7 @@ void embb_core_set_init(embb_core_set_t* core_set, int initializer) {
#ifdef EMBB_HAS_HEADER_SYSINFO
#include <sys/sysinfo.h>
#endif
#ifdef __FreeBSD__
#elif defined EMBB_HAS_HEADER_SYSCTL
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
......@@ -102,7 +101,7 @@ void embb_core_set_init(embb_core_set_t* core_set, int initializer) {
unsigned int embb_core_count_available() {
#ifdef EMBB_HAS_HEADER_SYSINFO
return get_nprocs();
#elif defined __FreeBSD__
#elif defined EMBB_HAS_HEADER_SYSCTL
const size_t kBufferSize = sizeof(unsigned int);
char buf[kBufferSize];
size_t len = kBufferSize;
......
......@@ -65,8 +65,6 @@ static void embb_log_write_internal(
char const * message,
va_list argp) {
if (log_level <= embb_log_global_log_level) {
char msg_buffer[400];
char buffer[500];
char * log_level_str = " ";
char const * channel_str = channel;
void * log_context = embb_log_global_log_context;
......@@ -94,11 +92,15 @@ static void embb_log_write_internal(
break;
}
#if defined(EMBB_COMPILER_MSVC)
char msg_buffer[400];
char buffer[500];
vsprintf_s(msg_buffer, sizeof(msg_buffer), message, argp);
sprintf_s(buffer, sizeof(buffer), "[%s] - [%s] %s",
channel_str, log_level_str, msg_buffer);
embb_log_global_log_function(log_context, buffer);
#elif defined(EMBB_COMPILER_GNUC)
char msg_buffer[400];
char buffer[500];
vsnprintf(msg_buffer, sizeof(msg_buffer), message, argp);
snprintf(buffer, sizeof(buffer), "[%s] - [%s] %s",
channel_str, log_level_str, msg_buffer);
......
......@@ -157,6 +157,10 @@ int embb_thread_equal(const embb_thread_t* lhs, const embb_thread_t* rhs) {
#ifdef EMBB_HAS_GLIB_CPU
#include <sched.h>
#elif defined EMBB_HAS_HEADER_CPUSET
#include <pthread_np.h>
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#ifdef EMBB_HAS_HEADER_SYSINFO
......@@ -203,23 +207,26 @@ int embb_thread_create(embb_thread_t* thread, const embb_core_set_t* core_set,
int status = pthread_attr_init(&attr);
if (status != 0) return EMBB_ERROR;
if (core_set != NULL) {
#ifdef EMBB_HAS_GLIB_CPU
#if defined(EMBB_HAS_GLIB_CPU) || defined(EMBB_HAS_HEADER_CPUSET)
assert(embb_core_count_available() < CPU_SETSIZE &&
"Core sets on Linux systems are only supported up to CPU_SETSIZE "
"processors!");
"Core sets are only supported up to CPU_SETSIZE processors!");
#ifdef EMBB_HAS_GLIB_CPU
cpu_set_t cpuset;
#else
cpuset_t cpuset;
#endif
CPU_ZERO(&cpuset); /* Disable all processors */
for (unsigned int i = 0; i < embb_core_count_available(); i++) {
if (embb_core_set_contains(core_set, i)) {
CPU_SET(i, &cpuset);
}
}
status = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
status = pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
if (status != 0) return EMBB_ERROR;
#else /* EMBB_HAS_GLIB_CPU */
#else
embb_log_write("base_c", EMBB_LOG_LEVEL_WARNING, "Could not set thread "
"affinity, since no implementation available!\n");
#endif /* else EMBB_HAS_GLIB_CPU */
#endif
}
/* Dynamic allocation of thread arguments. Freed on call of join. */
......
......@@ -177,6 +177,12 @@ void AllocTest::TestMixedAllocs() {
expected += (1 + 1) * EMBB_CACHE_LINE_SIZE + 3 * sizeof(size_t) - 1;
#endif // else EMBB_DEBUG
PT_EXPECT_EQ(allocated, expected);
embb_free(plain);
embb_free_aligned(aligned);
embb_free_aligned(cache_aligned);
PT_EXPECT(embb_get_bytes_allocated() == 0);
}
} // namespace test
......
......@@ -39,6 +39,8 @@
#include <embb/base/c/log.h>
#include <iostream>
#include <embb/base/c/memory_allocation.h>
PT_MAIN("Base C") {
embb_log_set_log_level(EMBB_LOG_LEVEL_WARNING);
unsigned int max_threads =
......@@ -55,4 +57,6 @@ PT_MAIN("Base C") {
PT_RUN(embb::base::test::ConditionVarTest);
PT_RUN(embb::base::test::ThreadTest);
PT_RUN(embb::base::test::ThreadSpecificStorageTest);
PT_EXPECT(embb_get_bytes_allocated() == 0);
}
......@@ -521,7 +521,7 @@ class CacheAlignedAllocatable {
/*
* Forward declaration
*/
template <typename T>
template <typename Type>
class Allocator;
/*
......@@ -534,8 +534,8 @@ class Allocator < void > {
typedef const void* const_pointer;
typedef void value_type;
template <typename U> struct rebind {
typedef Allocator<U> other;
template <typename OtherType> struct rebind {
typedef Allocator<OtherType> other;
};
};
......@@ -552,36 +552,36 @@ class Allocator < void > {
*
* \ingroup CPP_BASE_MEMORY_ALLOCATION
*/
template <typename T>
template <typename Type>
class Allocator {
public:
/** Quantity of elements type */
typedef size_t size_type;
typedef size_t size_type;
/** Difference between two pointers type */
typedef ptrdiff_t difference_type;
typedef ptrdiff_t difference_type;
/** Pointer to element type */
typedef T* pointer;
typedef Type* pointer;
/** Pointer to constant element type */
typedef const T* const_pointer;
typedef const Type* const_pointer;
/** Reference to element type */
typedef T& reference;
typedef Type& reference;
/** Reference to constant element type */
typedef const T& const_reference;
typedef const Type& const_reference;
/** Element type */
typedef T value_type;
typedef Type value_type;
/**
* Rebind allocator to type U
* Rebind allocator to type OtherType
*/
template <typename U> struct rebind {
template <typename OtherType> struct rebind {
/** Type to rebind to */
typedef Allocator<U> other;
typedef Allocator<OtherType> other;
};
/**
......@@ -602,8 +602,8 @@ class Allocator {
*
* Allows construction from allocators for different types (rebind)
*/
template <typename U> Allocator(
const Allocator<U>&
template <typename OtherType> Allocator(
const Allocator<OtherType>&
/**< [IN] Other allocator object*/
)
throw() {}
......@@ -642,7 +642,7 @@ class Allocator {
}
/**
* Allocates but doesn't initialize storage for elements of type T
* Allocates but doesn't initialize storage for elements of type Type
*
* \threadsafe
*
......@@ -724,7 +724,7 @@ class Allocator {
/*
* Forward declaration
*/
template <typename T>
template <typename Type>
class AllocatorCacheAligned;
/*
......@@ -737,8 +737,8 @@ class AllocatorCacheAligned < void > {
typedef const void* const_pointer;
typedef void value_type;
template <typename U> struct rebind {
typedef AllocatorCacheAligned<U> other;
template <typename OtherType> struct rebind {
typedef AllocatorCacheAligned<OtherType> other;
};
};
......@@ -755,36 +755,36 @@ class AllocatorCacheAligned < void > {
*
* \ingroup CPP_BASE_MEMORY_ALLOCATION
*/
template< typename T >
class AllocatorCacheAligned : public Allocator < T > {
template< typename Type >
class AllocatorCacheAligned : public Allocator < Type > {
public:
/** Quantity of elements type */
typedef size_t size_type;
typedef size_t size_type;
/** Difference between two pointers type */
typedef ptrdiff_t difference_type;
typedef ptrdiff_t difference_type;
/** Pointer to element type */
typedef T* pointer;
typedef Type* pointer;
/** Pointer to constant element type */
typedef const T* const_pointer;
typedef const Type* const_pointer;
/** Reference to element type */
typedef T& reference;
typedef Type& reference;
/** Reference to constant element type */
typedef const T& const_reference;
typedef const Type& const_reference;
/** Element type */
typedef T value_type;
typedef Type value_type;
/**
* Rebind allocator to type U
* Rebind allocator to type OtherType
*/
template <typename U> struct rebind {
template <typename OtherType> struct rebind {
/** Type to rebind to */
typedef Allocator<U> other;
typedef Allocator<OtherType> other;
};
/**
......@@ -799,16 +799,16 @@ class AllocatorCacheAligned : public Allocator < T > {
const AllocatorCacheAligned& a
/**< [IN] Other allocator object */
) throw()
: Allocator < T >(a) { }
: Allocator < Type >(a) { }
/**
* Constructs allocator object
*
* Allows construction from allocators for different types (rebind)
*/
template<typename U>
template<typename OtherType>
AllocatorCacheAligned(
const AllocatorCacheAligned<U>&
const AllocatorCacheAligned<OtherType>&
/**< [IN] Other allocator object*/
) throw() { }
......@@ -818,7 +818,7 @@ class AllocatorCacheAligned : public Allocator < T > {
~AllocatorCacheAligned() throw() { }
/**
* Allocates but doesn't initialize storage for elements of type T
* Allocates but doesn't initialize storage for elements of type Type
*
* \threadsafe
*
......
......@@ -346,8 +346,6 @@ void AtomicTest::BasicTests() {
//Boolean operations...
// And
i = 0;
i &= 0;
PT_EXPECT(i == 0);
i &= 1;
PT_EXPECT(i == 0);
i = 1;
......
......@@ -35,6 +35,8 @@
#include <atomic_test.h>
#include <memory_allocation_test.h>
#include <embb/base/c/memory_allocation.h>
PT_MAIN("Base C++") {
unsigned int max_threads =
static_cast<unsigned int>(2 * partest::TestSuite::GetDefaultNumThreads());
......@@ -47,4 +49,6 @@ PT_MAIN("Base C++") {
PT_RUN(embb::base::test::AtomicTest);
PT_RUN(embb::base::test::MemoryAllocationTest);
PT_RUN(embb::base::test::ThreadTest);
PT_EXPECT(embb_get_bytes_allocated() == 0);
}
......@@ -93,10 +93,10 @@ void MutexTest::PostLockGuardCount() {
}
void MutexTest::TestUniqueLock() {
{ // Test standard usage and releasing
#ifdef EMBB_USE_EXCEPTIONS
bool exception_thrown = false;
bool exception_thrown = false;
#endif
{ // Test standard usage and releasing
UniqueLock<Mutex> lock(mutex_);
PT_EXPECT_EQ(lock.OwnsLock(), true);
......
......@@ -27,6 +27,8 @@
#ifndef EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_
#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_
#include <embb/base/internal/config.h>
/*
* The following algorithm uses hazard pointers and a lock-free value pool for
* memory management. For a description of the algorithm, see
......@@ -62,28 +64,29 @@ T LockFreeMPMCQueueNode<T>::GetElement() {
}
} // namespace internal
template< typename T, typename ValuePool >
void LockFreeMPMCQueue<T, ValuePool>::
DeletePointerCallback(internal::LockFreeMPMCQueueNode<T>* to_delete) {
template< typename Type, typename ValuePool >
void LockFreeMPMCQueue<Type, ValuePool>::
DeletePointerCallback(internal::LockFreeMPMCQueueNode<Type>* to_delete) {
objectPool.Free(to_delete);
}
template< typename T, typename ValuePool >
LockFreeMPMCQueue<T, ValuePool>::~LockFreeMPMCQueue() {
template< typename Type, typename ValuePool >
LockFreeMPMCQueue<Type, ValuePool>::~LockFreeMPMCQueue() {
// Nothing to do here, did not allocate anything.
}
template< typename T, typename ValuePool >
LockFreeMPMCQueue<T, ValuePool>::LockFreeMPMCQueue(size_t capacity) :
template< typename Type, typename ValuePool >
LockFreeMPMCQueue<Type, ValuePool>::LockFreeMPMCQueue(size_t capacity) :
capacity(capacity),
// Disable "this is used in base member initializer" warning.
// We explicitly want this.
#ifdef _MSC_VER
#ifdef EMBB_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4355)
#endif
delete_pointer_callback(*this, &LockFreeMPMCQueue<T>::DeletePointerCallback),
#ifdef _MSC_VER
delete_pointer_callback(*this,
&LockFreeMPMCQueue<Type>::DeletePointerCallback),
#ifdef EMBB_COMPILER_MSVC
#pragma warning(pop)
#endif
hazardPointer(delete_pointer_callback, NULL, 2),
......@@ -94,26 +97,26 @@ capacity(capacity),
embb::base::Thread::GetThreadsMaxCount() +
capacity + 1) {
// Allocate dummy node to reduce the number of special cases to consider.
internal::LockFreeMPMCQueueNode<T>* dummyNode = objectPool.Allocate();
internal::LockFreeMPMCQueueNode<Type>* dummyNode = objectPool.Allocate();
// Initially, head and tail point to the dummy node.
head = dummyNode;
tail = dummyNode;
}
template< typename T, typename ValuePool >
size_t LockFreeMPMCQueue<T, ValuePool>::GetCapacity() {
template< typename Type, typename ValuePool >
size_t LockFreeMPMCQueue<Type, ValuePool>::GetCapacity() {
return capacity;
}
template< typename T, typename ValuePool >
bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) {
template< typename Type, typename ValuePool >
bool LockFreeMPMCQueue<Type, ValuePool>::TryEnqueue(Type const& element) {
// Get node from the pool containing element to enqueue.
internal::LockFreeMPMCQueueNode<T>* node = objectPool.Allocate(element);
internal::LockFreeMPMCQueueNode<Type>* node = objectPool.Allocate(element);
// Queue full, cannot enqueue
if (node == NULL)
return false;
internal::LockFreeMPMCQueueNode<T>* my_tail;
internal::LockFreeMPMCQueueNode<Type>* my_tail;
for (;;) {
my_tail = tail;
......@@ -124,14 +127,14 @@ bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) {
continue; // Hazard pointer outdated, retry
}
internal::LockFreeMPMCQueueNode<T>* my_tail_next = my_tail->GetNext();
internal::LockFreeMPMCQueueNode<Type>* my_tail_next = my_tail->GetNext();
if (my_tail == tail) {
// If the next pointer of the tail node is null, the tail pointer
// points to the last object. We try to set the next pointer of the
// tail node to our new node.
if (my_tail_next == NULL) {
internal::LockFreeMPMCQueueNode<T>* expected = NULL;
internal::LockFreeMPMCQueueNode<Type>* expected = NULL;
// This fails if the next pointer of the "cached" tail is not null
// anymore, i.e., another thread added a node before we could complete.
if (my_tail->GetNext().CompareAndSwap(expected, node))
......@@ -151,13 +154,13 @@ bool LockFreeMPMCQueue<T, ValuePool>::TryEnqueue(T const& element) {
return true;
}
template< typename T, typename ValuePool >
bool LockFreeMPMCQueue<T, ValuePool>::TryDequeue(T & element) {
internal::LockFreeMPMCQueueNode<T>* my_head;
internal::LockFreeMPMCQueueNode<T>* my_tail;
internal::LockFreeMPMCQueueNode<T>* my_next;
internal::LockFreeMPMCQueueNode<T>* expected;
T data;
template< typename Type, typename ValuePool >
bool LockFreeMPMCQueue<Type, ValuePool>::TryDequeue(Type & element) {
internal::LockFreeMPMCQueueNode<Type>* my_head;
internal::LockFreeMPMCQueueNode<Type>* my_tail;
internal::LockFreeMPMCQueueNode<Type>* my_next;
internal::LockFreeMPMCQueueNode<Type>* expected;
Type data;
for (;;) {
my_head = head;
hazardPointer.GuardPointer(0, my_head);
......
......@@ -27,6 +27,8 @@
#ifndef EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_
#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_
#include <embb/base/internal/config.h>
/*
* The following algorithm uses hazard pointers and a lock-free value pool for
* memory management. For a description of the algorithm, see
......@@ -59,24 +61,24 @@ namespace internal {
}
} // namespace internal
template< typename T, typename ValuePool >
void LockFreeStack< T, ValuePool >::
DeletePointerCallback(internal::LockFreeStackNode<T>* to_delete) {
template< typename Type, typename ValuePool >
void LockFreeStack< Type, ValuePool >::
DeletePointerCallback(internal::LockFreeStackNode<Type>* to_delete) {
objectPool.Free(to_delete);
}
template< typename T, typename ValuePool >
LockFreeStack< T, ValuePool >::LockFreeStack(size_t capacity) :
template< typename Type, typename ValuePool >
LockFreeStack< Type, ValuePool >::LockFreeStack(size_t capacity) :
capacity(capacity),
// Disable "this is used in base member initializer" warning.
// We explicitly want this.
#ifdef _MSC_VER
#ifdef EMBB_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4355)
#endif
delete_pointer_callback(*this,
&LockFreeStack<T>::DeletePointerCallback),
#ifdef _MSC_VER
&LockFreeStack<Type>::DeletePointerCallback),
#ifdef EMBB_COMPILER_MSVC
#pragma warning(pop)
#endif
hazardPointer(delete_pointer_callback, NULL, 1),
......@@ -88,19 +90,19 @@ capacity(capacity),
capacity) {
}
template< typename T, typename ValuePool >
size_t LockFreeStack< T, ValuePool >::GetCapacity() {
template< typename Type, typename ValuePool >
size_t LockFreeStack< Type, ValuePool >::GetCapacity() {
return capacity;
}
template< typename T, typename ValuePool >
LockFreeStack< T, ValuePool >::~LockFreeStack() {
template< typename Type, typename ValuePool >
LockFreeStack< Type, ValuePool >::~LockFreeStack() {
// Nothing to do here, did not allocate anything.
}
template< typename T, typename ValuePool >
bool LockFreeStack< T, ValuePool >::TryPush(T const& element) {
internal::LockFreeStackNode<T>* newNode =
template< typename Type, typename ValuePool >
bool LockFreeStack< Type, ValuePool >::TryPush(Type const& element) {
internal::LockFreeStackNode<Type>* newNode =
objectPool.Allocate(element);
// Stack full, cannot push
......@@ -108,16 +110,16 @@ bool LockFreeStack< T, ValuePool >::TryPush(T const& element) {
return false;
for (;;) {
internal::LockFreeStackNode<T>* top_cached = top;
internal::LockFreeStackNode<Type>* top_cached = top;
newNode->SetNext(top_cached);
if (top.CompareAndSwap(top_cached, newNode))
return true;
}
}
template< typename T, typename ValuePool >
bool LockFreeStack< T, ValuePool >::TryPop(T & element) {
internal::LockFreeStackNode<T>* top_cached = top;
template< typename Type, typename ValuePool >
bool LockFreeStack< Type, ValuePool >::TryPop(Type & element) {
internal::LockFreeStackNode<Type>* top_cached = top;
for (;;) {
top_cached = top;
......@@ -146,7 +148,7 @@ bool LockFreeStack< T, ValuePool >::TryPop(T & element) {
}
}
T data = top_cached->GetElement();
Type data = top_cached->GetElement();
// We don't need to read from this reference anymore, unguard it
hazardPointer.GuardPointer(0, NULL);
......
......@@ -29,39 +29,44 @@
namespace embb {
namespace containers {
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
GetSmallestPowerByTwoValue(int value) {
int result = 1;
while (result < value) result <<= 1;
return result;
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
bool LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::IsLeaf(
int node ) {
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
IsLeaf(int node) {
if (node >= size - 1 && node <= 2 * size - 1) {
return true;
}
return false;
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
bool LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
IsValid(int node) {
return (node >= 0 && node <= 2 * size - 1);
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
GetLeftChildIndex(int node) {
int index = 2 * node + 1;
assert(IsValid(index));
return index;
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
GetRightChildIndex(int node) {
int index = 2 * node + 2;
assert(IsValid(index));
......@@ -75,16 +80,18 @@ NodeIndexToPoolIndex(int node) {
return(node - (size - 1));
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
PoolIndexToNodeIndex(int index) {
int node = index + (size - 1);
assert(IsLeaf(node));
return node;
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
bool LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
bool LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
IsRoot(int node) {
return(0 == node);
}
......@@ -97,14 +104,15 @@ GetParentNode(int node) {
return parent;
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
allocate_rec(int node, T& element) {
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
allocate_rec(int node, Type& element) {
// If we are a leaf, we try to allocate a cell using CAS.
if (IsLeaf(node)) {
int pool_index = NodeIndexToPoolIndex(node);
T expected = pool[pool_index];
Type expected = pool[pool_index];
if (expected == Undefined)
return -1;
......@@ -120,7 +128,7 @@ allocate_rec(int node, T& element) {
int desired;
// Try to decrement node value.
// This is the point, where the algorithm becomes not wait-free. We have to
// atomically decrement the value in the node iff the result is greater than
// atomically decrement the value in the node if the result is greater than
// or equal to zero. This cannot be done atomically.
do {
current = tree[node];
......@@ -141,8 +149,9 @@ allocate_rec(int node, T& element) {
return rightResult;
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
void LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
void LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
Fill(int node, int elementsToStore, int power2Value) {
if (IsLeaf(node))
return;
......@@ -165,15 +174,17 @@ Fill(int node, int elementsToStore, int power2Value) {
}
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
int LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
Allocate(T & element) {
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
int LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
Allocate(Type & element) {
return allocate_rec(0, element);
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
void LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
Free(T element, int index) {
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
void LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
Free(Type element, int index) {
assert(element != Undefined);
// Put the element back
......@@ -188,9 +199,10 @@ Free(T element, int index) {
}
}
template< typename T, T Undefined, class PoolAllocator, class TreeAllocator >
template< typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
template< typename ForwardIterator >
LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
LockFreeTreeValuePool(ForwardIterator first, ForwardIterator last) {
// Number of elements to store
real_size = static_cast<int>(::std::distance(first, last));
......@@ -218,12 +230,14 @@ LockFreeTreeValuePool(ForwardIterator first, ForwardIterator last) {
Fill(0, static_cast<int>(::std::distance(first, last)), size);
}
template<typename T, T Undefined, class PoolAllocator, class TreeAllocator >
LockFreeTreeValuePool<T, Undefined, PoolAllocator, TreeAllocator>::
template<typename Type, Type Undefined, class PoolAllocator,
class TreeAllocator >
LockFreeTreeValuePool<Type, Undefined, PoolAllocator, TreeAllocator>::
~LockFreeTreeValuePool() {
poolAllocator.deallocate(pool, static_cast<size_t>(real_size));
treeAllocator.deallocate(tree, static_cast<size_t>(tree_size));
}
} // namespace containers
} // namespace embb
......
......@@ -29,60 +29,60 @@
namespace embb {
namespace containers {
template<class T, typename ValuePool, class ObjectAllocator>
ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::ReturningTrueIterator(size_t count_value) :
count_value(count_value),
ret_value(true)
{}
template<class T, typename ValuePool, class ObjectAllocator>
typename ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
typename ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::self_type
ObjectPool<T, ValuePool, ObjectAllocator>::
ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::operator++() {
self_type i = *this;
count_value++;
return i;
}
template<class T, typename ValuePool, class ObjectAllocator>
typename ObjectPool<T, ValuePool, ObjectAllocator>::
ReturningTrueIterator::self_type ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
typename ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::self_type ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::operator++(int) {
count_value++;
return *this;
}
template<class T, typename ValuePool, class ObjectAllocator>
typename ObjectPool<T, ValuePool, ObjectAllocator>::
ReturningTrueIterator::reference ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
typename ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::reference ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::operator*() {
return ret_value;
}
template<class T, typename ValuePool, class ObjectAllocator>
typename ObjectPool<T, ValuePool, ObjectAllocator>::
ReturningTrueIterator::pointer ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
typename ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::pointer ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::operator->() {
return &ret_value;
}
template<class T, typename ValuePool, class ObjectAllocator>
bool ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
bool ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::operator==(const self_type& rhs) {
return count_value == rhs.count_value;
}
template<class T, typename ValuePool, class ObjectAllocator>
bool ObjectPool<T, ValuePool, ObjectAllocator>::
template<class Type, typename ValuePool, class ObjectAllocator>
bool ObjectPool<Type, ValuePool, ObjectAllocator>::
ReturningTrueIterator::operator!=(const self_type& rhs) {
return count_value != rhs.count_value;
}
template<class T, typename ValuePool, class ObjectAllocator>
bool ObjectPool<T, ValuePool, ObjectAllocator>::
IsContained(const T &obj) const {
template<class Type, typename ValuePool, class ObjectAllocator>
bool ObjectPool<Type, ValuePool, ObjectAllocator>::
IsContained(const Type &obj) const {
if ((&obj < &objects[0]) || (&obj > &objects[capacity - 1])) {
return false;
} else {
......@@ -90,104 +90,104 @@ IsContained(const T &obj) const {
}
}
template<class T, typename ValuePool, class ObjectAllocator>
int ObjectPool<T, ValuePool, ObjectAllocator>::
GetIndexOfObject(const T &obj) const {
template<class Type, typename ValuePool, class ObjectAllocator>
int ObjectPool<Type, ValuePool, ObjectAllocator>::
GetIndexOfObject(const Type &obj) const {
assert(IsContained(obj));
return(static_cast<int>(&obj - &objects[0]));
}
template<class T, typename ValuePool, class ObjectAllocator>
T* ObjectPool<T, ValuePool, ObjectAllocator>::AllocateRaw() {
template<class Type, typename ValuePool, class ObjectAllocator>
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::AllocateRaw() {
bool val;
int allocated_index = p.Allocate(val);
if (allocated_index == -1) {
return NULL;
} else {
T* ret_pointer = &(objects[allocated_index]);
Type* ret_pointer = &(objects[allocated_index]);
return ret_pointer;
}
}
template<class T, typename ValuePool, class ObjectAllocator>
size_t ObjectPool<T, ValuePool, ObjectAllocator>::GetCapacity() {
template<class Type, typename ValuePool, class ObjectAllocator>
size_t ObjectPool<Type, ValuePool, ObjectAllocator>::GetCapacity() {
return capacity;
}
template<class T, typename ValuePool, class ObjectAllocator>
ObjectPool<T, ValuePool, ObjectAllocator>::ObjectPool(size_t capacity) :
template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::ObjectPool(size_t capacity) :
capacity(capacity),
p(ReturningTrueIterator(0), ReturningTrueIterator(capacity)) {
// Allocate the objects (without construction, just get the memory)
objects = objectAllocator.allocate(capacity);
}
template<class T, typename ValuePool, class ObjectAllocator>
void ObjectPool<T, ValuePool, ObjectAllocator>::Free(T* obj) {
template<class Type, typename ValuePool, class ObjectAllocator>
void ObjectPool<Type, ValuePool, ObjectAllocator>::Free(Type* obj) {
int index = GetIndexOfObject(*obj);
obj->~T();
obj->~Type();
p.Free(true, index);
}
template<class T, typename ValuePool, class ObjectAllocator>
T* ObjectPool<T, ValuePool, ObjectAllocator>::Allocate() {
T* rawObject = AllocateRaw();
template<class Type, typename ValuePool, class ObjectAllocator>
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate() {
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)T();
new (rawObject)Type();
return rawObject;
}
template<class T, typename ValuePool, class ObjectAllocator>
template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1>
T* ObjectPool<T, ValuePool, ObjectAllocator>::Allocate(
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1) {
T* rawObject = AllocateRaw();
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)T(param1);
new (rawObject)Type(param1);
return rawObject;
}
template<class T, typename ValuePool, class ObjectAllocator>
template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1, typename Param2>
T* ObjectPool<T, ValuePool, ObjectAllocator>::Allocate(
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1, Param2 const& param2) {
T* rawObject = AllocateRaw();
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)T(param1, param2);
new (rawObject)Type(param1, param2);
return rawObject;
}
template<class T, typename ValuePool, class ObjectAllocator>
template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1, typename Param2, typename Param3>
T* ObjectPool<T, ValuePool, ObjectAllocator>::Allocate(
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1, Param2 const& param2,
Param3 const& param3) {
T* rawObject = AllocateRaw();
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)T(param1, param2, param3);
new (rawObject)Type(param1, param2, param3);
return rawObject;
}
template<class T, typename ValuePool, class ObjectAllocator>
template<class Type, typename ValuePool, class ObjectAllocator>
template<typename Param1, typename Param2, typename Param3, typename Param4>
T* ObjectPool<T, ValuePool, ObjectAllocator>::Allocate(
Type* ObjectPool<Type, ValuePool, ObjectAllocator>::Allocate(
Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4) {
T* rawObject = AllocateRaw();
Type* rawObject = AllocateRaw();
if (rawObject != NULL)
new (rawObject)T(param1, param2, param3, param4);
new (rawObject)Type(param1, param2, param3, param4);
return rawObject;
}
template<class T, typename ValuePool, class ObjectAllocator>
ObjectPool<T, ValuePool, ObjectAllocator>::~ObjectPool() {
template<class Type, typename ValuePool, class ObjectAllocator>
ObjectPool<Type, ValuePool, ObjectAllocator>::~ObjectPool() {
// Deallocate the objects
objectAllocator.deallocate(objects, capacity);
}
......
......@@ -29,20 +29,20 @@
namespace embb {
namespace containers {
template<typename T, T Undefined, class Allocator >
void WaitFreeArrayValuePool<T, Undefined, Allocator>::
Free(T element, int index) {
template<typename Type, Type Undefined, class Allocator >
void WaitFreeArrayValuePool<Type, Undefined, Allocator>::
Free(Type element, int index) {
assert(element != Undefined);
// Just put back the element
pool[index].Store(element);
}
template<typename T, T Undefined, class Allocator >
int WaitFreeArrayValuePool<T, Undefined, Allocator>::
Allocate(T & element) {
template<typename Type, Type Undefined, class Allocator >
int WaitFreeArrayValuePool<Type, Undefined, Allocator>::
Allocate(Type & element) {
for (int i = 0; i != size; ++i) {
T expected;
Type expected;
// If the memory cell is not available, go ahead
if (Undefined == (expected = pool[i].Load()))
......@@ -58,9 +58,9 @@ Allocate(T & element) {
return -1;
}
template<typename T, T Undefined, class Allocator >
template<typename Type, Type Undefined, class Allocator >
template<typename ForwardIterator>
WaitFreeArrayValuePool<T, Undefined, Allocator>::
WaitFreeArrayValuePool<Type, Undefined, Allocator>::
WaitFreeArrayValuePool(ForwardIterator first, ForwardIterator last) {
size_t dist = static_cast<size_t>(std::distance(first, last));
......@@ -77,8 +77,8 @@ WaitFreeArrayValuePool(ForwardIterator first, ForwardIterator last) {
}
}
template<typename T, T Undefined, class Allocator >
WaitFreeArrayValuePool<T, Undefined, Allocator>::~WaitFreeArrayValuePool() {
template<typename Type, Type Undefined, class Allocator >
WaitFreeArrayValuePool<Type, Undefined, Allocator>::~WaitFreeArrayValuePool() {
allocator.deallocate(pool, (size_t)size);
}
} // namespace containers
......
......@@ -36,21 +36,21 @@
namespace embb {
namespace containers {
template<typename T, class Allocator>
WaitFreeSPSCQueue<T, Allocator>::WaitFreeSPSCQueue(size_t capacity) :
template<typename Type, class Allocator>
WaitFreeSPSCQueue<Type, Allocator>::WaitFreeSPSCQueue(size_t capacity) :
capacity(capacity),
head_index(0),
tail_index(0) {
queue_array = allocator.allocate(capacity);
}
template<typename T, class Allocator>
size_t WaitFreeSPSCQueue<T, Allocator>::GetCapacity() {
template<typename Type, class Allocator>
size_t WaitFreeSPSCQueue<Type, Allocator>::GetCapacity() {
return capacity;
}
template<typename T, class Allocator>
bool WaitFreeSPSCQueue<T, Allocator>::TryEnqueue(T const & element) {
template<typename Type, class Allocator>
bool WaitFreeSPSCQueue<Type, Allocator>::TryEnqueue(Type const & element) {
if (head_index - tail_index == capacity)
return false;
......@@ -59,19 +59,19 @@ bool WaitFreeSPSCQueue<T, Allocator>::TryEnqueue(T const & element) {
return true;
}
template<typename T, class Allocator>
bool WaitFreeSPSCQueue<T, Allocator>::TryDequeue(T & element) {
template<typename Type, class Allocator>
bool WaitFreeSPSCQueue<Type, Allocator>::TryDequeue(Type & element) {
if (tail_index - head_index == 0)
return false;
T x = queue_array[head_index % capacity];
Type x = queue_array[head_index % capacity];
this->head_index++;
element = x;
return true;
}
template<typename T, class Allocator>
WaitFreeSPSCQueue<T, Allocator>::~WaitFreeSPSCQueue() {
template<typename Type, class Allocator>
WaitFreeSPSCQueue<Type, Allocator>::~WaitFreeSPSCQueue() {
allocator.deallocate(queue_array, capacity);
}
} // namespace containers
......
......@@ -46,20 +46,20 @@ namespace internal {
* Single linked lists, contains the element (\c element) and a pointer to the
* next node (\c next).
*
* \tparam T Element type
* \tparam Type Element type
*/
template< typename T >
template< typename Type >
class LockFreeMPMCQueueNode {
private:
/**
* Pointer to the next node
*/
embb::base::Atomic< LockFreeMPMCQueueNode< T >* > next;
embb::base::Atomic< LockFreeMPMCQueueNode< Type >* > next;
/**
* The stored element
*/
T element;
Type element;
public:
/**
......@@ -73,7 +73,7 @@ class LockFreeMPMCQueueNode {
* Creates a queue node
*/
LockFreeMPMCQueueNode(
T const& element
Type const& element
/**< [IN] The element of this queue node */);
/**
......@@ -81,12 +81,12 @@ class LockFreeMPMCQueueNode {
*
* \return The next pointer
*/
embb::base::Atomic< LockFreeMPMCQueueNode< T >* > & GetNext();
embb::base::Atomic< LockFreeMPMCQueueNode< Type >* > & GetNext();
/**
* Returns the element held by this node
*/
T GetElement();
Type GetElement();
};
} // namespace internal
......@@ -99,11 +99,11 @@ class LockFreeMPMCQueueNode {
*
* \see WaitFreeSPSCQueue
*
* \tparam T Type of the queue elements
* \tparam Type Type of the queue elements
* \tparam ValuePool Type of the value pool used as basis for the ObjectPool
* which stores the elements.
*/
template< typename T,
template< typename Type,
typename ValuePool = embb::containers::LockFreeTreeValuePool < bool, false >
>
class LockFreeMPMCQueue {
......@@ -120,35 +120,35 @@ class LockFreeMPMCQueue {
* Callback to the method that is called by hazard pointers if a pointer is
* not hazardous anymore, i.e., can safely be reused.
*/
embb::base::Function < void, internal::LockFreeMPMCQueueNode<T>* >
embb::base::Function < void, internal::LockFreeMPMCQueueNode<Type>* >
delete_pointer_callback;
/**
* The hazard pointer object, used for memory management.
*/
embb::containers::internal::HazardPointer
< internal::LockFreeMPMCQueueNode<T>* > hazardPointer;
< internal::LockFreeMPMCQueueNode<Type>* > hazardPointer;
/**
* The object pool, used for lock-free memory allocation.
*/
ObjectPool< internal::LockFreeMPMCQueueNode<T>, ValuePool > objectPool;
ObjectPool< internal::LockFreeMPMCQueueNode<Type>, ValuePool > objectPool;
/**
* Atomic pointer to the head node of the queue
*/
embb::base::Atomic< internal::LockFreeMPMCQueueNode<T>* > head;
embb::base::Atomic< internal::LockFreeMPMCQueueNode<Type>* > head;
/**
* Atomic pointer to the tail node of the queue
*/
embb::base::Atomic< internal::LockFreeMPMCQueueNode<T>* > tail;
embb::base::Atomic< internal::LockFreeMPMCQueueNode<Type>* > tail;
/**
* The callback function, used to cleanup non-hazardous pointers.
* \see delete_pointer_callback
*/
void DeletePointerCallback(internal::LockFreeMPMCQueueNode<T>* to_delete);
void DeletePointerCallback(internal::LockFreeMPMCQueueNode<Type>* to_delete);
public:
/**
......@@ -157,8 +157,8 @@ class LockFreeMPMCQueue {
* \memory
* Let \c t be the maximum number of threads and \c x be <tt>2.5*t+1</tt>.
* Then, <tt>x*(3*t+1)</tt> elements of size <tt>sizeof(void*)</tt>, \c x
* elements of size <tt>sizeof(T)</tt>, and \c capacity+1 elements of size
* <tt>sizeof(T)</tt> are allocated.
* elements of size <tt>sizeof(Type)</tt>, and \c capacity+1 elements of size
* <tt>sizeof(Type)</tt> are allocated.
*
* \notthreadsafe
*
......@@ -198,7 +198,7 @@ class LockFreeMPMCQueue {
* \see CPP_CONCEPTS_QUEUE
*/
bool TryEnqueue(
T const& element
Type const& element
/**< [IN] Const reference to the element that shall be enqueued */);
/**
......@@ -212,7 +212,7 @@ class LockFreeMPMCQueue {
* \see CPP_CONCEPTS_QUEUE
*/
bool TryDequeue(
T & element
Type & element
/**< [IN, OUT] Reference to the dequeued element.
Unchanged, if the operation
was not successful. */);
......
......@@ -53,9 +53,9 @@
*
* \par Requirements
* - Let \c Stack be the stack class
* - Let \c T be the element type of the stack
* - Let \c Type be the element type of the stack
* - Let \c capacity be a value of type \c size_t
* - Let \c element be a reference to an element of type \c T
* - Let \c element be a reference to an element of type \c Type
*
* \par Valid Expressions
* <table>
......@@ -65,11 +65,11 @@
* <th>Description</th>
* </tr>
* <tr>
* <td>\code{.cpp} Stack<T>(capacity) \endcode</td>
* <td>\code{.cpp} Stack<Type>(capacity) \endcode</td>
* <td>Nothing</td>
* <td>
* Constructs a stack with capacity \c capacity that holds elements of
* type \c T.
* type \c Type.
* </td>
* </tr>
* <tr>
......@@ -165,11 +165,11 @@ class LockFreeStackNode {
*
* \ingroup CPP_CONTAINERS_STACKS
*
* \tparam T Type of the stack elements
* \tparam Type Type of the stack elements
* \tparam ValuePool Type of the value pool used as basis for the ObjectPool
* which stores the elements.
*/
template< typename T,
template< typename Type,
typename ValuePool = embb::containers::LockFreeTreeValuePool < bool, false > >
class LockFreeStack {
private:
......@@ -183,29 +183,29 @@ class LockFreeStack {
* Callback to the method that is called by hazard pointers if a pointer is
* not hazardous anymore, i.e., can safely be reused.
*/
embb::base::Function<void, internal::LockFreeStackNode<T>*>
embb::base::Function<void, internal::LockFreeStackNode<Type>*>
delete_pointer_callback;
/**
* The hazard pointer object, used for memory management.
*/
internal::HazardPointer<internal::LockFreeStackNode<T>*> hazardPointer;
internal::HazardPointer<internal::LockFreeStackNode<Type>*> hazardPointer;
/**
* The callback function, used to cleanup non-hazardous pointers.
* \see delete_pointer_callback
*/
void DeletePointerCallback(internal::LockFreeStackNode<T>* to_delete);
void DeletePointerCallback(internal::LockFreeStackNode<Type>* to_delete);
/**
* The object pool, used for lock-free memory allocation.
*/
ObjectPool< internal::LockFreeStackNode<T>, ValuePool > objectPool;
ObjectPool< internal::LockFreeStackNode<Type>, ValuePool > objectPool;
/**
* Atomic pointer to the top node of the stack (element that is popped next)
*/
embb::base::Atomic<internal::LockFreeStackNode<T>*> top;
embb::base::Atomic<internal::LockFreeStackNode<Type>*> top;
public:
/**
......@@ -214,8 +214,8 @@ class LockFreeStack {
* \memory
* Let \c t be the maximum number of threads and \c x be <tt>1.25*t+1</tt>.
* Then, <tt>x*(3*t+1)</tt> elements of size <tt>sizeof(void*)</tt>, \c x
* elements of size <tt>sizeof(T)</tt>, and \c capacity elements of size
* <tt>sizeof(T)</tt> are allocated.
* elements of size <tt>sizeof(Type)</tt>, and \c capacity elements of size
* <tt>sizeof(Type)</tt> are allocated.
*
* \notthreadsafe
*
......@@ -256,7 +256,7 @@ class LockFreeStack {
* \see CPP_CONCEPTS_STACK
*/
bool TryPush(
T const& element
Type const& element
/**< [IN] Const reference to the element that shall be pushed */
);
......@@ -271,7 +271,7 @@ class LockFreeStack {
* \see CPP_CONCEPTS_STACK
*/
bool TryPop(
T & element
Type & element
/**< [IN,OUT] Reference to the popped element. Unchanged, if the operation
was not successful. */
);
......
......@@ -41,15 +41,15 @@ namespace containers {
*
* \see WaitFreeArrayValuePool
*
* \tparam T Element type (must support atomic operations such as \c int).
* \tparam Type Element type (must support atomic operations such as \c int).
* \tparam Undefined Bottom element (cannot be stored in the pool)
* \tparam PoolAllocator Allocator used to allocate the pool array
* \tparam TreeAllocator Allocator used to allocate the array representing the
* binary tree.
*/
template<typename T,
T Undefined,
class PoolAllocator = embb::base::Allocator< embb::base::Atomic<T> >,
template<typename Type,
Type Undefined,
class PoolAllocator = embb::base::Allocator< embb::base::Atomic<Type> >,
class TreeAllocator = embb::base::Allocator < embb::base::Atomic<int> >
>
class LockFreeTreeValuePool {
......@@ -95,7 +95,7 @@ class LockFreeTreeValuePool {
*
* The algorithm for allocating an element starts at the root node and
* recursively traverses the tree. It tries to decrement a node (a decrement
* is actually a conditional decrement, i.e., a node is decremented iff the
* is actually a conditional decrement, i.e., a node is decremented if the
* result is not less than 0. This is the place, where the algorithm is not
* wait-free anymore, as this cannot be implemented atomically.) and if
* successful, calls itself on the left child, if not successful, on the right
......@@ -135,7 +135,7 @@ class LockFreeTreeValuePool {
embb::base::Atomic<int>* tree;
// The actual pool
embb::base::Atomic<T>* pool;
embb::base::Atomic<Type>* pool;
PoolAllocator poolAllocator;
TreeAllocator treeAllocator;
......@@ -235,7 +235,7 @@ class LockFreeTreeValuePool {
int allocate_rec(
int node,
/**< [IN] Node index */
T& element
Type& element
/**< [IN,OUT] Allocated element, if there is any */
);
......@@ -260,8 +260,8 @@ class LockFreeTreeValuePool {
*
* \memory Let <tt>n = \c std::distance(first, last))</tt> and \c k be the
* minimum number such that <tt>n <= 2^k holds</tt>. Then,
* <tt>((2^k)-1) * sizeof(embb::Atomic<int>) + n*sizeof(embb::Atomic<T>)</tt>
* bytes of memory are allocated.
* <tt>((2^k)-1) * sizeof(embb::Atomic<int>) +
* n*sizeof(embb::Atomic<Type>)</tt> bytes of memory are allocated.
*
* \notthreadsafe
*
......@@ -294,7 +294,7 @@ class LockFreeTreeValuePool {
* \see CPP_CONCEPTS_VALUE_POOL
*/
int Allocate(
T & element
Type & element
/**< [IN,OUT] Reference to the allocated element. Unchanged, if the
operation was not successful. */
);
......@@ -309,7 +309,7 @@ class LockFreeTreeValuePool {
* \see CPP_CONCEPTS_VALUE_POOL
*/
void Free(
T element,
Type element,
/**< [IN] Element to be returned to the pool */
int index
/**< [IN] Index of the element as obtained by Allocate() */
......
......@@ -48,15 +48,15 @@ namespace containers {
*
* \ingroup CPP_CONTAINERS_POOLS
*
* \tparam T Element type
* \tparam Type Element type
* \tparam ValuePool Type of the underlying value pool, determines whether
* the object pool is wait-free or lock-free
* \tparam ObjectAllocator Type of allocator used to allocate objects
*/
template<class T,
template<class Type,
typename ValuePool =
embb::containers::WaitFreeArrayValuePool< bool, false >,
class ObjectAllocator = embb::base::Allocator<T> >
class ObjectAllocator = embb::base::Allocator<Type> >
class ObjectPool {
private:
/**
......@@ -67,7 +67,7 @@ class ObjectPool {
/**
* Array holding the allocated object
*/
T* objects;
Type* objects;
/**
* Capacity of the object pool
......@@ -105,15 +105,15 @@ class ObjectPool {
bool ret_value;
};
bool IsContained(const T &obj) const;
int GetIndexOfObject(const T &obj) const;
T* AllocateRaw();
bool IsContained(const Type &obj) const;
int GetIndexOfObject(const Type &obj) const;
Type* AllocateRaw();
public:
/**
* Constructs an object pool with capacity \c capacity.
*
* \memory Allocates \c capacity elements of type \c T.
* \memory Allocates \c capacity elements of type \c Type.
*
* \notthreadsafe
*/
......@@ -147,7 +147,7 @@ class ObjectPool {
* \note The element must have been allocated with Allocate().
*/
void Free(
T* obj
Type* obj
/**< [IN] Pointer to the object to be freed */
);
......@@ -162,22 +162,22 @@ class ObjectPool {
*
* \param ... Arguments of arbitrary type, passed to the object's constructor
*/
T* Allocate(...);
Type* Allocate(...);
#else
T* Allocate();
Type* Allocate();
template<typename Param1>
T* Allocate(Param1 const& param1);
Type* Allocate(Param1 const& param1);
template<typename Param1, typename Param2>
T* Allocate(Param1 const& param1, Param2 const& param2);
Type* Allocate(Param1 const& param1, Param2 const& param2);
template<typename Param1, typename Param2, typename Param3>
T* Allocate(Param1 const& param1, Param2 const& param2,
Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3);
template<typename Param1, typename Param2, typename Param3, typename Param4>
T* Allocate(Param1 const& param1, Param2 const& param2,
Type* Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4);
#endif
......
......@@ -48,11 +48,11 @@ namespace containers {
*
* \par Requirements
* - Let \c Pool be the pool class
* - Let \c T be the element type of the pool. Atomic operations must be
* possible on \c T.
* - Let \c b, d be objects of type \c T
* - Let \c Type be the element type of the pool. Atomic operations must be
* possible on \c Type.
* - Let \c b, d be objects of type \c Type
* - Let \c i, j be forward iterators supporting \c std::distance.
* - Let \c c be an object of type \c T&
* - Let \c c be an object of type \c Type&
* - Let \c e be a value of type \c int
*
* \par Valid Expressions
......@@ -64,13 +64,13 @@ namespace containers {
* <th>Description</th>
* </tr>
* <tr>
* <td>\code{.cpp} Pool<T, b>(i, j) \endcode
* <td>\code{.cpp} Pool<Type, b>(i, j) \endcode
* </td>
* <td>Nothing</td>
* <td>
* Constructs a value pool holding elements of type \c T, where \c b is the
* bottom element. The bottom element cannot be stored in the pool, it is
* exclusively used to mark empty cells. The pool initially contains
* Constructs a value pool holding elements of type \c Type, where \c b is
* the bottom element. The bottom element cannot be stored in the pool, it
* is exclusively used to mark empty cells. The pool initially contains
* \c std::distance(i, j) elements which are copied during construction from
* the range \c [i, j). A concrete class satisfying the value pool concept
* might provide additional template parameters for specifying allocators.
......@@ -107,17 +107,17 @@ namespace containers {
*
* \see LockFreeTreeValuePool
*
* \tparam T Element type (must support atomic operations such as \c int).
* \tparam Type Element type (must support atomic operations such as \c int).
* \tparam Undefined Bottom element (cannot be stored in the pool)
* \tparam Allocator Allocator used to allocate the pool array
*/
template<typename T,
T Undefined,
class Allocator = embb::base::Allocator< embb::base::Atomic<T> > >
template<typename Type,
Type Undefined,
class Allocator = embb::base::Allocator< embb::base::Atomic<Type> > >
class WaitFreeArrayValuePool {
private:
int size;
embb::base::Atomic<T>* pool;
embb::base::Atomic<Type>* pool;
WaitFreeArrayValuePool();
Allocator allocator;
......@@ -131,7 +131,7 @@ class WaitFreeArrayValuePool {
/**
* Constructs a pool and fills it with the elements in the specified range.
*
* \memory Dynamically allocates <tt>n*sizeof(embb::base::Atomic<T>)</tt>
* \memory Dynamically allocates <tt>n*sizeof(embb::base::Atomic<Type>)</tt>
* bytes, where <tt>n = std::distance(first, last)</tt> is the number
* of pool elements.
*
......@@ -166,7 +166,7 @@ class WaitFreeArrayValuePool {
* \see CPP_CONCEPTS_VALUE_POOL
*/
int Allocate(
T & element
Type & element
/**< [IN,OUT] Reference to the allocated element. Unchanged, if the
operation was not successful. */
);
......@@ -181,7 +181,7 @@ class WaitFreeArrayValuePool {
* \see CPP_CONCEPTS_VALUE_POOL
*/
void Free(
T element,
Type element,
/**< [IN] Element to be returned to the pool */
int index
/**< [IN] Index of the element as obtained by Allocate() */
......
......@@ -53,9 +53,9 @@
*
* \par Requirements
* - Let \c Queue be the queue class
* - Let \c T be the element type of the queue
* - Let \c Type be the element type of the queue
* - Let \c capacity be a value of type \c size_t
* - Let \c element be a reference to an element of type \c T
* - Let \c element be a reference to an element of type \c Type
*
* \par Valid Expressions
* <table>
......@@ -65,7 +65,7 @@
* <th>Description</th>
* </tr>
* <tr>
* <td>\code{.cpp} Queue<T>(capacity) \endcode</td>
* <td>\code{.cpp} Queue<Type>(capacity) \endcode</td>
* <td>Nothing</td>
* <td>
* Constructs a queue with capacity \c capacity that holds elements of
......@@ -114,10 +114,10 @@ namespace containers {
*
* \see LockFreeMPMCQueue
*
* \tparam T Type of the queue elements
* \tparam Type Type of the queue elements
* \tparam Allocator Allocator type for allocating queue elements.
*/
template<typename T, class Allocator = embb::base::Allocator< T > >
template<typename Type, class Allocator = embb::base::Allocator< Type > >
class WaitFreeSPSCQueue {
private:
/**
......@@ -133,7 +133,7 @@ class WaitFreeSPSCQueue {
/**
* Array holding the queue elements
*/
T* queue_array;
Type* queue_array;
/**
* Index of the head in the \c queue_array
......@@ -149,7 +149,7 @@ class WaitFreeSPSCQueue {
/**
* Creates a queue with the specified capacity.
*
* \memory Allocates \c capacity elements of type \c T.
* \memory Allocates \c capacity elements of type \c Type.
*
* \notthreadsafe
*
......@@ -190,7 +190,7 @@ class WaitFreeSPSCQueue {
* \see CPP_CONCEPTS_QUEUE
*/
bool TryEnqueue(
T const & element
Type const & element
/**< [IN] Const reference to the element that shall be enqueued */
);
......@@ -208,9 +208,9 @@ class WaitFreeSPSCQueue {
* \see CPP_CONCEPTS_QUEUE
*/
bool TryDequeue(
T & element
/**< [IN,OUT] Reference to the dequeued element. Unchanged, if the operation
was not successful. */
Type & element
/**< [IN,OUT] Reference to the dequeued element. Unchanged, if the
operation was not successful. */
);
};
} // namespace containers
......
......@@ -26,18 +26,23 @@
#include "./hazard_pointer_test.h"
#include <embb/base/internal/config.h>
namespace embb {
namespace containers {
namespace test {
HazardPointerTest::HazardPointerTest() :
#ifdef _MSC_VER
#ifdef EMBB_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4355)
#endif
delete_pointer_callback(*this, &HazardPointerTest::DeletePointerCallback),
#ifdef _MSC_VER
#ifdef EMBB_COMPILER_MSVC
#pragma warning(pop)
#endif
object_pool(NULL),
stack(NULL),
hp(NULL),
n_threads(static_cast<int>
(partest::TestSuite::GetDefaultNumThreads())) {
n_elements_per_thread = 100;
......@@ -57,7 +62,8 @@ n_threads(static_cast<int>
Pre(&HazardPointerTest::HazardPointerTest1_Pre, this).
Add(
&HazardPointerTest::HazardPointerTest1_ThreadMethod,
this, static_cast<size_t>(n_threads));
this, static_cast<size_t>(n_threads)).
Post(&HazardPointerTest::HazardPointerTest1_Post, this);
}
void HazardPointerTest::HazardPointerTest1_Pre() {
......@@ -72,6 +78,12 @@ void HazardPointerTest::HazardPointerTest1_Pre() {
1);
}
void HazardPointerTest::HazardPointerTest1_Post() {
delete object_pool;
delete stack;
delete hp;
}
void HazardPointerTest::HazardPointerTest1_ThreadMethod() {
unsigned int thread_index;
embb_internal_thread_index(&thread_index);
......
......@@ -59,6 +59,7 @@ class HazardPointerTest : public partest::TestCase {
*/
HazardPointerTest();
void HazardPointerTest1_Pre();
void HazardPointerTest1_Post();
void HazardPointerTest1_ThreadMethod();
void DeletePointerCallback(embb::base::Atomic<int>* to_delete);
};
......
......@@ -39,6 +39,8 @@
#include <embb/containers/lock_free_stack.h>
#include <embb/containers/lock_free_mpmc_queue.h>
#include <embb/base/c/memory_allocation.h>
#define COMMA ,
PT_MAIN("Data Structures C++") {
......@@ -67,4 +69,6 @@ PT_MAIN("Data Structures C++") {
PT_RUN(embb::containers::test::ObjectPoolTest
<embb::containers::WaitFreeArrayValuePool<bool COMMA false> >);
PT_EXPECT(embb_get_bytes_allocated() == 0);
}
......@@ -32,7 +32,7 @@ ObjectPoolTestStruct::ObjectPoolTestStruct(int thread_id)
: thread_id(thread_id) {
}
int ObjectPoolTestStruct::GetThreadId() {
int ObjectPoolTestStruct::GetThreadId() const {
return thread_id;
}
} // namespace test
......
......@@ -39,7 +39,7 @@ class ObjectPoolTestStruct {
int thread_id;
public:
explicit ObjectPoolTestStruct(int thread_id);
int GetThreadId();
int GetThreadId() const;
};
template<typename ValuePool>
......
......@@ -30,6 +30,8 @@
#include <algorithm>
#include <vector>
#include <embb/base/internal/config.h>
namespace embb {
namespace containers {
namespace test {
......@@ -54,13 +56,13 @@ n_threads(static_cast<int>
TOTAL_PRODUCE_CONSUME_COUNT).
Post(&QueueTest::QueueTestSingleProducedSingleConsumer_Post, this);
#ifdef _MSC_VER
#ifdef EMBB_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4127)
#endif
if (MultipleProducers == true &&
MultipleConsumers == true) {
#ifdef _MSC_VER
#ifdef EMBB_COMPILER_MSVC
#pragma warning(pop)
#endif
CreateUnit("QueueTestMultipleThreadsMultipleProducerMultipleConsumer").
......
......@@ -55,7 +55,7 @@ class Process< Slices, Serial, Inputs<Slices, I1, I2, I3, I4, I5>,
explicit Process(FunctionType function)
: executor_(function) {
input_clock_expected_ = 0;
next_clock_ = 0;
inputs_.SetListener(this);
}
......@@ -68,16 +68,7 @@ class Process< Slices, Serial, Inputs<Slices, I1, I2, I3, I4, I5>,
}
virtual void Run(int clock) {
bool ordered = Serial;
if (ordered) {
// force ordering
while (input_clock_expected_ != clock) embb::base::Thread::CurrentYield();
}
executor_.Execute(clock, inputs_, outputs_);
//inputs_.Clear(clock);
input_clock_expected_ = clock + 1;
}
InputsType & GetInputs() {
......@@ -104,20 +95,35 @@ class Process< Slices, Serial, Inputs<Slices, I1, I2, I3, I4, I5>,
}
virtual void OnClock(int clock) {
const int idx = clock % Slices;
if (!inputs_.AreAtClock(clock))
EMBB_THROW(embb::base::ErrorException,
"Some inputs are not at expected clock.")
action_[idx] = Action(this, clock);
sched_->Spawn(action_[idx]);
bool ordered = Serial;
if (ordered) {
lock_.Lock();
for (int ii = next_clock_; ii < next_clock_ + Slices; ii++) {
if (!inputs_.AreAtClock(ii)) {
break;
}
next_clock_ = ii + 1;
Run(ii);
}
lock_.Unlock();
} else {
const int idx = clock % Slices;
action_[idx] = Action(this, clock);
sched_->Spawn(action_[idx]);
}
}
private:
InputsType inputs_;
OutputsType outputs_;
ExecutorType executor_;
embb::base::Atomic<int> input_clock_expected_;
int next_clock_;
Action action_[Slices];
SpinLock lock_;
};
} // namespace internal
......
......@@ -51,7 +51,7 @@ class Sink< Slices, Inputs<Slices, I1, I2, I3, I4, I5> >
explicit Sink(FunctionType function)
: executor_(function) {
input_clock_expected_ = 0;
next_clock_ = 0;
inputs_.SetListener(this);
}
......@@ -64,17 +64,10 @@ class Sink< Slices, Inputs<Slices, I1, I2, I3, I4, I5> >
}
virtual void Run(int clock) {
//const int idx = clock % Slices;
// force ordering
while (input_clock_expected_ != clock) embb::base::Thread::CurrentYield();
if (inputs_.AreNoneBlank(clock)) {
executor_.Execute(clock, inputs_);
}
listener_->OnClock(clock);
input_clock_expected_ = clock + 1;
}
InputsType & GetInputs() {
......@@ -87,26 +80,31 @@ class Sink< Slices, Inputs<Slices, I1, I2, I3, I4, I5> >
}
virtual void OnClock(int clock) {
lock_.Lock();
TrySpawn(clock);
lock_.Unlock();
}
private:
InputsType inputs_;
ExecutorType executor_;
embb::base::Atomic<int> input_clock_expected_;
int next_clock_;
Action action_[Slices];
ClockListener * listener_;
SpinLock lock_;
void TrySpawn(int clock) {
const int idx = clock % Slices;
if (!inputs_.AreAtClock(clock))
EMBB_THROW(embb::base::ErrorException,
"Some inputs are not at expected clock.")
action_[idx] = Action(this, clock);
sched_->Spawn(action_[idx]);
lock_.Lock();
for (int ii = next_clock_; ii < next_clock_ + Slices; ii++) {
if (!inputs_.AreAtClock(ii)) {
break;
}
next_clock_ = ii + 1;
Run(ii);
}
lock_.Unlock();
}
};
......
......@@ -66,11 +66,8 @@ class Source< Slices, Outputs<Slices, O1, O2, O3, O4, O5> >
}
virtual bool Start(int clock) {
while (clock != next_clock_) embb::base::Thread::CurrentYield();
if (not_done_) {
const int idx = clock % Slices;
action_[idx] = Action(this, clock);
sched_->Spawn(action_[idx]);
Run(clock);
}
return not_done_;
}
......
......@@ -32,10 +32,11 @@
#include <embb/mtapi/mtapi.h>
#include <embb/base/function.h>
#include <embb/base/c/memory_allocation.h>
#include <embb/dataflow/dataflow.h>
typedef embb::dataflow::Network<4> MyNetwork;
typedef embb::dataflow::Network<8> MyNetwork;
typedef MyNetwork::ConstantSource< int > MyConstantSource;
typedef MyNetwork::Source< int > MySource;
typedef MyNetwork::SerialProcess< MyNetwork::Inputs<int>::Type,
......@@ -103,7 +104,7 @@ class ArraySink {
Init();
}
void Print() {
void Print() const {
std::cout << values_[0];
for (int ii = 1; ii < SIZE; ii++) {
std::cout << ", " << values_[ii];
......@@ -118,7 +119,7 @@ class ArraySink {
pos_ = 0;
}
bool Check() {
bool Check() const {
for (int ii = 0; ii < SIZE; ii++) {
int expected;
if (0 == (ii % 2))
......@@ -203,4 +204,6 @@ void SimpleTest::TestBasic() {
}
embb::mtapi::Node::Finalize();
PT_EXPECT(embb_get_bytes_allocated() == 0);
}
......@@ -102,7 +102,7 @@ This pipeline can be easily implemented using the Dataflow building block. As th
%
Then, we have to construct a \emph{network}. A network consists of a set of processes that are connected by communication channels.
%\footnote{Pipelines belong to the most simple networks, where the processes are connected in string-like (linear) fashion.}
%\embb provides a class template \lstinline|Network| that can be customized to your needs. For the moment, we are satisfied with the default configuration and omit the template arguments:
\embb provides a class template \lstinline|Network| that can be customized to your needs. For the moment, we are using 2 as a template argument:
%
\\\inputlisting{../examples/dataflow/dataflow_network-snippet.h}
%
......@@ -153,7 +153,7 @@ Once all connections have been established, we can start the network:
%
\\\inputlisting{../examples/dataflow/dataflow_run-snippet.h}
%
The integer passed to the network's function operator specifies the maximum number of elements that can be in the network at a time. The number of elements is limited to avoid that the network is flooded with new elements before the previous elements have been processed. In a linear pipeline, for example, this may happen if the source is faster than the sink. In our example, at most four elements may be processed simultaneously: one in the source, one in the sink, and two in the middle stage (see above). Finding an optimal value depends on the application and usually requires some experimentation. In general, large values boost the throughput but also increase the latency. Conversely, small values reduce the latency but may lead to a drop of performance in terms of throughput.
The integer given as a template parameter to the network specifies the maximum number of elements that can be in the network at a time. The number of elements is limited to avoid that the network is flooded with new elements before the previous elements have been processed. In a linear pipeline, for example, this may happen if the source is faster than the sink. In our example, at most four elements may be processed simultaneously: one in the source, one in the sink, and two in the middle stage (see above). Finding an optimal value depends on the application and usually requires some experimentation. In general, large values boost the throughput but also increase the latency. Conversely, small values reduce the latency but may lead to a drop of performance in terms of throughput.
Note that you will probably not observe a speedup when you run this program on a multicore processor. One reason for this is that input$/$output operations like reading a file from the hard disk and writing the output to the screen are typically a bottleneck. Moreover, the amount of work done in the middle stage of the pipeline (\textbf{replace}) is rather low. To outweigh the overhead for parallel execution, the amount of work must be much higher. In image processing, for example, a single pipeline stage may process a complete image. To sum up, we haven chosen this example for its simplicity, not for its efficiency.
......
......@@ -81,7 +81,7 @@
% 1st paramter: a square(!) picture
% 2nd parameter: short tile (one line)
% 3rd parameter: long title (up to two lines)
\gentitlepage{pics/chicago-square.jpg}{\LARGE Siemens Corporate Technology | \monthword{\month} 2014}{\scalebox{0.9}{Embedded Multicore Building Blocks}\\\scalebox{0.9}{Introduction and Tutorial}}
\gentitlepage{pics/chicago-square.jpg}{\LARGE Siemens Corporate Technology | \monthword{\month} 2015}{\scalebox{0.9}{Embedded Multicore Building Blocks}\\\scalebox{0.9}{Introduction and Tutorial}}
% List the authors and contributors on the second page, right after the cover page
% 1st parameter: contributors (optional)
......
......@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <embb/base/c/thread.h>
#include <embb/base/c/memory_allocation.h>
#include <embb_mtapi_test_config.h>
#include <embb_mtapi_test_group.h>
......@@ -195,5 +196,7 @@ void GroupTest::TestBasic() {
mtapi_finalize(&status);
MTAPI_CHECK_STATUS(status);
PT_EXPECT(embb_get_bytes_allocated() == 0);
embb_mtapi_log_info("...done\n\n");
}
......@@ -24,6 +24,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <embb/base/c/memory_allocation.h>
#include <embb_mtapi_test_config.h>
#include <embb_mtapi_test_init_finalize.h>
......@@ -66,5 +68,7 @@ void InitFinalizeTest::TestBasic() {
MTAPI_CHECK_STATUS(status);
}
PT_EXPECT(embb_get_bytes_allocated() == 0);
embb_mtapi_log_info("...done\n\n");
}
......@@ -29,6 +29,7 @@
#include <embb_mtapi_test_config.h>
#include <embb_mtapi_test_queue.h>
#include <embb/base/c/memory_allocation.h>
#include <embb/base/c/internal/unused.h>
#define JOB_TEST_TASK 42
......@@ -129,5 +130,7 @@ void QueueTest::TestBasic() {
mtapi_finalize(&status);
MTAPI_CHECK_STATUS(status);
PT_EXPECT(embb_get_bytes_allocated() == 0);
embb_mtapi_log_info("...done\n\n");
}
......@@ -29,6 +29,7 @@
#include <embb_mtapi_test_config.h>
#include <embb_mtapi_test_task.h>
#include <embb/base/c/memory_allocation.h>
#include <embb/base/c/internal/unused.h>
#define JOB_TEST_TASK 42
......@@ -162,5 +163,7 @@ void TaskTest::TestBasic() {
mtapi_finalize(&status);
MTAPI_CHECK_STATUS(status);
PT_EXPECT(embb_get_bytes_allocated() == 0);
embb_mtapi_log_info("...done\n\n");
}
......@@ -29,6 +29,8 @@
#include <mtapi_cpp_test_config.h>
#include <mtapi_cpp_test_group.h>
#include <embb/base/c/memory_allocation.h>
struct result_example_struct {
mtapi_uint_t value1;
mtapi_uint_t value2;
......@@ -81,5 +83,6 @@ void GroupTest::TestBasic() {
embb::mtapi::Node::Finalize();
PT_EXPECT(embb_get_bytes_allocated() == 0);
//std::cout << "...done" << std::endl << std::endl;
}
......@@ -29,6 +29,8 @@
#include <mtapi_cpp_test_config.h>
#include <mtapi_cpp_test_queue.h>
#include <embb/base/c/memory_allocation.h>
#define JOB_TEST_TASK 42
#define TASK_TEST_ID 23
#define QUEUE_TEST_ID 17
......@@ -63,5 +65,6 @@ void QueueTest::TestBasic() {
embb::mtapi::Node::Finalize();
PT_EXPECT(embb_get_bytes_allocated() == 0);
//std::cout << "...done" << std::endl << std::endl;
}
......@@ -31,6 +31,8 @@
#include <mtapi_cpp_test_config.h>
#include <mtapi_cpp_test_task.h>
#include <embb/base/c/memory_allocation.h>
#define JOB_TEST_TASK 42
#define TASK_TEST_ID 23
......@@ -117,5 +119,6 @@ void TaskTest::TestBasic() {
embb::mtapi::Node::Finalize();
PT_EXPECT(embb_get_bytes_allocated() == 0);
//std::cout << "...done" << std::endl << std::endl;
}
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