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