diff --git a/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h b/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h index a6cd15f..8d9e034 100644 --- a/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h +++ b/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h @@ -107,6 +107,8 @@ void ForEachRecursive(RAI first, RAI last, Function unary, difference_type distance = std::distance(first, last); if (distance == 0) { return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for ForEach"); } unsigned int num_cores = policy.GetCoreCount(); if (num_cores == 0) { diff --git a/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h b/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h index 7e7b0f7..a56d582 100644 --- a/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h +++ b/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h @@ -213,23 +213,24 @@ class MergeSortFunctor { } }; -} // namespace internal - template -void MergeSort( +void MergeSortIteratorCheck( RAI first, RAI last, RAITemp temporary_first, ComparisonFunction comparison, const embb::mtapi::ExecutionPolicy& policy, - size_t block_size + size_t block_size, + std::random_access_iterator_tag ) { typedef typename std::iterator_traits::difference_type difference_type; - typedef internal::MergeSortFunctor + typedef MergeSortFunctor functor_t; difference_type distance = std::distance(first, last); if (distance == 0) { - EMBB_THROW(embb::base::ErrorException, "Distance for ForEach is 0"); + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for MergeSort"); } unsigned int num_cores = policy.GetCoreCount(); if (num_cores == 0) { @@ -247,7 +248,7 @@ void MergeSort( "Not enough MTAPI tasks available to perform merge sort"); } - internal::BlockSizePartitioner partitioner(first, last, block_size); + BlockSizePartitioner partitioner(first, last, block_size); functor_t functor(0, partitioner.Size() - 1, temporary_first, @@ -264,7 +265,16 @@ void MergeSort( task.Wait(MTAPI_INFINITE); } -// @NOTE: Why is there no type guard for RAI? +} // namespace internal + +template +void MergeSort(RAI first, RAI last, RAITemp temporary_first, + ComparisonFunction comparison, const embb::mtapi::ExecutionPolicy& policy, + size_t block_size) { + typedef typename std::iterator_traits::iterator_category category; + internal::MergeSortIteratorCheck(first, last, temporary_first, comparison, + policy, block_size, category()); +} } // namespace algorithms } // namespace embb diff --git a/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h b/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h index 4ce296f..1b4bb2f 100644 --- a/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h +++ b/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h @@ -186,16 +186,19 @@ class QuickSortFunctor { QuickSortFunctor(const QuickSortFunctor&); }; -} // namespace internal - template -void QuickSort(RAI first, RAI last, ComparisonFunction comparison, - const embb::mtapi::ExecutionPolicy& policy, size_t block_size) { +void QuickSortIteratorCheck(RAI first, RAI last, + ComparisonFunction comparison, + const embb::mtapi::ExecutionPolicy& policy, + size_t block_size, + std::random_access_iterator_tag) { embb::mtapi::Node& node = embb::mtapi::Node::GetInstance(); typedef typename std::iterator_traits::difference_type difference_type; difference_type distance = std::distance(first, last); - if (distance <= 0) { + if (distance == 0) { return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for QuickSort"); } unsigned int num_cores = policy.GetCoreCount(); if (num_cores == 0) { @@ -210,13 +213,23 @@ void QuickSort(RAI first, RAI last, ComparisonFunction comparison, EMBB_THROW(embb::base::ErrorException, "Not enough MTAPI tasks available for performing quick sort"); } - internal::QuickSortFunctor functor( + QuickSortFunctor functor( first, last, comparison, policy, block_size); mtapi::Task task = node.Spawn(mtapi::Action(base::MakeFunction( - functor, &internal::QuickSortFunctor::Action))); + functor, &QuickSortFunctor::Action))); task.Wait(MTAPI_INFINITE); } +} // namespace internal + +template +void QuickSort(RAI first, RAI last, ComparisonFunction comparison, + const embb::mtapi::ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::iterator_category category; + internal::QuickSortIteratorCheck(first, last, comparison, + policy, block_size, category()); +} + } // namespace algorithms } // namespace embb diff --git a/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h b/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h index e8fb907..86892a2 100644 --- a/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h +++ b/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h @@ -129,7 +129,9 @@ ReturnType ReduceRecursive(RAI first, RAI last, ReturnType neutral, typedef typename std::iterator_traits::difference_type difference_type; difference_type distance = std::distance(first, last); if (distance == 0) { - EMBB_THROW(embb::base::ErrorException, "Distance for Reduce is 0"); + return neutral; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for Reduce"); } unsigned int num_cores = policy.GetCoreCount(); if (num_cores == 0) { diff --git a/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h b/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h index ab2a21b..a3b6c99 100644 --- a/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h +++ b/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h @@ -173,14 +173,16 @@ void ScanIteratorCheck(RAIIn first, RAIIn last, RAIOut output_iterator, std::random_access_iterator_tag) { typedef typename std::iterator_traits::difference_type difference_type; difference_type distance = std::distance(first, last); - if (distance <= 0) { + if (distance == 0) { return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for Scan"); } unsigned int num_cores = policy.GetCoreCount(); if (num_cores == 0) { EMBB_THROW(embb::base::ErrorException, "No cores in execution policy"); } - + ReturnType values[MTAPI_NODE_MAX_TASKS_DEFAULT]; if (block_size == 0) { block_size = static_cast(distance) / num_cores; diff --git a/algorithms_cpp/include/embb/algorithms/invoke.h b/algorithms_cpp/include/embb/algorithms/invoke.h index b3ed429..1484d5a 100644 --- a/algorithms_cpp/include/embb/algorithms/invoke.h +++ b/algorithms_cpp/include/embb/algorithms/invoke.h @@ -58,7 +58,7 @@ typedef embb::base::Function InvokeFunctionType; template void Invoke( Function1 func1, - /**< [in] First function to invoke */ + /**< [in] First function object to invoke */ ...); /** @@ -72,7 +72,7 @@ void Invoke( template void Invoke( Function1 func1, - /**< [in] Function to invoke */ + /**< [in] Function object to invoke */ ..., const embb::mtapi::ExecutionPolicy & policy /**< [in] embb::mtapi::ExecutionPolicy to use */ diff --git a/algorithms_cpp/include/embb/algorithms/merge_sort.h b/algorithms_cpp/include/embb/algorithms/merge_sort.h index 5b921f2..432f767 100644 --- a/algorithms_cpp/include/embb/algorithms/merge_sort.h +++ b/algorithms_cpp/include/embb/algorithms/merge_sort.h @@ -167,9 +167,20 @@ void MergeSortAllocate( typedef base::Allocation Alloc; typename std::iterator_traits::difference_type distance = last - first; typedef typename std::iterator_traits::value_type value_type; + if (distance == 0) { + return; + } else if (distance < 0) { + EMBB_THROW(embb::base::ErrorException, "Negative range for MergeSort"); + } value_type* temporary = static_cast( Alloc::Allocate(distance * sizeof(value_type))); - MergeSort(first, last, temporary, comparison, policy, block_size); + EMBB_TRY { + MergeSort(first, last, temporary, comparison, policy, block_size); + } EMBB_CATCH (embb::base::ErrorException & e) { + // embb exception handling does not support catch(...) and rethrow yet. + Alloc::Free(temporary); + EMBB_THROW(embb::base::ErrorException, e.what()); + } Alloc::Free(temporary); } diff --git a/algorithms_cpp/include/embb/algorithms/reduce.h b/algorithms_cpp/include/embb/algorithms/reduce.h index 5cdb732..9058ee9 100644 --- a/algorithms_cpp/include/embb/algorithms/reduce.h +++ b/algorithms_cpp/include/embb/algorithms/reduce.h @@ -68,10 +68,10 @@ namespace algorithms { * \tparam RAI Random access iterator * \tparam ReturnType Type of result of reduction operation, deduced from * \c neutral - * \tparam ReductionFunction Binary reduction function with signature + * \tparam ReductionFunction Binary reduction function object with signature * ReturnType ReductionFunction(ReturnType, ReturnType). - * \tparam TransformationFunction Unary transformation function with signature - * ReturnType TransformationFunction(typename + * \tparam TransformationFunction Unary transformation function object with + * signature ReturnType TransformationFunction(typename * std::iterator_traits::value_type) */ templateReturnType ScanFunction(ReturnType, ReturnType) - * \tparam TransformationFunction Unary transformation function with signature - * ReturnType TransformationFunction(typename + * \tparam TransformationFunction Unary transformation function object with + * signature ReturnType TransformationFunction(typename * std::iterator_traits::value_type). */ template::iterator second = vector.begin() + 1; + ForEach(second, vector.begin(), Square()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif } void ForEachTest::StressTest() { diff --git a/algorithms_cpp/test/merge_sort_test.cc b/algorithms_cpp/test/merge_sort_test.cc index cbcbf0b..d7b1170 100644 --- a/algorithms_cpp/test/merge_sort_test.cc +++ b/algorithms_cpp/test/merge_sort_test.cc @@ -156,28 +156,28 @@ void MergeSortTest::TestRanges() { } } -//void MergeSortTest::TestBlockSizes() { -// using embb::algorithms::MergeSortAllocate; -// using embb::algorithms::ExecutionPolicy; -// size_t count = 4; -// std::vector init(count); -// std::vector vector(count); -// std::vector vector_copy(count); -// for (size_t i = count - 1; i > 0; i--) { -// init[i] = static_cast(i+2); -// } -// vector_copy = init; -// std::sort(vector_copy.begin(), vector_copy.end()); -// -// for (size_t block_size = 1; block_size < count + 2; block_size++) { -// vector = init; -// MergeSortAllocate(vector.begin(), vector.end(), std::less(), -// ExecutionPolicy(), block_size); -// for (size_t i = 0; i < count; i++) { -// PT_EXPECT_EQ(vector[i], vector_copy[i]); -// } -// } -//} +void MergeSortTest::TestBlockSizes() { + using embb::algorithms::MergeSortAllocate; + using embb::mtapi::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = count - 1; i > 0; i--) { + init[i] = static_cast(i+2); + } + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end()); + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + vector = init; + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(), block_size); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], vector_copy[i]); + } + } +} void MergeSortTest::TestPolicy() { using embb::algorithms::MergeSortAllocate; @@ -201,17 +201,43 @@ void MergeSortTest::TestPolicy() { vector = init; MergeSortAllocate(vector.begin(), vector.end(), std::less(), - ExecutionPolicy(true)); + ExecutionPolicy(true)); for (size_t i = 0; i < count; i++) { PT_EXPECT_EQ(vector_copy[i], vector[i]); } vector = init; MergeSortAllocate(vector.begin(), vector.end(), std::less(), - ExecutionPolicy(true, 1)); + ExecutionPolicy(true, 1)); for (size_t i = 0; i < count; i++) { PT_EXPECT_EQ(vector_copy[i], vector[i]); } + + // MergeSort on empty list should not throw: + MergeSortAllocate(vector.begin(), vector.begin(), std::less()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + MergeSortAllocate(second, vector.begin(), std::less()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif } void MergeSortTest::StressTest() { diff --git a/algorithms_cpp/test/merge_sort_test.h b/algorithms_cpp/test/merge_sort_test.h index 10545fb..7dbc8b0 100644 --- a/algorithms_cpp/test/merge_sort_test.h +++ b/algorithms_cpp/test/merge_sort_test.h @@ -58,7 +58,7 @@ class MergeSortTest : public partest::TestCase { /** * Tests various block sizes for the workers. */ - //void TestBlockSizes(); + void TestBlockSizes(); /** * Tests setting policies (without checking their actual execution). diff --git a/algorithms_cpp/test/quick_sort_test.cc b/algorithms_cpp/test/quick_sort_test.cc index 8f724ca..863b278 100644 --- a/algorithms_cpp/test/quick_sort_test.cc +++ b/algorithms_cpp/test/quick_sort_test.cc @@ -218,6 +218,32 @@ void QuickSortTest::TestPolicy() { for (size_t i = 0; i < count; i++) { PT_EXPECT_EQ(vector_copy[i], vector[i]); } + + // MergeSort on empty list should not throw: + QuickSort(vector.begin(), vector.begin(), std::less()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + QuickSort(vector.begin(), vector.end(), std::less(), + ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + QuickSort(second, vector.begin(), std::less()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif } void QuickSortTest::StressTest() { diff --git a/algorithms_cpp/test/reduce_test.cc b/algorithms_cpp/test/reduce_test.cc index 2ebebe5..c8ab5ae 100644 --- a/algorithms_cpp/test/reduce_test.cc +++ b/algorithms_cpp/test/reduce_test.cc @@ -181,6 +181,31 @@ void ReduceTest::TestPolicy() { Identity(), ExecutionPolicy(true)), sum); PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), Identity(), ExecutionPolicy(true, 1)), sum); + // Empty list should return neutral element: + PT_EXPECT_EQ(Reduce(vector.begin(), vector.begin(), 41, std::plus(), + Identity(), ExecutionPolicy(true, 1)), 41); +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + Reduce(vector.begin(), vector.end(), 0, + std::plus(), Identity(), + ExecutionPolicy(false)); + } catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + Reduce(second, vector.begin(), 0, std::plus()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif } void ReduceTest::StressTest() { diff --git a/algorithms_cpp/test/scan_test.cc b/algorithms_cpp/test/scan_test.cc index e88effd..e902a81 100644 --- a/algorithms_cpp/test/scan_test.cc +++ b/algorithms_cpp/test/scan_test.cc @@ -290,6 +290,35 @@ void ScanTest::TestPolicy() { expected += vector[i]; PT_EXPECT_EQ(expected, outputVector[i]); } + // Empty list should not throw and not change output: + outputVector = init; + std::vector::iterator out_it = outputVector.begin(); + Scan(vector.begin(), vector.begin(), out_it, 0, std::plus()); + PT_EXPECT(out_it == outputVector.begin()); + +#ifdef EMBB_USE_EXCEPTIONS + bool empty_core_set_thrown = false; + try { + Scan(vector.begin(), vector.end(), outputVector.begin(), + 0, std::plus(), Identity(), + ExecutionPolicy(false)); + } + catch (embb::base::ErrorException &) { + empty_core_set_thrown = true; + } + PT_EXPECT_MSG(empty_core_set_thrown, + "Empty core set should throw ErrorException"); + bool negative_range_thrown = false; + try { + std::vector::iterator second = vector.begin() + 1; + Scan(second, vector.begin(), outputVector.begin(), 0, std::plus()); + } + catch (embb::base::ErrorException &) { + negative_range_thrown = true; + } + PT_EXPECT_MSG(negative_range_thrown, + "Negative range should throw ErrorException"); +#endif } void ScanTest::StressTest() { diff --git a/base_cpp/include/embb/base/internal/thread_closures.h b/base_cpp/include/embb/base/internal/thread_closures.h index 214cb6e..03273ac 100644 --- a/base_cpp/include/embb/base/internal/thread_closures.h +++ b/base_cpp/include/embb/base/internal/thread_closures.h @@ -36,8 +36,8 @@ namespace internal { /** * Thread closure for thread start function with no arguments. * - * Provides a thread start function calling a callable entity such as a function - * pointer or functor. + * Provides a thread start function from which a priorly stored function object + * is called. */ template struct ThreadClosure { @@ -56,8 +56,8 @@ struct ThreadClosure { /** * Thread closure for thread start function with one argument. * - * Provides a thread start function calling a callable entity such as a function - * pointer or functor. + * Provides a thread start function from which a priorly stored function object + * is called. */ template struct ThreadClosureArg1 { @@ -78,8 +78,8 @@ struct ThreadClosureArg1 { /** * Thread closure for thread start function with two arguments. * - * Provides a thread start function calling a callable entity such as a function - * pointer or functor. + * Provides a thread start function from which a priorly stored function object + * is called. */ template struct ThreadClosureArg2 { diff --git a/base_cpp/include/embb/base/thread.h b/base_cpp/include/embb/base/thread.h index 7691020..a2278ea 100644 --- a/base_cpp/include/embb/base/thread.h +++ b/base_cpp/include/embb/base/thread.h @@ -154,12 +154,12 @@ class Thread { * \memory A small constant amount of memory to store the function. This * memory is freed the thread is joined. * \notthreadsafe - * \tparam Function Type of callable + * \tparam Function Function object type */ template explicit Thread( Function function - /**< [IN] Callable (without arguments, must be copyable) */ + /**< [IN] Copyable function object, callable without arguments */ ); /** @@ -174,14 +174,14 @@ class Thread { * \memory A small constant amount of memory to store the function. This * memory is freed the thread is joined. * \notthreadsafe - * \tparam Function Type of callable + * \tparam Function Function object type */ template explicit Thread( CoreSet& core_set, /**< [IN] Set of cores on which the thread shall be executed. */ Function function - /**< [IN] Callable (without arguments, must be copyable) */ + /**< [IN] Copyable function object, callable without arguments */ ); /** @@ -196,13 +196,13 @@ class Thread { * \memory A small constant amount of memory to store the function. This * memory is freed the thread is joined. * \notthreadsafe - * \tparam Function Type of callable + * \tparam Function Function object type * \tparam Argument Type of argument */ template Thread( Function function, - /**< [IN] Callable (with one argument, must be copyable) */ + /**< [IN] Copyable function object, callable with one argument */ Arg arg /**< [IN] Argument for function (must be copyable) */ ); @@ -219,14 +219,14 @@ class Thread { * \memory A small constant amount of memory to store the function. This * memory is freed the thread is joined. * \notthreadsafe - * \tparam Function Type of callable + * \tparam Function Function object type * \tparam Arg1 Type of first argument * \tparam Arg2 Type of second argument */ template Thread( Function function, - /**< [IN] Callable (with two arguments, must be copyable) */ + /**< [IN] Copyable function object, callable with two arguments */ Arg1 arg1, /**< [IN] First argument for function (must be copyable) */ Arg2 arg2 diff --git a/mtapi_c/include/embb/mtapi/c/mtapi.h b/mtapi_c/include/embb/mtapi/c/mtapi.h index 51460cc..5347046 100644 --- a/mtapi_c/include/embb/mtapi/c/mtapi.h +++ b/mtapi_c/include/embb/mtapi/c/mtapi.h @@ -68,7 +68,7 @@ * * * Action Function - * The callable, an executable function of an action, invoked by the + * The executable function of an action, invoked by the * MTAPI runtime when a task is started. * * diff --git a/mtapi_cpp/include/embb/mtapi/action.h b/mtapi_cpp/include/embb/mtapi/action.h index fc92f8f..68e9776 100644 --- a/mtapi_cpp/include/embb/mtapi/action.h +++ b/mtapi_cpp/include/embb/mtapi/action.h @@ -51,13 +51,13 @@ class Action { } /** - * Constructs an Action from any entity that provides an - * operator() (TaskContext &). + * Constructs an Action from a function object. + * + * \tparam Function Function object */ template Action( - Function func /**< [in] Anything that provides an - operator() (TaskContext &). */ + Function func /**< [in] Function object */ ) : function_(func) , execution_policy_() { @@ -65,13 +65,13 @@ class Action { } /** - * Constructs an Action from any entity that provides an - * operator() (TaskContext &) and an Affinity. + * Constructs an Action from a function object and an Affinity. + * + * \tparam Function Function object */ template Action( - Function func, /**< [in] Anything that provides an - operator() (TaskContext &). */ + Function func, /**< [in] Function object */ ExecutionPolicy execution_policy /**< [in] Execution policy */ ) : function_(func)