Commit 11e59de4 by Danila Klimenko

Merge branch 'master' into embb334_lockfree_tree

parents a585b9c7 f725637e
Embedded Multicore Building Blocks (EMB²)
=========================================
Version 0.3.1
-------------
### Features:
- None
### Changes and improvements:
- Removed one function argument from algorithms::Invoke
- Added "explicit" specifier to base type constructor of Atomic<BaseType*>
- Added "const" qualifier to dereference operator and member access operator of AtomicPointer<>
- Changed AtomicBase<>::CompareAndSwap to atomically return expected value
- Replaced constant in dataflow_cpp_test_simple.cc with corresponding macro
- Added initialization of atomic variable in hazard_pointer_test.cc to avoid warning with GCC 5.1
- Changed initial value of allocated_object_from_different_thread
- Added tests for ID Pool and check for memory leaks
- Updated unit test for the UniqueLock::Swap
### Bug fixes:
- Fixed implementation of ID pool (provided fewer elements than specified by capacity)
- Fixed unsigned overflow bug in timed wait function of condition variables
- Fixed implementation of UniqueLock::Swap
### Build system:
- Improved CMake output for automatic initialization option
- Fixed cpplint and unsigned/signed warnings
### Documentation:
- Fixed documentation of UniqueLock class
- Updated README file
Version 0.3.0
-------------
......
......@@ -28,7 +28,7 @@ cmake_minimum_required (VERSION 2.8.9)
# Version number
set (EMBB_BASE_VERSION_MAJOR 0)
set (EMBB_BASE_VERSION_MINOR 3)
set (EMBB_BASE_VERSION_PATCH 0)
set (EMBB_BASE_VERSION_PATCH 1)
# Fix compilation for CMake versions >= 3.1
#
......@@ -100,6 +100,13 @@ else()
endif()
message(" (set with command line option -DWARNINGS_ARE_ERRORS=ON/OFF)")
if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON)
message("-- MTAPI/Tasks automatic initialization enabled (default)")
else()
message("-- MTAPI/Tasks automatic initialization disabled")
endif()
message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)")
include(CMakeCommon/SetCompilerFlags.cmake)
SetGNUCompilerFlags(compiler_libs compiler_flags)
SetVisualStudioCompilerFlags(compiler_libs compiler_flags)
......
......@@ -323,6 +323,8 @@ Known Bugs and Limitations
is bounded by a predefined but modifiable constant (see functions
embb_thread_get_max_count() / embb_thread_set_max_count() and class
embb::base::Thread).
- While MTAPI fully supports heterogeneous systems, the algorithms and
dataflow components are currently limited to homogeneous systems.
Development and Contribution
......
......@@ -49,33 +49,37 @@ typedef embb::base::Function<void> InvokeFunctionType;
#ifdef DOXYGEN
/**
* Spawns one to ten function objects at once and runs them in parallel.
* Spawns two to ten function objects at once and runs them in parallel.
*
* Blocks until all of them are done.
*
* \ingroup CPP_ALGORITHMS_INVOKE
*/
template<typename Function1, ...>
template<typename Function1, typename Function2, ...>
void Invoke(
Function1 func1,
/**< [in] First function object to invoke */
Function2 func2,
/**< [in] Second function object to invoke */
...);
/**
* Spawns one to ten function objects at once and runs them in parallel using the
* Spawns two to ten function objects at once and runs them in parallel using the
* given embb::mtapi::ExecutionPolicy.
*
* Blocks until all of them are done.
*
* \ingroup CPP_ALGORITHMS_INVOKE
*/
template<typename Function1, ...>
template<typename Function1, typename Function2, ...>
void Invoke(
Function1 func1,
/**< [in] Function object to invoke */
Function2 func2,
/**< [in] Second function object to invoke */
...,
const embb::mtapi::ExecutionPolicy & policy
/**< [in] embb::mtapi::ExecutionPolicy to use */
const embb::tasks::ExecutionPolicy & policy
/**< [in] embb::tasks::ExecutionPolicy to use */
);
#else // DOXYGEN
......@@ -118,13 +122,6 @@ class TaskWrapper {
};
} // namespace internal
template<typename Function1>
void Invoke(
Function1 func1,
const embb::tasks::ExecutionPolicy& policy) {
internal::TaskWrapper<Function1> wrap1(func1, policy);
}
template<typename Function1, typename Function2>
void Invoke(
Function1 func1,
......@@ -290,12 +287,6 @@ template<typename Function1, typename Function2, typename Function3,
internal::TaskWrapper<Function10> wrap10(func10, policy);
}
template<typename Function1>
void Invoke(
Function1 func1) {
Invoke(func1, embb::tasks::ExecutionPolicy());
}
template<typename Function1, typename Function2>
void Invoke(
Function1 func1,
......
......@@ -44,7 +44,6 @@ static void Invocable10() {}
void InvokeTest::Test() {
using embb::algorithms::Invoke;
Invoke(&Invocable1);
Invoke(&Invocable1, &Invocable2);
Invoke(&Invocable1, &Invocable2, &Invocable3);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4);
......@@ -61,4 +60,24 @@ void InvokeTest::Test() {
&Invocable6, &Invocable7, &Invocable8, &Invocable9);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10);
embb::tasks::ExecutionPolicy policy;
Invoke(&Invocable1, &Invocable2, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, policy);
Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5,
&Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10,
policy);
}
......@@ -83,8 +83,8 @@ int embb_condition_wait_until(embb_condition_t* condition_var,
embb_time_t now;
embb_time_now(&now);
/* Check if absolute timepoint (in milliseconds) still is in the future */
if (time->seconds * 1000 + time->nanoseconds / 1000000
- now.seconds * 1000 - now.nanoseconds / 1000000 > 0) {
if ((time->seconds * 1000 + time->nanoseconds / 1000000)
> (now.seconds * 1000 + now.nanoseconds / 1000000)) {
/* Convert to (unsigned type) milliseconds and round up */
DWORD time_diff = (DWORD) (
time->seconds * 1000 + time->nanoseconds / 1000000
......
......@@ -105,7 +105,7 @@ void ConditionVarTest::TestTimedWaitTimeouts() {
embb_time_t time;
embb_duration_t duration = EMBB_DURATION_INIT;
// Wait for now tests already passed time point
// Wait for "now" tests already passed time point
embb_time_now(&time);
embb_mutex_lock(&mutex);
int status = embb_condition_wait_until(&cond, &mutex, &time);
......
......@@ -36,6 +36,9 @@ namespace test {
TimeTest::TimeTest() {
CreateUnit("Time in duration").Add(&TimeTest::TestTimeInDuration, this);
CreateUnit("Monotonicity").Add(
&TimeTest::TestMonotonicity, this,
1, partest::TestSuite::GetDefaultNumIterations() * 10);
}
void TimeTest::TestTimeInDuration() {
......@@ -48,6 +51,20 @@ void TimeTest::TestTimeInDuration() {
PT_EXPECT_EQ(status, EMBB_SUCCESS);
}
void TimeTest::TestMonotonicity() {
embb_time_t first;
embb_time_t second;
int status1 = embb_time_in(&first, embb_duration_zero());
int status2 = embb_time_in(&second, embb_duration_zero());
PT_EXPECT_EQ(status1, EMBB_SUCCESS);
PT_EXPECT_EQ(status2, EMBB_SUCCESS);
unsigned long long first_abs = first.seconds * 1000 +
first.nanoseconds / 1000000;
unsigned long long second_abs = second.seconds * 1000 +
second.nanoseconds / 1000000;
PT_EXPECT_GE(second_abs, first_abs);
}
} // namespace test
} // namespace base
} // namespace embb
......@@ -42,9 +42,14 @@ class TimeTest : public partest::TestCase {
private:
/**
* Tests time in duration method.
* Tests time-in-duration method.
*/
void TestTimeInDuration();
/**
* Tests that succeedingly taken times are monotonously increasing.
*/
void TestMonotonicity();
};
} // namespace test
......
......@@ -28,6 +28,7 @@
#define EMBB_BASE_INTERNAL_MUTEX_INL_H_
#include <cassert>
#include <algorithm>
namespace embb {
namespace base {
......@@ -95,8 +96,8 @@ void UniqueLock<Mutex>::Unlock() {
template<typename Mutex>
void UniqueLock<Mutex>::Swap(UniqueLock<Mutex>& other) {
locked_ = other.locked_;
mutex_ = other.Release();
std::swap(mutex_, other.mutex_);
std::swap(locked_, other.locked_);
}
template<typename Mutex>
......
......@@ -439,11 +439,11 @@ class UniqueLock {
void Unlock();
/**
* Transfers ownership of a mutex to this lock.
* Exchanges ownership of the wrapped mutex with another lock.
*/
void Swap(
UniqueLock<Mutex>& other
/**< [IN/OUT] Lock from which ownership shall be transferred */
/**< [IN/OUT] The lock to exchange ownership with */
);
/**
......
......@@ -191,13 +191,21 @@ void MutexTest::TestUniqueLock() {
}
{ // Test lock swapping
UniqueLock<> lock1;
UniqueLock<> lock2(mutex_);
PT_EXPECT_EQ(lock1.OwnsLock(), false);
PT_EXPECT_EQ(lock2.OwnsLock(), true);
lock1.Swap(lock2);
UniqueLock<> lock1(mutex_);
PT_EXPECT_EQ(lock1.OwnsLock(), true);
{
UniqueLock<> lock2;
PT_EXPECT_EQ(lock2.OwnsLock(), false);
lock1.Swap(lock2);
PT_EXPECT_EQ(lock1.OwnsLock(), false);
PT_EXPECT_EQ(lock2.OwnsLock(), true);
}
// At this point, "lock2" was destroyed and "mutex_" must be unlocked.
UniqueLock<> lock3(mutex_, embb::base::try_lock);
PT_EXPECT_EQ(lock3.OwnsLock(), true);
}
}
......
......@@ -97,7 +97,7 @@ void HazardPointerTest::HazardPointerTest1_ThreadMethod() {
PT_ASSERT(success == true);
embb::base::Atomic<int>* allocated_object_from_different_thread;
embb::base::Atomic<int>* allocated_object_from_different_thread(0);
int diff_count = 0;
......
......@@ -39,7 +39,7 @@
#define NUM_SLICES 8
#define TEST_COUNT 12
typedef embb::dataflow::Network<8> MyNetwork;
typedef embb::dataflow::Network<NUM_SLICES> MyNetwork;
typedef MyNetwork::ConstantSource< int > MyConstantSource;
typedef MyNetwork::Source< int > MySource;
typedef MyNetwork::SerialProcess< MyNetwork::Inputs<int>::Type,
......@@ -156,9 +156,7 @@ void SimpleTest::TestBasic() {
core_set,
1024, // max tasks (default: 1024)
128, // max groups (default: 128)
// Currently needs to be initialized
// with (max_queues + 1), see defect embb449
num_cores + 1, // max queues (default: 16)
num_cores, // max queues (default: 16)
1024, // queue capacity (default: 1024)
4); // num priorities (default: 4)
......
......@@ -71,7 +71,7 @@ mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that) {
/* acquire position to fetch id from */
mtapi_uint_t id_position = that->get_id_position;
that->get_id_position++;
if (that->capacity <= that->get_id_position) {
if (that->capacity < that->get_id_position) {
that->get_id_position = 0;
}
......@@ -97,7 +97,7 @@ void embb_mtapi_id_pool_deallocate(
/* acquire position to put id to */
mtapi_uint_t id_position = that->put_id_position;
that->put_id_position++;
if (that->capacity <= that->put_id_position) {
if (that->capacity < that->put_id_position) {
that->put_id_position = 0;
}
......
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <embb_mtapi_test_id_pool.h>
#include <vector>
IdPoolTest::IdPoolTest() {
CreateUnit("mtapi id pool test single threaded").
Add(&IdPoolTest::TestBasic, this, 1, 1000).
Pre(&IdPoolTest::TestBasicPre, this).
Post(&IdPoolTest::TestBasicPost, this);
CreateUnit("mtapi id pool test concurrent").
Add(&IdPoolTest::TestParallel, this, concurrent_accessors_id_pool_2
, 20).
Post(&IdPoolTest::TestParallelPost, this).
Pre(&IdPoolTest::TestParallelPre, this);
}
void IdPoolTest::TestParallel() {
// allocate ID_ELEMENTS_PER_ACCESSOR elements. Each test thread is
// guaranteed to be able to allocate this amount of elements.
TestAllocateDeallocateNElementsFromPool(id_pool_parallel,
id_elements_per_accessor);
}
void IdPoolTest::TestParallelPre() {
// create second id pool with CONCURRENT_ACCESSORS_ID_POOL_2*
// ID_ELEMENTS_PER_ACCESSOR elements
embb_mtapi_id_pool_initialize(&id_pool_parallel,
concurrent_accessors_id_pool_2*id_elements_per_accessor);
}
void IdPoolTest::TestParallelPost() {
// after the parallel tests, try to again allocate and deallocate all
// elements sequentially.
TestAllocateDeallocateNElementsFromPool(id_pool_parallel,
concurrent_accessors_id_pool_2*id_elements_per_accessor, true);
// finalize pool
embb_mtapi_id_pool_finalize(&id_pool_parallel);
}
void IdPoolTest::TestBasic() {
TestAllocateDeallocateNElementsFromPool(id_pool, id_pool_size_1, true);
}
void IdPoolTest::TestBasicPre() {
// create id pool with ID_POOL_SIZE_1 elements
embb_mtapi_id_pool_initialize(&id_pool, id_pool_size_1);
}
void IdPoolTest::TestBasicPost() {
// finalize pool
embb_mtapi_id_pool_finalize(&id_pool);
}
void IdPoolTest::TestAllocateDeallocateNElementsFromPool(
embb_mtapi_id_pool_t &pool,
int count_elements,
bool empty_check) {
std::vector<unsigned int> allocated;
for (int i = 0; i != count_elements; ++i) {
allocated.push_back(embb_mtapi_id_pool_allocate(&pool));
}
// the allocated elements should be disjunctive, and never invalid element
for (unsigned int x = 0; x != allocated.size(); ++x) {
PT_ASSERT(allocated[x] != EMBB_MTAPI_IDPOOL_INVALID_ID);
for (unsigned int y = 0; y != allocated.size(); ++y) {
if (x == y) {
continue;
}
PT_ASSERT(allocated[x] != allocated[y]);
}
}
// now the id pool should be empty... try ten times to get an id,
// we should always get the invalid element
if (empty_check) {
for (int i = 0; i != 10; ++i) {
PT_ASSERT_EQ(embb_mtapi_id_pool_allocate(&pool),
static_cast<unsigned int>(EMBB_MTAPI_IDPOOL_INVALID_ID)
)
}
}
// now return allocated elements in a shuffled manner.
::std::random_shuffle(allocated.begin(), allocated.end());
for (int i = 0; i != count_elements; ++i) {
embb_mtapi_id_pool_deallocate(&pool,
allocated[static_cast<unsigned int>(i)]);
}
}
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
#define MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
#include <partest/partest.h>
#include <embb_mtapi_id_pool_t.h>
// for shuffling a vector
#include <algorithm>
class IdPoolTest : public partest::TestCase {
public:
embb_mtapi_id_pool_t id_pool;
embb_mtapi_id_pool_t id_pool_parallel;
IdPoolTest();
private:
static const unsigned int id_pool_size_1 = 100;
static const unsigned int concurrent_accessors_id_pool_2 = 10;
static const unsigned int id_elements_per_accessor = 10;
/**
* We create a pool of size number_accessors*elements_per_accessor, so
* at each time we can guarantee each thread to be able to allocate
* elements_per_accessor elements.
* We create number_accessor threads, where each thread iteratively
* allocates and frees elements_per_accessor elements, which in each case
* has to be successful. Additionally, the sanity checks from the basic tests
* are repeated. The TestParallelPost function also repeats all
* sequential tests.
*/
void TestParallel();
void TestParallelPre();
void TestParallelPost();
/**
* Create a pool of size N. We repeatedly allocate and free N elements, check
* if the pool always returns disjunctive ids and check that the pool never
* returns the invalid element, if the pool is not empty. Check that the
* invalid element is returned if the pool is empty.
*/
void TestBasic();
void TestBasicPre();
void TestBasicPost();
static void TestAllocateDeallocateNElementsFromPool(
embb_mtapi_id_pool_t &pool,
int count_elements,
bool empty_check = false);
};
#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_ID_POOL_H_
......@@ -37,6 +37,9 @@
#include <embb_mtapi_test_group.h>
#include <embb_mtapi_test_queue.h>
#include <embb_mtapi_test_error.h>
#include <embb_mtapi_test_id_pool.h>
#include <embb/base/c/memory_allocation.h>
PT_MAIN("MTAPI C") {
embb_log_set_log_level(EMBB_LOG_LEVEL_NONE);
......@@ -48,4 +51,7 @@ PT_MAIN("MTAPI C") {
PT_RUN(InitFinalizeTest);
PT_RUN(GroupTest);
PT_RUN(QueueTest);
PT_RUN(IdPoolTest);
PT_EXPECT(embb_get_bytes_allocated() == 0);
}
......@@ -5,14 +5,10 @@ file(GLOB_RECURSE EMBB_MTAPI_CPP_HEADERS "include/*.h")
file(GLOB_RECURSE EMBB_MTAPI_CPP_TEST_SOURCES "test/*.cc" "test/*.h")
if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON)
message("-- Automatic initialization enabled (default)")
set(MTAPI_CPP_AUTOMATIC_INITIALIZE 1)
else()
set(MTAPI_CPP_AUTOMATIC_INITIALIZE 0)
message("-- Automatic initialization disabled")
endif()
message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)")
# Execute the GroupSources macro
include(${CMAKE_SOURCE_DIR}/CMakeCommon/GroupSourcesMSVC.cmake)
......
......@@ -5,13 +5,10 @@ file(GLOB_RECURSE EMBB_TASKS_CPP_HEADERS "include/*.h")
file(GLOB_RECURSE EMBB_TASKS_CPP_TEST_SOURCES "test/*.cc" "test/*.h")
if (USE_AUTOMATIC_INITIALIZATION STREQUAL ON)
message("-- Automatic initialization enabled (default)")
set(TASKS_CPP_AUTOMATIC_INITIALIZE 1)
else()
set(TASKS_CPP_AUTOMATIC_INITIALIZE 0)
message("-- Automatic initialization disabled")
endif()
message(" (set with command line option -DUSE_AUTOMATIC_INITIALIZATION=ON/OFF)")
configure_file("include/embb/tasks/internal/cmake_config.h.in"
"include/embb/tasks/internal/cmake_config.h")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment