From faca57bba33b3124353a9766ea632c564b9d54e2 Mon Sep 17 00:00:00 2001 From: Tobias Schuele Date: Wed, 1 Oct 2014 14:00:25 +0200 Subject: [PATCH] Initial version of open source release. --- .gitattributes | 1 + .gitignore | 21 +++++++++++++++++++++ CMakeCommon/CheckEnableTests.cmake | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeCommon/CopyInstallFiles.cmake | 39 +++++++++++++++++++++++++++++++++++++++ CMakeCommon/CreateDoxygenDocumentationTarget.cmake | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeCommon/GroupSourcesMSVC.cmake | 41 +++++++++++++++++++++++++++++++++++++++++ CMakeCommon/SetCompilerFlags.cmake | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeCommon/SetInstallPaths.cmake | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ COPYING.txt | 40 ++++++++++++++++++++++++++++++++++++++++ README.txt | 305 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/CMakeLists.txt | 37 +++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/algorithms.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/count.h | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/execution_policy.h | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/for_each.h | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/identity.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/count-inl.h | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/partition-inl.h | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/partition.h | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/internal/scan-inl.h | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/invoke.h | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/merge_sort.h | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/quick_sort.h | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/reduce.h | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/scan.h | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/include/embb/algorithms/zip_iterator.h | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/src/dummy.cc | 28 ++++++++++++++++++++++++++++ algorithms_cpp/src/execution_policy.cc | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/count_test.cc | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/count_test.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/for_each_test.cc | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/for_each_test.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/invoke_test.cc | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/invoke_test.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/main.cc | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/merge_sort_test.cc | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/merge_sort_test.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/partitioner_test.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/partitioner_test.h | 40 ++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/quick_sort_test.cc | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/quick_sort_test.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/reduce_test.cc | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/reduce_test.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/scan_test.cc | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/scan_test.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/zip_iterator_test.cc | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ algorithms_cpp/test/zip_iterator_test.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/CMakeLists.txt | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/atomic.h | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/base.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/condition_variable.h | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/core_set.h | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/counter.h | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/duration.h | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/errors.h | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/and_assign.h | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/atomic_variables.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/compare_and_swap.h | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/fetch_and_add.h | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/load.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/memory_barrier.h | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/or_assign.h | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/store.h | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/swap.h | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/atomic/xor_assign.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/bitset.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/cmake_config.h.in | 46 ++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/config.h | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/macro_helper.h | 33 +++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/platform.h | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/thread_index.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/internal/unused.h | 38 ++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/log.h | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/memory_allocation.h | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/mutex.h | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/thread.h | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/thread_specific_storage.h | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/include/embb/base/c/time.h | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/atomic.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/atomicfunc_32.asm | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/atomicfunc_64.asm | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/condition_variable.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/core_set.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/counter.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/duration.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/internal/thread_index.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/log.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/memory_allocation.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/mutex.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/thread.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/thread_specific_storage.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/src/time.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/README.txt | 4 ++++ base_c/test/alloc_test.cc | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/alloc_test.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/condition_var_test.cc | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/condition_var_test.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/core_set_test.cc | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/core_set_test.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/counter_test.cc | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/counter_test.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/duration_test.cc | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/duration_test.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/main.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/mutex_test.cc | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/mutex_test.h | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/thread_index_test.cc | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/thread_index_test.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/thread_specific_storage_test.cc | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/thread_specific_storage_test.h | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/thread_test.cc | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/thread_test.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/time_test.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ base_c/test/time_test.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/CMakeLists.txt | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/atomic.h | 542 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/base.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/condition_variable.h | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/core_set.h | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/duration.h | 525 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/exceptions.h | 279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/function.h | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/atomic/atomic_base.h | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/atomic/atomic_implementation.h | 305 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/atomic/atomic_integer.h | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/atomic/atomic_pointer.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/atomic/atomic_utility.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/cmake_config.h.in | 44 ++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/condition_variable-inl.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/config.h | 38 ++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/duration-inl.h | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/function0.h | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/function1.h | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/function2.h | 442 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/function3.h | 490 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/function4.h | 541 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/function5.h | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/functionT.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/memory_allocation-inl.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/mutex-inl.h | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/nil.h | 43 +++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/platform.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/thread-inl.h | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/thread_closures.h | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/internal/thread_specific_storage-inl.h | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/memory_allocation.h | 938 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/mutex.h | 491 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/thread.h | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/thread_specific_storage.h | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/include/embb/base/time.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/condition_variable.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/core_set.cc | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/duration.cc | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/function.cc | 35 +++++++++++++++++++++++++++++++++++ base_cpp/src/functionN.lua | 527 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/memory_allocation.cc | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/mutex.cc | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/thread.cc | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/src/time.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/atomic_test.cc | 393 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/atomic_test.h | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/condition_var_test.cc | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/condition_var_test.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/core_set_test.cc | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/core_set_test.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/duration_test.cc | 45 +++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/duration_test.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/main.cc | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/memory_allocation_test.cc | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/memory_allocation_test.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/mutex_test.cc | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/mutex_test.h | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/thread_specific_storage_test.cc | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/thread_specific_storage_test.h | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/thread_test.cc | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/thread_test.h | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ base_cpp/test/time_test.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/CMakeLists.txt | 33 +++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/containers.h | 43 +++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h | 405 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/hazard_pointer.h | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/object_pool-inl.h | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/lock_free_mpmc_queue.h | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/lock_free_stack.h | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/lock_free_tree_value_pool.h | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/object_pool.h | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/wait_free_array_value_pool.h | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/wait_free_spsc_queue.h | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/src/dummy.cc | 26 ++++++++++++++++++++++++++ containers_cpp/test/hazard_pointer_test.cc | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/hazard_pointer_test.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/main.cc | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/object_pool_test-inl.h | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/object_pool_test.cc | 40 ++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/object_pool_test.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/pool_test-inl.h | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/pool_test.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/queue_test-inl.h | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/queue_test.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/stack_test-inl.h | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/stack_test.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/CMakeLists.txt | 36 ++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/dataflow.h | 40 ++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/action.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/clock_listener.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/constant_source.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/in.h | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/inputs.h | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/node.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/out.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/outputs.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/process.h | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/process_executor.h | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/scheduler.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/select.h | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/signal.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/sink.h | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/sink_executor.h | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/source.h | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/source_executor.h | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/spinlock.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/switch.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/tuple.h | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/internal/typelist.h | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/include/embb/dataflow/network.h | 884 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/src/dummy.cc | 26 ++++++++++++++++++++++++++ dataflow_cpp/test/dataflow_cpp_test_simple.cc | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/test/dataflow_cpp_test_simple.h | 40 ++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/test/dataflow_cpp_test_tuple.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/test/dataflow_cpp_test_tuple.h | 40 ++++++++++++++++++++++++++++++++++++++++ dataflow_cpp/test/main.cc | 37 +++++++++++++++++++++++++++++++++++++ doc/examples/CMakeLists.txt | 26 ++++++++++++++++++++++++++ doc/examples/algorithms/counting/count-snippet.h | 3 +++ doc/examples/algorithms/counting/count_if-snippet.h | 3 +++ doc/examples/algorithms/counting/counting-fragmented.cc | 16 ++++++++++++++++ doc/examples/algorithms/counting/setup-snippet.h | 1 + doc/examples/algorithms/for_each/doubling-snippet.h | 3 +++ doc/examples/algorithms/for_each/doubling_zip-snippet.h | 7 +++++++ doc/examples/algorithms/for_each/for_each-fragmented.cc | 27 +++++++++++++++++++++++++++ doc/examples/algorithms/for_each/setup_zip-snippet.h | 5 +++++ doc/examples/algorithms/invoke/invocation-snippet.h | 2 ++ doc/examples/algorithms/invoke/invoke-fragmented.cc | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/examples/algorithms/invoke/packages-snippet.h | 3 +++ doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h | 7 +++++++ doc/examples/algorithms/invoke/quick_sort-snippet.h | 6 ++++++ doc/examples/algorithms/reduce/dot_product-snippet.h | 9 +++++++++ doc/examples/algorithms/reduce/parallel-snippet.h | 2 ++ doc/examples/algorithms/reduce/range_init-snippet.h | 4 ++++ doc/examples/algorithms/reduce/reduce-fragmented.cc | 24 ++++++++++++++++++++++++ doc/examples/algorithms/reduce/second_range_init-snippet.h | 4 ++++ doc/examples/algorithms/reduce/sequential-snippet.h | 4 ++++ doc/examples/algorithms/scan/prefix_sum-snippet.h | 3 +++ doc/examples/algorithms/scan/scan-fragmented.cc | 29 +++++++++++++++++++++++++++++ doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h | 5 +++++ doc/examples/algorithms/scan/setup-snippet.h | 5 +++++ doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h | 3 +++ doc/examples/algorithms/sorting/quick_sort-snippet.h | 2 ++ doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h | 1 + doc/examples/algorithms/sorting/range_define-snippet.h | 1 + doc/examples/algorithms/sorting/sorting-fragmented.cc | 30 ++++++++++++++++++++++++++++++ doc/examples/containers/object_pool-fragmented.cc | 20 ++++++++++++++++++++ doc/examples/containers/object_pool-snippet.h | 11 +++++++++++ doc/examples/containers/object_pool_2-snippet.h | 2 ++ doc/examples/containers/queues-fragmented.cc | 14 ++++++++++++++ doc/examples/containers/queues-snippet.h | 15 +++++++++++++++ doc/examples/containers/stack-fragmented.cc | 11 +++++++++++ doc/examples/containers/stack-snippet.h | 15 +++++++++++++++ doc/examples/dataflow/dataflow_add-snippet.h | 3 +++ doc/examples/dataflow/dataflow_comparator-snippet.h | 8 ++++++++ doc/examples/dataflow/dataflow_connect-snippet.h | 2 ++ doc/examples/dataflow/dataflow_consumer-snippet.h | 9 +++++++++ doc/examples/dataflow/dataflow_declare_add_sources-snippet.h | 20 ++++++++++++++++++++ doc/examples/dataflow/dataflow_declare_replace-snippet.h | 5 +++++ doc/examples/dataflow/dataflow_declare_sink-snippet.h | 3 +++ doc/examples/dataflow/dataflow_declare_source-snippet.h | 3 +++ doc/examples/dataflow/dataflow_include-snippet.h | 1 + doc/examples/dataflow/dataflow_linear-fragmented.cc | 35 +++++++++++++++++++++++++++++++++++ doc/examples/dataflow/dataflow_network-snippet.h | 2 ++ doc/examples/dataflow/dataflow_nonlinear-fragmented.cc | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/examples/dataflow/dataflow_producer-snippet.h | 12 ++++++++++++ doc/examples/dataflow/dataflow_replace_function-snippet.h | 4 ++++ doc/examples/dataflow/dataflow_run-snippet.h | 1 + doc/examples/dataflow/dataflow_sink_function-snippet.h | 3 +++ doc/examples/dataflow/dataflow_source_function-snippet.h | 3 +++ doc/examples/insert_snippets.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/examples/main.cc | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/examples/mtapi/mtapi_c-fragmented.cc | 40 ++++++++++++++++++++++++++++++++++++++++ doc/examples/mtapi/mtapi_c_action_signature-snippet.h | 9 +++++++++ doc/examples/mtapi/mtapi_c_calc_direct-snippet.h | 7 +++++++ doc/examples/mtapi/mtapi_c_calc_task-snippet.h | 15 +++++++++++++++ doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h | 5 +++++ doc/examples/mtapi/mtapi_c_finalize-snippet.h | 7 +++++++ doc/examples/mtapi/mtapi_c_initialize-snippet.h | 25 +++++++++++++++++++++++++ doc/examples/mtapi/mtapi_c_register_action-snippet.h | 17 +++++++++++++++++ doc/examples/mtapi/mtapi_c_start_task-snippet.h | 15 +++++++++++++++ doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h | 12 ++++++++++++ doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h | 21 +++++++++++++++++++++ doc/examples/mtapi/mtapi_c_wait_task-snippet.h | 2 ++ doc/examples/mtapi/mtapi_check_status-snippet.h | 7 +++++++ doc/examples/mtapi/mtapi_cpp-fragmented.cc | 38 ++++++++++++++++++++++++++++++++++++++ doc/examples/mtapi/mtapi_cpp_action_signature-snippet.h | 5 +++++ doc/examples/mtapi/mtapi_cpp_calc_direct-snippet.h | 6 ++++++ doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h | 10 ++++++++++ doc/examples/mtapi/mtapi_cpp_get_node-snippet.h | 1 + doc/examples/mtapi/mtapi_cpp_start_task-snippet.h | 9 +++++++++ doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h | 2 ++ doc/examples/mtapi/mtapi_terminating_condition-snippet.h | 3 +++ doc/examples/mtapi/mtapi_write_back-snippet.h | 3 +++ doc/examples/stl_for_each/function-snippet.h | 1 + doc/examples/stl_for_each/function_define-snippet.h | 3 +++ doc/examples/stl_for_each/functor-snippet.h | 1 + doc/examples/stl_for_each/functor_define-snippet.h | 5 +++++ doc/examples/stl_for_each/lambda-snippet.h | 2 ++ doc/examples/stl_for_each/manual-snippet.h | 3 +++ doc/examples/stl_for_each/setup-snippet.h | 4 ++++ doc/examples/stl_for_each/stl_for_each-fragmented.cc | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/images/embb.jpg | Bin 0 -> 69802 bytes doc/images/embb.png | Bin 0 -> 29299 bytes doc/images/embb.pptx | Bin 0 -> 46944 bytes doc/reference/Doxyfile.in | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/reference/DoxygenHTMLStyle.css | 3 +++ doc/reference/DoxygenLayout.xml | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/reference/embb.dox | 42 ++++++++++++++++++++++++++++++++++++++++++ doc/reference/index.html | 15 +++++++++++++++ doc/tutorial/content/algorithms.tex | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/content/containers.tex | 46 ++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/content/dataflow.tex | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/content/introduction.tex | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/content/mtapi.tex | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/content/preface.tex | 11 +++++++++++ doc/tutorial/customize.tex | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/pics/chicago-square.jpg | Bin 0 -> 990066 bytes doc/tutorial/pics/cover/logo_multicore_expert-center_siemens.png | Bin 0 -> 8913 bytes doc/tutorial/pics/cover/sie_logo_layer_petrol_rgb.png | Bin 0 -> 24537 bytes doc/tutorial/references.bib | 6 ++++++ doc/tutorial/sty/README.txt | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/sty/siemens_core.sty | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/sty/siemens_double.sty | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/sty/siemens_single.sty | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/tutorial/tutorial.tex | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/CMakeLists.txt | 41 +++++++++++++++++++++++++++++++++++++++++ mtapi_c/include/embb/mtapi/c/mtapi.h | 3478 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/include/mtapi.h | 34 ++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_action_t.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_action_t.h | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_alloc.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_alloc.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_attr.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_attr.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_group_t.c | 390 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_group_t.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_id_pool_t.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_id_pool_t.h | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_job_t.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_job_t.h | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_log.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_node_t.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_node_t.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_pool_template-inl.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_pool_template.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_queue_t.c | 504 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_queue_t.h | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_scheduler_t.c | 601 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_scheduler_t.h | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_spinlock_t.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_spinlock_t.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_context_t.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_context_t.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_queue_t.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_queue_t.h | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_t.c | 464 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_t.h | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_task_visitor_function_t.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_thread_context_t.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/embb_mtapi_thread_context_t.h | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_action_attributes_t.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_affinity_t.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_group_attributes_t.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_node_attributes_t.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_queue_attributes_t.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_status_t.h | 41 +++++++++++++++++++++++++++++++++++++++++ mtapi_c/src/mtapi_task_attributes_t.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_config.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_group.cc | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_group.h | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_init_finalize.cc | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_init_finalize.h | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_queue.cc | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_queue.h | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_task.cc | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/embb_mtapi_test_task.h | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_c/test/main.cc | 45 +++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/CMakeLists.txt | 35 +++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/action.h | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/affinity.h | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/continuation.h | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/group.h | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/mtapi.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/node.h | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/queue.h | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/task.h | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/include/embb/mtapi/taskcontext.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/affinity.cc | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/continuation.cc | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/continuationstage.h | 43 +++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/group.cc | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/node.cc | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/queue.cc | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/task.cc | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/src/taskcontext.cc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/main.cc | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_config.h | 36 ++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_group.cc | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_group.h | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_queue.cc | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_queue.h | 40 ++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_task.cc | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mtapi_cpp/test/mtapi_cpp_test_task.h | 40 ++++++++++++++++++++++++++++++++++++++++ partest.tar | Bin 0 -> 161792 bytes scripts/create_tarball.sh | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/insert_license.sh | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/license.txt | 26 ++++++++++++++++++++++++++ scripts/license_scripts.txt | 24 ++++++++++++++++++++++++ scripts/remove_license.sh | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/run_cpplint.sh | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/run_tests_cygwin.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ scripts/run_tests_linux.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ scripts/run_tests_windows.bat | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 443 files changed, 53906 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CMakeCommon/CheckEnableTests.cmake create mode 100644 CMakeCommon/CopyInstallFiles.cmake create mode 100644 CMakeCommon/CreateDoxygenDocumentationTarget.cmake create mode 100644 CMakeCommon/GroupSourcesMSVC.cmake create mode 100644 CMakeCommon/SetCompilerFlags.cmake create mode 100644 CMakeCommon/SetInstallPaths.cmake create mode 100644 CMakeLists.txt create mode 100644 COPYING.txt create mode 100644 README.txt create mode 100644 algorithms_cpp/CMakeLists.txt create mode 100644 algorithms_cpp/include/embb/algorithms/algorithms.h create mode 100644 algorithms_cpp/include/embb/algorithms/count.h create mode 100644 algorithms_cpp/include/embb/algorithms/execution_policy.h create mode 100644 algorithms_cpp/include/embb/algorithms/for_each.h create mode 100644 algorithms_cpp/include/embb/algorithms/identity.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/count-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/partition-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/partition.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/internal/scan-inl.h create mode 100644 algorithms_cpp/include/embb/algorithms/invoke.h create mode 100644 algorithms_cpp/include/embb/algorithms/merge_sort.h create mode 100644 algorithms_cpp/include/embb/algorithms/quick_sort.h create mode 100644 algorithms_cpp/include/embb/algorithms/reduce.h create mode 100644 algorithms_cpp/include/embb/algorithms/scan.h create mode 100644 algorithms_cpp/include/embb/algorithms/zip_iterator.h create mode 100644 algorithms_cpp/src/dummy.cc create mode 100644 algorithms_cpp/src/execution_policy.cc create mode 100644 algorithms_cpp/test/count_test.cc create mode 100644 algorithms_cpp/test/count_test.h create mode 100644 algorithms_cpp/test/for_each_test.cc create mode 100644 algorithms_cpp/test/for_each_test.h create mode 100644 algorithms_cpp/test/invoke_test.cc create mode 100644 algorithms_cpp/test/invoke_test.h create mode 100644 algorithms_cpp/test/main.cc create mode 100644 algorithms_cpp/test/merge_sort_test.cc create mode 100644 algorithms_cpp/test/merge_sort_test.h create mode 100644 algorithms_cpp/test/partitioner_test.cc create mode 100644 algorithms_cpp/test/partitioner_test.h create mode 100644 algorithms_cpp/test/quick_sort_test.cc create mode 100644 algorithms_cpp/test/quick_sort_test.h create mode 100644 algorithms_cpp/test/reduce_test.cc create mode 100644 algorithms_cpp/test/reduce_test.h create mode 100644 algorithms_cpp/test/scan_test.cc create mode 100644 algorithms_cpp/test/scan_test.h create mode 100644 algorithms_cpp/test/zip_iterator_test.cc create mode 100644 algorithms_cpp/test/zip_iterator_test.h create mode 100644 base_c/CMakeLists.txt create mode 100644 base_c/include/embb/base/c/atomic.h create mode 100644 base_c/include/embb/base/c/base.h create mode 100644 base_c/include/embb/base/c/condition_variable.h create mode 100644 base_c/include/embb/base/c/core_set.h create mode 100644 base_c/include/embb/base/c/counter.h create mode 100644 base_c/include/embb/base/c/duration.h create mode 100644 base_c/include/embb/base/c/errors.h create mode 100644 base_c/include/embb/base/c/internal/atomic/and_assign.h create mode 100644 base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in create mode 100644 base_c/include/embb/base/c/internal/atomic/atomic_variables.h create mode 100644 base_c/include/embb/base/c/internal/atomic/compare_and_swap.h create mode 100644 base_c/include/embb/base/c/internal/atomic/fetch_and_add.h create mode 100644 base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h create mode 100644 base_c/include/embb/base/c/internal/atomic/load.h create mode 100644 base_c/include/embb/base/c/internal/atomic/memory_barrier.h create mode 100644 base_c/include/embb/base/c/internal/atomic/or_assign.h create mode 100644 base_c/include/embb/base/c/internal/atomic/store.h create mode 100644 base_c/include/embb/base/c/internal/atomic/swap.h create mode 100644 base_c/include/embb/base/c/internal/atomic/xor_assign.h create mode 100644 base_c/include/embb/base/c/internal/bitset.h create mode 100644 base_c/include/embb/base/c/internal/cmake_config.h.in create mode 100644 base_c/include/embb/base/c/internal/config.h create mode 100644 base_c/include/embb/base/c/internal/macro_helper.h create mode 100644 base_c/include/embb/base/c/internal/platform.h create mode 100644 base_c/include/embb/base/c/internal/thread_index.h create mode 100644 base_c/include/embb/base/c/internal/unused.h create mode 100644 base_c/include/embb/base/c/log.h create mode 100644 base_c/include/embb/base/c/memory_allocation.h create mode 100644 base_c/include/embb/base/c/mutex.h create mode 100644 base_c/include/embb/base/c/thread.h create mode 100644 base_c/include/embb/base/c/thread_specific_storage.h create mode 100644 base_c/include/embb/base/c/time.h create mode 100644 base_c/src/atomic.c create mode 100644 base_c/src/atomicfunc_32.asm create mode 100644 base_c/src/atomicfunc_64.asm create mode 100644 base_c/src/condition_variable.c create mode 100644 base_c/src/core_set.c create mode 100644 base_c/src/counter.c create mode 100644 base_c/src/duration.c create mode 100644 base_c/src/internal/thread_index.c create mode 100644 base_c/src/log.c create mode 100644 base_c/src/memory_allocation.c create mode 100644 base_c/src/mutex.c create mode 100644 base_c/src/thread.c create mode 100644 base_c/src/thread_specific_storage.c create mode 100644 base_c/src/time.c create mode 100644 base_c/test/README.txt create mode 100644 base_c/test/alloc_test.cc create mode 100644 base_c/test/alloc_test.h create mode 100644 base_c/test/condition_var_test.cc create mode 100644 base_c/test/condition_var_test.h create mode 100644 base_c/test/core_set_test.cc create mode 100644 base_c/test/core_set_test.h create mode 100644 base_c/test/counter_test.cc create mode 100644 base_c/test/counter_test.h create mode 100644 base_c/test/duration_test.cc create mode 100644 base_c/test/duration_test.h create mode 100644 base_c/test/main.cc create mode 100644 base_c/test/mutex_test.cc create mode 100644 base_c/test/mutex_test.h create mode 100644 base_c/test/thread_index_test.cc create mode 100644 base_c/test/thread_index_test.h create mode 100644 base_c/test/thread_specific_storage_test.cc create mode 100644 base_c/test/thread_specific_storage_test.h create mode 100644 base_c/test/thread_test.cc create mode 100644 base_c/test/thread_test.h create mode 100644 base_c/test/time_test.cc create mode 100644 base_c/test/time_test.h create mode 100644 base_cpp/CMakeLists.txt create mode 100644 base_cpp/include/embb/base/atomic.h create mode 100644 base_cpp/include/embb/base/base.h create mode 100644 base_cpp/include/embb/base/condition_variable.h create mode 100644 base_cpp/include/embb/base/core_set.h create mode 100644 base_cpp/include/embb/base/duration.h create mode 100644 base_cpp/include/embb/base/exceptions.h create mode 100644 base_cpp/include/embb/base/function.h create mode 100644 base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h create mode 100644 base_cpp/include/embb/base/internal/atomic/atomic_base.h create mode 100644 base_cpp/include/embb/base/internal/atomic/atomic_implementation.h create mode 100644 base_cpp/include/embb/base/internal/atomic/atomic_integer.h create mode 100644 base_cpp/include/embb/base/internal/atomic/atomic_pointer.h create mode 100644 base_cpp/include/embb/base/internal/atomic/atomic_utility.h create mode 100644 base_cpp/include/embb/base/internal/cmake_config.h.in create mode 100644 base_cpp/include/embb/base/internal/condition_variable-inl.h create mode 100644 base_cpp/include/embb/base/internal/config.h create mode 100644 base_cpp/include/embb/base/internal/duration-inl.h create mode 100644 base_cpp/include/embb/base/internal/function0.h create mode 100644 base_cpp/include/embb/base/internal/function1.h create mode 100644 base_cpp/include/embb/base/internal/function2.h create mode 100644 base_cpp/include/embb/base/internal/function3.h create mode 100644 base_cpp/include/embb/base/internal/function4.h create mode 100644 base_cpp/include/embb/base/internal/function5.h create mode 100644 base_cpp/include/embb/base/internal/functionT.h create mode 100644 base_cpp/include/embb/base/internal/memory_allocation-inl.h create mode 100644 base_cpp/include/embb/base/internal/mutex-inl.h create mode 100644 base_cpp/include/embb/base/internal/nil.h create mode 100644 base_cpp/include/embb/base/internal/platform.h create mode 100644 base_cpp/include/embb/base/internal/thread-inl.h create mode 100644 base_cpp/include/embb/base/internal/thread_closures.h create mode 100644 base_cpp/include/embb/base/internal/thread_specific_storage-inl.h create mode 100644 base_cpp/include/embb/base/memory_allocation.h create mode 100644 base_cpp/include/embb/base/mutex.h create mode 100644 base_cpp/include/embb/base/thread.h create mode 100644 base_cpp/include/embb/base/thread_specific_storage.h create mode 100644 base_cpp/include/embb/base/time.h create mode 100644 base_cpp/src/condition_variable.cc create mode 100644 base_cpp/src/core_set.cc create mode 100644 base_cpp/src/duration.cc create mode 100644 base_cpp/src/function.cc create mode 100644 base_cpp/src/functionN.lua create mode 100644 base_cpp/src/memory_allocation.cc create mode 100644 base_cpp/src/mutex.cc create mode 100644 base_cpp/src/thread.cc create mode 100644 base_cpp/src/time.cc create mode 100644 base_cpp/test/atomic_test.cc create mode 100644 base_cpp/test/atomic_test.h create mode 100644 base_cpp/test/condition_var_test.cc create mode 100644 base_cpp/test/condition_var_test.h create mode 100644 base_cpp/test/core_set_test.cc create mode 100644 base_cpp/test/core_set_test.h create mode 100644 base_cpp/test/duration_test.cc create mode 100644 base_cpp/test/duration_test.h create mode 100644 base_cpp/test/main.cc create mode 100644 base_cpp/test/memory_allocation_test.cc create mode 100644 base_cpp/test/memory_allocation_test.h create mode 100644 base_cpp/test/mutex_test.cc create mode 100644 base_cpp/test/mutex_test.h create mode 100644 base_cpp/test/thread_specific_storage_test.cc create mode 100644 base_cpp/test/thread_specific_storage_test.h create mode 100644 base_cpp/test/thread_test.cc create mode 100644 base_cpp/test/thread_test.h create mode 100644 base_cpp/test/time_test.h create mode 100644 containers_cpp/CMakeLists.txt create mode 100644 containers_cpp/include/embb/containers/containers.h create mode 100644 containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/hazard_pointer.h create mode 100644 containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/object_pool-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h create mode 100644 containers_cpp/include/embb/containers/lock_free_mpmc_queue.h create mode 100644 containers_cpp/include/embb/containers/lock_free_stack.h create mode 100644 containers_cpp/include/embb/containers/lock_free_tree_value_pool.h create mode 100644 containers_cpp/include/embb/containers/object_pool.h create mode 100644 containers_cpp/include/embb/containers/wait_free_array_value_pool.h create mode 100644 containers_cpp/include/embb/containers/wait_free_spsc_queue.h create mode 100644 containers_cpp/src/dummy.cc create mode 100644 containers_cpp/test/hazard_pointer_test.cc create mode 100644 containers_cpp/test/hazard_pointer_test.h create mode 100644 containers_cpp/test/main.cc create mode 100644 containers_cpp/test/object_pool_test-inl.h create mode 100644 containers_cpp/test/object_pool_test.cc create mode 100644 containers_cpp/test/object_pool_test.h create mode 100644 containers_cpp/test/pool_test-inl.h create mode 100644 containers_cpp/test/pool_test.h create mode 100644 containers_cpp/test/queue_test-inl.h create mode 100644 containers_cpp/test/queue_test.h create mode 100644 containers_cpp/test/stack_test-inl.h create mode 100644 containers_cpp/test/stack_test.h create mode 100644 dataflow_cpp/CMakeLists.txt create mode 100644 dataflow_cpp/include/embb/dataflow/dataflow.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/action.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/clock_listener.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/constant_source.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/in.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/inputs.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/node.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/out.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/outputs.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/process.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/process_executor.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/scheduler.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/select.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/signal.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/sink.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/sink_executor.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/source.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/source_executor.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/spinlock.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/switch.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/tuple.h create mode 100644 dataflow_cpp/include/embb/dataflow/internal/typelist.h create mode 100644 dataflow_cpp/include/embb/dataflow/network.h create mode 100644 dataflow_cpp/src/dummy.cc create mode 100644 dataflow_cpp/test/dataflow_cpp_test_simple.cc create mode 100644 dataflow_cpp/test/dataflow_cpp_test_simple.h create mode 100644 dataflow_cpp/test/dataflow_cpp_test_tuple.cc create mode 100644 dataflow_cpp/test/dataflow_cpp_test_tuple.h create mode 100644 dataflow_cpp/test/main.cc create mode 100644 doc/examples/CMakeLists.txt create mode 100644 doc/examples/algorithms/counting/count-snippet.h create mode 100644 doc/examples/algorithms/counting/count_if-snippet.h create mode 100644 doc/examples/algorithms/counting/counting-fragmented.cc create mode 100644 doc/examples/algorithms/counting/setup-snippet.h create mode 100644 doc/examples/algorithms/for_each/doubling-snippet.h create mode 100644 doc/examples/algorithms/for_each/doubling_zip-snippet.h create mode 100644 doc/examples/algorithms/for_each/for_each-fragmented.cc create mode 100644 doc/examples/algorithms/for_each/setup_zip-snippet.h create mode 100644 doc/examples/algorithms/invoke/invocation-snippet.h create mode 100644 doc/examples/algorithms/invoke/invoke-fragmented.cc create mode 100644 doc/examples/algorithms/invoke/packages-snippet.h create mode 100644 doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h create mode 100644 doc/examples/algorithms/invoke/quick_sort-snippet.h create mode 100644 doc/examples/algorithms/reduce/dot_product-snippet.h create mode 100644 doc/examples/algorithms/reduce/parallel-snippet.h create mode 100644 doc/examples/algorithms/reduce/range_init-snippet.h create mode 100644 doc/examples/algorithms/reduce/reduce-fragmented.cc create mode 100644 doc/examples/algorithms/reduce/second_range_init-snippet.h create mode 100644 doc/examples/algorithms/reduce/sequential-snippet.h create mode 100644 doc/examples/algorithms/scan/prefix_sum-snippet.h create mode 100644 doc/examples/algorithms/scan/scan-fragmented.cc create mode 100644 doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h create mode 100644 doc/examples/algorithms/scan/setup-snippet.h create mode 100644 doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h create mode 100644 doc/examples/algorithms/sorting/quick_sort-snippet.h create mode 100644 doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h create mode 100644 doc/examples/algorithms/sorting/range_define-snippet.h create mode 100644 doc/examples/algorithms/sorting/sorting-fragmented.cc create mode 100644 doc/examples/containers/object_pool-fragmented.cc create mode 100644 doc/examples/containers/object_pool-snippet.h create mode 100644 doc/examples/containers/object_pool_2-snippet.h create mode 100644 doc/examples/containers/queues-fragmented.cc create mode 100644 doc/examples/containers/queues-snippet.h create mode 100644 doc/examples/containers/stack-fragmented.cc create mode 100644 doc/examples/containers/stack-snippet.h create mode 100644 doc/examples/dataflow/dataflow_add-snippet.h create mode 100644 doc/examples/dataflow/dataflow_comparator-snippet.h create mode 100644 doc/examples/dataflow/dataflow_connect-snippet.h create mode 100644 doc/examples/dataflow/dataflow_consumer-snippet.h create mode 100644 doc/examples/dataflow/dataflow_declare_add_sources-snippet.h create mode 100644 doc/examples/dataflow/dataflow_declare_replace-snippet.h create mode 100644 doc/examples/dataflow/dataflow_declare_sink-snippet.h create mode 100644 doc/examples/dataflow/dataflow_declare_source-snippet.h create mode 100644 doc/examples/dataflow/dataflow_include-snippet.h create mode 100644 doc/examples/dataflow/dataflow_linear-fragmented.cc create mode 100644 doc/examples/dataflow/dataflow_network-snippet.h create mode 100644 doc/examples/dataflow/dataflow_nonlinear-fragmented.cc create mode 100644 doc/examples/dataflow/dataflow_producer-snippet.h create mode 100644 doc/examples/dataflow/dataflow_replace_function-snippet.h create mode 100644 doc/examples/dataflow/dataflow_run-snippet.h create mode 100644 doc/examples/dataflow/dataflow_sink_function-snippet.h create mode 100644 doc/examples/dataflow/dataflow_source_function-snippet.h create mode 100644 doc/examples/insert_snippets.py create mode 100644 doc/examples/main.cc create mode 100644 doc/examples/mtapi/mtapi_c-fragmented.cc create mode 100644 doc/examples/mtapi/mtapi_c_action_signature-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_calc_direct-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_calc_task-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_finalize-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_initialize-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_register_action-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_start_task-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h create mode 100644 doc/examples/mtapi/mtapi_c_wait_task-snippet.h create mode 100644 doc/examples/mtapi/mtapi_check_status-snippet.h create mode 100644 doc/examples/mtapi/mtapi_cpp-fragmented.cc create mode 100644 doc/examples/mtapi/mtapi_cpp_action_signature-snippet.h create mode 100644 doc/examples/mtapi/mtapi_cpp_calc_direct-snippet.h create mode 100644 doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h create mode 100644 doc/examples/mtapi/mtapi_cpp_get_node-snippet.h create mode 100644 doc/examples/mtapi/mtapi_cpp_start_task-snippet.h create mode 100644 doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h create mode 100644 doc/examples/mtapi/mtapi_terminating_condition-snippet.h create mode 100644 doc/examples/mtapi/mtapi_write_back-snippet.h create mode 100644 doc/examples/stl_for_each/function-snippet.h create mode 100644 doc/examples/stl_for_each/function_define-snippet.h create mode 100644 doc/examples/stl_for_each/functor-snippet.h create mode 100644 doc/examples/stl_for_each/functor_define-snippet.h create mode 100644 doc/examples/stl_for_each/lambda-snippet.h create mode 100644 doc/examples/stl_for_each/manual-snippet.h create mode 100644 doc/examples/stl_for_each/setup-snippet.h create mode 100644 doc/examples/stl_for_each/stl_for_each-fragmented.cc create mode 100644 doc/images/embb.jpg create mode 100644 doc/images/embb.png create mode 100644 doc/images/embb.pptx create mode 100644 doc/reference/Doxyfile.in create mode 100644 doc/reference/DoxygenHTMLStyle.css create mode 100644 doc/reference/DoxygenLayout.xml create mode 100644 doc/reference/embb.dox create mode 100644 doc/reference/index.html create mode 100644 doc/tutorial/content/algorithms.tex create mode 100644 doc/tutorial/content/containers.tex create mode 100644 doc/tutorial/content/dataflow.tex create mode 100644 doc/tutorial/content/introduction.tex create mode 100644 doc/tutorial/content/mtapi.tex create mode 100644 doc/tutorial/content/preface.tex create mode 100644 doc/tutorial/customize.tex create mode 100644 doc/tutorial/pics/chicago-square.jpg create mode 100644 doc/tutorial/pics/cover/logo_multicore_expert-center_siemens.png create mode 100644 doc/tutorial/pics/cover/sie_logo_layer_petrol_rgb.png create mode 100644 doc/tutorial/references.bib create mode 100644 doc/tutorial/sty/README.txt create mode 100644 doc/tutorial/sty/siemens_core.sty create mode 100644 doc/tutorial/sty/siemens_double.sty create mode 100644 doc/tutorial/sty/siemens_single.sty create mode 100644 doc/tutorial/tutorial.tex create mode 100644 mtapi_c/CMakeLists.txt create mode 100644 mtapi_c/include/embb/mtapi/c/mtapi.h create mode 100644 mtapi_c/include/mtapi.h create mode 100644 mtapi_c/src/embb_mtapi_action_t.c create mode 100644 mtapi_c/src/embb_mtapi_action_t.h create mode 100644 mtapi_c/src/embb_mtapi_alloc.c create mode 100644 mtapi_c/src/embb_mtapi_alloc.h create mode 100644 mtapi_c/src/embb_mtapi_attr.c create mode 100644 mtapi_c/src/embb_mtapi_attr.h create mode 100644 mtapi_c/src/embb_mtapi_group_t.c create mode 100644 mtapi_c/src/embb_mtapi_group_t.h create mode 100644 mtapi_c/src/embb_mtapi_id_pool_t.c create mode 100644 mtapi_c/src/embb_mtapi_id_pool_t.h create mode 100644 mtapi_c/src/embb_mtapi_job_t.c create mode 100644 mtapi_c/src/embb_mtapi_job_t.h create mode 100644 mtapi_c/src/embb_mtapi_log.h create mode 100644 mtapi_c/src/embb_mtapi_node_t.c create mode 100644 mtapi_c/src/embb_mtapi_node_t.h create mode 100644 mtapi_c/src/embb_mtapi_pool_template-inl.h create mode 100644 mtapi_c/src/embb_mtapi_pool_template.h create mode 100644 mtapi_c/src/embb_mtapi_queue_t.c create mode 100644 mtapi_c/src/embb_mtapi_queue_t.h create mode 100644 mtapi_c/src/embb_mtapi_scheduler_t.c create mode 100644 mtapi_c/src/embb_mtapi_scheduler_t.h create mode 100644 mtapi_c/src/embb_mtapi_spinlock_t.c create mode 100644 mtapi_c/src/embb_mtapi_spinlock_t.h create mode 100644 mtapi_c/src/embb_mtapi_task_context_t.c create mode 100644 mtapi_c/src/embb_mtapi_task_context_t.h create mode 100644 mtapi_c/src/embb_mtapi_task_queue_t.c create mode 100644 mtapi_c/src/embb_mtapi_task_queue_t.h create mode 100644 mtapi_c/src/embb_mtapi_task_t.c create mode 100644 mtapi_c/src/embb_mtapi_task_t.h create mode 100644 mtapi_c/src/embb_mtapi_task_visitor_function_t.h create mode 100644 mtapi_c/src/embb_mtapi_thread_context_t.c create mode 100644 mtapi_c/src/embb_mtapi_thread_context_t.h create mode 100644 mtapi_c/src/mtapi_action_attributes_t.c create mode 100644 mtapi_c/src/mtapi_affinity_t.c create mode 100644 mtapi_c/src/mtapi_group_attributes_t.c create mode 100644 mtapi_c/src/mtapi_node_attributes_t.c create mode 100644 mtapi_c/src/mtapi_queue_attributes_t.c create mode 100644 mtapi_c/src/mtapi_status_t.h create mode 100644 mtapi_c/src/mtapi_task_attributes_t.c create mode 100644 mtapi_c/test/embb_mtapi_test_config.h create mode 100644 mtapi_c/test/embb_mtapi_test_group.cc create mode 100644 mtapi_c/test/embb_mtapi_test_group.h create mode 100644 mtapi_c/test/embb_mtapi_test_init_finalize.cc create mode 100644 mtapi_c/test/embb_mtapi_test_init_finalize.h create mode 100644 mtapi_c/test/embb_mtapi_test_queue.cc create mode 100644 mtapi_c/test/embb_mtapi_test_queue.h create mode 100644 mtapi_c/test/embb_mtapi_test_task.cc create mode 100644 mtapi_c/test/embb_mtapi_test_task.h create mode 100644 mtapi_c/test/main.cc create mode 100644 mtapi_cpp/CMakeLists.txt create mode 100644 mtapi_cpp/include/embb/mtapi/action.h create mode 100644 mtapi_cpp/include/embb/mtapi/affinity.h create mode 100644 mtapi_cpp/include/embb/mtapi/continuation.h create mode 100644 mtapi_cpp/include/embb/mtapi/group.h create mode 100644 mtapi_cpp/include/embb/mtapi/mtapi.h create mode 100644 mtapi_cpp/include/embb/mtapi/node.h create mode 100644 mtapi_cpp/include/embb/mtapi/queue.h create mode 100644 mtapi_cpp/include/embb/mtapi/task.h create mode 100644 mtapi_cpp/include/embb/mtapi/taskcontext.h create mode 100644 mtapi_cpp/src/affinity.cc create mode 100644 mtapi_cpp/src/continuation.cc create mode 100644 mtapi_cpp/src/continuationstage.h create mode 100644 mtapi_cpp/src/group.cc create mode 100644 mtapi_cpp/src/node.cc create mode 100644 mtapi_cpp/src/queue.cc create mode 100644 mtapi_cpp/src/task.cc create mode 100644 mtapi_cpp/src/taskcontext.cc create mode 100644 mtapi_cpp/test/main.cc create mode 100644 mtapi_cpp/test/mtapi_cpp_test_config.h create mode 100644 mtapi_cpp/test/mtapi_cpp_test_group.cc create mode 100644 mtapi_cpp/test/mtapi_cpp_test_group.h create mode 100644 mtapi_cpp/test/mtapi_cpp_test_queue.cc create mode 100644 mtapi_cpp/test/mtapi_cpp_test_queue.h create mode 100644 mtapi_cpp/test/mtapi_cpp_test_task.cc create mode 100644 mtapi_cpp/test/mtapi_cpp_test_task.h create mode 100644 partest.tar create mode 100644 scripts/create_tarball.sh create mode 100644 scripts/insert_license.sh create mode 100644 scripts/license.txt create mode 100644 scripts/license_scripts.txt create mode 100644 scripts/remove_license.sh create mode 100644 scripts/run_cpplint.sh create mode 100644 scripts/run_tests_cygwin.sh create mode 100644 scripts/run_tests_linux.sh create mode 100644 scripts/run_tests_windows.bat diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5a0a63e --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa84d68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# The following files are ignored by Git + +*.TMP +*.o +*.exe +build* +*~ +*# + +# Eclipse CDT project files +.project +.cproject +.settings/ + +# Latex output files +*.aux +*.log +*.out +*.pdf +*.toc +*.synctex.gz diff --git a/CMakeCommon/CheckEnableTests.cmake b/CMakeCommon/CheckEnableTests.cmake new file mode 100644 index 0000000..b1dddfd --- /dev/null +++ b/CMakeCommon/CheckEnableTests.cmake @@ -0,0 +1,50 @@ +# Copyright (c) 2014, 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. + +## CheckPartestInstall: Checks if partest install path is defined +# Reads: PARTEST_INSTALL_PREFIX +# Sets: partest_includepath_var +# partest_libpath_var +# +function(CheckPartestInstall BUILD_TESTS _partest_includepath _partest_libpath) + if (BUILD_TESTS STREQUAL ON) + set (PARTEST_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/partest_install PARENT_SCOPE) + set (PARTEST_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/partest_install) + + set(${_partest_includepath} ${PARTEST_INSTALL_PREFIX}/include PARENT_SCOPE) + set(${_partest_libpath} ${PARTEST_INSTALL_PREFIX}/lib PARENT_SCOPE) + + set (tar_extraction_directory ${CMAKE_CURRENT_BINARY_DIR}) + message("-- Extracting partest to directoy ${tar_extraction_directory}/partest") + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xf ${PROJECT_SOURCE_DIR}/partest.tar + WORKING_DIRECTORY ${tar_extraction_directory} + ) + + if (TARGET PARTEST) + else() + add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/partest ${CMAKE_CURRENT_BINARY_DIR}/partest_build) + endif() + endif() +endfunction() diff --git a/CMakeCommon/CopyInstallFiles.cmake b/CMakeCommon/CopyInstallFiles.cmake new file mode 100644 index 0000000..00c4ef1 --- /dev/null +++ b/CMakeCommon/CopyInstallFiles.cmake @@ -0,0 +1,39 @@ +# Copyright (c) 2014, 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. + +# Copies the binary target bin after build to the destination. +# +function(CopyBin + BIN bin + DEST destination + ) + get_target_property(bin_full_name ${bin} LOCATION) + + add_custom_command( + TARGET ${bin} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${bin_full_name} ${destination} + #COMMENT "Copying ${bin} to ${destination}" + ) +endfunction() diff --git a/CMakeCommon/CreateDoxygenDocumentationTarget.cmake b/CMakeCommon/CreateDoxygenDocumentationTarget.cmake new file mode 100644 index 0000000..c3c759d --- /dev/null +++ b/CMakeCommon/CreateDoxygenDocumentationTarget.cmake @@ -0,0 +1,52 @@ +# Copyright (c) 2014, 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. + +## DOXYGEN DOCUMENTATION +# +# Is only generated when option(.* ON) is set... +function (CreateDoxygenDocumentationTarget) + option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" ON) + if(BUILD_DOCUMENTATION) + FIND_PACKAGE(Doxygen) + #if (NOT DOXYGEN_FOUND) + # message(FATAL_ERROR + # "Doxygen is needed to build the documentation. Please install it correctly") + #endif() + #-- Configure the Template Doxyfile for our specific project + configure_file(doc/reference/Doxyfile.in + ${PROJECT_BINARY_DIR}/Doxyfile @ONLY IMMEDIATE) + #-- Add a custom target to run Doxygen when ever the project is built + if (TARGET doxygen) + # Do nothing, since the repeated adding causes an error + else() + add_custom_target ( + doxygen + #ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile + SOURCES ${PROJECT_BINARY_DIR}/Doxyfile) + # IF you do NOT want the documentation to be generated EVERY time you build the project + # then leave out the 'ALL' keyword from the above command. + endif() + endif() +endfunction() diff --git a/CMakeCommon/GroupSourcesMSVC.cmake b/CMakeCommon/GroupSourcesMSVC.cmake new file mode 100644 index 0000000..2568d00 --- /dev/null +++ b/CMakeCommon/GroupSourcesMSVC.cmake @@ -0,0 +1,41 @@ +# Copyright (c) 2014, 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. + +## Visual Studio Project Adaptions +# +# Make a browsable file hierarchy when opening +# the generated visual c++ solution project in visual studio +function(GroupSourcesMSVC directory) + file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${directory} ${PROJECT_SOURCE_DIR}/${directory}/*) + foreach(child ${children}) + if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${directory}/${child}) + GroupSourcesMSVC(${directory}/${child}) + else() + string(REPLACE "/" "\\" groupname ${directory}) + string(REPLACE "src" "Sources" groupname ${groupname}) + string(REPLACE "include" "Includes" groupname ${groupname}) + source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${directory}/${child}) + endif() + endforeach() +endfunction() diff --git a/CMakeCommon/SetCompilerFlags.cmake b/CMakeCommon/SetCompilerFlags.cmake new file mode 100644 index 0000000..60b66da --- /dev/null +++ b/CMakeCommon/SetCompilerFlags.cmake @@ -0,0 +1,94 @@ +# Copyright (c) 2014, 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. + +## DETERMINE COMPILER AND LINKER FLAGS +# +function(SetGNUCompilerFlags compiler_libs) + if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(compiler_libs pthread rt PARENT_SCOPE) + # -Wall -> All warnings + # -Wextra -> Even more warnings + # -Werror -> Warnings are errors + set(warning_flags "-Wall -Wextra") + if (WARNINGS_ARE_ERRORS STREQUAL ON) + set(warning_flags "${warning_flags} -Werror") + endif() + if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -std=c99 ${warning_flags}" + PARENT_SCOPE) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DEMBB_DEBUG" + PARENT_SCOPE) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG" + PARENT_SCOPE) + endif() + if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++03 ${warning_flags}" + PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DEMBB_DEBUG" + PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG" + PARENT_SCOPE) + endif() + endif() +endfunction() + +function(SetVisualStudioCompilerFlags) + if(MSVC) + # /Wall -> All warnings + # /WX -> Warnings as errors + # + # Globally deactivated warning numbers (by flag \wd#): + # 4820 -> Deactivates warning "2/4/... bytes padding added after some type" + # 4514 -> Deactivates warning "'fct': unreferenced inline function has + # been removed" + # 4668 -> Deactivates warning "'macro' is not defined as preprocessor macro, + # replacing with '0' in #if/#elif" + # 4710 -> Deactivates warning "Function not inlined" + # 4350 -> Deactivates warning "Behavior change ...", which warns a + # behavior change since VS C++ 2002, when using R-values as + # L-value arguments. This warning occurs a lot in the VC libs. + # 4571 -> Deactivates warning that when compiling with /EHs, + # a catch(...) block will not catch a structured exception. + # 4625 -> Deactivates warning for derived classes + # when copy constructor could not be generated because + # a base class copy constructor is inaccessible + # 4626 -> Deactivates warning for derived classes + # when assignment operator could not be generated because + # a base class assignment operator is inaccessible + # 4711 -> Deactivates warning for inline functions + # This is only an informational warning about which functions + # have been inlined by the compiler. + # 4255 -> Deactivates warning "no function prototype given converting () to (void)" + # + # Locally suppressed warnings (should not be globally suppressed): + # 4640 -> Information that local static variable initialization is not + # thread-safe. + set(warning_flags "/Wall /wd4820 /wd4514 /wd4668 /wd4710 /wd4350 /wd4571 /wd4625 /wd4626 /wd4711 /wd4255") + if (WARNINGS_ARE_ERRORS STREQUAL ON) + set(warning_flags "${warning_flags} /WX") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warning_flags}" PARENT_SCOPE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${warning_flags}" PARENT_SCOPE) + endif() +endfunction() diff --git a/CMakeCommon/SetInstallPaths.cmake b/CMakeCommon/SetInstallPaths.cmake new file mode 100644 index 0000000..af791ff --- /dev/null +++ b/CMakeCommon/SetInstallPaths.cmake @@ -0,0 +1,77 @@ +# Copyright (c) 2014, 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. + +## Sets the install base path for headers, libraries, and the documentation +# +function(SetInstallPaths) + if (DEFINED INSTALL_PREFIX) + # User given install path given when calling cmake as "-DINSTALL_PREFIX=...". + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}) + set(INSTALL_PREFIX_DOCS "${CMAKE_INSTALL_PREFIX}/doc") + else() + # Default install path is in build directory. + if (DEFINED UNIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") + set(INSTALL_PREFIX_DOCS "/usr/local/share/doc/${CMAKE_PROJECT_NAME}-${EMBB_BASE_VERSION_MAJOR}.${EMBB_BASE_VERSION_MINOR}") + else() + file(TO_CMAKE_PATH "$ENV{ProgramFiles}" _PROG_FILES) # 32-bit dir on win32, useless to us on win64 + file(TO_CMAKE_PATH "$ENV{ProgramFiles(x86)}" _PROG_FILES_X86) # 32-bit dir: only set on win64 + file(TO_CMAKE_PATH "$ENV{ProgramW6432}" _PROG_FILES_W6432) # 64-bit dir: only set on win64 + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # 64-bit build on win64 + set(_PROGFILESDIR "${_PROG_FILES_W6432}") + else() + if(_PROG_FILES_W6432) + # 32-bit build on win64 + set(_PROGFILESDIR "${_PROG_FILES_X86}") + else() + # 32-bit build on win32 + set(_PROGFILESDIR "${_PROG_FILES}") + endif() + endif() + set(CMAKE_INSTALL_PREFIX "${_PROGFILESDIR}/${CMAKE_PROJECT_NAME}-${EMBB_BASE_VERSION_MAJOR}.${EMBB_BASE_VERSION_MINOR}.${EMBB_BASE_VERSION_PATCH}") + + set(INSTALL_PREFIX_DOCS "${CMAKE_INSTALL_PREFIX}/doc") + message(${INSTALL_PREFIX_DOCS}) + IF (WIN32) + STRING(REPLACE "\\" "\\\\" CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ) + STRING(REPLACE "/" "\\\\" CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} ) + STRING(REPLACE "\\" "\\\\" INSTALL_PREFIX_DOCS ${INSTALL_PREFIX_DOCS} ) + STRING(REPLACE "/" "\\\\" INSTALL_PREFIX_DOCS ${INSTALL_PREFIX_DOCS} ) + endif() + endif() + endif() + + set(INSTALL_PREFIX ${INSTALL_PREFIX} PARENT_SCOPE) + set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} PARENT_SCOPE) + set(INSTALL_PREFIX_DOCS ${INSTALL_PREFIX_DOCS} PARENT_SCOPE) + + message("-- Installation path is ${CMAKE_INSTALL_PREFIX}") + if (INSTALL_DOCS STREQUAL "ON") + message("-- Installation path for documentation is ${INSTALL_PREFIX_DOCS}") + else() + message("-- Disabled installation of documentation") + endif() +endfunction() diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e68d998 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,144 @@ +# Copyright (c) 2014, 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. + +project (EMBB) +cmake_minimum_required (VERSION 2.8.9) + +# Version number +set (EMBB_BASE_VERSION_MAJOR 0) +set (EMBB_BASE_VERSION_MINOR 2) +set (EMBB_BASE_VERSION_PATCH 0) + +if(NOT CMAKE_BUILD_TYPE) +set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: Debug Release + RelWithDebInfo MinSizeRel." FORCE) +endif(NOT CMAKE_BUILD_TYPE) + +## Command line options +# +# The set option will be converted to uppercase letters by cmake!! --> ON/OFF +# Note that the help string (second argument) cannot be printed by cmake. +# +option(BUILD_TESTS "Specify whether tests should be built" ON) +option(BUILD_EXAMPLES "Specify whether examples should be built" OFF) +option(USE_EXCEPTIONS "Specify whether exceptions should be activated in C++" ON) +option(INSTALL_DOCS "Specify whether Doxygen docs should be installed" ON) +option(WARNINGS_ARE_ERRORS "Specify whether warnings should be treated as errors" OFF) + +## LOCAL INSTALLATION OF SUBPROJECT BINARIES +# +include(CMakeCommon/CopyInstallFiles.cmake) # Needed in all subprojects +set(local_install_dir ${CMAKE_CURRENT_BINARY_DIR}/binaries) + +if (WARNINGS_ARE_ERRORS STREQUAL ON) + message("-- Warnings are treated as errors") + set(EMBB_USE_EXCEPTIONS 1) +else() + message("-- Warnings are not treated as errors (default)") +endif() +message(" (set with command line option -DWARNINGS_ARE_ERRORS=ON/OFF)") + +include(CMakeCommon/SetCompilerFlags.cmake) +SetGNUCompilerFlags(compiler_libs compiler_flags) +SetVisualStudioCompilerFlags(compiler_libs compiler_flags) + +## Exception handling in C++ +# +if (USE_EXCEPTIONS STREQUAL ON) + message("-- Exceptions enabled (default) ") + set(EMBB_USE_EXCEPTIONS 1) +else() + message("-- Exceptions disabled") + set(EMBB_NO_EXCEPTIONS) # A preprocessor define + if (CMAKE_COMPILER_IS_GNUCXX) + LIST(APPEND ${CMAKE_CXX_FLAGS} "-fno-exceptions") + elseif (MSVC) + LIST(FIND ${CMAKE_CXX_FLAGS} "/EHsc" EXCEPTION_FLAG) + if (EXCEPTION_FLAG) + string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_HAS_EXCEPTIONS=0") + endif() +endif() +message(" (set with command line option -DUSE_EXCEPTIONS=ON/OFF)") + +## Copy test execution script to local binaries folder +# +if (DEFINED CYGWIN) + set(test_script_in run_tests_cygwin.sh) + set(test_script_out run_tests.sh) +elseif (DEFINED UNIX) + set(test_script_in run_tests_linux.sh) + set(test_script_out run_tests.sh) +else() + set(test_script_in run_tests_windows.bat) + set(test_script_out run_tests.bat) +endif() +execute_process( + COMMAND ${CMAKE_COMMAND} -E copy ../scripts/${test_script_in} binaries/${test_script_out} + ) + +## Test and Partest build +# +include(CMakeCommon/CheckEnableTests.cmake) +if (BUILD_TESTS STREQUAL ON) + message("-- Building tests enabled (default)") +else() + message("-- Building tests disabled") +endif() +message(" (set with command line option -DBUILD_TESTS=ON/OFF)") +CheckPartestInstall(${BUILD_TESTS} partest_includepath partest_libpath) + +## SUBPROJECTS +# +add_subdirectory(base_c) +add_subdirectory(base_cpp) +add_subdirectory(mtapi_c) +add_subdirectory(mtapi_cpp) +add_subdirectory(containers_cpp) +add_subdirectory(algorithms_cpp) +add_subdirectory(dataflow_cpp) +if (BUILD_EXAMPLES STREQUAL ON) + message("-- Building examples enabled") + add_subdirectory(doc/examples) +else() + message("-- Building examples disabled (default)") +endif() +message(" (set with command line option -DBUILD_EXAMPLES=ON/OFF)") + +## INSTALLATION +# +include(CMakeCommon/SetInstallPaths.cmake) +SetInstallPaths() + +## DOXYGEN +# +include(CMakeCommon/CreateDoxygenDocumentationTarget.cmake) +CreateDoxygenDocumentationTarget() + +if (INSTALL_DOCS STREQUAL ON) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doc/ + DESTINATION ${INSTALL_PREFIX_DOCS} FILES_MATCHING PATTERN "*.*" PATTERN "CMakeLists.txt" EXCLUDE) +endif() diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..11aeca5 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,40 @@ +This project is licensed under the BSD 2-clause license given below. + +The MTAPI header file is copyrighted by the Multicore Association +(http://www.multicore-association.org/) and licensed under the BSD 3-clause +license (see file include\embb\mtapi\c\mtapi.h). + +================================================================================ + +Copyright (c) 2014 Siemens AG. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted to Siemens employees only in order to +develop Siemens owned applications. + +Redistribution and use in source and binary forms, with or without +modification, are NOT permitted to external companies or persons unless they + + * use it to develop applications for Siemens + * commit themselves to not redistribute the software elsewhere. + +Any redistribution must meet the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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 OWNER 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. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e2ddc8b --- /dev/null +++ b/README.txt @@ -0,0 +1,305 @@ +========================================= +Embedded Multicore Building Blocks (EMBB) +========================================= + + +Overview +======== + +The Embedded Multicore Building Blocks (EMBB) are an easy to use yet powerful +and efficient C/C++ library for the development of parallel applications. EMBB +has been specifically designed for embedded systems and the typical +requirements that accompany them, such as real-time capability and constraints +on memory consumption. As a major advantage, low-level operations are hidden +in the library which relieves software developers from the burden of thread +management and synchronization. This not only improves productivity of +parallel software development, but also results in increased reliability and +performance of the applications. + +EMBB is independent of the hardware architecture (x86, ARM, ...) and runs on +various platforms, from small devices to large systems containing numerous +processor cores. It builds on MTAPI, a standardized programming interface for +leveraging task parallelism in embedded systems containing symmetric or +asymmetric multicore processors. A core feature of MTAPI is low-overhead +scheduling of fine-grained tasks among the available cores during runtime. +Unlike existing libraries, EMBB supports task priorities, which allows the +creation of soft real-time systems. Additionally, the scheduling strategy can +be optimized for non-functional requirements such as minimal latency and +fairness. + +Besides the task scheduler, EMBB provides basic parallel algorithms, concurrent +data structures, and skeletons for implementing stream processing +applications. These building blocks are largely implemented in a non-blocking +fashion, thus preventing frequently encountered pitfalls like lock contention, +deadlocks, and priority inversion. As another advantage in real-time systems, +the algorithms and data structures give certain progress guarantees. For +example, wait-free data structures guarantee system-wide progress which means +that every operation completes within a finite number of steps independently +of any other concurrent operations on the same data structure. + + +Community and Contact +===================== + +Project home: https://github.com/siemens/embb + +Git: https://github.com/siemens/embb.git + git@github.com:siemens/embb.git + +Mailing lists: embb-announcements@googlegroups.com + embb-dev@googlegroups.com (development) + +Subscription: https://groups.google.com/forum/#!forum/embb-announcements/join + https://groups.google.com/forum/#!forum/embb-dev/join + +Contact: embb.info@gmail.com or tobias.schuele@siemens.com + + +License +======= + +See the file "COPYING.txt" in the project's root directory. + + +Requirements +============ + +This project is based on the standards C99 (for C code) and C++03 (for C++ +code) to be usable on a wide range of target systems. It has been tested on +the following OS/compiler/architecture combinations: + + - Linux (Ubuntu 12.10) / GCC 4.8.1 / x86, x86_64 + - Linux (Ubuntu 14.04) / GCC 4.8.2 / ARMv7 + - Windows + * MSVC 12.0.21005.1 REL / x86, x86_64 + * MSVC 11.0.50727.42 VSLRSTAGE / x86, x86_64 + +Other compilers and operating systems may be supported without any changes to +the source code. The project includes unit tests that can be used to find out +whether a system not officially supported is suitable to run EMBB. If there is +a requirement to support a system on which the unit tests do not pass, please +contact us: embb-dev@googlegroups.com. + + +Directory Structure +=================== + +EMBB is a technology stack consisting of various building blocks. For some of +them, there exist C and C++ versions, others are only implemented in C++. The +directory names are postfixed with either "_cpp" or "_c" for the C++ and C +versions, respectively. Currently, EMBB contains the following components: + + - base: base_c, base_cpp + - mtapi: mtapi_c, mtapi_cpp + - algorithms: algorithms_cpp + - dataflow: dataflow_cpp + - containers: containers_cpp + +Each component consists of an include, a src, and a test subfolder that contain +the header files, source files, and unit tests, respectively. + +Component base_c contains abstractions for threading, synchronization, atomic +operations, and other functionalities. As the name indicates, the code is +implemented in C. Component base_cpp is mainly a C++ wrapper around the base_c +functionalities. Component mtapi_c is a task scheduler written in C and +mtapi_cpp a C++ wrapper for the scheduler. Component algorithms_cpp provides +high-level constructs for typical parallelization task in C++, and +dataflow_cpp generic skeletons for the development of parallel stream-based +applications. Finally, component containers_cpp provides containers, i.e., +data structures for storing object in an organized and thread-safe way. + + +Build and Installation +====================== + +EMBB is built using CMake (version 2.8.9 or higher). CMake is a build file +generator which allows to abstract from the concrete build tools. To generate +and invoke the platform-specific build files, open a shell (on Windows, use +the Visual Studio developer shell to have the correct environment variables) +and change to the project's root directory. Create a subdirectory, where you +want to build the library, e.g., "build". Change to that subdirectory. It is +assumed that the project's root directory is now the parent directory. + +1. Generation of native build files +----------------------------------- + +Choose an appropriate build file generator for your system. + + - For Linux, GCC, x86/x86_64/ARM: "Unix Makefiles" + - For Windows, MSVC of VS 2013, x86: "Visual Studio 12" + - For Windows, MSVC of VS 2013, x86_64: "Visual Studio 12 Win64" + - For Windows, MSVC of VS 2012, x86: "Visual Studio 11" + - For Windows, MSVC of VS 2012, x86_64: "Visual Studio 11 Win64" + +A list of all available generators can be displayed by typing "cmake" without +any options. The build files can be generated using the following command: + + cmake -G .. [OPTIONS] + +Note that on Linux, the architecture (32/64 bit) cannot be selected by the +generator. However, the build mode (Release/Debug) can be specified using the +option -DCMAKE_BUILD_TYPE=[Release|Debug]. If no build mode is given on Linux, +the default (Release) is used. The Visual Studio generators create build files +for both modes (the selection is done at build time). + +EMBB can be built with and without C++ exception handling, which has to be +specified on build file generation. When exceptions are turned off, an error +message is emitted and the program aborts in case of an exception within EMBB. +To disable exceptions, add the option -DUSE_EXCEPTIONS=OFF. + +The tutorial of EMBB comes with example source files in doc/examples/. These +can be built with the other source files using CMake option -DBUILD_EXAMPLES=ON +in the generation step. Note, however, that the examples use C++11 features and +require a corresponding compiler. + +Now you can generate the build files as shown by the following examples. + +For a Linux Debug build with exception handling, type + + cmake -G "Unix Makefiles" .. -DCMAKE_BUILD_TYPE=Debug + +For a Windows build (VS 2013, x86) without exception handling, type + + cmake -G "Visual Studio 12" .. -DUSE_EXCEPTIONS=OFF + +Note that "Visual Studio 12" refers to the version number of Visual Studio and +not to the year in which it was released (2013). + +2. Compiling and linking +------------------------ + +As the next step, you can compile the library using the generated build files. +On Linux, the build mode (Release|Debug) is already given in the build files, +whereas on Windows, it has to be specified now. + +For a Linux build, type + + cmake --build . + +For a Windows Release build, type + + cmake --build . --config Release + +3. Running the tests +-------------------- + +To check whether EMBB was compiled correctly, run the tests. The test +executables are contained in the subfolder "binaries". + +On Linux, type + + binaries/run_tests.sh + +On Windows, type + + binaries\run_tests.bat + +If no error message occurs, EMBB is working fine. + +4. Installation +--------------- + +The default installation path on Linux is + + /usr/local/ + +and on Windows + + C:\Program Files\embb-X.Y.Z\ or C:\Program Files (x86)\embb-X.Y.Z + +depending on the target architecture. + +If you want a different installation path, you can change it now by typing + + cmake -DINSTALL_PREFIX=YourCustomPath .. + +The option "-DINSTALL_PREFIX=YourCustomPath" can also be given in Step 1. + +To install the files, use the command + + cmake --build . --target install + +which copies the contents of the "install" folder to the "bin", "lib", and +"include" folders in the installation path. For the default paths, the +installation has to be run with administrator / root privileges. + + +Using the Library +================= + +To use EMBB, the include files have to be made available during compilation of +your application and the libraries have to be added during linking. + +1. Using C++ +------------ + +If you want to use the C++ functionalities of EMBB, you have to link the +following libraries (names will be different on Windows and on Linux) in the +given order: + + embb_base, embb_base_cpp, embb_mtapi_c, embb_mtapi_cpp, embb_containers_cpp, + embb_algorithms_cpp, embb_dataflow_cpp + +The C++ header files can be included as follows: + + #include + #include + #include + #include + +2. Using C +---------- + +The following libraries have to be linked in the given order: + + embb_base_c, mtapi_c + +The C header files can be included as follows: + + #include or #include + #include + + +Documentation +============= + +EMBB comes with a tutorial, example programs, and an HTML reference +documentation describing the APIs, which can be found in the "doc" folder. +The root document of the HTML reference is "doc/reference/index.html". + + +Code Quality +============ + +For the C++ parts of EMBB, we respect most rules of the "Google C++ Style +Guide" which are checked using the cpplint tool. However, we ignore some +rules, as they are not applicable or yield false results for this project. +For example, we respect the include order of the Google Style Guide, but use +<> instead of "" for project includes, which confuses the cpplint tool. +Moreover, we do not tolerate compiler warnings and regularly check the source +code using Cppcheck, a static analysis tool for C++. + + +Known Bugs and Limitations +========================== + +- The usage of EMBB atomic operations with types of size 1 or 2 bytes such + as 'bool' does not work on ARMv7. This is an alignment issue which can cause + stack corruption. + + +Links +===== + + - Multicore Association: + http://www.multicore-association.org + - MTAPI: + http://www.multicore-association.org/workgroup/mtapi.php + - CMake: + http://www.cmake.org/ + - Google C++ Style Guide + http://google-styleguide.googlecode.com/svn/trunk/cppguide.html + - cpplint: + http://google-styleguide.googlecode.com/svn/trunk/cpplint/ + - Cppcheck: + http://cppcheck.sourceforge.net/ diff --git a/algorithms_cpp/CMakeLists.txt b/algorithms_cpp/CMakeLists.txt new file mode 100644 index 0000000..4088cec --- /dev/null +++ b/algorithms_cpp/CMakeLists.txt @@ -0,0 +1,37 @@ +project (project_embb_algorithms) + +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_ALGORITHMS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(../CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_ALGORITHMS_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_ALGORITHMS_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_cpp/include) + +add_library(embb_algorithms_cpp ${EMBB_ALGORITHMS_CPP_SOURCES} + ${EMBB_ALGORITHMS_CPP_HEADERS}) +target_link_libraries(embb_algorithms_cpp embb_mtapi_cpp) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_algorithms_cpp_test ${EMBB_ALGORITHMS_CPP_TEST_SOURCES}) + target_link_libraries(embb_algorithms_cpp_test embb_algorithms_cpp + embb_mtapi_cpp embb_mtapi_c partest embb_base_cpp + embb_base_c ${compiler_libs}) + CopyBin(BIN embb_algorithms_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_algorithms_cpp DESTINATION lib) diff --git a/algorithms_cpp/include/embb/algorithms/algorithms.h b/algorithms_cpp/include/embb/algorithms/algorithms.h new file mode 100644 index 0000000..56f3eb4 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/algorithms.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_ALGORITHMS_H_ +#define EMBB_ALGORITHMS_ALGORITHMS_H_ + +/** + * \defgroup CPP_ALGORITHMS Algorithms + * High-level parallel algorithms and functionalities. + * \ingroup CPP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_ALGORITHMS_ALGORITHMS_H_ diff --git a/algorithms_cpp/include/embb/algorithms/count.h b/algorithms_cpp/include/embb/algorithms/count.h new file mode 100644 index 0000000..7e9a9ea --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/count.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_COUNT_H_ +#define EMBB_ALGORITHMS_COUNT_H_ + +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_COUNT Counting + * Parallel count operation + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Counts in parallel the number of elements in a range that are equal to + * the specified value. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. + * + * \return The number of elements that are equal to \c value + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * operations. + * \see CountIf(), ExecutionPolicy + * \tparam RAI Random access iterator + * \tparam ValueType Type of \c value that is compared to the elements in the + * range using the \c operator==. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + const ValueType& value, + /**< [IN] Value that the elements in the range are compared to using + \c operator== */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the counting algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +/** + * Counts in parallel the number of elements in a range for which the comparison + * function returns \c true. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. + * + * \return The number of elements for which \c comparison returns true + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * function. + * \see Count(), ExecutionPolicy + * \tparam RAI Random access iterator + * \tparam ComparisonFunction Unary predicate with argument of type + * std::iterator_traits::value_type. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range + RAI last, */ + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison, + /**< [IN] Unary predicate used to test the elements in the range. Elements for + which \c comparison returns true are counted. */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the counting algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + RAI last, + const ValueType& value + ) { + return Count(first, last, value, ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + RAI last, + const ValueType& value, + const ExecutionPolicy& policy + ) { + return Count(first, last, value, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +typename std::iterator_traits::difference_type Count( + RAI first, + RAI last, + const ValueType& value, + const ExecutionPolicy& policy, + size_t block_size + ); + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + RAI last, + ComparisonFunction comparison + ) { + return CountIf(first, last, comparison, ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + RAI last, + ComparisonFunction comparison, + const ExecutionPolicy& policy + ) { + return CountIf(first, last, comparison, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +typename std::iterator_traits::difference_type CountIf( + RAI first, + RAI last, + ComparisonFunction comparison, + const ExecutionPolicy& policy, + size_t block_size + ); + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_COUNT_H_ diff --git a/algorithms_cpp/include/embb/algorithms/execution_policy.h b/algorithms_cpp/include/embb/algorithms/execution_policy.h new file mode 100644 index 0000000..bf72c29 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/execution_policy.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_EXECUTION_POLICY_H_ +#define EMBB_ALGORITHMS_EXECUTION_POLICY_H_ + +#include +#include + +namespace embb { +namespace algorithms { +/** + * Describes the execution policy of a parallel algorithm. + * The execution policy comprises + * - the affinity of tasks to MTAPI worker threads (not CPU cores) and + * - the priority of the spawned tasks. + * + * \ingroup CPP_ALGORITHMS_SCAN + * \ingroup CPP_ALGORITHMS_REDUCTION + * \ingroup CPP_ALGORITHMS_FOREACH + * \ingroup CPP_ALGORITHMS_COUNT + * \ingroup CPP_ALGORITHMS_SORTING + */ +class ExecutionPolicy{ + public: + /** + * Constructs the default execution policy. + * Sets the affinity to all worker threads and the priority to the default + * value. + */ + ExecutionPolicy(); + + /** + * Constructs an execution policy with the specified affinity and priority. + */ + ExecutionPolicy( + bool initial_affinity, /**< + [IN] \c true sets the affinity to all worker threads, \c false to no + worker threads. */ + mtapi_uint_t priority /**< + [IN] Priority for the execution policy. */ + ); + + /** + * Constructs an execution policy with the specified priority. + * Sets the affinity to all worker threads. + */ + explicit ExecutionPolicy( + mtapi_uint_t priority /**< + [IN] Priority for the execution policy. */ + ); + + /** + * Constructs an execution policy with the specified affinity. + * Sets the priority to the default value. + */ + explicit ExecutionPolicy( + bool initial_affinity /**< + [IN] \c true sets the affinity to all worker threads, \c false to no + worker threads. */ + ); + + /** + * Sets affinity to a specific worker thread. + */ + void AddWorker( + mtapi_uint_t worker + /**< [IN] Worker thread index */ + ); + + /** + * Removes affinity to a specific worker thread. + */ + void RemoveWorker( + mtapi_uint_t worker + /**< [IN] Worker thread index */ + ); + + /** + * Checks if affinity to a specific worker thread is set. + * + * \return \c true if affinity is set, otherwise \c false + */ + bool IsSetWorker( + mtapi_uint_t worker + /**< [IN] Worker thread index */ + ); + + /** + * Returns the affinity + * + * \return the affinity + */ + const mtapi::Affinity &GetAffinity() const; + + /** Returns the priority + * + * \return the priority + */ + mtapi_uint_t GetPriority() const; + + private: + /** + * Default priority. + * Currently set to 0 = MAX priority. + */ + static const mtapi_uint_t DefaultPriority; + + /** + * Task Affinity. + * Maps the affinity of tasks to MTAPI worker threads (not CPU cores). + */ + mtapi::Affinity affinity_; + + /** + * Task Priority. + */ + mtapi_uint_t priority_; +}; +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_EXECUTION_POLICY_H_ diff --git a/algorithms_cpp/include/embb/algorithms/for_each.h b/algorithms_cpp/include/embb/algorithms/for_each.h new file mode 100644 index 0000000..e775cff --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/for_each.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_FOR_EACH_H_ +#define EMBB_ALGORITHMS_FOR_EACH_H_ + +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_FOREACH Foreach + * Parallel foreach loop + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Applies a unary function to the elements of a range in parallel. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the order in which the function is applied to + * the elements. + * \see ExecutionPolicy, ZipIterator + * \tparam RAI Random access iterator + * \tparam Function Unary function with argument of type + * std::iterator_traits::value_type. + */ +template +void ForEach( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + Function unary, + /**< [IN] Unary function applied to each element in the range */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the foreach loop execution */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are treated in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void ForEach( + RAI first, + RAI last, + Function unary + ) { + ForEach(first, last, unary, ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void ForEach( + RAI first, + RAI last, + Function unary, + const ExecutionPolicy& policy + ) { + ForEach(first, last, unary, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +void ForEach( + RAI first, + RAI last, + Function unary, + const ExecutionPolicy& policy, + size_t block_size + ); + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_FOR_EACH_H_ diff --git a/algorithms_cpp/include/embb/algorithms/identity.h b/algorithms_cpp/include/embb/algorithms/identity.h new file mode 100644 index 0000000..9baaac0 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/identity.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_IDENTITY_H_ +#define EMBB_ALGORITHMS_IDENTITY_H_ + +namespace embb { +namespace algorithms { + +/** + * Unary identity functor. + * + * \ingroup CPP_ALGORITHMS_SCAN + * \ingroup CPP_ALGORITHMS_REDUCTION + * \ingroup CPP_ALGORITHMS_FOREACH + * \ingroup CPP_ALGORITHMS_COUNT + * \ingroup CPP_ALGORITHMS_SORTING + */ +struct Identity { + /** + * Returns \c value unchanged. + * + * \return \c value + * \tparam Type Any type + */ + template + Type& operator()( + Type& value + /**< [IN] Value that is returned unchanged */ + ) { + return value; + } + + /** + * Returns \c value unchanged. + * + * \return \c value + * \tparam Type Any type + */ + template + const Type& operator()( + const Type& value + /**< [IN] Value that is returned unchanged */ + ) { + return value; + } +}; + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_IDENTITY_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/count-inl.h b/algorithms_cpp/include/embb/algorithms/internal/count-inl.h new file mode 100644 index 0000000..c43e41b --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/count-inl.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_COUNT_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_COUNT_INL_H_ + +#include +#include + +namespace embb { +namespace algorithms { +namespace internal { + +template +class ValueComparisonFunction{ + public: + explicit ValueComparisonFunction(const ValueType &value) + :value_(value) {} + ValueComparisonFunction(const ValueComparisonFunction &other) + :value_(other.value_) {} + + template + int operator()(ElementType element) { + if(element == value_) + return 1; + else + return 0; + } + private: + const ValueType &value_; + ValueComparisonFunction &operator=(const ValueComparisonFunction &other); +}; + +template +class FunctionComparisonFunction{ + public: + explicit FunctionComparisonFunction(Function function) + :function_(function) {} + FunctionComparisonFunction(const FunctionComparisonFunction &other) + :function_(other.function_) {} + + template + int operator()(ElementType element) { + if(function_(element)) + return 1; + else + return 0; + } + private: + Function function_; + FunctionComparisonFunction &operator=(const FunctionComparisonFunction & + other); +}; + +} // namespace internal + +template +typename std::iterator_traits::difference_type + Count(RAI first, RAI last, const ValueType& value, + const ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type Difference; + return Reduce(first, last, Difference(0), std::plus(), + internal::ValueComparisonFunction(value), policy, + block_size); +} + +template +typename std::iterator_traits::difference_type + CountIf(RAI first, RAI last, ComparisonFunction comparison, + const ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type Difference; + return Reduce(first, last, Difference(0), std::plus(), + internal::FunctionComparisonFunction + (comparison), policy, block_size); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_COUNT_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h b/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h new file mode 100644 index 0000000..ee5cdc1 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/for_each-inl.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_FOR_EACH_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_FOR_EACH_INL_H_ + +#include + +#include +#include +#include +#include + +namespace embb { +namespace algorithms { + +namespace internal { + +template +class ForEachFunctor { + public: + /** + * Constructs a for-each functor with arguments. + */ + ForEachFunctor(RAI first, RAI last, Function unary, + const ExecutionPolicy& policy, size_t block_size) + : first_(first), last_(last), unary_(unary), policy_(policy), + block_size_(block_size) { + } + + void Action(mtapi::TaskContext&) { + size_t distance = static_cast(std::distance(first_, last_)); + if (distance == 0) return; + if (distance <= block_size_) { // leaf case -> do work + for (RAI curIter(first_); curIter != last_; ++curIter) { + unary_(*curIter); + } + } else { // recurse further + ChunkPartitioner partitioner(first_, last_, 2); + ForEachFunctor functorL(partitioner[0].GetFirst(), + partitioner[0].GetLast(), unary_, policy_, block_size_); + ForEachFunctor functorR(partitioner[1].GetFirst(), + partitioner[1].GetLast(), unary_, policy_, block_size_); + + mtapi::Node& node = mtapi::Node::GetInstance(); + mtapi::Task taskL = node.Spawn(mtapi::Action(base::MakeFunction( + functorL, &ForEachFunctor::Action), + policy_.GetAffinity()), policy_.GetPriority()); + mtapi::Task taskR = node.Spawn(mtapi::Action(base::MakeFunction( + functorR, &ForEachFunctor::Action), + policy_.GetAffinity()), policy_.GetPriority()); + taskL.Wait(MTAPI_INFINITE); + taskR.Wait(MTAPI_INFINITE); + } + } + + private: + RAI first_; + RAI last_; + Function unary_; + const ExecutionPolicy& policy_; + size_t block_size_; + + /** + * Disables assignment. + */ + ForEachFunctor& operator=(const ForEachFunctor&); +}; + +template +void ForEachRecurcive(RAI first, RAI last, Function unary, + const ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + assert(distance > 0); + mtapi::Node& node = mtapi::Node::GetInstance(); + // Determine actually used block size + if (block_size == 0) { + block_size = (static_cast(distance) / node.GetCoreCount()); + if (block_size == 0) { + block_size = 1; + } + } + // Perform check of task number sufficiency + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, "Not enough MTAPI tasks available " + "to perform the parallel foreach loop"); + } + ForEachFunctor functor(first, last, unary, policy, block_size); + mtapi::Task task = node.Spawn(mtapi::Action( + base::MakeFunction(functor, + &ForEachFunctor::Action), + policy.GetAffinity()), policy.GetPriority()); + task.Wait(MTAPI_INFINITE); +} + +template +void ForEachIteratorCheck(RAI first, RAI last, Function unary, + const ExecutionPolicy& policy, size_t block_size, + std::random_access_iterator_tag) { + return ForEachRecurcive(first, last, unary, policy, block_size); +} + +} // namespace internal + +template +void ForEach(RAI first, RAI last, Function unary, const ExecutionPolicy& policy, + size_t block_size) { + typename std::iterator_traits::iterator_category category; + internal::ForEachIteratorCheck(first, last, unary, policy, block_size, + category); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_FOR_EACH_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h b/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h new file mode 100644 index 0000000..b032d28 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/merge_sort-inl.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_MERGE_SORT_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_MERGE_SORT_INL_H_ + +#include +#include +#include + +#include +#include +#include + +namespace embb { +namespace algorithms { + +namespace internal { + +/** + * Contains the merge sort MTAPI action function and data needed there. + */ +template +class MergeSortFunctor { + public: + typedef typename std::iterator_traits::value_type value_type; + + MergeSortFunctor(RAI first, RAI last, RAITemp temporary_first, + ComparisonFunction comparison, const ExecutionPolicy& policy, + size_t block_size, const RAI& global_first, int depth) + : first_(first), last_(last), temp_first_(temporary_first), + comparison_(comparison), policy_(policy), block_size_(block_size), + global_first_(global_first), depth_(depth) { + } + + void Action(mtapi::TaskContext& context) { + typedef typename std::iterator_traits::difference_type difference_type; + size_t distance = static_cast(std::distance(first_, last_)); + if (distance <= 1) { + if(!CloneBackToInput() && distance != 0) { + RAITemp temp_first = temp_first_; + temp_first += std::distance(global_first_, first_); + *temp_first = *first_; + } + return; + } + internal::ChunkPartitioner partitioner(first_, last_, 2); + MergeSortFunctor functorL( + partitioner[0].GetFirst(), partitioner[0].GetLast(), temp_first_, + comparison_, policy_, block_size_, global_first_, depth_ + 1); + MergeSortFunctor functorR( + partitioner[1].GetFirst(), partitioner[1].GetLast(), temp_first_, + comparison_, policy_, block_size_, global_first_, depth_ + 1); + + if (distance <= block_size_) { + functorL.Action(context); + functorR.Action(context); + } else { + mtapi::Node& node = mtapi::Node::GetInstance(); + mtapi::Task taskL = node.Spawn(mtapi::Action(base::MakeFunction(functorL, + &MergeSortFunctor::Action), + policy_.GetAffinity()), policy_.GetPriority()); + mtapi::Task taskR = node.Spawn(mtapi::Action(base::MakeFunction(functorR, + &MergeSortFunctor::Action), + policy_.GetAffinity()), policy_.GetPriority()); + taskL.Wait(MTAPI_INFINITE); + taskR.Wait(MTAPI_INFINITE); + } + + if(CloneBackToInput()) { + difference_type first = std::distance(global_first_, functorL.first_); + difference_type mid = std::distance(global_first_, functorR.first_); + difference_type last = std::distance(global_first_, functorR.last_); + SerialMerge(temp_first_ + first, temp_first_ + mid, + temp_first_ + last, functorL.first_, comparison_); + } else { + SerialMerge(functorL.first_, functorR.first_, functorR.last_, + temp_first_ + std::distance(global_first_, functorL.first_), + comparison_); + } + } + + /** + * Determines the input and output arrays for one level in merge sort. + * + * \return \c true if the temporary data range is input and the array to be + * sorted is output. \c false, if the other way around. + */ + bool CloneBackToInput() { + return depth_ % 2 == 0 ? true : false; + } + + private: + RAI first_; + RAI last_; + RAITemp temp_first_; + ComparisonFunction comparison_; + const ExecutionPolicy &policy_; + size_t block_size_; + const RAI& global_first_; + int depth_; + + MergeSortFunctor(const MergeSortFunctor&); + MergeSortFunctor& operator=(const MergeSortFunctor&); + + template + void SerialMerge(RAIIn first, RAIIn mid, RAIIn last, RAIOut out, + ComparisonFunction comparison) { + RAIIn save_mid = mid; + while ((first != save_mid) && (mid != last)) { + if (comparison(*first, *mid)) { + *out = *first; + ++out; + ++first; + } else { + *out = *mid; + ++out; + ++mid; + } + } + while (first != save_mid) { + *out = *first; + ++out; + ++first; + } + while(mid != last) { + *out = *mid; + ++out; + ++mid; + } + } +}; + +} // namespace internal + +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison, + const ExecutionPolicy& policy, + size_t block_size + ) { + typedef typename std::iterator_traits::difference_type difference_type; + embb::mtapi::Node &node = embb::mtapi::Node::GetInstance(); + difference_type distance = last - first; + assert(distance >= 0); + + assert(block_size == 0); // TODO(Georgios Christodoulou): + // Take block size into account + + if (block_size == 0) { + block_size= (static_cast(distance) / node.GetCoreCount()); + if (block_size == 0) + block_size = 1; + } + if (((distance/block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Not enough MTAPI tasks available to perform the merge sort"); + } + + internal::MergeSortFunctor functor( + first, last, temporary_first, comparison, policy, block_size, first, 0); + mtapi::Task task = node.Spawn(mtapi::Action(base::MakeFunction(functor, + &internal::MergeSortFunctor::Action), + policy.GetAffinity()), policy.GetPriority()); + + task.Wait(MTAPI_INFINITE); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_MERGE_SORT_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/partition-inl.h b/algorithms_cpp/include/embb/algorithms/internal/partition-inl.h new file mode 100644 index 0000000..a58ac48 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/partition-inl.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_PARTITION_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_PARTITION_INL_H_ + +namespace embb { +namespace algorithms { +namespace internal { + +template +ChunkDescriptor::ChunkDescriptor(ForwardIterator first, + ForwardIterator last) : + first(first), last(last) { +} + +template +ForwardIterator ChunkDescriptor::GetFirst() const { + return first; +} + +template +ForwardIterator ChunkDescriptor::GetLast() const { + return last; +} + +template +BlockSizePartitioner::BlockSizePartitioner( + ForwardIterator first, ForwardIterator last, size_t chunkSize) : + first(first), last(last), chunkSize(chunkSize) { + elements_count = static_cast(std::distance(first, last)); + chunks = elements_count / chunkSize; + if (elements_count % chunkSize != 0) + chunks++; +} + +template +size_t BlockSizePartitioner::Size() { + return chunks; +} + +template +const ChunkDescriptor + BlockSizePartitioner::operator[]( + size_t const& index) const { + ForwardIterator first_new = first; + std::advance(first_new, index * chunkSize); + + ForwardIterator last_new = first_new; + + if (index == elements_count / chunkSize) { + std::advance(last_new, elements_count % chunkSize); + } else { + std::advance(last_new, chunkSize); + } + + return ChunkDescriptor(first_new, last_new); +} + +template +size_t ChunkPartitioner::Size() { + return size; +} + +template +ChunkPartitioner::ChunkPartitioner(ForwardIterator first, + ForwardIterator last, size_t amountChunks) : + first(first), last(last) { + if (amountChunks > 0) { + size = amountChunks; + } else { + // if no concrete chunk size was given, use number of cores... + mtapi::Node& node = mtapi::Node::GetInstance(); + size = node.GetCoreCount(); + } + + elements_count = static_cast(std::distance(first, last)); + if (size > elements_count) { + // if we want to make more chunks than we have elements, correct + // the number of chunks + size = elements_count; + } + standard_chunk_size = elements_count / size; + bigger_chunk_count = elements_count % size; +} + +template +const ChunkDescriptor + ChunkPartitioner::operator[]( + size_t const& index) const { + typedef typename std::iterator_traits::difference_type + difference_type; + size_t prec_elements_count = 0; + + if (index <= bigger_chunk_count) { + prec_elements_count = index * (standard_chunk_size + 1); + } else { + prec_elements_count = (standard_chunk_size + 1) * bigger_chunk_count + + standard_chunk_size * (index - bigger_chunk_count); + } + + size_t cur_elements_count = + (index < bigger_chunk_count) ? + (standard_chunk_size + 1) : standard_chunk_size; + + ForwardIterator first_new = first; + std::advance(first_new, prec_elements_count); + + first_new = first + static_cast(prec_elements_count); + ForwardIterator last_new = first_new; + std::advance(last_new, cur_elements_count); + + return ChunkDescriptor(first_new, last_new); +} + +} // namespace internal +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_PARTITION_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/partition.h b/algorithms_cpp/include/embb/algorithms/internal/partition.h new file mode 100644 index 0000000..83a3933 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/partition.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_PARTITION_H_ +#define EMBB_ALGORITHMS_INTERNAL_PARTITION_H_ + +#include + +namespace embb { +namespace algorithms { +namespace internal { +/** + * A chunk descriptor. + * + * Describes a single partition of a 1-dimensional + * partitioning, using first and last iterator. + * + * \tparam ForwardIterator Type of the iterator. + */ + +template +class ChunkDescriptor { + private: + ForwardIterator first; + ForwardIterator last; + + public: + /** + * Constructor. + * + * \param first The first iterator. + * \param last The last iterator + */ + ChunkDescriptor(ForwardIterator first, ForwardIterator last); + + /** + * Gets the first iterator. + * + * \return The first iterator. + * + * \waitfree + */ + ForwardIterator GetFirst() const; + + /** + * Gets the last iterator. + * + * \return The last iterator. + * + * \waitfree + */ + ForwardIterator GetLast() const; +}; + +/** + * Partitioner Interface. + * + * Describes the interface for accessing a 1-dimensional partitioning. + * + * \tparam ForwardIterator Type of the iterator. + */ +template +class IPartitioner { + public: + virtual ~IPartitioner() {} + + private: + /** + * Gets the amount of partitions. + * + * \return A size_t. + * + * \waitfree + */ + virtual size_t Size() = 0; + + /** + * Gets a single partition. + * + * \param index Zero-based index of the partitioner. Range: [0;Size()-1] + * + * \return The indexed value. + * + * \waitfree + */ + virtual const ChunkDescriptor operator[]( + size_t const& index) const = 0; +}; + +/** + * A block size partitioner. + * + * Partitions a 1-dim. collection of elements with total order that provides a + * forward iterator into partitions of size chunkSize. If no chunkSize is given, + * chunkSize is set to 1. + * + * Example: + * + * int A[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; + * const int N = (sizeof(A) / sizeof(int) ); + * embb::algorithms::ChunkPartitioner< int* > partitioner(A, A + N, 5); + * + * With that, the array is partitioned into chunks of size 5. We therefore + * have following partitions: + * 1: [1,2,3,4,5] + * 2: [6,7,8,9,10] + * 3: [11,12,13] + * + * \tparam ForwardIterator Type of the iterator. + */ +template +class BlockSizePartitioner : IPartitioner < ForwardIterator > { + private: + ForwardIterator first; + ForwardIterator last; + size_t chunkSize; + size_t elements_count; + size_t chunks; + + public: + /** + * Constructor. + * + * + * \param first The first iterator of the collection. + * \param last The last iterator of the collection. + * \param chunkSize (Optional) size of the chunk. + */ + BlockSizePartitioner( + ForwardIterator first, ForwardIterator last, size_t chunkSize = 1); + + /** + * See IPartitioner + * + * \waitfree + */ + virtual size_t Size(); + + /** + * See IPartitioner + * + * \waitfree + */ + virtual const ChunkDescriptor operator[]( + size_t const& index) const; +}; + +/** + * A chunk partitioner. + * + * Partitions a 1-dim. collection of elements with total order that provides a + * forward iterator into amountChunks partitions. If no amountChunks is given, + * the collection is split into number of cores partitions. + * + * It is avoided to have unbalanced partitions, so they are equally "filled" up. + * With that, we have at most two partition sizes: basic_size and basic_size+1. + * The partitions with basic_size+1 are the ones at the firstning. + * + * If a higher number of chunks shall be produced, is more than contained elements, + * the number of chunks is reduced to the number of elements. + * + * Example: + * + * int A[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13 }; + * const int N = (sizeof(A) / sizeof(int) ); + * embb::algorithms::ChunkPartitioner< int* > partitioner(A, A + N, 5); + * + * With that, the array is partitioned into chunks of size 5. We therefore + * have following partitions: + * 1: [1,2,3] + * 2: [3,5,6] + * 3: [7,8,9] + * 4: [10,11] + * 5: [12,13] + * + * \tparam ForwardIterator Type of the iterator. + */ +template +class ChunkPartitioner : IPartitioner < ForwardIterator > { + private: + size_t size; + size_t elements_count; + ForwardIterator first; + ForwardIterator last; + size_t standard_chunk_size; + size_t bigger_chunk_count; + + public: + /** + * See IPartitioner + * + * \waitfree + */ + virtual size_t Size(); + + /** + * Constructor. + * + * See class documentation. + * + * \waitfree + * + * \param first The first. + * \param last The last. + * \param amountChunks (Optional) the amount chunks. + */ + ChunkPartitioner(ForwardIterator first, ForwardIterator last, + size_t amountChunks = 0); + + /** + * See IPartitioner + * + * \waitfree + */ + virtual const ChunkDescriptor operator[]( + size_t const& index) const; +}; + +} // namespace internal +} // namespace algorithms +} // namespace embb + +#include + + +#endif // EMBB_ALGORITHMS_INTERNAL_PARTITION_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h b/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h new file mode 100644 index 0000000..2e831d1 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/quick_sort-inl.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_QUICK_SORT_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_QUICK_SORT_INL_H_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace embb { +namespace algorithms { + +namespace internal { + +template +class QuickSortFunctor { + public: + /** + * Constructs a functor. + */ + QuickSortFunctor(RAI first, RAI last, ComparisonFunction comparison, + const ExecutionPolicy& policy, size_t block_size) + : first_(first), last_(last), comparison_(comparison), policy_(policy), + block_size_(block_size) { + } + + /** + * MTAPI action function and starting point of the parallel quick sort. + */ + void Action(mtapi::TaskContext&) { + Difference distance = last_ - first_; + if (distance <= 1) { + return; + } else { + Difference pivot = MedianOfNine(first_, last_); + RAI mid = first_ + pivot; + mid = SerialPartition(first_, last_, mid); + if (distance <= static_cast(block_size_)) { + SerialQuickSort(first_, mid); + SerialQuickSort(mid, last_); + } else { + mtapi::Node& node = mtapi::Node::GetInstance(); + QuickSortFunctor functor_l(first_, mid, comparison_, policy_, + block_size_); + mtapi::Task task_l = node.Spawn(mtapi::Action(base::MakeFunction( + functor_l, &QuickSortFunctor::Action))); + QuickSortFunctor functor_r(mid, last_, comparison_, policy_, + block_size_); + mtapi::Task task_r = node.Spawn(mtapi::Action(base::MakeFunction( + functor_r, &QuickSortFunctor::Action))); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + } + } + } + + private: + RAI first_; + RAI last_; + ComparisonFunction comparison_; + const ExecutionPolicy& policy_; + size_t block_size_; + + typedef typename std::iterator_traits::difference_type Difference; + + /** + * Computes the pseudo-median of nine by using MedianOfThree(). + */ + Difference MedianOfNine(RAI first, RAI last) { + Difference distance = last - first; + Difference offset = distance / static_cast(8); + if (offset == 0) { + return distance / 2; + } + Difference pseudo_median_of_nine = MedianOfThree( + first, + MedianOfThree(first, static_cast(0), offset, offset * 2), + MedianOfThree(first, offset * 3, offset * 4, offset * 5), + MedianOfThree(first, offset * 6, offset * 7, distance - 1)); + return pseudo_median_of_nine; + } + + /** + * Computes the median of three. + */ + Difference MedianOfThree(RAI first, Difference left, Difference mid, + Difference right) { + if (comparison_(*(first + left), *(first + mid))) { + if (comparison_(*(first + mid), *(first + right))) { + return mid; + } else { + if (comparison_(*(first + left), *(first + right))) + return right; + else + return left; + } + } else { + if (comparison_(*(first + right), *(first + mid))) { + return mid; + } else { + if (comparison_(*(first + right), *(first + left))) + return right; + else + return left; + } + } + } + + /** + * Performs a quick sort partitioning as serial computation. + */ + RAI SerialPartition(RAI first, RAI last, RAI pivot) { + while (first != last) { + while (comparison_(*first, *pivot)) { + ++first; + if (first == last) + return first; + } + do { + --last; + if (first == last) return first; + } while (comparison_(*pivot, *last)); + std::swap(*first, *last); + if(pivot == first) { + pivot = last; + } else if (pivot == last) { + pivot = first; + } + ++first; + } + return first; + } + + /** + * Performs the quick sort algorithm as serial computation. + */ + void SerialQuickSort(RAI first, RAI last) { + if (last - first <= 1) { + return; + } else { + Difference pivot = MedianOfNine(first, last); + RAI mid = first + pivot; + mid = SerialPartition(first, last, mid); + SerialQuickSort(first, mid); + SerialQuickSort(mid, last); + } + } + + /** + * Disables assignment. + */ + QuickSortFunctor& operator=(const QuickSortFunctor&); + + /** + * Disables Copying. + */ + QuickSortFunctor(const QuickSortFunctor&); +}; + +} // namespace internal + +template +void QuickSort(RAI first, RAI last, ComparisonFunction comparison, + const ExecutionPolicy& policy, size_t block_size) { + embb::mtapi::Node& node = embb::mtapi::Node::GetInstance(); + typename std::iterator_traits::difference_type distance = last - first; + assert(distance > 0); + if (block_size == 0) { + block_size= (static_cast(distance) / node.GetCoreCount()); + if (block_size == 0) + block_size = 1; + } + if (((distance / block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Not enough MTAPI tasks available for performing quick sort"); + } + internal::QuickSortFunctor functor( + first, last, comparison, policy, block_size); + mtapi::Task task = node.Spawn(mtapi::Action(base::MakeFunction( + functor, &internal::QuickSortFunctor::Action))); + task.Wait(MTAPI_INFINITE); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_QUICK_SORT_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h b/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h new file mode 100644 index 0000000..01a02d5 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/reduce-inl.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_REDUCE_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_REDUCE_INL_H_ + +#include +#include + +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace internal { + +template +class ReduceFunctor { + public: + ReduceFunctor(RAI first, RAI last, ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const ExecutionPolicy &policy, size_t block_size, + ReturnType& result) + : + first_(first), last_(last), neutral_(neutral), reduction_(reduction), + transformation_(transformation), policy_(policy), + block_size_(block_size), result_(result) { + } + + void Action(mtapi::TaskContext&) { + if (first_ == last_) { + return; + } + size_t distance = static_cast(std::distance(first_, last_)); + if (distance <= block_size_) { // leaf case -> do work + ReturnType result(neutral_); + for (RAI iter = first_; iter != last_; ++iter) { + result = reduction_(result, transformation_(*iter)); + } + result_ = result; + } else { // recurse further + internal::ChunkPartitioner partitioner(first_, last_, 2); + ReturnType result_l(neutral_); + ReturnType result_r(neutral_); + ReduceFunctor functor_l(partitioner[0].GetFirst(), + partitioner[0].GetLast(), + neutral_, reduction_, transformation_, policy_, + block_size_, result_l); + ReduceFunctor functor_r(partitioner[1].GetFirst(), + partitioner[1].GetLast(), + neutral_, reduction_, transformation_, policy_, + block_size_, result_r); + mtapi::Node& node = mtapi::Node::GetInstance(); + mtapi::Task task_l = node.Spawn(mtapi::Action(base::MakeFunction( + functor_l, &ReduceFunctor::Action), policy_.GetAffinity()), + policy_.GetPriority()); + mtapi::Task task_r = node.Spawn(mtapi::Action(base::MakeFunction( + functor_r, &ReduceFunctor::Action), policy_.GetAffinity()), + policy_.GetPriority()); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + result_ = reduction_(result_l, result_r); + } + } + + private: + RAI first_; + RAI last_; + ReturnType neutral_; + ReductionFunction reduction_; + TransformationFunction transformation_; + const ExecutionPolicy& policy_; + size_t block_size_; + ReturnType& result_; + + ReduceFunctor& operator=(const ReduceFunctor&); + ReduceFunctor(const ReduceFunctor&); +}; + +template +ReturnType ReduceRecurcive(RAI first, RAI last, ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + assert(distance > 0); + + mtapi::Node& node = mtapi::Node::GetInstance(); + size_t used_block_size = block_size; + if (used_block_size == 0) { + used_block_size = static_cast(distance) / node.GetCoreCount(); + if (used_block_size == 0) used_block_size = 1; + } + + if (((distance / used_block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Number of computation tasks required in reduction would " + "exceed MTAPI maximum number of tasks"); + } + + ReturnType result = neutral; + typedef ReduceFunctor Functor; + Functor functor(first, last, neutral, reduction, transformation, policy, + used_block_size, result); + mtapi::Task task = node.Spawn(mtapi::Action(base::MakeFunction( + functor, &Functor::Action), policy.GetAffinity()), policy.GetPriority()); + task.Wait(MTAPI_INFINITE); + return result; +} + +template +ReturnType ReduceIteratorCheck(RAI first, RAI last, ReductionFunction reduction, + TransformationFunction transformation, + ReturnType neutral, + const ExecutionPolicy& policy, size_t block_size, + std::random_access_iterator_tag) { + return ReduceRecurcive(first, last, neutral, reduction, transformation, + policy, block_size); +} + +} // namespace internal + +template +ReturnType Reduce(RAI first, RAI last, ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const ExecutionPolicy& policy, size_t block_size) { + typename std::iterator_traits::iterator_category category; + return internal::ReduceIteratorCheck(first, last, reduction, transformation, + neutral, policy, block_size, category); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_REDUCE_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h b/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h new file mode 100644 index 0000000..95b65bb --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/internal/scan-inl.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INTERNAL_SCAN_INL_H_ +#define EMBB_ALGORITHMS_INTERNAL_SCAN_INL_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace algorithms { +namespace internal { + +template +class ScanFunctor { + public: + ScanFunctor(RAIIn first, RAIIn last, RAIOut output_iterator, + ReturnType neutral, ScanFunction scan, + TransformationFunction transformation, + const ExecutionPolicy& policy, + size_t block_size, ReturnType* tree_values, size_t node_id, + bool going_down) + : policy_(policy), first_(first), last_(last), + output_iterator_(output_iterator), scan_(scan), + transformation_(transformation), + neutral_(neutral), block_size_(block_size), tree_values_(tree_values), + node_id_(node_id), parent_value_(neutral), is_first_pass_(going_down) { + } + + void Action(mtapi::TaskContext&) { + if (first_ == last_) { + return; + } + size_t distance = static_cast(std::distance(first_, last_)); + if (distance <= block_size_) { // leaf case -> do work + if (is_first_pass_) { + RAIIn iter_in = first_; + RAIOut iter_out = output_iterator_; + ReturnType result = transformation_(*first_); + *iter_out = result; + ++iter_in; + ++iter_out; + while (iter_in != last_) { + result = scan_(result, transformation_(*iter_in)); + *iter_out = result; + ++iter_in; + ++iter_out; + } + SetTreeValue(result); + } else { // Second pass + RAIIn iter_in = first_; + RAIOut iter_out = output_iterator_; + while (iter_in != last_) { + *iter_out = scan_(parent_value_, *iter_out); + ++iter_in; + ++iter_out; + } + } + } else { + internal::ChunkPartitioner partitioner(first_, last_, 2); + ScanFunctor functor_l(partitioner[0].GetFirst(), partitioner[0].GetLast(), + output_iterator_, neutral_, scan_, transformation_, + policy_, block_size_, tree_values_, node_id_, + is_first_pass_); + ScanFunctor functor_r(partitioner[1].GetFirst(), partitioner[1].GetLast(), + output_iterator_, neutral_, scan_, transformation_, + policy_, block_size_, tree_values_, node_id_, + is_first_pass_); + functor_l.SetID(1); + functor_r.SetID(2); + std::advance(functor_r.output_iterator_, + std::distance(functor_l.first_, functor_r.first_)); + if (!is_first_pass_) { + functor_l.parent_value_ = parent_value_; + functor_r.parent_value_ = functor_l.GetTreeValue() + parent_value_; + } + mtapi::Node& node = mtapi::Node::GetInstance(); + mtapi::Task task_l = node.Spawn(mtapi::Action(base::MakeFunction( + functor_l, &ScanFunctor::Action), + policy_.GetAffinity()), + policy_.GetPriority()); + mtapi::Task task_r = node.Spawn(mtapi::Action(base::MakeFunction( + functor_r, &ScanFunctor::Action), + policy_.GetAffinity()), + policy_.GetPriority()); + task_l.Wait(MTAPI_INFINITE); + task_r.Wait(MTAPI_INFINITE); + SetTreeValue(scan_(functor_l.GetTreeValue(), functor_r.GetTreeValue())); + } + } + + ReturnType GetTreeValue() { + return tree_values_[node_id_]; + } + + void SetTreeValue(ReturnType value) { + tree_values_[node_id_] = value; + } + + private: + const ExecutionPolicy& policy_; + RAIIn first_; + RAIIn last_; + RAIOut output_iterator_; + ScanFunction scan_; + TransformationFunction transformation_; + ReturnType neutral_; + size_t block_size_; + ReturnType* tree_values_; + size_t node_id_; + ReturnType parent_value_; + bool is_first_pass_; + + void SetID(int is_left) { + if (is_left == 1) { + node_id_ = 2 * node_id_ + 1; + } else if (is_left == 2) { + node_id_ = 2 * node_id_ + 2; + } + } + + /** + * Disables assignment. + */ + ScanFunctor& operator=(const ScanFunctor&); + + /** + * Disables copying. + */ + ScanFunctor(const ScanFunctor&); +}; + +template +void ScanIteratorCheck(RAIIn first, RAIIn last, RAIOut output_iterator, + ReturnType neutral, ScanFunction scan, + TransformationFunction transformation, + const ExecutionPolicy& policy, size_t block_size, + std::random_access_iterator_tag) { + typedef typename std::iterator_traits::difference_type difference_type; + difference_type distance = std::distance(first, last); + if (distance <= 0) { + return; + } + mtapi::Node& node = mtapi::Node::GetInstance(); + ReturnType values[MTAPI_NODE_MAX_TASKS_DEFAULT]; + size_t used_block_size = block_size; + if (block_size == 0) { + used_block_size = static_cast(distance) / node.GetCoreCount(); + if (used_block_size == 0) used_block_size = 1; + } + if (((distance / used_block_size) * 2) + 1 > MTAPI_NODE_MAX_TASKS_DEFAULT) { + EMBB_THROW(embb::base::ErrorException, + "Number of computation tasks required in scan " + "exceeds MTAPI maximum number of tasks"); + } + + // first pass. Calculates prefix sums for leaves and when recursion returns + // it creates the tree. + typedef ScanFunctor Functor; + Functor functor_down(first, last, output_iterator, neutral, scan, + transformation, policy, used_block_size, values, 0, + true); + mtapi::Task task_down = node.Spawn(mtapi::Action(base::MakeFunction( + functor_down, &Functor::Action), + policy.GetAffinity()), policy.GetPriority()); + task_down.Wait(MTAPI_INFINITE); + + // Second pass. Gives to each leaf the part of the prefix missing + Functor functor_up(first, last, output_iterator, neutral, scan, + transformation, policy, used_block_size, values, 0, false); + mtapi::Task task_up = node.Spawn(mtapi::Action(base::MakeFunction( + functor_up, &Functor::Action), + policy.GetAffinity()), policy.GetPriority()); + task_up.Wait(MTAPI_INFINITE); +} + +} // namespace internal + +template +void Scan(RAIIn first, RAIIn last, RAIOut output_iterator, ReturnType neutral, + ScanFunction scan, TransformationFunction transformation, + const ExecutionPolicy& policy, size_t block_size) { + typedef typename std::iterator_traits::iterator_category category; + internal::ScanIteratorCheck(first, last, output_iterator, neutral, + scan, transformation, policy, block_size, category()); +} + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INTERNAL_SCAN_INL_H_ diff --git a/algorithms_cpp/include/embb/algorithms/invoke.h b/algorithms_cpp/include/embb/algorithms/invoke.h new file mode 100644 index 0000000..80765fa --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/invoke.h @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_INVOKE_H_ +#define EMBB_ALGORITHMS_INVOKE_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_INVOKE Invoke + * Parallel invocation of functions. + * \ingroup CPP_ALGORITHMS + */ + + +/** + * Function type used by Invoke. + * \ingroup CPP_ALGORITHMS_INVOKE + */ +typedef embb::base::Function InvokeFunctionType; + +#ifdef DOXYGEN + +/** + * Spawns one to ten function objects at once and runs them in parallel. + * + * Blocks until all of them are done. + * + * \ingroup CPP_ALGORITHMS_INVOKE + */ +template +void Invoke( + Function1 func1, + /**< [in] First function to invoke */ + ...); + +/** +* Spawns one to ten function objects at once and runs them in parallel using the +* given ExecutionPolicy. +* +* Blocks until all of them are done. +* +* \ingroup CPP_ALGORITHMS_INVOKE +*/ +template +void Invoke( + Function1 func1, + /**< [in] Function to invoke */ + ..., + const ExecutionPolicy & policy /**< [in] ExecutionPolicy to use */ + ); + +#else // DOXYGEN + +namespace internal { + +/** + * Spawns an MTAPI task on construction and waits for it on destruction. + */ +template +class TaskWrapper { + public: + /** + * Wraps the function into an mtapi::Action and spawns an mtapi::Task. + */ + explicit TaskWrapper( + Function function, + const ExecutionPolicy& policy) + : function_(function), task_() { + mtapi::Action action(embb::base::MakeFunction(*this, &TaskWrapper::Run), + policy.GetAffinity()); + task_ = mtapi::Node::GetInstance().Spawn(action, policy.GetPriority()); + } + + /** + * Waits until the task has finished execution. + */ + ~TaskWrapper() { + task_.Wait(MTAPI_INFINITE); + } + + private: + Function function_; + mtapi::Task task_; + + void Run(embb::mtapi::TaskContext&) { + function_(); + } +}; +} // namespace internal + +template +void Invoke( + Function1 func1) { + Invoke(func1, ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2) { + Invoke(func1, func2, ExecutionPolicy()); +} +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3) { + Invoke(func1, func2, func3, ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4) { + Invoke(func1, func2, func3, func4, ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5) { + Invoke(func1, func2, func3, func4, func5, ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6) { + Invoke(func1, func2, func3, func4, func5, func6, ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7) { + Invoke(func1, func2, func3, func4, func5, func6, func7, ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8) { + Invoke(func1, func2, func3, func4, func5, func6, func7, func8, + ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9) { + Invoke(func1, func2, func3, func4, func5, func6, func7, func8, func9, + ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9, + Function10 func10) { + Invoke(func1, func2, func3, func4, func5, func6, func7, func8, func9, func10, + ExecutionPolicy()); +} + +template +void Invoke( + Function1 func1, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); + internal::TaskWrapper wrap8(func8, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); + internal::TaskWrapper wrap8(func8, policy); + internal::TaskWrapper wrap9(func9, policy); +} + +template +void Invoke( + Function1 func1, + Function2 func2, + Function3 func3, + Function4 func4, + Function5 func5, + Function6 func6, + Function7 func7, + Function8 func8, + Function9 func9, + Function10 func10, + const ExecutionPolicy& policy) { + internal::TaskWrapper wrap1(func1, policy); + internal::TaskWrapper wrap2(func2, policy); + internal::TaskWrapper wrap3(func3, policy); + internal::TaskWrapper wrap4(func4, policy); + internal::TaskWrapper wrap5(func5, policy); + internal::TaskWrapper wrap6(func6, policy); + internal::TaskWrapper wrap7(func7, policy); + internal::TaskWrapper wrap8(func8, policy); + internal::TaskWrapper wrap9(func9, policy); + internal::TaskWrapper wrap10(func10, policy); +} + +#endif // else DOXYGEN + +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_INVOKE_H_ diff --git a/algorithms_cpp/include/embb/algorithms/merge_sort.h b/algorithms_cpp/include/embb/algorithms/merge_sort.h new file mode 100644 index 0000000..1cb2304 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/merge_sort.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_MERGE_SORT_H_ +#define EMBB_ALGORITHMS_MERGE_SORT_H_ + +#include +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_SORTING Sorting + * Parallel merge sort and quick sort algorithms + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Sorts a range of elements using a parallel merge sort algorithm with implicit + * allocation of dynamic memory. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. Since the algorithm does not sort in-place, it requires + * additional memory which is implicitly allocated by the function. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \memory Array with last-first elements of type + * std::iterator_traits::value_type. + * \threadsafe if the elements in the range [first,last) are not + * modified by another thread while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + operations. + * \see ExecutionPolicy, MergeSort() + * \tparam RAI Random access iterator + * \tparam ComparisonFunction Binary predicate with both arguments of type + * std::iterator_traits::value_type. + */ +template +void MergeSortAllocate( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison + = std::less::value_type>(), + /**< [IN] Binary predicate used to establish the sorting order. An element + \c a appears before an element \c b in the sorted range if + comparison(a, b) == true. The default value uses the + less-than relation. */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the merge sort algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +/** + * Sorts a range of elements using a parallel merge sort algorithm without + * implicit allocation of dynamic memory. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. Since the algorithm does not sort in-place, it requires + * additional memory which must be provided by the user. The range pointed to + * by \c temporary_first must have the same number of elements as the range to + * be sorted, and the elements of both ranges must have the same type. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the ranges [first,last) and + * [temporary_first,temporary_first+(last-first) are not + * modified by another thread while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * operations. + * \see ExecutionPolicy, MergeSortAllocate() + * \tparam RAI Random access iterator + * \tparam RAITemp Random access iterator for temporary memory. Has to have the + * same value type as RAI. + * \tparam ComparisonFunction Binary predicate with both arguments of type + * std::iterator_traits::value_type. + */ +template +void MergeSort( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator to last plus one element to be sorted */ + RAITemp temporary_first, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison + = std::less::value_type>(), + /**< [IN] Binary predicate used to establish the sorting order. An element + \c a appears before an element \c b in the sorted range if + comparison(a, b) == true. The default value uses the + less-than relation. */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the merge sort algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSortAllocate( + RAI first, + RAI last + ) { + MergeSortAllocate(first, last, + std::less::value_type>(), + ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSortAllocate( + RAI first, + RAI last, + ComparisonFunction comparison + ) { + MergeSortAllocate(first, last, comparison, ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSortAllocate( + RAI first, + RAI last, + ComparisonFunction comparison, + const ExecutionPolicy& policy + ) { + MergeSortAllocate(first, last, comparison, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +void MergeSortAllocate( + RAI first, + RAI last, + ComparisonFunction comparison, + const ExecutionPolicy& policy, + size_t block_size + ) { + typedef base::Allocation Alloc; + typename std::iterator_traits::difference_type distance = last - first; + typedef typename std::iterator_traits::value_type value_type; + value_type* temporary = static_cast( + Alloc::Allocate(distance * sizeof(value_type))); + MergeSort(first, last, temporary, comparison, policy, block_size); + Alloc::Free(temporary); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first + ) { + MergeSort(first, last, temporary_first, + std::less::value_type>(), + ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison + ) { + MergeSort(first, last, temporary_first, comparison, ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void MergeSort( + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison, + const ExecutionPolicy& policy + ) { + MergeSort(first, last, temporary_first, comparison, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +void MergeSort( + const ExecutionPolicy& policy, + RAI first, + RAI last, + RAITemp temporary_first, + ComparisonFunction comparison, + size_t block_size + ); + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_MERGE_SORT_H_ diff --git a/algorithms_cpp/include/embb/algorithms/quick_sort.h b/algorithms_cpp/include/embb/algorithms/quick_sort.h new file mode 100644 index 0000000..cc61330 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/quick_sort.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_QUICK_SORT_H_ +#define EMBB_ALGORITHMS_QUICK_SORT_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \ingroup CPP_ALGORITHMS_SORTING + * \{ + */ + +#ifdef DOXYGEN + +/** + * Sorts a range of elements using a parallel quick sort algorithm. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. The algorithm sorts in-place and requires no additional memory. + * It has, however, a worst-case time complexity of + * O((last-first)2). + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range [first,last) are not + * modified by another thread while the algorithm is executed. + * \note No guarantee is given on the execution order of the comparison + * operations. + * \see ExecutionPolicy, MergeSort() + * \tparam RAI Random access iterator + * \tparam ComparisonFunction Binary predicate with both arguments of type + * std::iterator_traits::value_type. + */ +template +void QuickSort( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ComparisonFunction comparison + = std::less::value_type>(), + /**< [IN] Binary predicate used to establish the sorting order. An element + \c a appears before an element \c b in the sorted range if + comparison(a, b) == true. The default value uses the + less-than relation. */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the quick sort algorithm */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are sorted in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. Note that quick sort does not guarantee a + partitioning into evenly sized blocks, as the partitions depend on + the values to be sorted. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void QuickSort( + RAI first, + RAI last + ) { + QuickSort(first, last, + std::less::value_type>(), + ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void QuickSort( + RAI first, + RAI last, + ComparisonFunction comparison + ) { + QuickSort(first, last, comparison, ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void QuickSort( + RAI first, + RAI last, + ComparisonFunction comparison, + const ExecutionPolicy& policy + ) { + QuickSort(first, last, comparison, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +void QuickSort( + RAI first, + RAI last, + ComparisonFunction comparison, + const ExecutionPolicy& policy, + size_t block_size + ); + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb +#include + +#endif // EMBB_ALGORITHMS_QUICK_SORT_H_ diff --git a/algorithms_cpp/include/embb/algorithms/reduce.h b/algorithms_cpp/include/embb/algorithms/reduce.h new file mode 100644 index 0000000..015ce76 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/reduce.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_REDUCE_H_ +#define EMBB_ALGORITHMS_REDUCE_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_REDUCTION Reduction + * Parallel reduction computation + * \ingroup CPP_ALGORITHMS + * \{ + */ + +#ifdef DOXYGEN + +/** + * Performs a parallel reduction operation on a range of elements. + * + * The range consists of the elements from \c first to \c last, excluding the + * last element. The type of the result (\c ReturnType) is deduced from the + * \c neutral element. + * + * \return + * reduction(transformation(*first), ..., transformation(*(last-1))) + * where the reduction function is applied pairwise. + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the order in which the functions \c reduction + * and \c transformation are applied to the elements.\n + * For all \c x of type \c ReturnType it must hold that + * reduction(x, neutral) == x. \n + * The reduction operation need not be commutative but must be + * associative, i.e., reduction(x, reduction(y, z)) == + * reduction(reduction(x, y), z)) for all \c x, \c y, \c z of type + * \c ReturnType. + * \see ExecutionPolicy, ZipIterator, Identity + * \tparam RAI Random access iterator + * \tparam ReturnType Type of result of reduction operation, deduced from + * \c neutral + * \tparam ReductionFunction Binary reduction function with signature + * ReturnType ReductionFunction(ReturnType, ReturnType). + * \tparam TransformationFunction Unary transformation function with signature + * ReturnType TransformationFunction(typename + * std::iterator_traits::value_type) + */ +template +ReturnType Reduce( + RAI first, + /**< [IN] Random access iterator pointing to the first element of the range */ + RAI last, + /**< [IN] Random access iterator pointing to the last plus one element of the + range */ + ReturnType neutral, + /**< [IN] Neutral element of the reduction operation. */ + ReductionFunction reduction, + /**< [IN] Reduction operation to be applied to the elements of the range */ + TransformationFunction transformation = Identity(), + /**< [IN] Transforms the elements of the range before the reduction operation + is applied */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the reduction computation */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are treated in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction + ) { + return Reduce(first, last, neutral, reduction, Identity(), ExecutionPolicy(), + 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation + ) { + return Reduce(first, last, neutral, reduction, transformation, + ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const ExecutionPolicy& policy + ) { + return Reduce(first, last, neutral, reduction, transformation, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +ReturnType Reduce( + RAI first, + RAI last, + ReturnType neutral, + ReductionFunction reduction, + TransformationFunction transformation, + const ExecutionPolicy& policy, + size_t block_size + ); + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_REDUCE_H_ diff --git a/algorithms_cpp/include/embb/algorithms/scan.h b/algorithms_cpp/include/embb/algorithms/scan.h new file mode 100644 index 0000000..aa6f0c8 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/scan.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_SCAN_H_ +#define EMBB_ALGORITHMS_SCAN_H_ + +#include +#include + +namespace embb { +namespace algorithms { + +/** + * \defgroup CPP_ALGORITHMS_SCAN Scan + * Parallel scan computation + * \ingroup CPP_ALGORITHMS + * + * \{ + */ + +#ifdef DOXYGEN + +/** + * Performs a parallel scan (or prefix) computation on a range of elements. + * + * The algorithm reads an input range and writes its result to a separate output + * range. The input range consists of the elements from \c first to \c last, + * excluding the last element. The output range consists of the elements from + * \c output_first to output_first + std::difference(last - first). + * + * The algorithm performs two runs on the given range. Hence, a performance + * speedup can only be expected on processors with more than two cores. + * + * \throws embb::base::ErrorException if not enough MTAPI tasks can be created + * to satisfy the requirements of the algorithm. + * \threadsafe if the elements in the range are not modified by another thread + * while the algorithm is executed. + * \note No guarantee is given on the order in which the functions \c scan + * and \c transformation are applied to the elements.\n + * For all \c x of type \c ReturnType it must hold that + * reduction(x, neutral) == x. \n + * The reduction operation need not be commutative but must be + * associative, i.e., reduction(x, reduction(y, z)) == + * reduction(reduction(x, y), z)) for all \c x, \c y, \c z of type + * \c ReturnType. + * \see ExecutionPolicy, Identity, ZipIterator + * \tparam RAIIn Random access iterator type of input range + * \tparam RAIOut Random access iterator type of output range + * \tparam ReturnType Type of output elements of scan operation, deduced from + * \c neutral + * \tparam ScanFunction Binary scan function with signature + * ReturnType ScanFunction(ReturnType, ReturnType) + * \tparam TransformationFunction Unary transformation function with signature + * ReturnType TransformationFunction(typename + * std::iterator_traits::value_type). + */ +template +void Scan( + RAIIn first, + /**< [IN] Random access iterator pointing to the first element of the input + range */ + RAIIn last, + /**< [IN] Random access iterator pointing to the last plus one element of the + input range */ + RAIOut output_first, + /**< [IN] Random access iterator pointing to the first element of the output + range */ + ReturnType neutral, + /**< [IN] Neutral element of the \c scan operation. */ + ScanFunction scan, + /**< [IN] Scan operation to be applied to the elements of the input range */ + TransformationFunction transformation = Identity(), + /**< [IN] Transforms the elements of the input range before the scan operation + is applied */ + const ExecutionPolicy& policy = ExecutionPolicy(), + /**< [IN] ExecutionPolicy for the scan computation */ + size_t block_size = 0 + /**< [IN] Lower bound for partitioning the range of elements into blocks that + are treated in parallel. Partitioning of a block stops if its size + is less than or equal to \c block_size. The default value 0 means + that the minimum block size is determined automatically depending on + the number of elements in the range divided by the number of + available cores. */ + ); + +#else // DOXYGEN + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan + ) { + Scan(first, last, output_iterator, neutral, scan, Identity(), + ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan, + TransformationFunction transformation + ) { + Scan(first, last, output_iterator, neutral, scan, transformation, + ExecutionPolicy(), 0); +} + +/** + * Overload of above described Doxygen dummy with less arguments. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan, + TransformationFunction transformation, + const ExecutionPolicy& policy + ) { + Scan(first, last, output_iterator, neutral, scan, transformation, policy, 0); +} + +/** + * Overload of above described Doxygen dummy. + */ +template +void Scan( + RAIIn first, + RAIIn last, + RAIOut output_iterator, + ReturnType neutral, + ScanFunction scan, + TransformationFunction transformation, + const ExecutionPolicy& policy, + size_t block_size + ); + +#endif // else DOXYGEN + +/** + * \} + */ + +} // namespace algorithms +} // namespace embb + +#include + +#endif // EMBB_ALGORITHMS_SCAN_H_ diff --git a/algorithms_cpp/include/embb/algorithms/zip_iterator.h b/algorithms_cpp/include/embb/algorithms/zip_iterator.h new file mode 100644 index 0000000..a509136 --- /dev/null +++ b/algorithms_cpp/include/embb/algorithms/zip_iterator.h @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2014, 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 EMBB_ALGORITHMS_ZIP_ITERATOR_H_ +#define EMBB_ALGORITHMS_ZIP_ITERATOR_H_ + +#include +#include +#include + +namespace embb { +namespace algorithms { +/** + * \defgroup CPP_ALGORITHMS_ZIP_ITERATOR Zip Iterator + * Zip two iterators + * \ingroup CPP_ALGORITHMS + * \{ + */ + +/** + * Container for the values of two dereferenced iterators. + * The values contained are of type + * std::iterator_traits::reference. + * \notthreadsafe + * \tparam TypeA Type of the first value + * \tparam TypeB Type of the first value + * + */ +template +class ZipPair { + public: + /** + * Constructs a pair from two values. + */ + ZipPair( + TypeA first, + /**< [IN] First value*/ + TypeB second + /**< [IN] Second value*/ + ) + :first_(first), second_(second) {} + + /** + * Copies a pair. + */ + ZipPair( + const ZipPair& other + /**< [IN] pair to copy */ + ) + :first_(other.first_), second_(other.second_) {} + + /** + * Returns the first value of the pair. + * + * \return The first value of the pair. + */ + TypeA First() { + return first_; + } + + /** + * Returns the second value of the pair. + * + * \return The second value of the pair + */ + TypeB Second() { + return second_; + } + + /** + * Returns the first value of the pair. + * + * \return The first value of the pair. + */ + const TypeA First() const { + return first_; + } + + /** + * Returns the second value of the pair. + * + * \return The second value of the pair + */ + const TypeB Second() const { + return second_; + } + + private: + TypeA first_; + TypeB second_; + + /** + * Disable assignment since references cannot be assigned. + */ + ZipPair &operator=(const ZipPair&); +}; + +/** + * Zip container for two iterators. + * This container allows zipping two iterators together, thus enabling them + * to be used in situations where only one iterator can be used. Any operation + * applied to the zip iterator will subsequently be applied to the contained + * iterators. Dereferencing the iterator will return a ZipPair containing the + * values referenced by the iterators. + * \notthreadsafe + * \note It is required that the two iterators have the same \c difference_type + * or that at least the first iterator's \c difference_type can be implicitly + * converted to the second iterator's \c difference_type. Moreover, when + * calculating the distance between two ZipIterators, the distances between both + * pairs of iterators are equal. + * \see ZipPair + * \tparam IteratorA First iterator + * \tparam IteratorB Second iterator + */ +template +class ZipIterator{ + public: + /** + * \name Iterator Typedefs + * Necessary typedefs for iterators (std compatibility). + * \{ + */ + typedef std::random_access_iterator_tag iterator_category; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef typename std::iterator_traits::reference RefA; + typedef typename std::iterator_traits::reference RefB; + typedef typename std::iterator_traits::value_type ValueA; + typedef typename std::iterator_traits::value_type ValueB; + typedef ZipPair value_type; + typedef ZipPair reference; + typedef ZipPair pointer; + /** + * \} + */ + + /** + * Constructs a zip iterator from two iterators of any type. + */ + ZipIterator( + IteratorA iter_a, + /**< [IN] First iterator*/ + IteratorB iter_b + /**< [IN] Second iterator*/ + ) + :iter_a_(iter_a), iter_b_(iter_b) {} + + /** + * Compares two zip iterators for equality. + * + * \return \c true if zip iterators are equal, otherwise \c false + */ + bool operator==( + const ZipIterator &other + /**< [IN] Reference to right-hand side of equality operator */ + ) const { + return iter_a_ == other.iter_a_ && iter_b_ == other.iter_b_; + } + + /** + * Compares two zip iterators for inequality. + * + * \return \c true if any iterator doesn't equal the other, otherwise \c false + */ + bool operator!=( + const ZipIterator &other + /**< [IN] Reference to right-hand side of inequality operator */ + ) const { + return iter_a_ != other.iter_a_ || iter_b_ != other.iter_b_; + } + + /** + * Applies prefix increment on both iterators. + */ + void operator++() { + ++iter_a_; + ++iter_b_; + } + + /** + * Applies prefix decrement on both iterators. + */ + void operator--() { + --iter_a_; + --iter_b_; + } + + /** + * Returns an instance of a zip iterator where both iterators have been + * advanced by the specified distance. + * + * \return New zip iterator containing the advanced iterators + */ + ZipIterator operator+( + difference_type distance + /**< [IN] Number of elements to advance the iterators */ + ) const { + ZipIterator new_iterator(*this); + new_iterator.iter_a_ += distance; + new_iterator.iter_b_ += distance; + + return new_iterator; + } + + /** + * Returns an instance of a zip iterator where both iterators have been + * regressed by the specified distance. + * + * \return New zip iterator containing the regressed iterators + */ + ZipIterator operator-( + difference_type distance + /**< [IN] Number of elements to regress the iterators */ + ) const { + ZipIterator new_iterator(*this); + new_iterator.iter_a_ -= distance; + new_iterator.iter_b_ -= distance; + + return new_iterator; + } + + /** + * Advances both iterators by the specified distance. + * + * \return Reference to \c *this + */ + ZipIterator& operator+=( + difference_type distance + /**< [IN] Number of elements to advance the iterators */ + ) { + iter_a_ += distance; + iter_b_ += distance; + return *this; + } + + /** + * Regresses both iterators by the specified distance. + * + * \return Reference to \c *this + */ + ZipIterator& operator-=( + difference_type distance + /**< [IN] Number of elements to regress the iterators */ + ) { + iter_a_ -= distance; + iter_b_ -= distance; + return *this; + } + + /** + * Computes the distance between two zip iterators. + * It is assumed that both iterator pairs have the same distance. + * + * \return The distance between the zip iterators + */ + difference_type operator-( + const ZipIterator &other + /**< [IN] Reference to right-hand side of subtraction operator */ + ) const { + assert(iter_a_ - other.iter_a_ == iter_b_ - other.iter_b_); + return iter_a_ - other.iter_a_; + } + + /** + * Dereferences the zip iterator. + * + * \return ZipPair containing the dereferenced values. + */ + reference operator*() const { + return ZipPair(*iter_a_, *iter_b_); + } + + private: + IteratorA iter_a_; + IteratorB iter_b_; +}; + +/** + * Creates a zip iterator from two iterators. + * This is a convenience function which avoids calling the constructor of the + * templated class. + * + * \return Constructed zip iterator + * \tparam IteratorA Type of first iterator + * \tparam IteratorB Type of second iterator + */ +template +ZipIterator Zip( + IteratorA iter_a, + /**< [IN] First iterator */ + IteratorB iter_b + /**< [IN] Second iterator */ + ) { + return ZipIterator(iter_a, iter_b); +} + +/** + * \} + */ +} // namespace algorithms +} // namespace embb + +#endif // EMBB_ALGORITHMS_ZIP_ITERATOR_H_ diff --git a/algorithms_cpp/src/dummy.cc b/algorithms_cpp/src/dummy.cc new file mode 100644 index 0000000..cbdaf49 --- /dev/null +++ b/algorithms_cpp/src/dummy.cc @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, 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. + */ + +void dummy_so_that_cmake_can_determinie_linker_language_for_algorithms() { +} diff --git a/algorithms_cpp/src/execution_policy.cc b/algorithms_cpp/src/execution_policy.cc new file mode 100644 index 0000000..1ea0a2e --- /dev/null +++ b/algorithms_cpp/src/execution_policy.cc @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 + +namespace embb { +namespace algorithms { + +ExecutionPolicy::ExecutionPolicy() : + affinity_(), priority_(DefaultPriority) { +} + +ExecutionPolicy::ExecutionPolicy(bool initial_affinity, mtapi_uint_t priority) +:affinity_(initial_affinity), priority_(priority) { +} + +ExecutionPolicy::ExecutionPolicy(mtapi_uint_t priority) +:affinity_(), priority_(priority) { +} + +ExecutionPolicy::ExecutionPolicy(bool initial_affinity) +:affinity_(initial_affinity), priority_(DefaultPriority) { +} + +void ExecutionPolicy::AddWorker(mtapi_uint_t worker) { + affinity_.Add(worker); +} + +void ExecutionPolicy::RemoveWorker(mtapi_uint_t worker) { + affinity_.Remove(worker); +} + +bool ExecutionPolicy::IsSetWorker(mtapi_uint_t worker) { + return affinity_.IsSet(worker); +} + +const mtapi::Affinity &ExecutionPolicy::GetAffinity() const { + return affinity_; +} + +mtapi_uint_t ExecutionPolicy::GetPriority() const { + return priority_; +} + +const mtapi_uint_t ExecutionPolicy::DefaultPriority = 0; + +} // namespace algorithms +} // namespace embb diff --git a/algorithms_cpp/test/count_test.cc b/algorithms_cpp/test/count_test.cc new file mode 100644 index 0000000..4efd422 --- /dev/null +++ b/algorithms_cpp/test/count_test.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include + +struct IsEven{ + bool operator()(int val) { + if (val % 2 == 0) + return true; + else + return false; + } +}; + +bool IsEvenFunction(int val) { + if (val % 2 == 0) + return true; + else + return false; +} + +CountTest::CountTest() { + CreateUnit("Different data structures") + .Add(&CountTest::TestDataStructures, this); + CreateUnit("CountIf").Add(&CountTest::TestCountIf, this); + CreateUnit("Ranges").Add(&CountTest::TestRanges, this); + CreateUnit("Block sizes").Add(&CountTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&CountTest::TestPolicy, this); + CreateUnit("Stress test").Add(&CountTest::StressTest, this); +} + +void CountTest::TestDataStructures() { + using embb::algorithms::Count; + const int size =10; + int array[] = {10, 20, 30, 30, 20, 10, 10, 20, 20, 20}; + std::vector vector(array, array + size); + std::deque deque(array, array + size); + const std::vector const_vector(array, array + size); + + PT_EXPECT_EQ(Count(array, array + size, 10), 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10), 3); + PT_EXPECT_EQ(Count(deque.begin(), deque.end(), 10), 3); + PT_EXPECT_EQ(Count(const_vector.begin(), const_vector.end(), 10), 3); +} + +void CountTest::TestCountIf() { + using embb::algorithms::CountIf; + const int size =10; + int array[] = {10, 21, 30, 31, 20, 11, 10, 21, 20, 20}; + PT_EXPECT_EQ(CountIf(array, array + size, IsEven()), 6); + PT_EXPECT_EQ(CountIf(array, array + size, &IsEvenFunction), 6); +} + +void CountTest::TestRanges() { + using embb::algorithms::Count; + size_t count = 4; + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + vector[i] = static_cast(-1); + } + + // Ommit first element + PT_EXPECT_EQ(Count(vector.begin() + 1, vector.end(), -1), 3); + + // Ommit last element + PT_EXPECT_EQ(Count(vector.begin(), vector.end() - 1, -1), 3); + + // Ommit first and last element + PT_EXPECT_EQ(Count(vector.begin() + 1, vector.end() - 1, -1), 2); + + // Only do first element + PT_EXPECT_EQ(Count(vector.begin(), vector.begin() + 1, -1), 1); + + // Only do last element + PT_EXPECT_EQ(Count(vector.end() - 1, vector.end(), -1), 1); + + // Only do second element + PT_EXPECT_EQ(Count(vector.begin() + 1, vector.begin() + 2, -1), 1); +} + +void CountTest::TestBlockSizes() { + using embb::algorithms::Count; + size_t count = 4; + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + vector[i] = -1; + } + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), -1), + static_cast(count)); + } +} + +void CountTest::TestPolicy() { + using embb::algorithms::Count; + using embb::algorithms::ExecutionPolicy; + int a[] = { 10, 20, 30, 30, 20, 10, 10, 20, 20, 20 }; + std::vector vector(a, a + (sizeof a / sizeof a[0])); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, ExecutionPolicy()), 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, ExecutionPolicy(true)), + 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, ExecutionPolicy(false)), + 3); + PT_EXPECT_EQ(Count(vector.begin(), vector.end(), 10, + ExecutionPolicy(true, 1)), 3); +} + +void CountTest::StressTest() { + using embb::algorithms::Count; + size_t count = embb::mtapi::Node::GetInstance().GetCoreCount() *10; + std::vector large_vector(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast(0); + } + PT_EXPECT_EQ(Count(large_vector.begin(), large_vector.end(), 0), + static_cast(count)); +} diff --git a/algorithms_cpp/test/count_test.h b/algorithms_cpp/test/count_test.h new file mode 100644 index 0000000..31a4a6c --- /dev/null +++ b/algorithms_cpp/test/count_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_COUNT_TEST_H_ +#define ALGORITHMS_CPP_TEST_COUNT_TEST_H_ + +#include + +/** + * Provides tests for the Reduce method. + */ +class CountTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + CountTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the count if functionality. + */ + void TestCountIf(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_COUNT_TEST_H_ diff --git a/algorithms_cpp/test/for_each_test.cc b/algorithms_cpp/test/for_each_test.cc new file mode 100644 index 0000000..95e8ae8 --- /dev/null +++ b/algorithms_cpp/test/for_each_test.cc @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include + +/** + * Functor to compute the square of a number. + * + * The result overwrites the original number. + */ +struct Square { + template + void operator()(Type& l) { + l = l * l; + } +}; + +/** + * Free function to compute the square of a number. + * + * The result overwrites the original number. + */ +static void SquareFunction(int &val) { + val = val * val; +} + +ForEachTest::ForEachTest() { + CreateUnit("Different data structures") + .Add(&ForEachTest::TestDataStructures, this); + CreateUnit("Function Pointers").Add(&ForEachTest::TestFunctionPointers, this); + CreateUnit("Ranges").Add(&ForEachTest::TestRanges, this); + CreateUnit("Block sizes").Add(&ForEachTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&ForEachTest::TestPolicy, this); + CreateUnit("Stress test").Add(&ForEachTest::StressTest, this); +} + +void ForEachTest::TestDataStructures() { + using embb::algorithms::ForEach; + + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + + ForEach(array, array + kCountSize, Square()); + ForEach(vector.begin(), vector.end(), Square()); + ForEach(deque.begin(), deque.end(), Square()); + + for (size_t i = 0; i < kCountSize; i++) { + int expected = static_cast(i+2); + expected = expected * expected; + PT_EXPECT_EQ(expected, array[i]); + PT_EXPECT_EQ(expected, vector[i]); + PT_EXPECT_EQ(expected, deque[i]); + } +} + +void ForEachTest::TestFunctionPointers() { + using embb::algorithms::ForEach; + + std::vector vector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + } + ForEach(vector.begin(), vector.end(), &SquareFunction); + for (size_t i = 0; i < kCountSize; i++) { + int expected = static_cast(i+2); + expected = expected * expected; + PT_EXPECT_EQ(expected, vector[i]); + } +} + +void ForEachTest::TestRanges() { + using embb::algorithms::ForEach; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + // Ommit first element + vector = init; + ForEach(vector.begin() + 1, vector.end(), Square()); + PT_EXPECT_EQ(vector[0], init[0]); + for (size_t i = 1; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + // Ommit last element + vector = init; + ForEach(vector.begin(), vector.end() - 1, Square()); + for (size_t i = 0; i < count - 1; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + PT_EXPECT_EQ(vector[count - 1], init[count - 1]); + + // Ommit first and last element + vector = init; + ForEach(vector.begin() + 1, vector.end() - 1, Square()); + PT_EXPECT_EQ(vector[0], init[0]); + for (size_t i = 1; i < count - 1; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + PT_EXPECT_EQ(vector[count - 1], init[count - 1]); + + // Only do first element + vector = init; + ForEach(vector.begin(), vector.begin() + 1, Square()); + PT_EXPECT_EQ(vector[0], init[0] * init[0]); + for (size_t i = 1; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]); + } + + // Only do last element + vector = init; + ForEach(vector.end() - 1, vector.end(), Square()); + for (size_t i = 0; i < count - 1; i++) { + PT_EXPECT_EQ(vector[i], init[i]); + } + PT_EXPECT_EQ(vector[count - 1], init[count - 1] * init[count - 1]); + + // Only do second element + vector = init; + ForEach(vector.begin() + 1, vector.begin() + 2, Square()); + for (size_t i = 1; i < count; i++) { + if (i != 1) { + PT_EXPECT_EQ(vector[i], init[i]); + } else { + PT_EXPECT_EQ(vector[i], init[i] * init[i]); + } + } +} + +void ForEachTest::TestBlockSizes() { + using embb::algorithms::ForEach; + using embb::algorithms::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(), + block_size); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + } +} + +void ForEachTest::TestPolicy() { + using embb::algorithms::ForEach; + using embb::algorithms::ExecutionPolicy; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(true)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(false)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } + + vector = init; + ForEach(vector.begin(), vector.end(), Square(), ExecutionPolicy(true, 1)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], init[i]*init[i]); + } +} + +void ForEachTest::StressTest() { + using embb::algorithms::ForEach; + using embb::algorithms::ExecutionPolicy; + size_t count = embb::mtapi::Node::GetInstance().GetCoreCount() *10; + std::vector large_vector(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast((i + 2) % 1000); + } + ForEach(large_vector.begin(), large_vector.end(), Square(), ExecutionPolicy(), + 2000); + for (size_t i = 0; i < count; i++) { + int expected = static_cast((i + 2) % 1000); + expected = expected * expected; + PT_EXPECT_EQ(large_vector[i], expected); + } +} diff --git a/algorithms_cpp/test/for_each_test.h b/algorithms_cpp/test/for_each_test.h new file mode 100644 index 0000000..a72302d --- /dev/null +++ b/algorithms_cpp/test/for_each_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_FOR_EACH_TEST_H_ +#define ALGORITHMS_CPP_TEST_FOR_EACH_TEST_H_ + +#include + +/** + * Provides tests for the ForEach method. + */ +class ForEachTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + ForEachTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_FOR_EACH_TEST_H_ diff --git a/algorithms_cpp/test/invoke_test.cc b/algorithms_cpp/test/invoke_test.cc new file mode 100644 index 0000000..08fba2c --- /dev/null +++ b/algorithms_cpp/test/invoke_test.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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 +#include + +InvokeTest::InvokeTest() { + CreateUnit("Preliminary").Add(&InvokeTest::Test, this); +} + +static void Invocable1() {} +static void Invocable2() {} +static void Invocable3() {} +static void Invocable4() {} +static void Invocable5() {} +static void Invocable6() {} +static void Invocable7() {} +static void Invocable8() {} +static void Invocable9() {} +static void Invocable10() {} + +void InvokeTest::Test() { + using embb::algorithms::Invoke; + Invoke(&Invocable1); + Invoke(&Invocable1, &Invocable2); + Invoke(&Invocable1, &Invocable2, &Invocable3); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9); + Invoke(&Invocable1, &Invocable2, &Invocable3, &Invocable4, &Invocable5, + &Invocable6, &Invocable7, &Invocable8, &Invocable9, &Invocable10); +} diff --git a/algorithms_cpp/test/invoke_test.h b/algorithms_cpp/test/invoke_test.h new file mode 100644 index 0000000..2e3c896 --- /dev/null +++ b/algorithms_cpp/test/invoke_test.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_INVOKE_TEST_H_ +#define ALGORITHMS_CPP_TEST_INVOKE_TEST_H_ + +#include + +/** + * Provides tests for the Invoke method. + */ +class InvokeTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + InvokeTest(); + + private: + /** + * Tests ... + */ + void Test(); +}; + +#endif // ALGORITHMS_CPP_TEST_INVOKE_TEST_H_ diff --git a/algorithms_cpp/test/main.cc b/algorithms_cpp/test/main.cc new file mode 100644 index 0000000..cb61744 --- /dev/null +++ b/algorithms_cpp/test/main.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +int compute2_(int a) { + return a * 2; +} + +int compute1_() { + return 5; +} + +::std::string float_to_string(float val) { + std::stringstream s; + s << "Float: " << val; + + return std::string(s.str()); +} + +PT_MAIN("Algorithms") { + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + PT_RUN(PartitionerTest); + PT_RUN(ForEachTest); + PT_RUN(ReduceTest); + PT_RUN(ScanTest); + PT_RUN(CountTest); + PT_RUN(ZipIteratorTest); + PT_RUN(QuickSortTest); + PT_RUN(MergeSortTest); + PT_RUN(InvokeTest); + + embb::mtapi::Node::Finalize(); + // std::cout << "please press return to continue..." << std::endl; + // std::cin.get(); +} diff --git a/algorithms_cpp/test/merge_sort_test.cc b/algorithms_cpp/test/merge_sort_test.cc new file mode 100644 index 0000000..0ecbf73 --- /dev/null +++ b/algorithms_cpp/test/merge_sort_test.cc @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include +#include + +static bool DescendingComparisonFunction(double lhs, double rhs) { + return lhs < rhs ? true : false; +} + +MergeSortTest::MergeSortTest() { + CreateUnit("Different data structures") + .Add(&MergeSortTest::TestDataStructures, this); + CreateUnit("Function Pointers").Add(&MergeSortTest::TestFunctionPointers, + this); + CreateUnit("Ranges").Add(&MergeSortTest::TestRanges, this); + //CreateUnit("Block sizes").Add(&MergeSortTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&MergeSortTest::TestPolicy, this); + CreateUnit("Stress test").Add(&MergeSortTest::StressTest, this); +} + +void MergeSortTest::TestDataStructures() { + using embb::algorithms::MergeSortAllocate; + using embb::algorithms::ExecutionPolicy; + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end()); + MergeSortAllocate(array, array + kCountSize); + MergeSortAllocate(vector.begin(), vector.end()); + MergeSortAllocate(array, array + kCountSize, std::less(), + ExecutionPolicy(), 0); + MergeSortAllocate(deque.begin(), deque.end()); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], array[i]); + PT_EXPECT_EQ(vector_copy[i], vector[i]); + PT_EXPECT_EQ(vector_copy[i], deque[i]); + } +} + +void MergeSortTest::TestFunctionPointers() { + using embb::algorithms::MergeSortAllocate; + using embb::algorithms::ExecutionPolicy; + + std::vector vector(kCountSize); + for (size_t i = kCountSize - 1; i > 0; i--) { + vector[i] = static_cast(i + 2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end(), + &DescendingComparisonFunction); + MergeSortAllocate(vector.begin(), vector.end(), + &DescendingComparisonFunction); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void MergeSortTest::TestRanges() { + using embb::algorithms::MergeSortAllocate; + 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); + } + + // Ommit first element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end()); + MergeSortAllocate(vector.begin() + 1, vector.end()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end() - 1); + MergeSortAllocate(vector.begin(), vector.end() - 1); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit first and last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end() - 1); + MergeSortAllocate(vector.begin() + 1, vector.end() - 1); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do first two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.begin() + 2); + MergeSortAllocate(vector.begin(), vector.begin() + 2); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do last two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.end() - 2, vector_copy.end()); + MergeSortAllocate(vector.end() - 2, vector.end()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do second & third elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.begin() + 3); + MergeSortAllocate(vector.begin() + 1, vector.begin() + 3); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +//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::TestPolicy() { + 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 = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end()); + + MergeSortAllocate(vector.begin(), vector.end(), std::less(), + ExecutionPolicy()); + 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)); + 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(false)); + 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)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void MergeSortTest::StressTest() { + using embb::algorithms::MergeSortAllocate; + size_t count = embb::mtapi::Node::GetInstance().GetCoreCount() * 10; + std::vector large_vector(count); + std::vector vector_copy(count); + for (size_t i = count - 1; i > 0; i--) { + large_vector[i] = static_cast((i + 2) % 1000); + } + vector_copy = large_vector; + std::sort(vector_copy.begin(), vector_copy.end()); + MergeSortAllocate(large_vector.begin(), large_vector.end()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(large_vector[i], vector_copy[i]); + } +} diff --git a/algorithms_cpp/test/merge_sort_test.h b/algorithms_cpp/test/merge_sort_test.h new file mode 100644 index 0000000..a4d770b --- /dev/null +++ b/algorithms_cpp/test/merge_sort_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_MERGE_SORT_TEST_H_ +#define ALGORITHMS_CPP_TEST_MERGE_SORT_TEST_H_ + +#include + +/** + * Provides tests for the QuickSort method. + */ +class MergeSortTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + MergeSortTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + //void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_MERGE_SORT_TEST_H_ diff --git a/algorithms_cpp/test/partitioner_test.cc b/algorithms_cpp/test/partitioner_test.cc new file mode 100644 index 0000000..63df878 --- /dev/null +++ b/algorithms_cpp/test/partitioner_test.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include + +#include +#include + +PartitionerTest::PartitionerTest() { + CreateUnit("algorithms partitioner test"). + Add(&PartitionerTest::TestBasic, this); +} + +void PartitionerTest::TestBasic() { + int A[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + const int N = (sizeof(A) / sizeof(int) ); + + embb::algorithms::internal::ChunkPartitioner< int* > + partitioner(A, A + N, 5); + PT_EXPECT_EQ_MSG(*(partitioner[0].GetFirst()), 1, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[0].GetLast()-1), 3, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[1].GetFirst()), 4, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[1].GetLast()-1), 6, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[2].GetFirst()), 7, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[2].GetLast()-1), 9, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[3].GetFirst()), 10, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[3].GetLast()-1), 11, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[4].GetFirst()), 12, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner[4].GetLast()-1), 13, "Get end iterator"); + + PT_EXPECT_EQ_MSG(partitioner.Size(), size_t(5), "Check count of partitions"); + + embb::algorithms::internal::BlockSizePartitioner< int* > + partitioner2(A, A + N, 5); + + PT_EXPECT_EQ_MSG(*(partitioner2[0].GetFirst()), 1, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[0].GetLast() - 1), 5, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[1].GetFirst()), 6, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[1].GetLast() - 1), 10, "Get end iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[2].GetFirst()), 11, "Get start iterator"); + PT_EXPECT_EQ_MSG(*(partitioner2[2].GetLast() - 1), 13, "Get end iterator"); + + PT_EXPECT_EQ_MSG(partitioner2.Size(), size_t(3), "Check count of partitions"); +} + diff --git a/algorithms_cpp/test/partitioner_test.h b/algorithms_cpp/test/partitioner_test.h new file mode 100644 index 0000000..464b5a2 --- /dev/null +++ b/algorithms_cpp/test/partitioner_test.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ +#define ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ + +#include + +class PartitionerTest : public partest::TestCase { + public: + PartitionerTest(); + + private: + void TestBasic(); +}; + +#endif // ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ diff --git a/algorithms_cpp/test/quick_sort_test.cc b/algorithms_cpp/test/quick_sort_test.cc new file mode 100644 index 0000000..8d6cf65 --- /dev/null +++ b/algorithms_cpp/test/quick_sort_test.cc @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include +#include + +static bool DescendingComparisonFunction(double lhs, double rhs) { + if (lhs < rhs) { + return true; + } else { + return false; + } +} + +QuickSortTest::QuickSortTest() { + CreateUnit("Different data structures") + .Add(&QuickSortTest::TestDataStructures, this); + CreateUnit("Function Pointers").Add(&QuickSortTest::TestFunctionPointers, + this); + CreateUnit("Ranges").Add(&QuickSortTest::TestRanges, this); + CreateUnit("Block sizes").Add(&QuickSortTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&QuickSortTest::TestPolicy, this); + CreateUnit("Stress test").Add(&QuickSortTest::StressTest, this); +} + +void QuickSortTest::TestDataStructures() { + using embb::algorithms::QuickSort; + using embb::algorithms::ExecutionPolicy; + + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end()); + QuickSort(array, array + kCountSize, std::less()); + QuickSort(vector.begin(), vector.end()); + QuickSort(array, array + kCountSize, std::less(), ExecutionPolicy(), 1); + QuickSort(deque.begin(), deque.end()); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], array[i]); + PT_EXPECT_EQ(vector_copy[i], vector[i]); + PT_EXPECT_EQ(vector_copy[i], deque[i]); + } +} + +void QuickSortTest::TestFunctionPointers() { + using embb::algorithms::QuickSort; + + std::vector vector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i + 2); + } + std::vector vector_copy(vector); + std::sort(vector_copy.begin(), vector_copy.end(), + &DescendingComparisonFunction); + QuickSort(vector.begin(), vector.end(), &DescendingComparisonFunction); + for (size_t i = 0; i < kCountSize; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void QuickSortTest::TestRanges() { + using embb::algorithms::QuickSort; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector vector_copy(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + + // Ommit first element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end(), std::greater()); + QuickSort(vector.begin() + 1, vector.end(), std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end() - 1, std::greater()); + QuickSort(vector.begin(), vector.end() - 1, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Ommit first and last element + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.end() - 1, + std::greater()); + QuickSort(vector.begin() + 1, vector.end() - 1, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + + // Only do first two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.begin() + 2, std::greater()); + QuickSort(vector.begin(), vector.begin() + 2, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do last two elements + vector = init; + vector_copy = init; + std::sort(vector_copy.end() - 2, vector_copy.end(), std::greater()); + QuickSort(vector.end() - 2, vector.end(), std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + // Only do second & third elements + vector = init; + vector_copy = init; + std::sort(vector_copy.begin() + 1, vector_copy.begin() + 3, + std::greater()); + QuickSort(vector.begin() + 1, vector.begin() + 3, std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void QuickSortTest::TestBlockSizes() { + using embb::algorithms::QuickSort; + 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 = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end(), std::greater()); + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(), block_size); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector[i], vector_copy[i]); + } + } +} + +void QuickSortTest::TestPolicy() { + using embb::algorithms::QuickSort; + 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 = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + vector_copy = init; + std::sort(vector_copy.begin(), vector_copy.end(), std::greater()); + + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(true)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(false)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } + + vector = init; + QuickSort(vector.begin(), vector.end(), std::greater(), + ExecutionPolicy(true, 1)); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(vector_copy[i], vector[i]); + } +} + +void QuickSortTest::StressTest() { + using embb::algorithms::QuickSort; + size_t count = embb::mtapi::Node::GetInstance().GetCoreCount() *10; + std::vector large_vector(count); + std::vector vector_copy(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast((i + 2) % 1000); + } + vector_copy = large_vector; + std::sort(vector_copy.begin(), vector_copy.end(), std::greater()); + + QuickSort(large_vector.begin(), large_vector.end(), std::greater()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(large_vector[i], vector_copy[i]); + } +} diff --git a/algorithms_cpp/test/quick_sort_test.h b/algorithms_cpp/test/quick_sort_test.h new file mode 100644 index 0000000..220043e --- /dev/null +++ b/algorithms_cpp/test/quick_sort_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_QUICK_SORT_TEST_H_ +#define ALGORITHMS_CPP_TEST_QUICK_SORT_TEST_H_ + +#include + +/** + * Provides tests for the QuickSort method. + */ +class QuickSortTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + QuickSortTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_QUICK_SORT_TEST_H_ diff --git a/algorithms_cpp/test/reduce_test.cc b/algorithms_cpp/test/reduce_test.cc new file mode 100644 index 0000000..d4c48a8 --- /dev/null +++ b/algorithms_cpp/test/reduce_test.cc @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include + +/** + * Functor to compute the square of a number. + */ +struct Square { + template + Type operator()(Type& l) { + return l * l; + } +}; + +static int SquareFunction(int &val) { + return val * val; +} + +static int AddFunction(int lhs, int rhs) { + return lhs + rhs; +} + +ReduceTest::ReduceTest() { + CreateUnit("Different data structures") + .Add(&ReduceTest::TestDataStructures, this); + CreateUnit("Transform").Add(&ReduceTest::TestTransform, this); + CreateUnit("Function Pointers").Add(&ReduceTest::TestFunctionPointers, this); + CreateUnit("Ranges").Add(&ReduceTest::TestRanges, this); + CreateUnit("Block sizes").Add(&ReduceTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&ReduceTest::TestPolicy, this); + CreateUnit("Stress test").Add(&ReduceTest::StressTest, this); +} + +void ReduceTest::TestDataStructures() { + using embb::algorithms::Reduce; + int sum = 0; + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + + PT_EXPECT_EQ(Reduce(array, array + kCountSize, 0, std::plus()), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), static_cast(0), + std::plus()), sum); + PT_EXPECT_EQ(Reduce(deque.begin(), deque.end(), 0, std::plus()), sum); +} + +void ReduceTest::TestTransform() { + using embb::algorithms::Reduce; + int sum = 0; + std::vector vector(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + sum += static_cast((i + 2) * (i + 2)); + } + + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Square()), sum); +} + +void ReduceTest::TestFunctionPointers() { + using embb::algorithms::Reduce; + std::vector vector(kCountSize); + int sum = 0; + int sqr_sum = 0; + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i + 2); + sum += static_cast(i + 2); + sqr_sum += static_cast((i + 2) * (i + 2)); + } + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, &AddFunction), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, &AddFunction, + &SquareFunction), sqr_sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + &SquareFunction), sqr_sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, &AddFunction, + Square()), sqr_sum); +} + +void ReduceTest::TestRanges() { + using embb::algorithms::Reduce; + size_t count = 4; + int sum = 0; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + vector = init; + + // Ommit first element + PT_EXPECT_EQ(Reduce(vector.begin() + 1, vector.end(), 0, std::plus()), + sum - vector[0]); + // Ommit last element + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end() - 1, 0, std::plus()), + sum - vector[vector.size() - 1]); + // Ommit first and last element + PT_EXPECT_EQ(Reduce(vector.begin() + 1, vector.end() - 1, 0, + std::plus()), sum - vector[0] - vector[vector.size() - 1]); + // Only do first element + PT_EXPECT_EQ(Reduce(vector.begin(), vector.begin() + 1, 0, std::plus()), + vector[0]); + // Only do last element + PT_EXPECT_EQ(Reduce(vector.end() - 1, vector.end(), 0, std::plus()), + vector[vector.size() - 1]); + // Only do second element + PT_EXPECT_EQ(Reduce(vector.begin() + 1, vector.begin() + 2, 0, + std::plus()), vector[1]); +} + +void ReduceTest::TestBlockSizes() { + using embb::algorithms::Reduce; + size_t count = 4; + int sum = 0; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + vector = init; + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus()), + sum); + } +} + +void ReduceTest::TestPolicy() { + using embb::algorithms::Reduce; + using embb::algorithms::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = 4; + int sum = 0; + std::vector init(count); + std::vector vector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + sum += static_cast(i + 2); + } + vector = init; + + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Identity(), ExecutionPolicy()), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Identity(), ExecutionPolicy(true)), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, + std::plus(), Identity(), ExecutionPolicy(false)), sum); + PT_EXPECT_EQ(Reduce(vector.begin(), vector.end(), 0, std::plus(), + Identity(), ExecutionPolicy(true, 1)), sum); +} + +void ReduceTest::StressTest() { + using embb::algorithms::Reduce; + using embb::algorithms::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = embb::mtapi::Node::GetInstance().GetCoreCount() *10; + std::vector large_vector(count); + mtapi_int32_t expected = 0; + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast(i+2); + expected += large_vector[i]; + } + PT_EXPECT_EQ(Reduce(large_vector.begin(), large_vector.end(), + mtapi_int32_t(0), std::plus(), Identity(), + ExecutionPolicy(), 1960), expected); +} diff --git a/algorithms_cpp/test/reduce_test.h b/algorithms_cpp/test/reduce_test.h new file mode 100644 index 0000000..5bb7037 --- /dev/null +++ b/algorithms_cpp/test/reduce_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_REDUCE_TEST_H_ +#define ALGORITHMS_CPP_TEST_REDUCE_TEST_H_ + +#include + +/** + * Provides tests for the Reduce method. + */ +class ReduceTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + ReduceTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the reduce function with the transform interface. + */ + void TestTransform(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_REDUCE_TEST_H_ diff --git a/algorithms_cpp/test/scan_test.cc b/algorithms_cpp/test/scan_test.cc new file mode 100644 index 0000000..f1e10d0 --- /dev/null +++ b/algorithms_cpp/test/scan_test.cc @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include + +/** + * Functor to compute the square of a number. + */ +struct Square { + template + Type operator()(Type& l) { + return l * l; + } +}; + +static int SquareFunction(int &val) { + return val * val; +} + +static int AddFunction(int lhs, int rhs) { + return lhs + rhs; +} + +ScanTest::ScanTest() { + CreateUnit("Different data structures") + .Add(&ScanTest::TestDataStructures, this); + CreateUnit("Transform").Add(&ScanTest::TestTransform, this); + CreateUnit("Function Pointers").Add(&ScanTest::TestFunctionPointers, this); + CreateUnit("Ranges").Add(&ScanTest::TestRanges, this); + CreateUnit("Block sizes").Add(&ScanTest::TestBlockSizes, this); + CreateUnit("Policies").Add(&ScanTest::TestPolicy, this); + CreateUnit("Stress test").Add(&ScanTest::StressTest, this); +} + +void ScanTest::TestDataStructures() { + using embb::algorithms::Scan; + + int array[kCountSize]; + std::vector vector(kCountSize); + std::deque deque(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + array[i] = static_cast(i+2); + vector[i] = static_cast(i+2); + deque[i] = static_cast(i+2); + } + + int outputArray[kCountSize]; + std::vector outputVector(kCountSize); + std::deque outputDeque(kCountSize); + + Scan(array, array + kCountSize, outputArray, 0, std::plus()); + Scan(vector.begin(), vector.end(), outputVector.begin(), + static_cast(0), std::plus()); + Scan(deque.begin(), deque.end(), outputDeque.begin(), 0, std::plus()); + + int expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += array[i]; + + PT_EXPECT_EQ(expected, outputArray[i]); + PT_EXPECT_EQ(expected, outputVector[i]); + PT_EXPECT_EQ(expected, outputDeque[i]); + } +} + +void ScanTest::TestTransform() { + using embb::algorithms::Scan; + + std::vector vector(kCountSize); + std::vector outputVector(kCountSize); + + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + } + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Square()); + + int expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } +} + +void ScanTest::TestFunctionPointers() { + using embb::algorithms::Scan; + + std::vector vector(kCountSize); + std::vector init(kCountSize); + std::vector outputVector(kCountSize); + int sum = 0; + int sqr_sum = 0; + for (size_t i = 0; i < kCountSize; i++) { + vector[i] = static_cast(i+2); + init[i] = 0; + sum += static_cast(i + 2); + sqr_sum += static_cast((i + 2) * (i + 2)); + } + + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction); + int expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction, + &SquareFunction); + expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, &AddFunction, + Square()); + expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + &SquareFunction); + expected = 0; + for (size_t i = 0; i < kCountSize; i++) { + expected += vector[i] * vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } +} + +void ScanTest::TestRanges() { + using embb::algorithms::Scan; + using embb::algorithms::Identity; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector outputVector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + + // Ommit first element + outputVector = init; + Scan(vector.begin() + 1, vector.end(), outputVector.begin() + 1, + 0, std::plus()); + PT_EXPECT_EQ(outputVector[0], vector[0]); + int expected = 0; + for (size_t i = 1; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + // Ommit last element + outputVector = init; + Scan(vector.begin(), vector.end() - 1, outputVector.begin(), 0, + std::plus()); + expected = 0; + for (size_t i = 0; i < count - 1; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + PT_EXPECT_EQ(outputVector[count - 1], vector[count - 1]); + + // Ommit first and last element + outputVector = init; + Scan(vector.begin() + 1, vector.end() - 1, outputVector.begin() + 1, 0, + std::plus()); + PT_EXPECT_EQ(outputVector[0], vector[0]); + expected = 0; + for (size_t i = 1; i < count - 1; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + PT_EXPECT_EQ(outputVector[count - 1], vector[count - 1]); + + // Only do first element + outputVector = init; + Scan(vector.begin(), vector.begin() + 1, outputVector.begin(), 0, + std::plus()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(outputVector[i], vector[i]); + } + + // Only do last element + outputVector = init; + Scan(vector.end() - 1, vector.end(), outputVector.end() - 1, 0, + std::plus()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(outputVector[i], vector[i]); + } + + // Only do second element + outputVector = init; + Scan(vector.begin() + 1, vector.begin() + 2, outputVector.begin() + 1, 0, + std::plus()); + for (size_t i = 0; i < count; i++) { + PT_EXPECT_EQ(outputVector[i], vector[i]); + } +} + +void ScanTest::TestBlockSizes() { + using embb::algorithms::Scan; + using embb::algorithms::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector outputVector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + + for (size_t block_size = 1; block_size < count + 2; block_size++) { + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, + std::plus(), Identity(), ExecutionPolicy(), block_size); + int expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + } +} + +void ScanTest::TestPolicy() { + using embb::algorithms::Scan; + using embb::algorithms::ExecutionPolicy; + using embb::algorithms::Identity; + size_t count = 4; + std::vector init(count); + std::vector vector(count); + std::vector outputVector(count); + for (size_t i = 0; i < count; i++) { + init[i] = static_cast(i+2); + } + vector = init; + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy()); + int expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy(true)); + expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy(false)); + expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } + + outputVector = init; + Scan(vector.begin(), vector.end(), outputVector.begin(), 0, std::plus(), + Identity(), ExecutionPolicy(true, 1)); + expected = 0; + for (size_t i = 0; i < count; i++) { + expected += vector[i]; + PT_EXPECT_EQ(expected, outputVector[i]); + } +} + +void ScanTest::StressTest() { + using embb::algorithms::Scan; + using embb::algorithms::Identity; + using embb::algorithms::ExecutionPolicy; + size_t count = embb::mtapi::Node::GetInstance().GetCoreCount() *10; + std::vector large_vector(count); + std::vector large_vector_output(count); + for (size_t i = 0; i < count; i++) { + large_vector[i] = static_cast((i + 2) % 1000); + } + Scan(large_vector.begin(), large_vector.end(), large_vector_output.begin(), 0, + std::plus(), Identity(), ExecutionPolicy(), 2000); + int expected = 0; + for (size_t i = 0; i < count; i++) { + expected += large_vector[i]; + PT_EXPECT_EQ(expected, large_vector_output[i]); + } +} diff --git a/algorithms_cpp/test/scan_test.h b/algorithms_cpp/test/scan_test.h new file mode 100644 index 0000000..e1716ee --- /dev/null +++ b/algorithms_cpp/test/scan_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_SCAN_TEST_H_ +#define ALGORITHMS_CPP_TEST_SCAN_TEST_H_ + +#include + +/** + * Provides tests for the Scan method. + */ +class ScanTest : public partest::TestCase { + public: + /** + * Creates test units. + */ + ScanTest(); + + private: + /** + * Tests the compatibility with different data structures. + */ + void TestDataStructures(); + + /** + * Tests the scan function with the transform interface. + */ + void TestTransform(); + + /** + * Tests the compatibility with function pointers. + */ + void TestFunctionPointers(); + + /** + * Tests setting various ranges to be iterated. + */ + void TestRanges(); + + /** + * Tests various block sizes for the workers. + */ + void TestBlockSizes(); + + /** + * Tests setting policies (without checking their actual execution). + */ + void TestPolicy(); + + /** + * Stress tests by giving work for all workers. + */ + void StressTest(); + + static const size_t kCountSize = 5; +}; + +#endif // ALGORITHMS_CPP_TEST_SCAN_TEST_H_ diff --git a/algorithms_cpp/test/zip_iterator_test.cc b/algorithms_cpp/test/zip_iterator_test.cc new file mode 100644 index 0000000..6d59aba --- /dev/null +++ b/algorithms_cpp/test/zip_iterator_test.cc @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +#include +#include +#include +#include + +#include + +typedef std::vector::iterator VectorIterator; +typedef std::vector::const_iterator constVectorIterator; + +struct DotProductFunctor { + template + int operator()(embb::algorithms::ZipPair pair) { + return pair.First() * pair.Second(); + } + + template + int operator()(int lhs, embb::algorithms::ZipPair rhs) { + return lhs + rhs.First() * rhs.Second(); + } + + int operator()(int lhs, int rhs) { + return lhs + rhs; + } +}; + +/** + * Functor to compute the square of a number. + * The result overwrites the original number. + */ +struct Square { + void operator()(embb::algorithms::ZipPair pair) { + pair.First() = pair.First() * pair.First(); + pair.Second() = pair.Second() * pair.Second(); + } +}; + +ZipIteratorTest::ZipIteratorTest() { + CreateUnit("Zip foreach") + .Add(&ZipIteratorTest::TestZipForEach, this); + CreateUnit("Zip Reduce") + .Add(&ZipIteratorTest::TestZipReduce, this); + CreateUnit("Zip Scan") + .Add(&ZipIteratorTest::TestZipScan, this); + CreateUnit("Iterator Types") + .Add(&ZipIteratorTest::TestIteratorTypes, this); + CreateUnit("Double Zip") + .Add(&ZipIteratorTest::TestDoubleZip, this); +} + +void ZipIteratorTest::TestZipForEach() { + using embb::algorithms::ForEach; + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast((i + 1) % 1000); + vectorB[i] = static_cast((i + 2) % 1000); + } + ForEach( + embb::algorithms::Zip(vectorA.begin(), vectorB.begin()), + embb::algorithms::Zip(vectorA.end(), vectorB.end()), + Square()); + for (size_t i = 0; i < kCountSize; i++) { + int expected = static_cast((i + 1) % 1000); + expected = expected * expected; + PT_EXPECT_EQ(vectorA[i], expected); + expected = static_cast((i + 2) % 1000); + expected = expected * expected; + PT_EXPECT_EQ(vectorB[i], expected); + } +} + +void ZipIteratorTest::TestZipReduce() { + long sum = 0; + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i+2); + vectorB[i] = static_cast(i+2); + sum += static_cast((i + 2) * (i + 2)); + } + + std::vector::const_iterator iterA; + std::vector::const_iterator iterB; + embb::algorithms::ZipIterator + start_iterator = embb::algorithms::Zip(iterA = vectorA.begin(), + iterB = vectorB.begin()); + embb::algorithms::ZipIterator + end_iterator = embb::algorithms::Zip(iterA = vectorA.end(), + iterB = vectorB.end()); + + PT_EXPECT_EQ(embb::algorithms::Reduce(start_iterator, end_iterator, 0, + std::plus(), DotProductFunctor()), sum); +} + +void ZipIteratorTest::TestZipScan() { + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + std::vector vectorOut(kCountSize); + + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i+1); + vectorB[i] = static_cast(i+2); + } + + Scan(embb::algorithms::Zip(vectorA.begin(), vectorB.begin()), + embb::algorithms::Zip(vectorA.end(), vectorB.end()), + vectorOut.begin(), 0, std::plus(), DotProductFunctor(), + embb::algorithms::ExecutionPolicy(), 0); + + long sum = 0; + for (size_t i = 0; i < kCountSize; i++) { + sum += vectorA[i] * vectorB[i]; + PT_EXPECT_EQ(sum, vectorOut[i]); + } +} + +void ZipIteratorTest::TestIteratorTypes() { + long sum = 0; + + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + + int arrayA[kCountSize]; + int arrayB[kCountSize]; + + std::deque dequeA(kCountSize); + std::deque dequeB(kCountSize); + + const int constArrayA[] = {2, 3, 4, 5, 6, 7}; + const int constArrayB[] = {2, 3, 4, 5, 6, 7}; + + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i + 2); + vectorB[i] = static_cast(i + 2); + arrayA[i] = static_cast(i + 2); + arrayB[i] = static_cast(i + 2); + dequeA[i] = static_cast(i + 2); + dequeB[i] = static_cast(i + 2); + + sum += static_cast((i + 2) * (i + 2)); + } + + using embb::algorithms::Zip; + PT_EXPECT_EQ(Reduce(Zip(vectorA.begin(), vectorB.begin()), + Zip(vectorA.end(), vectorB.end()), 0, std::plus(), + DotProductFunctor()), sum); + PT_EXPECT_EQ(Reduce(Zip(dequeA.begin(), dequeB.begin()), + Zip(dequeA.end(), dequeB.end()), 0, std::plus(), + DotProductFunctor()), sum); + PT_EXPECT_EQ(Reduce(Zip(arrayA, arrayB), + Zip(arrayA + kCountSize, arrayB + kCountSize), 0, + std::plus(), DotProductFunctor()), sum); + PT_EXPECT_EQ(Reduce(Zip(constArrayA, constArrayB), + Zip(constArrayA + kCountSize, constArrayB + kCountSize), + 0, std::plus(), DotProductFunctor()), sum); +} + +struct MultiDotProductFunctor{ + mtapi_int64_t operator()( + embb::algorithms::ZipPair, + embb::algorithms::ZipPair > rhs ) { + return rhs.First().First() * rhs.First().Second() * + rhs.Second().First() * rhs.Second().Second(); + } +}; + +void ZipIteratorTest::TestDoubleZip() { + mtapi_int64_t sum = 0; + std::vector vectorA(kCountSize); + std::vector vectorB(kCountSize); + std::vector vectorC(kCountSize); + std::vector vectorD(kCountSize); + for (size_t i = 0; i < kCountSize; i++) { + vectorA[i] = static_cast(i + 1); + vectorB[i] = static_cast(i + 2); + vectorC[i] = static_cast(i + 3); + vectorD[i] = static_cast(i + 4); + sum += vectorA[i] * vectorB[i] * vectorC[i] * vectorD[i]; + } + + using embb::algorithms::Zip; + PT_EXPECT_EQ(Reduce( + Zip(Zip(vectorA.begin(), vectorB.begin()), + Zip(vectorC.begin(), vectorD.begin())), + Zip(Zip(vectorA.end(), vectorB.end()), + Zip(vectorC.end(), vectorD.end())), + mtapi_int64_t(0), std::plus(), + MultiDotProductFunctor()), sum); +} diff --git a/algorithms_cpp/test/zip_iterator_test.h b/algorithms_cpp/test/zip_iterator_test.h new file mode 100644 index 0000000..954a404 --- /dev/null +++ b/algorithms_cpp/test/zip_iterator_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 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 ALGORITHMS_CPP_TEST_ZIP_ITERATOR_TEST_H_ +#define ALGORITHMS_CPP_TEST_ZIP_ITERATOR_TEST_H_ + +#include + +/** + * Provides tests for the Zip Iterator class. + */ +class ZipIteratorTest : public partest::TestCase { + public: + ZipIteratorTest(); + + private: + /** + * Tests different iterator types. + */ + void TestIteratorTypes(); + + /** + * Tests the foreach with zipIterator. + */ + void TestZipForEach(); + + /** + * Tests the reduce with zipIterator. + */ + void TestZipReduce(); + + /** + * Tests the reduce with zipIterator. + */ + void TestZipScan(); + + /** + * Tests the double zipping scenario. + */ + void TestDoubleZip(); + + static const size_t kCountSize = 5; +}; + + +#endif // ALGORITHMS_CPP_TEST_ZIP_ITERATOR_TEST_H_ diff --git a/base_c/CMakeLists.txt b/base_c/CMakeLists.txt new file mode 100644 index 0000000..cfc98ef --- /dev/null +++ b/base_c/CMakeLists.txt @@ -0,0 +1,122 @@ +project (project_embb_base_c) + +include(../CMakeCommon/GroupSourcesMSVC.cmake) + + +## CODE FILE DETECTION +# +# Fetch all header and source files for lib and test build separately +file(GLOB_RECURSE EMBB_BASE_SOURCES "src/*.c" "src/*.h" "src/*.cc") +file(GLOB_RECURSE EMBB_BASE_HEADERS "include/embb/base/c/*.h") +if (BUILD_TESTS STREQUAL ON) + file(GLOB_RECURSE EMBB_BASE_TEST_SOURCES "test/*.cc" "test/*.h") + IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) + FOREACH(src_tmp ${EMBB_BASE_TEST_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) + ENDFOREACH(src_tmp) + ENDIF() +endif() + +IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) +FOREACH(src_tmp ${EMBB_BASE_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +ENDIF() + +include(CheckTypeSize) +check_type_size(char EMBB_CHAR_TYPE_SIZE) +check_type_size(short EMBB_SHORT_TYPE_SIZE) +check_type_size("unsigned short" EMBB_UNSIGNED_SHORT_TYPE_SIZE) +check_type_size(int EMBB_INT_TYPE_SIZE) +check_type_size("unsigned int" EMBB_UNSIGNED_INT_TYPE_SIZE) +check_type_size(long EMBB_LONG_TYPE_SIZE) +check_type_size("unsigned long" EMBB_UNSIGNED_LONG_TYPE_SIZE) +check_type_size("long long" EMBB_LONG_LONG_TYPE_SIZE) +check_type_size("unsigned long long" EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE) +check_type_size(intptr_t EMBB_INTPTR_T_TYPE_SIZE) +check_type_size(uintptr_t EMBB_UINTPTR_T_TYPE_SIZE) +check_type_size(size_t EMBB_SIZE_T_TYPE_SIZE) +check_type_size(ptrdiff_t EMBB_PTRDIFF_T_TYPE_SIZE) +check_type_size(uintmax_t EMBB_UINTMAX_T_TYPE_SIZE) + +# Create header file from input file +configure_file("include/embb/base/c/internal/atomic/atomic_sizes.h.in" + "include/embb/base/c/internal/atomic/atomic_sizes.h") + +## Compiling and linking assembler code for atomic operations in MSVC +# +# The assembler implementation for atomic operations in MSVC is +# contained in external asm files (inline assembler not possible). +# The lines below are used for compiling those file to object files +# and linking them to the embb library. +# +if(MSVC) + if(EMBB_INTPTR_T_TYPE_SIZE EQUAL 8) + set(EMBB_BASE_ASM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/atomicfunc_64.asm") + set(EMBB_BASE_ASM_OBJS "${CMAKE_CURRENT_BINARY_DIR}/atomicfunc_64.obj") + set(MSVC_ML ml64) + set(MSVC_SAFESEH "") + else() + set(EMBB_BASE_ASM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/atomicfunc_32.asm") + set(EMBB_BASE_ASM_OBJS "${CMAKE_CURRENT_BINARY_DIR}/atomicfunc_32.obj") + set(MSVC_ML ml) + set(MSVC_SAFESEH "/safeseh") + endif() + add_custom_command( + DEPENDS ${EMBB_BASE_ASM_SOURCES} + OUTPUT ${EMBB_BASE_ASM_OBJS} + COMMAND ${MSVC_ML} /c /Zi ${MSVC_SAFESEH} + ${EMBB_BASE_ASM_SOURCES} + /Fo ${EMBB_BASE_ASM_OBJS} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +## CONFIGURATION +# +# Check headers and create configure file with preprocessor defines +include(CheckIncludeFiles) # Includes module to perform checks +include(CheckSymbolExists) # Includes module to perform symbol checks +check_include_files("sys/sysinfo.h" EMBB_HAS_HEADER_SYSINFO) +link_libraries(${link_libraries} ${gnu_libs}) +set(CMAKE_EXTRA_INCLUDE_FILES sched.h) + check_type_size(cpu_set_t EMBB_HAS_GLIB_CPU) +set(CMAKE_EXTRA_INCLUDE_FILES) +if(DEFINED EMBB_HAS_GLIB_CPU) + add_definitions(-D_GNU_SOURCE) # Needed to activate CPU_ macros +endif() + +# Create header file from input file +configure_file("include/embb/base/c/internal/cmake_config.h.in" + "include/embb/base/c/internal/cmake_config.h") + +# Execute the GroupSources macro +GroupSourcesMSVC(include/embb/base/c) +GroupSourcesMSVC(src) +if (BUILD_TESTS STREQUAL ON) + GroupSourcesMSVC(test) +endif() + +## BUILD TARGETS +# +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + ) + +add_library(embb_base_c ${EMBB_BASE_SOURCES} ${EMBB_BASE_HEADERS} + ${EMBB_BASE_ASM_SOURCES} ${EMBB_BASE_ASM_OBJS}) + +if (BUILD_TESTS STREQUAL ON) + include_directories(test/ ${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_base_c_test ${EMBB_BASE_TEST_SOURCES}) + target_link_libraries(embb_base_c_test partest embb_base_c + ${compiler_libs}) + CopyBin(BIN embb_base_c_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_base_c DESTINATION lib) diff --git a/base_c/include/embb/base/c/atomic.h b/base_c/include/embb/base/c/atomic.h new file mode 100644 index 0000000..ebf4f09 --- /dev/null +++ b/base_c/include/embb/base/c/atomic.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_ATOMIC_H_ +#define EMBB_BASE_C_ATOMIC_H_ + +/** + * \defgroup C_BASE_ATOMIC Atomic + * + * \ingroup C_BASE + * + * Atomic operations. + * + * \anchor general_desc_atomic_base + * Atomic operations are not directly applied to fundamental types. Instead, + * there is for each character and integer type an associated atomic type that + * has the same bit width (if the target CPU supports atomic operations on + * that type): + * + * Fundamental type | Atomic type + * :------------------| :---------------------------------- + * char | embb_atomic_char + * short | embb_atomic_short + * unsigned short | embb_atomic_unsigned_short + * int | embb_atomic_int + * unsigned int | embb_atomic_unsigned_int + * long | embb_atomic_long + * unsigned long | embb_atomic_unsigned_long + * long long | embb_atomic_long_long + * unsigned long long | embb_atomic_unsigned_long_long + * intptr_t | embb_atomic_intptr_t + * uintptr_t | embb_atomic_uintptr_t + * size_t | embb_atomic_size_t + * ptrdiff_t | embb_atomic_ptrdiff_t + * uintmax_t | embb_atomic_uintmax_t + * + * Each of the atomic operations described in the following can be applied to + * the types listed above. However, to avoid unnecessary redundancy, we document + * them only once in a generic way. The keyword TYPE serves as a placeholder + * which has to be replaced by the concrete type (e.g., int). If the fundamental + * type contains spaces (e.g., unsigned int), "_" is used for concatenation + * (e.g. unsigned_int). + * + * Usage example: + * -------------- + * + * Store the value \c 5 in an atomic \c "unsigned int" variable. + * + * Step 1 (declare atomic variable): + * \code + * embb_atomic_unsigned_int my_var; + * \endcode + + * Step 2 (store the value): + * \code + * embb_atomic_store_unsigned_int( &my_var, 5 ); + * \endcode + * + * The current implementation guarantees sequential consistency (full fences) + * for all atomic operations. Relaxed memory models may be added in the future. + */ + +#ifdef DOXYGEN +/** + * Computes the logical "and" of the value stored in \p variable and \c value. + * + * The result is stored in \p variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE void embb_atomic_and_assign_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable which serves as left-hand side for + the "and" operation and is used to store the result. */ + TYPE value + /** [IN] Right-hand side of "and" operation, passed by value */ + ); + +/** + * Compares \p variable with \p expected and, if equivalent, swaps its value + * with \p desired. + * + * Stores \p desired in \p variable if the value of \p variable is equivalent + * to the value of \p expected. Otherwise, stores the value of \p variable in + * \p expected. + * + * \return != 0 if the values of \p variable and \p expected were equivalent \n + * 0 otherwise + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE int embb_atomic_compare_and_swap_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable */ + TYPE* expected, + /**< [IN,OUT] Pointer to expected value */ + TYPE desired + /**< [IN] Value to be stored in \p variable */ + ); + +/** + * Adds \p value to \p variable and returns its old value. + * + * \return The value before the operation + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE TYPE embb_atomic_fetch_and_add_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable*/ + TYPE value + /**< [IN] The value to be added to \p variable (can be negative) */ + ); + +/** + * Loads the value of \p variable and returns it. + * + * \return The value of the atomic variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE TYPE embb_atomic_load_TYPE( + const embb_atomic_TYPE* variable + /**< [IN] Pointer to atomic variable */ + ); + +/** + * Enforces a memory barrier (full fence). + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE void embb_atomic_memory_barrier(); + +/** + * Computes the logical "or" of the value stored in \p variable and \c value. + * + * The result is stored in \p variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE void embb_atomic_or_assign_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable which serves as left-hand side for + the "or" operation and is used to store the result. */ + TYPE value + /** [IN] Right-hand side of "or" operation, passed by value */ + ); + +/** + * Stores \p value in \p variable. + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE void embb_atomic_store_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable */ + int value + /**< [IN] Value to be stored */ + ); + +/** + * Swaps the current value of \p variable with \p value. + * + * \return The old value of \p variable + * + * \see \ref general_desc_atomic_base "Detailed description" for general + * information and the meaning of \b TYPE. + * + * \ingroup C_BASE_ATOMIC + * \waitfree + */ +EMBB_INLINE TYPE embb_atomic_swap_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable whose value is swapped */ + TYPE value + /** [IN] Value which will be stored in the atomic variable */ + ); + +/** +* Computes the logical "xor" of the value stored in \p variable and \c value. +* +* The result is stored in \p variable. +* +* \see \ref general_desc_atomic_base "Detailed description" for general +* information and the meaning of \b TYPE. +* +* \ingroup C_BASE_ATOMIC +* \waitfree +*/ +EMBB_INLINE void embb_atomic_xor_assign_TYPE( + embb_atomic_TYPE* variable, + /**< [IN,OUT] Pointer to atomic variable which serves as left-hand side for + the "xor" operation and is used to store the result. */ + TYPE value + /** [IN] Right-hand side of "xor" operation, passed by value */ + ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif //EMBB_BASE_C_ATOMIC_H_ diff --git a/base_c/include/embb/base/c/base.h b/base_c/include/embb/base/c/base.h new file mode 100644 index 0000000..bf13b59 --- /dev/null +++ b/base_c/include/embb/base/c/base.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_BASE_H_ +#define EMBB_BASE_C_BASE_H_ + +/** + * \defgroup C C Components + * Components written in C. + */ + +/** + * \defgroup C_BASE Base + * \ingroup C + * Platform-independent abstraction layer for multithreading and basic + * operations. + * + * This component provides basic functionalities, mainly for creating and + * synchronizing threads. Most of the functions are essentially wrappers for + * functions specific to the underlying operating system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* EMBB_BASE_C_BASE_H_ */ diff --git a/base_c/include/embb/base/c/condition_variable.h b/base_c/include/embb/base/c/condition_variable.h new file mode 100644 index 0000000..f26a0a4 --- /dev/null +++ b/base_c/include/embb/base/c/condition_variable.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_CONDITION_VARIABLE_H_ +#define EMBB_BASE_C_CONDITION_VARIABLE_H_ + +/** + * \defgroup C_BASE_CONDITION Condition Variable + * Condition variables for thread synchronization. + * + * Provides an abstraction from platform-specific condition variable + * implementations. Condition variables can be waited for with timeouts using + * relative durations and absolute time points. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing a condition variable. + */ +typedef opaque_type embb_condition_t; +#endif /* DOXYGEN */ + +/** + * Initializes a condition variable. + * + * \memory Potentially allocates dynamic memory + * \pre \c condition_var is not initialized + * \post If successful, \c condition_var is initialized + * \return EMBB_SUCCESS if successful \n + * EMBB_ERROR otherwise + * \notthreadsafe + * \see embb_condition_destroy() + */ +int embb_condition_init( + embb_condition_t* condition_var + /**< [OUT] Pointer to condition variable */ + ); + +/** + * Wakes up one thread waiting for \c condition_var. + * + * \pre \c condition_var is initialized + * \return EMBB_SUCCESS if signaling was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_all(), embb_condition_wait(), + * embb_condition_wait_until(), embb_condition_wait_for() + */ +int embb_condition_notify_one( + embb_condition_t* condition_var + /**< [IN/OUT] Pointer to condition variable */ + ); + +/** + * Wakes up all threads waiting for \c condition_var. + * + * \pre \c condition_var is initialized + * \return EMBB_SUCCESS if broadcast was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_wait(), + * embb_condition_wait_until(), embb_condition_wait_for() + */ +int embb_condition_notify_all( + embb_condition_t* condition_var + /**< [IN/OUT] Pointer to condition variable */ + ); + +/** + * Unlocks \c mutex and waits until the thread is woken up. + * + * \pre \c condition_var is initialized and \c mutex is locked by calling thread + * \post If successful, \c mutex is locked by the calling thread + * \return EMBB_SUCCESS if successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_notify_all(), + * embb_condition_wait_until(), embb_condition_wait_for() + */ +int embb_condition_wait( + embb_condition_t* condition_var, + /**< [IN/OUT] Pointer to condition variable */ + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Unlocks \c mutex and waits until the thread is woken up or \c time has + * passed. + * + * \pre \c condition_var is initialized and \c mutex is locked by calling thread + * \post If successful, \c mutex is locked by the calling thread + * \return EMBB_SUCCESS if successful \n + * EMBB_TIMEDOUT if mutex could not be locked until the specified point + * of time \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_notify_all(), + * embb_condition_wait(), embb_condition_wait_for() + */ +int embb_condition_wait_until( + embb_condition_t* condition_var, + /**< [IN/OUT] Pointer to condition variable */ + embb_mutex_t* mutex, + /**< [IN/OUT] Pointer to mutex */ + const embb_time_t* time + /**< [IN] Point of time until the thread waits */ + ); + +/** + * Unlocks \c mutex and waits until the thread is woken up or \c duration + * has passed. + * + * \pre \c condition_var is initialized and \c mutex is locked by calling thread + * \post If successful, \c mutex is locked by the calling thread + * \return EMBB_SUCCESS if successful \n + * EMBB_TIMEDOUT if mutex could not be locked within the specified time + * span \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_condition_notify_one(), embb_condition_notify_all(), + * embb_condition_wait(), embb_condition_wait_until() + */ +int embb_condition_wait_for( + embb_condition_t* condition_var, + /**< [IN/OUT] Pointer to condition variable */ + embb_mutex_t* mutex, + /**< [IN/OUT] Pointer to mutex */ + const embb_duration_t* duration + /**< [IN] Duration in microseconds the thread waits */ + ); + +/** + * Destroys \c condition_var and frees used memory. + * + * \pre \c condition_var is initialized and no thread is waiting for it using + * embb_condition_wait(), embb_condition_wait_for(), or + * embb_condition_wait_until(). + * \post \c condition_var is uninitialized + * \return EMBB_SUCCESS if destruction of condition variable was successful \n + * EMBB_ERROR otherwise + * \notthreadsafe + * \see embb_condition_init() + */ +int embb_condition_destroy( + embb_condition_t* condition_var + /**< [IN] Pointer to condition variable */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_CONDITION_VARIABLE_H_ */ diff --git a/base_c/include/embb/base/c/core_set.h b/base_c/include/embb/base/c/core_set.h new file mode 100644 index 0000000..876bb54 --- /dev/null +++ b/base_c/include/embb/base/c/core_set.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_CORE_SET_H_ +#define EMBB_BASE_C_CORE_SET_H_ + +#include + +/** + * \defgroup C_BASE_CORESET Core Set + * + * Core sets for thread-to-core affinities + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Opaque type representing a set of processor cores. + * + * An instance of this type represents a subset of processor cores. Core sets + * can be used to set thread-to-core affinities. A core in a core set might + * just represent a logical core (hyper-thread), depending on the underlying + * hardware. Each core is identified by a unique integer starting with 0. + * For example, the cores of a quad-core system are represented by the set + * {0,1,2,3}. + * + * \see embb_core_count_available() + */ +#ifdef DOXYGEN +typedef opaque_type embb_core_set_t; +#else +typedef struct embb_core_set_t { + uint64_t rep; +} embb_core_set_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Returns the number of available processor cores. + * + * If the processor supports hyper-threading, each hyper-thread is treated as a + * separate processor core. + * + * \return Number of cores including hyper-threads + * + * \notthreadsafe + */ +unsigned int embb_core_count_available(); + +/** + * Initializes the specified core set. + * + * The second parameter specifies whether the set is initially empty or contains + * all cores. + * + * \notthreadsafe + */ +void embb_core_set_init( + embb_core_set_t* core_set, + /**< [OUT] Core set to initialize */ + int initializer + /**< [IN] The set is initially empty if initializer == 0, otherwise it + contains all available processor cores. */ + ); + +/** + * Adds a core to the specified set. + * + * If the core is already contained in the set, the operation has no effect. + * + * \notthreadsafe + * \see embb_core_set_remove() + */ +void embb_core_set_add( + embb_core_set_t* core_set, + /**< [IN/OUT] Core set to be manipulated */ + unsigned int core_number + /**< [IN] Number of core to be added. */ + ); + +/** +* Removes a core from the specified set. +* +* If the core is not in the set, the operation has no effect. +* +* \notthreadsafe +* \see embb_core_set_add() +*/ +void embb_core_set_remove( + embb_core_set_t* core_set, + /**< [IN/OUT] Core set to be manipulated */ + unsigned int core_number + /**< [IN] Number of core to be removed */ + ); + +/** + * Determines whether a core is contained in the specified set. + * + * \return 0 if the core is not contained in the set, otherwise a number greater + * than zero. + * \notthreadsafe + */ +int embb_core_set_contains( + const embb_core_set_t* core_set, + /**< [IN] Core set */ + unsigned int core_number + /**< [IN] Number of core */ + ); + +/** + * Computes the intersection of core \c set1 and \c set2. + * + * The result is stored in \c set1. + * + * \notthreadsafe + * \see embb_core_set_union() + */ +void embb_core_set_intersection( + embb_core_set_t* set1, + /**< [IN/OUT] First set, gets overwritten by the result */ + const embb_core_set_t* set2 + /**< [IN] Second set */ + ); + +/** +* Computes the union of core \c set1 and \c set2. +* +* The result is stored in \c set1. +* +* \notthreadsafe +* \see embb_core_set_intersection() +*/ +void embb_core_set_union( + embb_core_set_t* set1, + /**< [IN/OUT] First set */ + const embb_core_set_t* set2 + /**< [IN] Second set */ + ); + +/** + * Returns the number of cores contained in the specified set. + * + * \notthreadsafe + * \return Number of cores in \c core_set + */ +unsigned int embb_core_set_count( + const embb_core_set_t* core_set + /**< [IN] Core set whose elements are counted */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_CORE_SET_H_ */ diff --git a/base_c/include/embb/base/c/counter.h b/base_c/include/embb/base/c/counter.h new file mode 100644 index 0000000..bf6ed07 --- /dev/null +++ b/base_c/include/embb/base/c/counter.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_COUNTER_H_ +#define EMBB_BASE_C_COUNTER_H_ + +/** + * \defgroup C_BASE_COUNTER Counter + * Thread-safe counter + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Opaque type representing a thread-safe counter. + */ +#ifdef DOXYGEN +typedef opaque_type embb_counter_t; +#else +typedef struct embb_counter_t { + embb_atomic_unsigned_int value; +} embb_counter_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Initializes \c counter and sets it to zero. + * + * \return EMBB_SUCCESS if counter could be initialized \n + * EMBB_ERROR otherwise + * + * \waitfree + */ +int embb_counter_init( + embb_counter_t* counter + /**< [OUT] Pointer to counter */ + ); + +/** + * Returns the current value of \c counter. + * + * \return Current value + * + * \waitfree + */ +unsigned int embb_counter_get( + embb_counter_t* counter + /**< [IN] Pointer to counter */ + ); + +/** + * Increments \c counter and returns the old value. + * + * \return Old, non-incremented value + * \waitfree + */ +unsigned int embb_counter_increment( + embb_counter_t* counter + /**< [IN,OUT] Pointer to counter */ + ); + +/** + * Decrements \c counter and returns the old value. + * + * \return Old, non-decremented value + * \waitfree + */ +unsigned int embb_counter_decrement( + embb_counter_t* counter + /**< [IN,OUT] Pointer to counter */ + ); + +/** + * Destroys an initialized counter. + * + * \pre Counter is initialized + * \post Counter is invalid and cannot be used anymore + * \waitfree + */ +void embb_counter_destroy( + embb_counter_t* counter + /**< [OUT] Pointer to counter */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_COUNTER_H_ */ diff --git a/base_c/include/embb/base/c/duration.h b/base_c/include/embb/base/c/duration.h new file mode 100644 index 0000000..75f3040 --- /dev/null +++ b/base_c/include/embb/base/c/duration.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_DURATION_H_ +#define EMBB_BASE_C_DURATION_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup C_BASE_DURATIONTIME Duration and Time + * + * Relative time durations and absolute time points + * + * \ingroup C_BASE + * \{ + * + * \name Duration + * \{ + */ + +/** + * Opaque type representing a relative time duration. + */ +#ifdef DOXYGEN +typedef opaque_type embb_duration_t; +#else +typedef struct embb_duration_t { + /** + * Seconds part of duration + */ + unsigned long long seconds; + /** + * Nanoseconds part of duration, smaller than a second + */ + unsigned long nanoseconds; +} embb_duration_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Macro for initializing a duration with zero length at definition. + */ +#ifdef DOXYGEN +#define EMBB_DURATION_INIT +#else +#define EMBB_DURATION_INIT {0, 0} +#endif /* defined(DOXYGEN) */ + +/** + * Returns duration with maximum ticks representable by implementation. + * + * \return Pointer to duration with maximum value + * \notthreadsafe + * \see embb_duration_min() + */ +const embb_duration_t* embb_duration_max(); + +/** + * Returns duration with minimum ticks representable by implementation. + * + * \return Pointer to duration with minimum value + * \notthreadsafe + * \see embb_duration_max() + */ +const embb_duration_t* embb_duration_min(); + +/** + * Returns duration of length zero. + * + * \return Pointer to duration of length zero + * \notthreadsafe + */ +const embb_duration_t* embb_duration_zero(); + +/** + * Set duration from nanosecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given nanosecond interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given nanosecond interval is too large to be + * represented by implementation + * \notthreadsafe + */ +int embb_duration_set_nanoseconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long nanoseconds + /**< [IN] Nanosecond ticks */ + ); + +/** + * Sets duration from microsecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given microsecond interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given microsecond interval is too large to be + * represented by implementation + * \notthreadsafe + */ +int embb_duration_set_microseconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long microseconds + /**< [IN] Microsecond ticks */ + ); + +/** + * Sets duration from millisecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given millisecond interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given millisecond interval is too large to be + * represented by implementation \n + * \notthreadsafe + */ +int embb_duration_set_milliseconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long milliseconds + /**< [IN] Millisecond ticks */ + ); + +/** + * Sets duration from second ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if given second interval is too small to be + * represented by implementation \n + * EMBB_OVERFLOW if given second interval is too large to be + * represented by implementation + * \notthreadsafe + */ +int embb_duration_set_seconds( + embb_duration_t* duration, + /**< [OUT] Pointer to duration */ + unsigned long long seconds + /**< [IN] Second ticks */ + ); + +/** + * Adds two durations. + * + * Computest the sum of \c rhs and \c lhs and stores the result in \c lhs. + * + * \return EMBB_SUCCESS \n + * EMBB_OVERFLOW if sum is greater than embb_duration_max() + * \notthreadsafe + */ +int embb_duration_add( + embb_duration_t* lhs, + /**< [IN/OUT] Left-hand side operand, overwritten by result of addition */ + const embb_duration_t* rhs + /**< [IN] Right-hand side operand of addition */ + ); + +/** + * Converts duration to nanosecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * nanosecond \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_nanoseconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* nanoseconds + /**< [OUT] Pointer to nanosecond ticks of duration */ + ); + +/** + * Converts duration to microsecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * microsecond \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_microseconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* microseconds + /**< [OUT] Pointer to microsecond ticks of duration */ + ); + +/** + * Converts duration to millisecond ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * millisecond \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_milliseconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* milliseconds + /**< [OUT] Pointer to millisecond ticks of duration */ + ); + +/** + * Converts duration to second ticks. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration contains fractions less than a + * second \n + * EMBB_OVERFLOW if duration is not representable by tick type + * \notthreadsafe + */ +int embb_duration_as_seconds( + const embb_duration_t* duration, + /**< [IN] Pointer to duration */ + unsigned long long* seconds + /**< [OUT] Pointer to second ticks of duration */ + ); + +/** + * Compares two durations. + * + * \return -1 if \c lhs < \c rhs \n + * 0 if \c lhs == \c rhs \n + * 1 if \c lhs > \c rhs + * \notthreadsafe + */ +int embb_duration_compare( + const embb_duration_t* lhs, + /**< [IN] Pointer to left-hand side operand */ + const embb_duration_t* rhs + /**< [IN] Pointer to right-hand side operand */ + ); + +/** + * \} + * \} + */ + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +#endif /* EMBB_BASE_C_DURATION_H_ */ diff --git a/base_c/include/embb/base/c/errors.h b/base_c/include/embb/base/c/errors.h new file mode 100644 index 0000000..0f46373 --- /dev/null +++ b/base_c/include/embb/base/c/errors.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_ERRORS_H_ +#define EMBB_BASE_C_ERRORS_H_ + +/** + * \defgroup C_BASE_ERROR Error + * + * Error codes for function return values + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Return value codes for functions. + */ +typedef enum { + EMBB_SUCCESS, /**< Successful */ + EMBB_NOMEM, /**< Error, not enough memory */ + EMBB_TIMEDOUT, /**< Error, timed out */ + EMBB_BUSY, /**< Resource busy */ + EMBB_OVERFLOW, /**< Error, numeric overflow */ + EMBB_UNDERFLOW, /**< Error, numeric underflow */ + EMBB_ERROR /**< Error, not further specified */ +} embb_errors_t; + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_ERRORS_H_ */ diff --git a/base_c/include/embb/base/c/internal/atomic/and_assign.h b/base_c/include/embb/base/c/internal/atomic/and_assign.h new file mode 100644 index 0000000..534e679 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/and_assign.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_AND_ASSIGN_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_AND_ASSIGN_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* + * The EMBB_DEFINE_[ATOMIC METHOD] macro defines, given the size in bytes, + * the assembler prefix for that size (e.g., "b" for 1 byte), the assembler + * implementation for [ATOMIC METHOD]. For GCC, inline assembler is used. + * For MSVC, an external assembler implementation is called, as Microsoft + * doesn't allow inline assembler with 64-bit code. In that case, the + * assembly code is linked from an external object file. + * + * The generated function has the following signature (in the and_assign.h file, + * analogous for other atomic methods): + * + * static inline void embb_internal__atomic_and_assign_[BYTE_SIZE] + * (EMBB_BASE_BASIC_TYPE_SIZE_[BYTE_SIZE] volatile* pointer_to_value, + * EMBB_BASE_BASIC_TYPE_SIZE_[BYTE_SIZE] value) + * + * BYTE_SIZE is the number of bytes passed to the macro. + * + */ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_AND_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value); \ + EMBB_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_AND_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX)\ + EMBB_INLINE void EMBB_CAT2(embb_internal__atomic_and_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + __asm__ __volatile__("lock and" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (value) \ + : \ + : "memory"); \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* + * The three or four macro calls below generate the methods for 1, 2, 4, and + * bytes, as stated in the macro definition. + */ +EMBB_DEFINE_AND_ASSIGN(1, "b") +EMBB_DEFINE_AND_ASSIGN(2, "w") +EMBB_DEFINE_AND_ASSIGN(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_AND_ASSIGN(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_AND_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + void EMBB_CAT2(embb_internal__atomic_and_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + value) { \ + register \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + t1, t2, t3; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %0\n\t" \ + "and %3, %2, %1\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %4, %3, %0\n\t" \ + "teq %4, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "+m" (*pointer_to_value), "+r" (value), "=r" (t1), "=r" (t2), "=r" (t3) \ + : \ + : "memory", "cc" ); \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_AND_ASSIGN(1, "b") +EMBB_DEFINE_AND_ASSIGN(2, "h") +EMBB_DEFINE_AND_ASSIGN(4, "") + +#else +#error "Unknown architecture" +#endif + +/* + * Now, using the basic functions above, we generate the respective functions + * for all basic data types, like "unsigned short". For that purpose, the + * following generator macro is used. This macro is called by the macros in the + * generator header, defining the implementation for the basic data types. + * + * For unsigned short and for and_assign.h, the following method would be + * generated (analogous for other atomic methods): + * + * static inline void embb_atomic_and_assign_unsigned_short ( + * embb_atomic_unsigned_short* variable, unsigned short value) { + * embb_internal__atomic_and_assign_2((EMBB_BASE_BASIC_TYPE_SIZE_2 volatile *) + * (&(variable->internal_variable)), *((EMBB_BASE_BASIC_TYPE_SIZE_2*) + * (&value))); } + * + * This generated function is supposed to be called by the user of the lib. + */ +#define EMBB_ATOMIC_INTERNAL_DEFINE_AND_ASSIGN_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE void EMBB_CAT2(embb_atomic_and_assign_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun;\ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + EMBB_CAT2(embb_internal__atomic_and_assign_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *) \ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE AND_ASSIGN_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_AND_ASSIGN_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in b/base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in new file mode 100644 index 0000000..b44d1f3 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/atomic_sizes.h.in @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_SIZES_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_SIZES_H_ + +#include +#include + +typedef uint8_t EMBB_BASE_BASIC_TYPE_SIZE_1; +typedef uint16_t EMBB_BASE_BASIC_TYPE_SIZE_2; +typedef uint32_t EMBB_BASE_BASIC_TYPE_SIZE_4; +typedef uint64_t EMBB_BASE_BASIC_TYPE_SIZE_8; + +/** + * CMake is used to set the correct size for types used in the atomic + * library. + * + */ +#define EMBB_CHAR_TYPE_SIZE ${EMBB_CHAR_TYPE_SIZE} +#define EMBB_SHORT_TYPE_SIZE ${EMBB_SHORT_TYPE_SIZE} +#define EMBB_UNSIGNED_SHORT_TYPE_SIZE ${EMBB_UNSIGNED_SHORT_TYPE_SIZE} +#define EMBB_INT_TYPE_SIZE ${EMBB_INT_TYPE_SIZE} +#define EMBB_UNSIGNED_INT_TYPE_SIZE ${EMBB_UNSIGNED_INT_TYPE_SIZE} +#define EMBB_LONG_TYPE_SIZE ${EMBB_LONG_TYPE_SIZE} +#define EMBB_UNSIGNED_LONG_TYPE_SIZE ${EMBB_UNSIGNED_LONG_TYPE_SIZE} +#define EMBB_LONG_LONG_TYPE_SIZE ${EMBB_LONG_LONG_TYPE_SIZE} +#define EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE ${EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE} +#define EMBB_INTPTR_T_TYPE_SIZE ${EMBB_INTPTR_T_TYPE_SIZE} +#define EMBB_UINTPTR_T_TYPE_SIZE ${EMBB_UINTPTR_T_TYPE_SIZE} +#define EMBB_SIZE_T_TYPE_SIZE ${EMBB_SIZE_T_TYPE_SIZE} +#define EMBB_PTRDIFF_T_TYPE_SIZE ${EMBB_PTRDIFF_T_TYPE_SIZE} +#define EMBB_UINTMAX_T_TYPE_SIZE ${EMBB_UINTMAX_T_TYPE_SIZE} + + +#ifdef EMBB_ARCH_X86_64 +# define EMBB_64_BIT_ATOMIC_AVAILABLE_VAL 1 +# define EMBB_64_BIT_ATOMIC_AVAILABLE +#else +# define EMBB_64_BIT_ATOMIC_AVAILABLE_VAL 0 +#endif + +#if( (EMBB_CHAR_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_CHAR_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_SHORT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_SHORT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_SHORT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_SHORT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_INT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_INT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_INT_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_INT_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_LONG_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_LONG_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UNSIGNED_LONG_LONG_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_INTPTR_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_INTPTR_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UINTPTR_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UINTPTR_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_SIZE_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_SIZE_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_PTRDIFF_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_PTRDIFF_T_TYPE_IS_ATOMIC +#endif + +#if( (EMBB_UINTMAX_T_TYPE_SIZE < 8) || (EMBB_64_BIT_ATOMIC_AVAILABLE_VAL == 1) ) +# define EMBB_UINTMAX_T_TYPE_IS_ATOMIC +#endif + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_SIZES_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/atomic_variables.h b/base_c/include/embb/base/c/internal/atomic/atomic_variables.h new file mode 100644 index 0000000..57aa9b3 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/atomic_variables.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_ATOMIC_VARIABLES_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_ATOMIC_VARIABLES_H_ + +#include +#include + +#ifdef EMBB_COMPILER_MSVC +#include +#endif + +#define EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE( \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, \ + EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX) \ + typedef struct \ +{ \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE internal_variable; \ +} EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX); + +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(char, char) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(short, short) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned short, unsigned_short) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(int, int) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned int, unsigned_int) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(long, long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned long, unsigned_long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(long long, long_long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(unsigned long long, unsigned_long_long) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(intptr_t, intptr_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(uintptr_t, uintptr_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(size_t, size_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(ptrdiff_t, ptrdiff_t) +EMBB_ATOMIC_INTERNAL_DEFINE_VARIABLE(uintmax_t, uintmax_t) + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_ATOMIC_VARIABLES_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/compare_and_swap.h b/base_c/include/embb/base/c/internal/atomic/compare_and_swap.h new file mode 100644 index 0000000..41a01ba --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/compare_and_swap.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_COMPARE_AND_SWAP_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_COMPARE_AND_SWAP_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_COMPARE_AND_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern int __fastcall EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* expected, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired); \ + EMBB_INLINE int __fastcall EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_PARAMETER_SIZE_BYTE)( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* expected, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired) { \ + int result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, expected, desired); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_COMPARE_AND_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_INLINE int EMBB_CAT2(embb_internal__atomic_compare_and_swap_, \ + EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* expected, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired) { \ + register char result; \ + __asm__ __volatile__ ("lock cmpxchg" EMBB_ATOMIC_X86_SIZE_SUFFIX\ + " %3, %0 \n\t" \ + "setz %2 \n\t" \ + : "+m" (*pointer_to_value), "+a" (*expected), "=q" (result) \ + : "q" (desired) \ + : "memory", "cc" ); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_COMPARE_AND_SWAP(1, "b") +EMBB_DEFINE_COMPARE_AND_SWAP(2, "w") +EMBB_DEFINE_COMPARE_AND_SWAP(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_COMPARE_AND_SWAP(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_COMPARE_AND_SWAP(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + int EMBB_CAT2(embb_internal__atomic_compare_and_swap_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* expected, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) desired) { \ + register \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + result, t1, t2; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "ldr %4, %1\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %3, %0\n\t" \ + "mov %2, #1\n\t" \ + "cmp %3, %4\n\t" \ + "bne fail_%=\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %5, %0\n\t" \ + "teq %2, #0\n\t" \ + "bne loop_%=\n\t" \ + "fail_%=:\n\t" \ + "str %3, %1\n\t" \ + "eor %2, %2, #1\n\t" \ + "isb" \ + : "+m" (*pointer_to_value), "+m" (*expected), \ + "=r" (result), "=r" (t1), "=r" (t2), "+r" (desired) \ + : \ + : "memory", "cc" ); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_COMPARE_AND_SWAP(1, "b") +EMBB_DEFINE_COMPARE_AND_SWAP(2, "h") +EMBB_DEFINE_COMPARE_AND_SWAP(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_COMPARE_AND_SWAP_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE int EMBB_CAT2(embb_atomic_compare_and_swap_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE* expected, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE desired) {\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) desired_pun;\ + memcpy(&desired_pun, &desired, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + return EMBB_CAT2(embb_internal__atomic_compare_and_swap_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *) \ + (&(variable->internal_variable)), (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *) expected, desired_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE COMPARE_AND_SWAP_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_COMPARE_AND_SWAP_H_ + diff --git a/base_c/include/embb/base/c/internal/atomic/fetch_and_add.h b/base_c/include/embb/base/c/internal/atomic/fetch_and_add.h new file mode 100644 index 0000000..3ea2490 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/fetch_and_add.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_FETCH_AND_ADD_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_FETCH_AND_ADD_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_FETCH_AND_ADD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value); \ + EMBB_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_PARAMETER_SIZE_BYTE) (\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) {\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_fetch_and_add_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, new_value); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_FETCH_AND_ADD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_PARAMETER_SIZE_BYTE) \ + (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) { \ + __asm__ __volatile__ ("lock xadd" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (new_value) \ + : \ + : "memory", "cc" ); \ + return new_value; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_FETCH_AND_ADD(1, "b") +EMBB_DEFINE_FETCH_AND_ADD(2, "w") +EMBB_DEFINE_FETCH_AND_ADD(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_FETCH_AND_ADD(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_FETCH_AND_ADD(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + EMBB_CAT2(embb_internal__atomic_fetch_and_add_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + new_value) { \ + register \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + t1, t2, t3; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %0\n\t" \ + "add %3, %2, %1\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %4, %3, %0\n\t" \ + "teq %4, #0\n\t" \ + "bne loop_%=\n\t" \ + "mov %1, %2\n\t" \ + "isb" \ + : "+m" (*pointer_to_value), "+r" (new_value), "=r" (t1), "=r" (t2), "=r" (t3) \ + : \ + : "memory", "cc" ); \ + return new_value; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_FETCH_AND_ADD(1, "b") +EMBB_DEFINE_FETCH_AND_ADD(2, "h") +EMBB_DEFINE_FETCH_AND_ADD(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_FETCH_AND_ADD_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE EMBB_ATOMIC_PARAMETER_TYPE_NATIVE EMBB_CAT2(embb_atomic_fetch_and_add_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun; \ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) return_val = EMBB_CAT2(embb_internal__atomic_fetch_and_add_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)(\ + (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE return_val_pun; \ + memcpy(&return_val_pun, &return_val, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + return return_val_pun; \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE FETCH_AND_ADD_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_FETCH_AND_ADD_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h b/base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h new file mode 100644 index 0000000..6f7ab9a --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/generate_atomic_implementation_template.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, 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. + */ + +// This file is used as a template, it is not a normal header file! +// Do not include it as a header! It is used for generating methods +// for all atomic types. + +#define EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_PARAMETER_POSTFIX, \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, \ + EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, \ + EMBB_ATOMIC_PARAMETER_TYPE_SIZE \ + ) \ + EMBB_CAT2(EMBB_ATOMIC_INTERNAL_DEFINE_, EMBB_ATOMIC_PARAMETER_POSTFIX)\ + (EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, \ + EMBB_ATOMIC_PARAMETER_TYPE_SIZE) + +#ifdef EMBB_CHAR_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC(EMBB_ATOMIC_METHOD_TO_GENERATE, \ + char, char, EMBB_CHAR_TYPE_SIZE) +#endif +#ifdef EMBB_SHORT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + short, short, EMBB_SHORT_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_SHORT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned short, unsigned_short, EMBB_UNSIGNED_SHORT_TYPE_SIZE) +#endif +#ifdef EMBB_INT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + int, int, EMBB_INT_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_INT_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned int, unsigned_int, EMBB_UNSIGNED_INT_TYPE_SIZE) +#endif +#ifdef EMBB_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + long, long, EMBB_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned long, unsigned_long, EMBB_UNSIGNED_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_LONG_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + long long, long_long, EMBB_LONG_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_UNSIGNED_LONG_LONG_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + unsigned long long, unsigned_long_long, EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE) +#endif +#ifdef EMBB_INTPTR_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + intptr_t, intptr_t, EMBB_INTPTR_T_TYPE_SIZE) +#endif +#ifdef EMBB_UINTPTR_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + uintptr_t, uintptr_t, EMBB_UINTPTR_T_TYPE_SIZE) +#endif +#ifdef EMBB_SIZE_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + size_t, size_t, EMBB_SIZE_T_TYPE_SIZE) +#endif +#ifdef EMBB_PTRDIFF_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + ptrdiff_t, ptrdiff_t, EMBB_PTRDIFF_T_TYPE_SIZE) +#endif +#ifdef EMBB_UINTMAX_T_TYPE_IS_ATOMIC +EMBB_GENERATE_ATOMIC_FUNC( EMBB_ATOMIC_METHOD_TO_GENERATE, \ + uintmax_t, uintmax_t, EMBB_UINTMAX_T_TYPE_SIZE) +#endif diff --git a/base_c/include/embb/base/c/internal/atomic/load.h b/base_c/include/embb/base/c/internal/atomic/load.h new file mode 100644 index 0000000..effb300 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/load.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_LOAD_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_LOAD_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_LOAD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value); \ + EMBB_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value) { \ + register EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_load_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_LOAD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value) { \ + /* no fence required for loads */ \ + register EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + __asm__ __volatile__("mov" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "=q" (result) \ + : "m" (*pointer_to_value) \ + : "memory"); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_LOAD(1, "b") +EMBB_DEFINE_LOAD(2, "w") +EMBB_DEFINE_LOAD(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_LOAD(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_LOAD(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + EMBB_CAT2(embb_internal__atomic_load_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value) { \ + /* no fence required for loads */ \ + register EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + result; \ + __asm__ __volatile__(\ + "dmb\n\t" \ + "ldr" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %0, %1\n\t" \ + "dmb" \ + : "=r" (result) \ + : "m" (*pointer_to_value) \ + : "memory"); \ + return result; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_LOAD(1, "b") +EMBB_DEFINE_LOAD(2, "h") +EMBB_DEFINE_LOAD(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_LOAD_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE EMBB_ATOMIC_PARAMETER_TYPE_NATIVE EMBB_CAT2(embb_atomic_load_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + const EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + return_val = (EMBB_CAT2(embb_internal__atomic_load_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)(\ + (EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)(&(variable->internal_variable)))); \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE return_val_pun; \ + memcpy(&return_val_pun, &return_val, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + return return_val_pun; \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE LOAD_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_LOAD_H_ + diff --git a/base_c/include/embb/base/c/internal/atomic/memory_barrier.h b/base_c/include/embb/base/c/internal/atomic/memory_barrier.h new file mode 100644 index 0000000..3fc1fb2 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/memory_barrier.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_MEMORY_BARRIER_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_MEMORY_BARRIER_H_ + +#include + +#ifndef DOXYGEN + +#ifdef EMBB_COMPILER_MSVC +#include +#endif + +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +extern void __fastcall embb_internal__atomic_memory_barrier_asm(); +// Read/write barrier +EMBB_INLINE void __fastcall embb_atomic_memory_barrier() { + _ReadWriteBarrier(); + embb_internal__atomic_memory_barrier_asm(); + _ReadWriteBarrier(); +} +#elif defined(EMBB_COMPILER_GNUC) +// Read/write barrier +EMBB_INLINE void embb_atomic_memory_barrier() { + __asm__ __volatile__ ("mfence" : : : "memory"); +} +#else +#error "No atomic fetch and store implementation found" +#endif + +#elif defined(EMBB_ARCH_ARM) + +EMBB_INLINE void embb_atomic_memory_barrier() { + __asm__ __volatile__ ("dmb" : : : "memory"); +} + +#endif + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_MEMORY_BARRIER_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/or_assign.h b/base_c/include/embb/base/c/internal/atomic/or_assign.h new file mode 100644 index 0000000..58a68f2 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/or_assign.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_OR_ASSIGN_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_OR_ASSIGN_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_OR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value); \ + EMBB_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_OR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_INLINE void EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + __asm__ __volatile__("lock or" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (value) \ + : \ + : "memory"); \ + } + +#else +#error "No atomic or assign implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_OR_ASSIGN(1, "b") +EMBB_DEFINE_OR_ASSIGN(2, "w") +EMBB_DEFINE_OR_ASSIGN(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_OR_ASSIGN(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_OR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + void EMBB_CAT2(embb_internal__atomic_or_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + register \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + t1, t2, t3; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %0\n\t" \ + "orr %3, %2, %1\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %4, %3, %0\n\t" \ + "teq %4, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "+m" (*pointer_to_value), "+r" (value), "=r" (t1), "=r" (t2), "=r" (t3) \ + : \ + : "memory", "cc" ); \ + } +#else +#error "No atomic or assign implementation found" +#endif + +EMBB_DEFINE_OR_ASSIGN(1, "b") +EMBB_DEFINE_OR_ASSIGN(2, "h") +EMBB_DEFINE_OR_ASSIGN(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_OR_ASSIGN_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE void EMBB_CAT2(embb_atomic_or_assign_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun;\ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + EMBB_CAT2(embb_internal__atomic_or_assign_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE OR_ASSIGN_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_OR_ASSIGN_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/store.h b/base_c/include/embb/base/c/internal/atomic/store.h new file mode 100644 index 0000000..59724c2 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/store.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_STORE_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_STORE_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_STORE(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX)\ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)_asm( \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value); \ + EMBB_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) \ + { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_store_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, new_value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_STORE(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX)\ + EMBB_INLINE void EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)(EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) {\ + /*the lock prefix is implicit for xchg*/ \ + __asm__ __volatile__("xchg" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (new_value) \ + : \ + : "memory"); \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_STORE(1, "b") +EMBB_DEFINE_STORE(2, "w") +EMBB_DEFINE_STORE(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_STORE(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_STORE(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_ARM_SIZE_SUFFIX)\ + EMBB_INLINE \ + void EMBB_CAT2(embb_internal__atomic_store_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* \ + pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) {\ + __asm__ __volatile__( \ + "dmb\n\t" \ + "str" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+r" (new_value) \ + : \ + : "memory"); \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_STORE(1, "b") +EMBB_DEFINE_STORE(2, "h") +EMBB_DEFINE_STORE(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_STORE_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE void EMBB_CAT2(embb_atomic_store_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun; \ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + EMBB_CAT2(embb_internal__atomic_store_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE STORE_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_STORE_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/swap.h b/base_c/include/embb/base/c/internal/atomic/swap.h new file mode 100644 index 0000000..6476021 --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/swap.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_SWAP_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_SWAP_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2 (embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value); \ + EMBB_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) __fastcall EMBB_CAT2 (embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) result; \ + _ReadWriteBarrier(); \ + result = EMBB_CAT2(embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, new_value); \ + _ReadWriteBarrier(); \ + return result; \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_INLINE EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) EMBB_CAT2(embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value)\ + { \ + /*the lock prefix is implicit for xchg*/ \ + __asm__ __volatile__("xchg" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (new_value) \ + : \ + : "memory"); \ + return new_value; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_SWAP(1, "b") +EMBB_DEFINE_SWAP(2, "w") +EMBB_DEFINE_SWAP(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_SWAP(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_SWAP(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) EMBB_CAT2 \ + (embb_internal__atomic_swap_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) new_value)\ + { \ + register \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + t1, t2; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %0\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %3, %1, %0\n\t" \ + "teq %3, #0\n\t" \ + "bne loop_%=\n\t" \ + "mov %1, %2\n\t" \ + "isb" \ + : "+m" (*pointer_to_value), \ + "+r" (new_value), "=r" (t1), "=r" (t2) \ + : \ + : "memory", "cc" ); \ + return new_value; \ + } +#else +#error "No atomic fetch and store implementation found" +#endif + +EMBB_DEFINE_SWAP(1, "b") +EMBB_DEFINE_SWAP(2, "h") +EMBB_DEFINE_SWAP(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_SWAP_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE EMBB_ATOMIC_PARAMETER_TYPE_NATIVE EMBB_CAT2(embb_atomic_swap_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun; \ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + return_val = EMBB_CAT2(embb_internal__atomic_swap_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)(&(variable->internal_variable)), value_pun); \ + EMBB_ATOMIC_PARAMETER_TYPE_NATIVE return_val_pun; \ + memcpy(&return_val_pun, &return_val, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE)); \ + return return_val_pun; \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE SWAP_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_SWAP_H_ diff --git a/base_c/include/embb/base/c/internal/atomic/xor_assign.h b/base_c/include/embb/base/c/internal/atomic/xor_assign.h new file mode 100644 index 0000000..c5a00bd --- /dev/null +++ b/base_c/include/embb/base/c/internal/atomic/xor_assign.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_ATOMIC_XOR_ASSIGN_H_ +#define EMBB_BASE_C_INTERNAL_ATOMIC_XOR_ASSIGN_H_ + +#ifndef DOXYGEN + +#include +#include +#include +#include +#include + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#ifdef EMBB_ARCH_X86 + +#ifdef EMBB_COMPILER_MSVC +#define EMBB_DEFINE_XOR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + extern void __fastcall EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_PARAMETER_SIZE_BYTE)_asm(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value); \ + EMBB_INLINE void __fastcall EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + _ReadWriteBarrier(); \ + EMBB_CAT2(embb_internal__atomic_xor_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)_asm(pointer_to_value, value); \ + _ReadWriteBarrier(); \ + } +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_XOR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, EMBB_ATOMIC_X86_SIZE_SUFFIX) \ + EMBB_INLINE void EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + __asm__ __volatile__("lock xor" EMBB_ATOMIC_X86_SIZE_SUFFIX " %1, %0" \ + : "+m" (*pointer_to_value), "+q" (value) \ + : \ + : "memory"); \ + } + +#else +#error "No atomic fetch xor store implementation found" +#endif + +/* +* The three or four macro calls below generate the methods for 1, 2, 4, and +* bytes, as stated in the macro definition. +*/ +EMBB_DEFINE_XOR_ASSIGN(1, "b") +EMBB_DEFINE_XOR_ASSIGN(2, "w") +EMBB_DEFINE_XOR_ASSIGN(4, "l") +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_DEFINE_XOR_ASSIGN(8, "q") +#endif + +#elif defined(EMBB_ARCH_ARM) + +#if defined(EMBB_COMPILER_GNUC) +#define EMBB_DEFINE_XOR_ASSIGN(EMBB_PARAMETER_SIZE_BYTE, \ + EMBB_ATOMIC_ARM_SIZE_SUFFIX) \ + EMBB_INLINE \ + void EMBB_CAT2(embb_internal__atomic_xor_assign_, \ + EMBB_PARAMETER_SIZE_BYTE)(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + volatile* pointer_to_value, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) value) { \ + register \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_PARAMETER_SIZE_BYTE) \ + t1, t2, t3; \ + __asm__ __volatile__ ( \ + "dmb\n\t" \ + "loop_%=:\n\t" \ + "ldrex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %2, %0\n\t" \ + "eor %3, %2, %1\n\t" \ + "strex" EMBB_ATOMIC_ARM_SIZE_SUFFIX " %4, %3, %0\n\t" \ + "teq %4, #0\n\t" \ + "bne loop_%=\n\t" \ + "isb" \ + : "+m" (*pointer_to_value), "+r" (value), "=r" (t1), "=r" (t2), "=r" (t3) \ + : \ + : "memory", "cc" ); \ + } +#else +#error "No atomic or assign implementation found" +#endif + +EMBB_DEFINE_XOR_ASSIGN(1, "b") +EMBB_DEFINE_XOR_ASSIGN(2, "h") +EMBB_DEFINE_XOR_ASSIGN(4, "") + +#else +#error "Unknown architecture" +#endif + +/* +* See file and_assign.h for a detailed (and operation independent) description +* of the following macro. +*/ +#define EMBB_ATOMIC_INTERNAL_DEFINE_XOR_ASSIGN_METHOD(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) \ + EMBB_INLINE void EMBB_CAT2(embb_atomic_xor_assign_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)(\ + EMBB_CAT2(embb_atomic_, EMBB_ATOMIC_PARAMETER_ATOMIC_TYPE_SUFFIX)* variable, EMBB_ATOMIC_PARAMETER_TYPE_NATIVE value) { \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) value_pun;\ + memcpy(&value_pun, &value, sizeof(EMBB_ATOMIC_PARAMETER_TYPE_NATIVE));\ + EMBB_CAT2(embb_internal__atomic_xor_assign_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE)((EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, EMBB_ATOMIC_PARAMETER_TYPE_SIZE) volatile *)\ + (&(variable->internal_variable)), value_pun); \ + } + +#undef EMBB_ATOMIC_METHOD_TO_GENERATE +#define EMBB_ATOMIC_METHOD_TO_GENERATE XOR_ASSIGN_METHOD +#include +#undef EMBB_ATOMIC_METHOD_TO_GENERATE + +#endif //DOXYGEN + +#endif //EMBB_BASE_C_INTERNAL_ATOMIC_XOR_ASSIGN_H_ diff --git a/base_c/include/embb/base/c/internal/bitset.h b/base_c/include/embb/base/c/internal/bitset.h new file mode 100644 index 0000000..6ff6959 --- /dev/null +++ b/base_c/include/embb/base/c/internal/bitset.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_BITSET_H_ +#define EMBB_BASE_C_INTERNAL_BITSET_H_ + +#include +#include +#include + +#include + +EMBB_INLINE void embb_bitset_set( + uint64_t * that, + unsigned int bit + ) { + assert(NULL != that); + assert(64 > bit); + *that |= (1ull << bit); +} + +EMBB_INLINE void embb_bitset_set_n( + uint64_t * that, + unsigned int count) { + assert(NULL != that); + assert(0 < count); + assert(64 >= count); + if (64 == count) { + *that = ~0ull; + } else { + *that = (1ull << count) - 1ull; + } +} + +EMBB_INLINE void embb_bitset_clear( + uint64_t * that, + unsigned int bit + ) { + assert(NULL != that); + assert(64 > bit); + *that &= ~(1ull << bit); +} + +EMBB_INLINE void embb_bitset_clear_all( + uint64_t * that + ) { + assert(NULL != that); + *that = 0ull; +} + +EMBB_INLINE unsigned int embb_bitset_is_set( + uint64_t const * that, + unsigned int bit + ) { + return (unsigned int)((*that & (1ull << bit)) ? 1 : 0); +} + +EMBB_INLINE void embb_bitset_intersect( + uint64_t * that, + uint64_t mask + ) { + assert(NULL != that); + *that &= mask; +} + +EMBB_INLINE void embb_bitset_union( + uint64_t * that, + uint64_t mask + ) { + assert(NULL != that); + *that |= mask; +} + +EMBB_INLINE unsigned int embb_bitset_count( + uint64_t const * that + ) { + unsigned int count = 0; + uint64_t mask = 1ull; + for (unsigned int ii = 0; ii < 64; ii++) { + if ((*that & mask) > 0) { + count++; + } + mask <<= 1; + } + return count; +} + +#endif // EMBB_BASE_C_INTERNAL_BITSET_H_ diff --git a/base_c/include/embb/base/c/internal/cmake_config.h.in b/base_c/include/embb/base/c/internal/cmake_config.h.in new file mode 100644 index 0000000..89f2f4e --- /dev/null +++ b/base_c/include/embb/base/c/internal/cmake_config.h.in @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_CMAKE_CONFIG_H_ +#define EMBB_BASE_C_INTERNAL_CMAKE_CONFIG_H_ + +/* This file is used as input for CMake. CMake creates a file cmake_config.h in + its current build directory under the path builddir/embb/base/internal/. From + there, the cmake_config.h can be included as usual using + #include + */ + +/** + * Is used to get the number of cores on certain systems. + */ +#cmakedefine EMBB_HAS_HEADER_SYSINFO + +/** + * Is used for Linux thread affinities. + */ +#cmakedefine EMBB_HAS_GLIB_CPU + +#endif /* EMBB_BASE_INTERNAL_CMAKE_CONFIG_H_ */ \ No newline at end of file diff --git a/base_c/include/embb/base/c/internal/config.h b/base_c/include/embb/base/c/internal/config.h new file mode 100644 index 0000000..daf563a --- /dev/null +++ b/base_c/include/embb/base/c/internal/config.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_CONFIG_H_ +#define EMBB_BASE_C_INTERNAL_CONFIG_H_ + +#include + +/* Define names: + * - threading platforms: EMBB_THREADING_ + * - compilers: EMBB_COMPILER_ + * - operating systems: EMBB_OS_ + */ + +#ifdef DOXYGEN +/* For Doxygen, simulate GNU compiler on 64 bit */ +#define __GNUC__ +#define __x86_64__ +#endif + +/* Most processors have cache lines with up to 64 Bytes, except for Itanium + * which has 128 bytes. + */ +#if defined(_M_IA64) || defined(_IA64) +#define EMBB_CACHE_LINE_SIZE 128 +#else +#define EMBB_CACHE_LINE_SIZE 64 +#endif + +/* For MSVC, if _DEBUG is set, set also EMBB_DEBUG. + * There is no such flag for GCC. Instead, cmake sets + * the EMBB_DEBUG flag... + */ +#ifdef _DEBUG +#define EMBB_DEBUG +#endif + +#ifdef __GNUC__ +#define EMBB_ALIGN(size) __attribute__ ((aligned(size))) +#elif defined _MSC_VER || defined __INTEL_COMPILER +#define EMBB_ALIGN(size) __declspec(align(size)) +#else +#error "Unsupported compiler" +#endif + +#if __GNUC__ +#define EMBB_INLINE static inline +#define EMBB_COMPILER_GNUC +#elif _MSC_VER +#define EMBB_INLINE __inline +#define EMBB_COMPILER_MSVC +#else +#define EMBB_INLINE inline +#define EMBB_COMPILER_UNKNOWN +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#define EMBB_ARCH_X86_64 +#define EMBB_ARCH_X86 +#define EMBB_HAS_CAS_64 +#elif defined(__i386) || defined(_M_IX86) +#define EMBB_ARCH_X86_32 +#define EMBB_ARCH_X86 +#elif defined(__arm__) +#define EMBB_ARCH_ARM +#else +#define EMBB_ARCH_UNKNOWN +#endif + +#if defined(EMBB_COMPILER_MSVC) +#define EMBB_THREADING_WINTHREADS +#elif defined(EMBB_COMPILER_GNUC) +#define EMBB_THREADING_POSIXTHREADS +#else +#error "No thread implementation could be determined" +#endif + +#endif /* EMBB_BASE_C_INTERNAL_CONFIG_H_ */ diff --git a/base_c/include/embb/base/c/internal/macro_helper.h b/base_c/include/embb/base/c/internal/macro_helper.h new file mode 100644 index 0000000..71bceb2 --- /dev/null +++ b/base_c/include/embb/base/c/internal/macro_helper.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_MACRO_HELPER_H_ +#define EMBB_BASE_C_INTERNAL_MACRO_HELPER_H_ + +#define EMBB_CAT2(_1, _2) _1 ## _2 +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +#endif /* EMBB_BASE_C_INTERNAL_MACRO_HELPER_H_ */ diff --git a/base_c/include/embb/base/c/internal/platform.h b/base_c/include/embb/base/c/internal/platform.h new file mode 100644 index 0000000..4ed71f4 --- /dev/null +++ b/base_c/include/embb/base/c/internal/platform.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_PLATFORM_H_ +#define EMBB_BASE_C_INTERNAL_PLATFORM_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define EMBB_TIME_MAX_SECONDS ULLONG_MAX +#define EMBB_DURATION_MAX_SECONDS 60 * 60 * 24 * 7 + +#ifdef EMBB_THREADING_WINTHREADS + +#ifdef EMBB_COMPILER_MSVC +// Suppress virtual functions but non-virtual constructor warning +// in windows headers +#pragma warning(push) +#pragma warning(disable : 4265) +#endif + +#define NOMINMAX +#include + +#ifdef EMBB_COMPILER_MSVC +#pragma warning(pop) // Reset warning 4640 +#endif + +struct embb_internal_thread_arg_t; + +/** + * Opaque handle for a thread. + */ +typedef struct embb_thread_t { + HANDLE embb_internal_handle; + struct embb_internal_thread_arg_t* embb_internal_arg; +} embb_thread_t; + +typedef DWORD embb_thread_id_t; +typedef CRITICAL_SECTION embb_mutex_t; +typedef CONDITION_VARIABLE embb_condition_t; + +#define EMBB_DURATION_MIN_NANOSECONDS 1000 + +#define EMBB_THREAD_SPECIFIC static __declspec(thread) + +#elif defined EMBB_THREADING_POSIXTHREADS /* EMBB_THREADING_WINTHREADS */ + +#include +#include +#include + +struct embb_internal_thread_arg_t; + +/** + * Opaque handle for a thread. + */ +typedef struct embb_thread_t { + pthread_t embb_internal_handle; + struct embb_internal_thread_arg_t* embb_internal_arg; +} embb_thread_t; + +typedef pthread_t embb_thread_id_t; +typedef pthread_mutex_t embb_mutex_t; +typedef pthread_cond_t embb_condition_t; + +#define EMBB_DURATION_MIN_NANOSECONDS 1 + +#define EMBB_THREAD_SPECIFIC __thread + +#else /* EMBB_THREADING_POSIXTHREADS */ + +#error "No threading platform defined!" + +#endif /* else */ + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +#endif /* EMBB_BASE_C_INTERNAL_PLATFORM_H_ */ diff --git a/base_c/include/embb/base/c/internal/thread_index.h b/base_c/include/embb/base/c/internal/thread_index.h new file mode 100644 index 0000000..67c6ba1 --- /dev/null +++ b/base_c/include/embb/base/c/internal/thread_index.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_THREAD_INDEX_H_ +#define EMBB_BASE_C_INTERNAL_THREAD_INDEX_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Tries to return the current thread's (internal) index. + * + * A new index has to be obtained only on first call of the function. Later + * calls always succeed, since they just return the index obtained in the + * first call. + * + * \pre embb_internal_thread_index_create() has been called. + * \return EMBB_SUCCESS, if an index could be obtained. + * EMBB_ERROR, if no more indices were available. + * \lockfree + */ +int embb_internal_thread_index( + unsigned int* index + /**< [OUT] Pointer to memory location to write thread index to. */ + ); + +/** + * Returns the maximum number of available thread indices. + * + * \lockfree + */ +int embb_internal_thread_index_max(); + +/** + * Sets the maximum number of thread indices. + * + * Data structures rely on the maximal number of indices. Thus, it should only + * be set at the very beginning of a program using EMBB, or in the beginning + * of a test case. + * + * \notthreadsafe + */ +void embb_internal_thread_index_set_max( + unsigned int max + /**< [IN] Maximum number of thread indices */ + ); + +/** + * Sets the thread index to zero. + * + * Should only be called in tests, in order to avoid a constantly growing + * index set. + * + * \waitfree + */ +void embb_internal_thread_index_reset(); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +#endif /* EMBB_BASE_C_INTERNAL_THREAD_INDEX_H_ */ + + + diff --git a/base_c/include/embb/base/c/internal/unused.h b/base_c/include/embb/base/c/internal/unused.h new file mode 100644 index 0000000..5e8d18d --- /dev/null +++ b/base_c/include/embb/base/c/internal/unused.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_INTERNAL_UNUSED_H_ +#define EMBB_BASE_C_INTERNAL_UNUSED_H_ + +#define EMBB_UNUSED(x) (void)(x) + +#ifdef NDEBUG +#define EMBB_UNUSED_IN_RELEASE(x) (void)(x) +#else +#define EMBB_UNUSED_IN_RELEASE(x) +#endif + +#endif /* EMBB_BASE_C_INTERNAL_UNUSED_H_ */ diff --git a/base_c/include/embb/base/c/log.h b/base_c/include/embb/base/c/log.h new file mode 100644 index 0000000..3faacc5 --- /dev/null +++ b/base_c/include/embb/base/c/log.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_LOG_H_ +#define EMBB_BASE_C_LOG_H_ + +#include + +/** + * \defgroup C_LOG Logging + * \ingroup C_BASE + * Simple logging facilities. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Log levels available for filtering the log. + * \ingroup C_LOG + */ +typedef enum { + EMBB_LOG_LEVEL_NONE, /**< show no log messages */ + EMBB_LOG_LEVEL_ERROR, /**< show errors only */ + EMBB_LOG_LEVEL_WARNING, /**< show warnings and errors */ + EMBB_LOG_LEVEL_INFO, /**< show info, warnings, and errors */ + EMBB_LOG_LEVEL_TRACE /**< show everything */ +} embb_log_level_t; + +/** + * Logging function type. + * This function is used by embb_log_write() to transfer a log message to its + * desired destination. The user may specify a pointer to a context that + * contains additional data (filter rules, file handles etc.) needed to put the + * message where it should go. This pointer might be NULL if no additional data + * is needed. + * \ingroup C_LOG + * \threadsafe + */ +typedef void(*embb_log_function_t)(void * context, char const * message); + +/** + * Default logging function. + * Writes to the given file (context needs to be a FILE*). + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_write_file( + void * context, /**< [in] User data, in this case a FILE* + file handle. */ + char const * message /**< [in] The message to write */ + ); + +/** + * Sets the global log level. + * This determines what messages will be shown, messages with a more detailed + * log level will be filtered out. The default log level is EMBB_LOG_LEVEL_NONE. + * \ingroup C_LOG + * \notthreadsafe + */ +void embb_log_set_log_level( + embb_log_level_t log_level /**< [in] Log level to use for + filtering */ + ); + +/** + * Sets the global logging function. + * The logging function implements the mechanism for transferring log messages + * to their destination. \c context is a pointer to data the user needs in the + * function to determine where the messages should go (may be NULL if no + * additional data is needed). The default logging function is + * embb_log_write_file() with context set to \c stdout. + * \see embb_log_function_t + * \ingroup C_LOG + * \notthreadsafe + */ +void embb_log_set_log_function( + void * context, /**< [in] User context to supply as the + first parameter of the logging + function*/ + embb_log_function_t func /**< [in] The logging function */ + ); + +/** + * Logs a message to the given channel with the specified log level. + * If the log level is greater than the configured log level for the channel, + * the message will be ignored. + * \see embb_log_set_log_level, embb_log_set_log_function + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_write( + char const * channel, /**< [in] User specified channel id + for filtering the log later on. + Might be NULL, channel identifier + will be "global" in that case */ + embb_log_level_t log_level, /**< [in] Log level to use */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +#if defined(EMBB_DEBUG) || defined(DOXYGEN) +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_TRACE using + * embb_log_write(). + * In non-debug builds, this function does nothing. + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_trace( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_INFO using + * embb_log_write(). + * In non-debug builds, this function does nothing. + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_info( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); +#else +#define embb_log_trace(...) +#define embb_log_info(...) +#endif + +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_WARNING using + * embb_log_write(). + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_warning( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +/** + * Logs a message to the given channel with EMBB_LOG_LEVEL_ERROR using + * embb_log_write(). + * \see embb_log_write + * \ingroup C_LOG + * \threadsafe + */ +void embb_log_error( + char const * channel, /**< [in] User specified channel id */ + char const * message, /**< [in] Message to convey, may use + \c printf style formatting */ + ... /**< Additional parameters determined by + the format specifiers in + \c message */ + ); + +#ifdef __cplusplus +} +#endif + +#endif /* EMBB_BASE_C_LOG_H_ */ diff --git a/base_c/include/embb/base/c/memory_allocation.h b/base_c/include/embb/base/c/memory_allocation.h new file mode 100644 index 0000000..d263cb0 --- /dev/null +++ b/base_c/include/embb/base/c/memory_allocation.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_MEMORY_ALLOCATION_H_ +#define EMBB_BASE_C_MEMORY_ALLOCATION_H_ + +/** +* \defgroup C_BASE_ALLOC Memory Allocation +* +* \ingroup C_BASE +* +* Functions for dynamic memory allocation +* +* There are functions for aligned and unaligned memory allocation. In debug +* mode, memory usage is tracked to detect memory leaks. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /** + * Allocates \p size bytes of memory. + * + * Keeps track of allocated memory in debug mode. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \see embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void* embb_alloc( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by embb_alloc() for some pointer + * \p ptr. + * + * Keeps track of freed memory in debug mode. + * + * \threadsafe + * + * \see embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void embb_free( + void* ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Allocates \p size bytes of memory with alignment \p alignment. + * + * This function can be used to align objects to certain boundaries such as + * cache lines, memory pages, etc. + * + * Keeps track of allocated memory in debug mode. + * + * It is not required that \p size is a multiple of \p alignment as, e.g., + * for the \c aligned\_alloc function of the C11 standard. + * + * \pre The alignment has to be power of 2 and a multiple of + * size(void*). + * \post The returned pointer is a multiple of \p alignment. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \memory Debug mode: Let \c n be the number of aligned cells necessary to + * fit the payload. Then, (n+1)*alignment+3*size_of(size_t)-1 + * bytes are allocated.
Release mode: \c size bytes are requested + * using the functions provided by the operating systems. + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * embb_free_aligned(). + * + * \see embb_alloc_cache_aligned(), embb_free_aligned(), + * embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void* embb_alloc_aligned( + size_t alignment, + /**< [IN] Alignment in bytes */ + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Allocates \p size bytes of cache-aligned memory. + * + * Specialized version of embb_alloc_aligned(). The alignment is chosen + * automatically (usually 64 bytes). + * + * Keeps track of allocated memory in debug mode. + * + * \post The returned pointer is a multiple of the cache line size. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \memory See embb_alloc_aligned() + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * embb_free_aligned(). + * + * \see embb_alloc_aligned(), embb_free_aligned(), embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void* embb_alloc_cache_aligned( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by an aligned method for \c ptr. + * + * The available aligned methods are embb_alloc_aligned() or + * embb_alloc_cache_aligned(). + * + * Keeps track of freed memory in debug mode. + * + * \threadsafe + * + * \see embb_alloc_aligned(), embb_alloc_cache_aligned(), + * embb_get_bytes_allocated() + * + * \ingroup C_BASE_ALLOC + */ + void embb_free_aligned( + void* ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Returns the total number of bytes currently allocated. + * + * Only the bytes allocated by embb_alloc(), embb_alloc_aligned(), and + * embb_alloc_cache_aligned() in debug mode are counted. + * + * \return Number of currently allocated bytes in debug mode, otherwise 0. + * + * \waitfree + * + * \see embb_alloc(), embb_alloc_aligned(), embb_alloc_cache_aligned() + * + * \ingroup C_BASE_ALLOC + */ + size_t embb_get_bytes_allocated(); +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // EMBB_BASE_C_MEMORY_ALLOCATION_H_ diff --git a/base_c/include/embb/base/c/mutex.h b/base_c/include/embb/base/c/mutex.h new file mode 100644 index 0000000..53f5e73 --- /dev/null +++ b/base_c/include/embb/base/c/mutex.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_MUTEX_H_ +#define EMBB_BASE_C_MUTEX_H_ + +/** + * \defgroup C_BASE_MUTEX Mutex + * + * Mutexes for thread synchronization + * + * Provides an abstraction from platform-specific mutex implementations. + * Plain and recursive mutexes are available, where the plain version can + * only be locked once by the same thread. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing a mutex. + */ +typedef opaque_type embb_mutex_t; +#endif /* DOXYGEN */ + +/** + * Types of mutexes to be used in embb_mutex_init() + */ +enum { + EMBB_MUTEX_PLAIN, + /**< Mutex can be locked only once by the same thread. */ + EMBB_MUTEX_RECURSIVE + /**< Mutex can be locked recursively by the same thread. */ +}; + +/** + * Initializes a mutex + * + * \post \c mutex is initialized + * \return EMBB_SUCCESS if mutex could be initialized \n + * EMBB_ERROR otherwise + * \memory (Potentially) allocates dynamic memory + * \notthreadsafe + * \see embb_mutex_destroy() + */ +int embb_mutex_init( + embb_mutex_t* mutex, + /**< [OUT] Pointer to mutex */ + int type + /**< [IN] EMBB_MUTEX_PLAIN or EMBB_MUTEX_RECURSIVE. There is no guarantee + that a mutex is non-recursive if the plain type is given. */ + ); + +/** + * Waits until the mutex can be locked and locks it. + * + * \pre \c mutex is initialized \n + * If the mutex type is plain, \c mutex must not be locked by the current + * thread. + * \post If successful, \c mutex is locked. + * \return EMBB_SUCCESS if mutex could be locked \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_mutex_try_lock(), embb_mutex_unlock() + */ +int embb_mutex_lock( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Tries to lock the mutex and returns immediately. + * + * \pre \c mutex is initialized + * \post If successful, \c mutex is locked + * + * \return EMBB_SUCCESS if mutex could be locked \n + * EMBB_BUSY if mutex could not be locked \n + * EMBB_ERROR if an error occurred + * \threadsafe + * \see embb_mutex_lock(), embb_mutex_unlock() + */ +int embb_mutex_try_lock( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Unlocks a locked mutex. + * + * \pre \c mutex has been locked by the current thread. + * \post If successful and when the given mutex type is plain, \c mutex is + * unlocked. If its type is recursive, \c mutex is only unlocked if the + * number of successful unlocks has reached the number of successful locks + * done by the current thread. + * \return EMBB_SUCCESS if the operation was successful \n + * EMBB_ERROR otherwise + * \threadsafe + * \see embb_mutex_lock(), embb_mutex_try_lock() + */ +int embb_mutex_unlock( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +/** + * Destroys a mutex and frees its resources. + * + * \pre \c mutex has been initialized + * \post \c mutex is uninitialized + * \notthreadsafe + * \see embb_mutex_init() + */ +void embb_mutex_destroy( + embb_mutex_t* mutex + /**< [IN/OUT] Pointer to mutex */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_MUTEX_H_ */ diff --git a/base_c/include/embb/base/c/thread.h b/base_c/include/embb/base/c/thread.h new file mode 100644 index 0000000..183731e --- /dev/null +++ b/base_c/include/embb/base/c/thread.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_THREAD_H_ +#define EMBB_BASE_C_THREAD_H_ + +/** + * \defgroup C_BASE_THREADS Thread + * + * Threads supporting thread-to-core affinities. + * + * Provides an abstraction from platform-specific threading implementations to + * create, manage, and join threads of execution. Support for thread-to-core + * affinities is given on thread creation by using the core set functionality. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#ifdef DOXYGEN +/** + * Opaque type representing a thread of execution. + */ +typedef opaque_type embb_thread_t; +#endif /* DOXYGEN */ + +/** + * Thread start function pointer type. + * + * The return value can be used to return a user-defined exit code when the + * thread is joined. + */ +typedef int(*embb_thread_start_t)(void*); + +/** + * Returns the maximum number of threads handled by EMB2. + * + * The maximum thread number concerns all threads in a program using + * EMB2 functionalities or data structures, regardless of whether + * a thread is started by EMB2 or other threading libraries. + * Each thread that makes use of EMB2 at least once consumes one + * entry in the internal tables. The entry is permanently consumed during a + * program run, even if the thread does not exist any longer. If more threads + * than the maximum thread count access EMB2, undefined behavior or + * abortion of program execution can occur. + * + * \return Maximum number of threads + * + * \lockfree + * + * \see embb_thread_set_max_count() + */ +unsigned int embb_thread_get_max_count(); + +/** + * Sets maximum number of threads handled by EMBB. + * + * It needs to be set before any EMB2 functionalities are used or + * data structures are defined, unless the default value is sufficient. + * + * \notthreadsafe + * \see embb_thread_get_max_count() + */ +void embb_thread_set_max_count( + unsigned int max + /**< [IN] Maximum number of threads */ + ); + +/** + * Returns the calling thread (that is, this thread). + * + * The returned handle is only valid for the thread calling the function. + * + * \return Calling thread + * + * \threadsafe + */ +embb_thread_t embb_thread_current(); + +/** + * Reschedule the current thread for later execution. + * + * This is only a request, the realization depends on the implementation and the + * scheduler employed by the operating system. + * + * \threadsafe + */ +void embb_thread_yield(); + +/** + * Creates and runs a thread. + * + * \pre The given thread is not running and has not yet been successfully + joined. + * \post On success, the given thread has started to run. + * \return EMBB_SUCCESS if the thread could be created. \n + * EMBB_NOMEM if there was insufficient amount of memory \n + * EMBB_ERROR otherwise. + * \memory Dynamically allocates a small constant amount of memory to store the + * function and argument pointers. This memory is freed when the thread + * is joined. + * \notthreadsafe + * \see embb_thread_join() + */ +int embb_thread_create( + embb_thread_t* thread, + /**< [OUT] Thread to be run */ + const embb_core_set_t* core_set, + /**< [IN] Set of cores on which the thread shall be executed. Can be NULL to + indicate automatic thread scheduling by the OS. */ + embb_thread_start_t function, + /**< [IN] Function which is executed by the thread when started. Has to be of + type embb_thread_start_t. */ + void* arg + /**< [IN/OUT] Argument to thread start function. Can be NULL. */ + ); + +/** + * Waits until the given thread has finished execution. + * + * \pre The given thread has been successfully created using + embb_thread_create(). + * \post If successful, the thread has finished execution and all memory + * associated to the thread has been freed. + * \return EMBB_SUCCESS if thread was joined \n + * EMBB_ERROR otherwise + * \notthreadsafe + * \see embb_thread_create() + */ +int embb_thread_join( + embb_thread_t* thread, + /**< [IN/OUT] Thread to be joined */ + int* result_code + /**< [OUT] Memory location (or NULL) for thread result code */ + ); + +/** + * Compares two threads represented by their handles for equality. + * + * \return Non-zero, if equal \n + * 0, otherwise + * \notthreadsafe + */ +int embb_thread_equal( + const embb_thread_t* lhs, + /**< [IN] First thread (left-hand side of equality sign) */ + const embb_thread_t* rhs + /**< [IN] Second thread (right-hand side of equality sign) */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_THREAD_H_ */ diff --git a/base_c/include/embb/base/c/thread_specific_storage.h b/base_c/include/embb/base/c/thread_specific_storage.h new file mode 100644 index 0000000..38457d6 --- /dev/null +++ b/base_c/include/embb/base/c/thread_specific_storage.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_THREAD_SPECIFIC_STORAGE_H_ +#define EMBB_BASE_C_THREAD_SPECIFIC_STORAGE_H_ + +/** + * \defgroup C_BASE_THREADSPECIFIC Thread-Specific Storage + * Thread-specific storage. + * + * Implements thread-specific storage (TSS), that is, memory locations that are + * individual for each thread. Each thread has its own slot for a memory + * address that can point to a (thread-specific) value. The value pointed to + * has to be managed, i.e., created and deleted, by the user. + * + * \ingroup C_BASE + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Opaque type representing a TSS. + */ +#ifdef DOXYGEN +typedef opaque_type embb_tss_t; +#else +typedef struct embb_tss_t { + void** values; + unsigned int size; +} embb_tss_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Creates thread-specific storage (TSS) pointer slots. + * + * \pre The given TSS has not yet been created or has already been deleted. + * \return EMBB_SUCCESS if storage could be created \n + * EMBB_NOMEM if not enough memory was available + * \memory embb_thread_get_max_count() pointers + * \notthreadsafe + * \see embb_tss_delete(), embb_thread_get_max_count() + */ +int embb_tss_create( + embb_tss_t* tss + /**< [OUT] Pointer to TSS */ + ); + +/** + * Sets thread-specific slot value of the current thread. + * + * The value pointed to needs to be managed (created, deleted) by the user. + * + * \pre The given TSS has been created + * \return EMBB_SUCCESS if value could be set \n + * EMBB_ERROR if no thread index could be obtained, that is, the + * maximum number of threads has been exceeded. + * \lockfree + * \see embb_tss_get(), embb_thread_get_max_count() + */ +int embb_tss_set( + embb_tss_t* tss, + /**< [IN/OUT] Pointer to TSS */ + void* value + /**< [IN] Pointer to be stored in TSS */ + ); + +/** + * Gets thread-specific TSS slot value of the current thread. + * + * \pre The given TSS has been created + * \return Thread-specific value if embb_tss_set() has previously been called + * with a valid address. NULL, if no value was set or the calling thread + * could not obtain a thread-specific index. + * \lockfree + * \see embb_tss_set() + */ +void* embb_tss_get( + const embb_tss_t* tss + /**< [IN] Pointer to TSS */ + ); + +/** + * Deletes all slots of the given TSS. + * + * Does not delete the values pointed to. + * + * \pre TSS has been created successfully + * \post All slots are deleted + * \notthreadsafe + * \see embb_tss_create() + */ +void embb_tss_delete( + embb_tss_t* tss + /**< [IN/OUT] Pointer to TSS */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + */ + +#endif /* EMBB_BASE_C_THREAD_SPECIFIC_STORAGE_H_ */ diff --git a/base_c/include/embb/base/c/time.h b/base_c/include/embb/base/c/time.h new file mode 100644 index 0000000..271242d --- /dev/null +++ b/base_c/include/embb/base/c/time.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_C_TIME_H_ +#define EMBB_BASE_C_TIME_H_ + +/** + * \addtogroup C_BASE_DURATIONTIME + * \{ + * + * \name Time + * \{ + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Opaque type representing an absolute time point + */ +#ifdef DOXYGEN +typedef opaque_type embb_time_t; +#else +typedef struct embb_time_t { + /** + * Seconds part of time point + */ + unsigned long long seconds; + /** + * Nanoseconds part of time point, smaller than one second + */ + unsigned long nanoseconds; +} embb_time_t; +#endif /* else defined(DOXYGEN) */ + +/** + * Sets time point to now. + * + * \notthreadsafe + * \see embb_time_in() + */ +void embb_time_now( + embb_time_t* time + /**< [OUT] Pointer to time point */ + ); + +/** + * Sets time point to now plus the given duration. + * + * \return EMBB_SUCCESS \n + * EMBB_UNDERFLOW if duration is smaller than implementation allows \n + * EMBB_OVERFLOW if time + duration is larger than implementation allows + * \notthreadsafe + * \see embb_time_now() + */ +int embb_time_in( + embb_time_t* time, + /**< [OUT] Pointer to time point */ + const embb_duration_t* duration + /**< [IN] Pointer to duration */ + ); + +/** + * Compares two time points. + * + * \return -1 if \c lhs < \c rhs \n + * 0 if \c lhs == \c rhs \n + * 1 if \c lhs > \c rhs + * \notthreadsafe + */ +int embb_time_compare( + const embb_time_t* lhs, + /**< [IN] Pointer to left-hand side operand */ + const embb_time_t* rhs + /**< [IN] Pointer to right-hand side operand */ + ); + +#ifdef __cplusplus +} /* Close extern "C" { */ +#endif + +/** + * \} + * \} + */ + +#endif /* EMBB_BASE_C_TIME_H_ */ diff --git a/base_c/src/atomic.c b/base_c/src/atomic.c new file mode 100644 index 0000000..efb2242 --- /dev/null +++ b/base_c/src/atomic.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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 + +#include + +// Break compiling, if we assumed wrong sizes for types... +// Function should never be called! +void __embb_atomic_internal_compile_time_checks() { + BUILD_BUG_ON(sizeof(char) != EMBB_CHAR_TYPE_SIZE); + BUILD_BUG_ON(sizeof(short) != EMBB_SHORT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned short) != EMBB_UNSIGNED_SHORT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(int) != EMBB_INT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned int) != EMBB_UNSIGNED_INT_TYPE_SIZE); + BUILD_BUG_ON(sizeof(long) != EMBB_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned long) != EMBB_UNSIGNED_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(long long) != EMBB_LONG_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(unsigned long long) != EMBB_UNSIGNED_LONG_LONG_TYPE_SIZE); + BUILD_BUG_ON(sizeof(intptr_t) != EMBB_INTPTR_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(uintptr_t) != EMBB_UINTPTR_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(size_t) != EMBB_SIZE_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(ptrdiff_t) != EMBB_PTRDIFF_T_TYPE_SIZE); + BUILD_BUG_ON(sizeof(uintmax_t) != EMBB_UINTMAX_T_TYPE_SIZE); +} diff --git a/base_c/src/atomicfunc_32.asm b/base_c/src/atomicfunc_32.asm new file mode 100644 index 0000000..c485ee5 --- /dev/null +++ b/base_c/src/atomicfunc_32.asm @@ -0,0 +1,160 @@ + .686p + .xmm + .model flat + +_TEXT SEGMENT + +_abort proto + +define_not_implemented macro name + public name + name proc + call _abort + ret + name endp +endm + +; ---- and_assign ------------------------------------------------------------- + +define_and_assign macro name, size, value + public name + name proc + lock and size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_and_assign_8_asm@8 +define_and_assign @embb_internal__atomic_and_assign_4_asm@8, dword, edx +define_and_assign @embb_internal__atomic_and_assign_2_asm@8, word, dx +define_and_assign @embb_internal__atomic_and_assign_1_asm@8, byte, dl + +; ---- compare_and_swap ------------------------------------------------------- + +define_compare_and_swap macro name, size, value, desired + public name + name proc + push edx + mov desired, size ptr [edx] + mov value, size ptr [esp+8] + lock cmpxchg size ptr [ecx], value + pop edx + mov size ptr [edx], desired + setz al + movzx eax, al + ret 4 + name endp +endm + +define_not_implemented @embb_internal__atomic_compare_and_swap_8_asm@12 +define_compare_and_swap @embb_internal__atomic_compare_and_swap_4_asm@12, dword, edx, eax +define_compare_and_swap @embb_internal__atomic_compare_and_swap_2_asm@12, word, dx, ax +define_compare_and_swap @embb_internal__atomic_compare_and_swap_1_asm@12, byte, dl, al + +; ---- fetch_and_add ---------------------------------------------------------- + +define_fetch_and_add macro name, size, value, return + public name + name proc + lock xadd size ptr [ecx], value + mov return, value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_fetch_and_add_8_asm@8 +define_fetch_and_add @embb_internal__atomic_fetch_and_add_4_asm@8, dword, edx, eax +define_fetch_and_add @embb_internal__atomic_fetch_and_add_2_asm@8, word, dx, ax +define_fetch_and_add @embb_internal__atomic_fetch_and_add_1_asm@8, byte, dl, al + +; ---- load ------------------------------------------------------------------- + +define_load macro name, size, return + public name + name proc + mov return, size ptr [ecx] + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_load_8_asm@4 +define_load @embb_internal__atomic_load_4_asm@4, dword, eax +define_load @embb_internal__atomic_load_2_asm@4, word, ax +define_load @embb_internal__atomic_load_1_asm@4, byte, al + +; ---- memory_barrier --------------------------------------------------------- + +define_memory_barrier macro name + public name + name proc + mfence + ret + name endp +endm + +define_memory_barrier @embb_internal__atomic_memory_barrier_asm@0 + +; ---- or_assign -------------------------------------------------------------- + +define_or_assign macro name, size, value + public name + name proc + lock or size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_or_assign_8_asm@8 +define_or_assign @embb_internal__atomic_or_assign_4_asm@8, dword, edx +define_or_assign @embb_internal__atomic_or_assign_2_asm@8, word, dx +define_or_assign @embb_internal__atomic_or_assign_1_asm@8, byte, dl + +; ---- store ------------------------------------------------------------------ + +define_store macro name, size, value + public name + name proc + mov size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_store_8_asm@8 +define_store @embb_internal__atomic_store_4_asm@8, dword, edx +define_store @embb_internal__atomic_store_2_asm@8, word, dx +define_store @embb_internal__atomic_store_1_asm@8, byte, dl + +; ---- swap ------------------------------------------------------------------- + +define_swap macro name, size, value, return + public name + name proc + lock xchg size ptr [ecx], value + mov return, value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_swap_8_asm@8 +define_swap @embb_internal__atomic_swap_4_asm@8, dword, edx, eax +define_swap @embb_internal__atomic_swap_2_asm@8, word, dx, ax +define_swap @embb_internal__atomic_swap_1_asm@8, byte, dl, al + +; ---- xor_assign ------------------------------------------------------------- + +define_xor_assign macro name, size, value + public name + name proc + lock xor size ptr [ecx], value + ret + name endp +endm + +define_not_implemented @embb_internal__atomic_xor_assign_8_asm@8 +define_xor_assign @embb_internal__atomic_xor_assign_4_asm@8, dword, edx +define_xor_assign @embb_internal__atomic_xor_assign_2_asm@8, word, dx +define_xor_assign @embb_internal__atomic_xor_assign_1_asm@8, byte, dl + +_TEXT ENDS + +END diff --git a/base_c/src/atomicfunc_64.asm b/base_c/src/atomicfunc_64.asm new file mode 100644 index 0000000..55e0567 --- /dev/null +++ b/base_c/src/atomicfunc_64.asm @@ -0,0 +1,153 @@ +_TEXT SEGMENT + +_abort proto + +define_not_implemented macro name + public name + name proc + call _abort + ret + name endp +endm + +; ---- and_assign ------------------------------------------------------------- + +define_and_assign macro name, size, value + public name + name proc + lock and size ptr [rcx], value + ret + name endp +endm + +define_and_assign embb_internal__atomic_and_assign_8_asm, qword, rdx +define_and_assign embb_internal__atomic_and_assign_4_asm, dword, edx +define_and_assign embb_internal__atomic_and_assign_2_asm, word, dx +define_and_assign embb_internal__atomic_and_assign_1_asm, byte, dl + +; ---- compare_and_swap ------------------------------------------------------- + +define_compare_and_swap macro name, size, value, desired + public name + name proc + mov desired, size ptr [rdx] + lock cmpxchg size ptr [rcx], value + mov size ptr [rdx], desired + setz al + movzx rax, al + ret + name endp +endm + +define_compare_and_swap embb_internal__atomic_compare_and_swap_8_asm, qword, r8, rax +define_compare_and_swap embb_internal__atomic_compare_and_swap_4_asm, dword, r8d, eax +define_compare_and_swap embb_internal__atomic_compare_and_swap_2_asm, word, r8w, ax +define_compare_and_swap embb_internal__atomic_compare_and_swap_1_asm, byte, r8b, al + +; ---- fetch_and_add ---------------------------------------------------------- + +define_fetch_and_add macro name, size, value, return + public name + name proc + lock xadd size ptr [rcx], value + mov return, value + ret + name endp +endm + +define_fetch_and_add embb_internal__atomic_fetch_and_add_8_asm, qword, rdx, rax +define_fetch_and_add embb_internal__atomic_fetch_and_add_4_asm, dword, edx, eax +define_fetch_and_add embb_internal__atomic_fetch_and_add_2_asm, word, dx, ax +define_fetch_and_add embb_internal__atomic_fetch_and_add_1_asm, byte, dl, al + +; ---- load ------------------------------------------------------------------- + +define_load macro name, size, return + public name + name proc + mov return, size ptr [rcx] + ret + name endp +endm + +define_load embb_internal__atomic_load_8_asm, qword, rax +define_load embb_internal__atomic_load_4_asm, dword, eax +define_load embb_internal__atomic_load_2_asm, word, ax +define_load embb_internal__atomic_load_1_asm, byte, al + +; ---- memory_barrier --------------------------------------------------------- + +define_memory_barrier macro name + public name + name proc + mfence + ret + name endp +endm + +define_memory_barrier embb_internal__atomic_memory_barrier_asm + +; ---- or_assign -------------------------------------------------------------- + +define_or_assign macro name, size, value + public name + name proc + lock or size ptr [rcx], value + ret + name endp +endm + +define_or_assign embb_internal__atomic_or_assign_8_asm, qword, rdx +define_or_assign embb_internal__atomic_or_assign_4_asm, dword, edx +define_or_assign embb_internal__atomic_or_assign_2_asm, word, dx +define_or_assign embb_internal__atomic_or_assign_1_asm, byte, dl + +; ---- store ------------------------------------------------------------------ + +define_store macro name, size, value + public name + name proc + mov size ptr [rcx], value + ret + name endp +endm + +define_store embb_internal__atomic_store_8_asm, qword, rdx +define_store embb_internal__atomic_store_4_asm, dword, edx +define_store embb_internal__atomic_store_2_asm, word, dx +define_store embb_internal__atomic_store_1_asm, byte, dl + +; ---- swap ------------------------------------------------------------------- + +define_swap macro name, size, value, return + public name + name proc + lock xchg size ptr [rcx], value + mov return, value + ret + name endp +endm + +define_swap embb_internal__atomic_swap_8_asm, qword, rdx, rax +define_swap embb_internal__atomic_swap_4_asm, dword, edx, eax +define_swap embb_internal__atomic_swap_2_asm, word, dx, ax +define_swap embb_internal__atomic_swap_1_asm, byte, dl, al + +; ---- xor_assign ------------------------------------------------------------- + +define_xor_assign macro name, size, value + public name + name proc + lock xor size ptr [rcx], value + ret + name endp +endm + +define_xor_assign embb_internal__atomic_xor_assign_8_asm, qword, rdx +define_xor_assign embb_internal__atomic_xor_assign_4_asm, dword, edx +define_xor_assign embb_internal__atomic_xor_assign_2_asm, word, dx +define_xor_assign embb_internal__atomic_xor_assign_1_asm, byte, dl + +_TEXT ENDS + +END diff --git a/base_c/src/condition_variable.c b/base_c/src/condition_variable.c new file mode 100644 index 0000000..f0880ab --- /dev/null +++ b/base_c/src/condition_variable.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include + +int embb_condition_wait_for(embb_condition_t* condition_var, + embb_mutex_t* mutex, + const embb_duration_t* duration) { + assert(condition_var != NULL); + assert(mutex != NULL); + embb_time_t time; + int status = embb_time_in(&time, duration); + if (status != EMBB_SUCCESS) { + return status; + } + return embb_condition_wait_until(condition_var, mutex, &time); +} + +#ifdef EMBB_THREADING_WINTHREADS + +int embb_condition_init(embb_condition_t* condition_var) { + assert(condition_var != NULL); + InitializeConditionVariable(condition_var); + return EMBB_SUCCESS; +} + +int embb_condition_notify_one(embb_condition_t* condition_var) { + assert(condition_var != NULL); + WakeConditionVariable(condition_var); + return EMBB_SUCCESS; +} + +int embb_condition_notify_all(embb_condition_t* condition_var) { + assert(condition_var != NULL); + WakeAllConditionVariable(condition_var); + return EMBB_SUCCESS; +} + +int embb_condition_wait(embb_condition_t* condition_var, + embb_mutex_t* mutex) { + assert(condition_var != NULL); + assert(mutex != NULL); + if (SleepConditionVariableCS(condition_var, mutex, INFINITE)) { + return EMBB_SUCCESS; + } + return EMBB_ERROR; +} + +int embb_condition_wait_until(embb_condition_t* condition_var, + embb_mutex_t* mutex, const embb_time_t* time) { + assert(condition_var != NULL); + assert(mutex != NULL); + assert(time != NULL); + /* The Windows API needs a time duration, so we need to convert the given time + by using the time now. */ + 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) { + /* Convert to (unsigned type) milliseconds and round up */ + DWORD time_diff = (DWORD) ( + time->seconds * 1000 + time->nanoseconds / 1000000 + - now.seconds * 1000 - now.nanoseconds / 1000000); + if (SleepConditionVariableCS(condition_var, mutex, time_diff) == 0) { + if (GetLastError() == ERROR_TIMEOUT) { + return EMBB_TIMEDOUT; + } else { + return EMBB_ERROR; + } + } + } else { + return EMBB_TIMEDOUT; + } + return EMBB_SUCCESS; +} + +int embb_condition_destroy(embb_condition_t* condition_var) { + assert(condition_var != NULL); + EMBB_UNUSED_IN_RELEASE(condition_var); + return EMBB_SUCCESS; +} + +#endif /* EMBB_THREADING_WINTHREADS */ + +#ifdef EMBB_THREADING_POSIXTHREADS + +int embb_condition_init(embb_condition_t* condition_var) { + assert(condition_var != NULL); + int result = pthread_cond_init(condition_var, NULL); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_notify_one(embb_condition_t* condition_var) { + assert(condition_var != NULL); + int result = pthread_cond_signal(condition_var); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_notify_all(embb_condition_t* condition_var) { + assert(condition_var != NULL); + int result = pthread_cond_broadcast(condition_var); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_wait(embb_condition_t* condition_var, embb_mutex_t* mutex) { + assert(condition_var != NULL); + assert(mutex != NULL); + int result = pthread_cond_wait(condition_var, mutex); + return result == 0 ? EMBB_SUCCESS : EMBB_ERROR; +} + +int embb_condition_wait_until(embb_condition_t* condition_var, + embb_mutex_t* mutex, const embb_time_t* time) { + assert(condition_var != NULL); + assert(mutex != NULL); + assert(time != NULL); + /* Convert EMBB time to Unix time format */ + struct timespec unix_time; + unix_time.tv_sec = time->seconds; + unix_time.tv_nsec = time->nanoseconds; + int result = pthread_cond_timedwait(condition_var, mutex, &unix_time); + if (result == ETIMEDOUT) { + return EMBB_TIMEDOUT; + } + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_condition_destroy(embb_condition_t* condition_var) { + assert(condition_var != NULL); + int status = pthread_cond_destroy(condition_var); + if (status != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +#endif /* EMBB_THREADING_POSIXTHREADS */ diff --git a/base_c/src/core_set.c b/base_c/src/core_set.c new file mode 100644 index 0000000..d2e2e2e --- /dev/null +++ b/base_c/src/core_set.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include + +#ifdef EMBB_THREADING_WINTHREADS + +/** + * For handling of more than 64 logical processors on Windows. + */ +typedef struct processor_info_t { + unsigned short group_count; + unsigned short processor_counts[64]; +} processor_info_t; + +/** + * Holds information about Windows processor groups. + * + * Is initialized on the first call of embb_core_set_init. + * + * !!! Not used at the moment. Results in a limitation of 64 logical processors. + */ +processor_info_t processor_info; + +unsigned int embb_core_count_available() { + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +} + +void embb_core_set_init(embb_core_set_t* core_set, int initializer) { + assert(core_set != NULL); + assert(embb_core_count_available() < 64 && + "Core sets are only supported up to 64 processors!"); + + /* Cache windows processor grouping information */ + if (processor_info.group_count == 0) { + /* Set relation group */ + LOGICAL_PROCESSOR_RELATIONSHIP rel = (LOGICAL_PROCESSOR_RELATIONSHIP)4; + /* Assume only one element of SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX is + returned to the buffer. */ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer; + /* The length is that of the buffer */ + DWORD length = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX); + BOOL status = GetLogicalProcessorInformationEx(rel, &buffer, &length); + assert(status == TRUE); + EMBB_UNUSED_IN_RELEASE(status); + processor_info.group_count = buffer.Group.ActiveGroupCount; + for (unsigned short i = 0; i < processor_info.group_count; i++) { + processor_info.processor_counts[i] = + (unsigned short)(buffer.Group.GroupInfo[i].ActiveProcessorCount); + } + } + + if (initializer == 0) { + embb_bitset_clear_all(&core_set->rep); + } else { + embb_bitset_set_n(&core_set->rep, embb_core_count_available()); + } +} + +#endif /* EMBB_THREADING_WINTHREADS */ + +#ifdef EMBB_THREADING_POSIXTHREADS + +#ifdef EMBB_HAS_HEADER_SYSINFO +#include +#endif + +unsigned int embb_core_count_available() { +#ifdef EMBB_HAS_HEADER_SYSINFO + return get_nprocs(); +#else +#error "No header sysinfo available!" +#endif +} + +void embb_core_set_init(embb_core_set_t* core_set, int initializer) { + assert(core_set != NULL); + assert(embb_core_count_available() < 64 && + "Core sets are only supported up to 64 processors!"); + if (initializer == 0) { + embb_bitset_clear_all(&core_set->rep); + } else { + embb_bitset_set_n(&core_set->rep, embb_core_count_available()); + } +} + +#endif /* EMBB_THREADING_POSIXTHREADS */ + +void embb_core_set_add(embb_core_set_t* core_set, unsigned int core_number) { + assert(core_set != NULL); + assert(core_number < embb_core_count_available()); + embb_bitset_set(&core_set->rep, core_number); +} + +void embb_core_set_remove(embb_core_set_t* core_set, unsigned int core_number) { + assert(core_set != NULL); + assert(core_number < embb_core_count_available()); + embb_bitset_clear(&core_set->rep, core_number); +} + +int embb_core_set_contains(const embb_core_set_t* core_set, + unsigned int core_number) { + assert(core_set != NULL); + assert(core_number < embb_core_count_available()); + return (int)(embb_bitset_is_set(&core_set->rep, core_number)); +} + +void embb_core_set_intersection(embb_core_set_t* set1, + const embb_core_set_t* set2) { + embb_bitset_intersect(&set1->rep, set2->rep); +} + +void embb_core_set_union(embb_core_set_t* set1, const embb_core_set_t* set2) { + embb_bitset_union(&set1->rep, set2->rep); +} + +unsigned int embb_core_set_count(const embb_core_set_t* core_set) { + return embb_bitset_count(&core_set->rep); +} diff --git a/base_c/src/counter.c b/base_c/src/counter.c new file mode 100644 index 0000000..1e8bb1c --- /dev/null +++ b/base_c/src/counter.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +int embb_counter_init(embb_counter_t* counter) { + assert(counter != NULL); + embb_atomic_store_unsigned_int(&(counter->value), 0); + return EMBB_SUCCESS; +} + +unsigned int embb_counter_get(embb_counter_t* counter) { + assert(counter != NULL); + return embb_atomic_load_unsigned_int(&(counter->value)); +} + +unsigned int embb_counter_increment(embb_counter_t* counter) { + assert(counter != NULL); + return embb_atomic_fetch_and_add_unsigned_int(&(counter->value), 1); +} + +unsigned int embb_counter_decrement(embb_counter_t* counter) { + assert(counter != NULL); + return embb_atomic_fetch_and_add_unsigned_int(&(counter->value), + (unsigned int)-1); +} + +void embb_counter_destroy(embb_counter_t* counter) { + assert(counter != NULL); + EMBB_UNUSED_IN_RELEASE(counter); +} + + diff --git a/base_c/src/duration.c b/base_c/src/duration.c new file mode 100644 index 0000000..62b5283 --- /dev/null +++ b/base_c/src/duration.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +const embb_duration_t* embb_duration_max() { + static embb_duration_t max = { EMBB_DURATION_MAX_SECONDS, 0 }; + return &max; +} + +const embb_duration_t* embb_duration_min() { + static embb_duration_t min = { 0, EMBB_DURATION_MIN_NANOSECONDS }; + return &min; +} + +const embb_duration_t* embb_duration_zero() { + static embb_duration_t zero = { 0, 0 }; + return &zero; +} + +int embb_duration_set_nanoseconds(embb_duration_t* duration, + unsigned long long nanoseconds) { + assert(duration != NULL); + if (nanoseconds > 0) { + if (embb_duration_min()->nanoseconds > nanoseconds) { + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds * 1000000000 + max->nanoseconds < nanoseconds) { + return EMBB_OVERFLOW; + } + } + duration->seconds = nanoseconds / 1000000000; + duration->nanoseconds = (nanoseconds % 1000000000); + return EMBB_SUCCESS; +} + +int embb_duration_set_microseconds(embb_duration_t* duration, + unsigned long long microseconds) { + assert(duration != NULL); + if (microseconds > 0) { + if (embb_duration_min()->nanoseconds > microseconds*1000) { + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds * 1000000 + max->nanoseconds / 1000 < microseconds) { + return EMBB_OVERFLOW; + } + } + duration->seconds = microseconds / 1000000; + duration->nanoseconds = (microseconds % 1000000) * 1000; + return EMBB_SUCCESS; +} + +int embb_duration_set_milliseconds(embb_duration_t* duration, + unsigned long long milliseconds) { + assert(duration != NULL); + if (milliseconds > 0) { + if (embb_duration_min()->nanoseconds > milliseconds*1000000) { + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds * 1000 + max->nanoseconds / 1000000 < milliseconds) { + return EMBB_OVERFLOW; + } + } + duration->seconds = milliseconds / 1000; + duration->nanoseconds = (milliseconds % 1000) * 1000000; + return EMBB_SUCCESS; +} + +int embb_duration_set_seconds(embb_duration_t* duration, + unsigned long long seconds) { + assert(duration != NULL); + if (seconds > 0) { + if (embb_duration_min()->nanoseconds > seconds*1000000000) { + return EMBB_UNDERFLOW; + } + const embb_duration_t* max = embb_duration_max(); + if (max->seconds + max->nanoseconds / 1000000000 < seconds) { + return EMBB_OVERFLOW; + } + } + duration->seconds = seconds; + duration->nanoseconds = 0; + return EMBB_SUCCESS; +} + +int embb_duration_add(embb_duration_t* lhs, const embb_duration_t* rhs) { + assert(lhs != NULL); + assert(rhs != NULL); + int carry = (int)((lhs->nanoseconds + rhs->nanoseconds) / 1000000000); + if (lhs->seconds + rhs->seconds + carry > EMBB_DURATION_MAX_SECONDS) { + return EMBB_OVERFLOW; + } + lhs->nanoseconds = (lhs->nanoseconds + rhs->nanoseconds) % 1000000000; + lhs->seconds = lhs->seconds + rhs->seconds + carry; + return EMBB_SUCCESS; +} + +int embb_duration_as_nanoseconds(const embb_duration_t* duration, + unsigned long long* nanoseconds) { + assert(duration != NULL); + assert(nanoseconds != NULL); + if (duration->seconds*1000000000 + duration->nanoseconds > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *nanoseconds = duration->seconds*1000000000 + duration->nanoseconds; + return EMBB_SUCCESS; +} + +int embb_duration_as_microseconds(const embb_duration_t* duration, + unsigned long long* microseconds) { + assert(duration != NULL); + assert(microseconds != NULL); + if (duration->nanoseconds % 1000 > 0) { + return EMBB_UNDERFLOW; + } + if (duration->seconds*1000000 + duration->nanoseconds/1000 > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *microseconds = duration->seconds*1000000 + duration->nanoseconds / 1000; + return EMBB_SUCCESS; +} + +int embb_duration_as_milliseconds(const embb_duration_t* duration, + unsigned long long* milliseconds) { + assert(duration != NULL); + assert(milliseconds != NULL); + if (duration->nanoseconds % 1000000 > 0) { + return EMBB_UNDERFLOW; + } + if (duration->seconds*1000 + duration->nanoseconds/1000000 > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *milliseconds = duration->seconds*1000 + duration->nanoseconds / 1000000; + return EMBB_SUCCESS; +} + +int embb_duration_as_seconds(const embb_duration_t* duration, + unsigned long long* seconds) { + assert(duration != NULL); + assert(seconds != NULL); + if (duration->nanoseconds % 1000000000 > 0) { + return EMBB_UNDERFLOW; + } + assert(duration->nanoseconds % 1000000000 == 0); + if (duration->seconds > ULLONG_MAX) { + return EMBB_OVERFLOW; + } + *seconds = duration->seconds; + return EMBB_SUCCESS; +} + +int embb_duration_compare(const embb_duration_t* lhs, + const embb_duration_t* rhs) { + assert(lhs != NULL); + assert(rhs != NULL); + assert(lhs->nanoseconds < 1000000000); + assert(rhs->nanoseconds < 1000000000); + + if (lhs->seconds > rhs->seconds) { + return 1; + } else if (lhs->seconds < rhs->seconds) { + return -1; + } else { /* Seconds are equal */ + if (lhs->nanoseconds > rhs->nanoseconds) { + return 1; + } else if (lhs->nanoseconds < rhs->nanoseconds) { + return -1; + } + } + return 0; +} diff --git a/base_c/src/internal/thread_index.c b/base_c/src/internal/thread_index.c new file mode 100644 index 0000000..fe3dbab --- /dev/null +++ b/base_c/src/internal/thread_index.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Provides global variable for maximal number of thread indices. + * + * This function has local scope. + */ +static int embb_max_number_thread_indices_max = 0; +static embb_atomic_int embb_max_number_thread_indices_flag = { 0 }; + +unsigned int* embb_max_number_thread_indices() { + int compare_to = 0; + if (embb_atomic_load_int(&embb_max_number_thread_indices_flag) != 2) { + if (embb_atomic_compare_and_swap_int( + &embb_max_number_thread_indices_flag, &compare_to, 1)) { + embb_max_number_thread_indices_max = + (int)(embb_core_count_available() * 2); + embb_atomic_store_int(&embb_max_number_thread_indices_flag, 2); + } + while (embb_atomic_load_int(&embb_max_number_thread_indices_flag) != 2) {} + } + return (unsigned int*) &embb_max_number_thread_indices_max; +} + +/** + * Provides global variable for current thread index counter. + * + * This function has local scope. + */ +static embb_atomic_int embb_thread_index_counter_flag = { 0 }; +static embb_counter_t embb_thread_index_counter_index; +embb_counter_t* embb_thread_index_counter() { + int compare_to = 0; + if (embb_atomic_load_int(&embb_thread_index_counter_flag) != 2) { + if (embb_atomic_compare_and_swap_int( + &embb_thread_index_counter_flag, &compare_to, 1)) { + embb_counter_init(&embb_thread_index_counter_index); + embb_atomic_store_int(&embb_thread_index_counter_flag, 2); + } + while (embb_atomic_load_int(&embb_thread_index_counter_flag) != 2) {} + } + return &embb_thread_index_counter_index; +} + +/** + * Tries to return the next free (internal) thread index. + * + * An index is only set, if there was still one available. + * + * This function has local scope. + * + * \return EMBB_SUCCESS, if a free index was available. EMBB_ERROR otherwise. + */ +int embb_try_get_next_thread_index(unsigned int* free_index) { + assert(free_index != NULL); + unsigned int index = embb_counter_increment(embb_thread_index_counter()); + if (index >= *embb_max_number_thread_indices()) { + embb_counter_decrement(embb_thread_index_counter()); + return EMBB_ERROR; + } + *free_index = index; + return EMBB_SUCCESS; +} + +/** + * Thread specific thread index. + * + * This variable has local scope. + * + * This index is only used in certain data structures that have to know the + * number of threads and their indices in advance. Only a limited number of + * indices is made available. + */ +EMBB_THREAD_SPECIFIC unsigned int embb_internal_thread_index_var = UINT_MAX; + +int embb_internal_thread_index(unsigned int* index) { + assert(index != NULL); + if (embb_internal_thread_index_var == UINT_MAX) { + int status = + embb_try_get_next_thread_index(&embb_internal_thread_index_var); + if (status == EMBB_ERROR) { + return EMBB_ERROR; + } + } + *index = embb_internal_thread_index_var; + return EMBB_SUCCESS; +} + +int embb_internal_thread_index_max() { + return (int)(*embb_max_number_thread_indices()); +} + +void embb_internal_thread_index_set_max(unsigned int max) { + *embb_max_number_thread_indices() = max; +} + +void embb_internal_thread_index_reset() { + embb_counter_init(embb_thread_index_counter()); +} diff --git a/base_c/src/log.c b/base_c/src/log.c new file mode 100644 index 0000000..8dce1e1 --- /dev/null +++ b/base_c/src/log.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +#include + +#include + +void embb_log_write_file( + void * context, + char const * message) { + FILE * ff = (FILE*)context; + fprintf(ff, "%s", message); + fflush(ff); +} + +static embb_log_level_t embb_log_global_log_level = + EMBB_LOG_LEVEL_NONE; + +static void * embb_log_global_log_context = NULL; + +static embb_log_function_t embb_log_global_log_function = embb_log_write_file; + +void embb_log_set_log_level( + embb_log_level_t log_level) { + embb_log_global_log_level = log_level; +} + +void embb_log_set_log_function( + void * context, + embb_log_function_t func) { + embb_log_global_log_context = context; + embb_log_global_log_function = func; +} + +static void embb_log_write_internal( + char const * channel, + embb_log_level_t log_level, + char const * message, + va_list argp) { + if (log_level <= embb_log_global_log_level) { + char msg_buffer[400]; + char buffer[500]; + char * log_level_str = " "; + char const * channel_str = channel; + void * log_context = embb_log_global_log_context; + if (NULL == channel_str) { + channel_str = " global "; + } + if (NULL == log_context) { + log_context = (void*)stdout; + } + switch (log_level) { + case EMBB_LOG_LEVEL_INFO: + log_level_str = "INFO "; + break; + case EMBB_LOG_LEVEL_ERROR: + log_level_str = "ERROR"; + break; + case EMBB_LOG_LEVEL_TRACE: + log_level_str = "TRACE"; + break; + + case EMBB_LOG_LEVEL_NONE: + case EMBB_LOG_LEVEL_WARNING: + default: + log_level_str = " "; + break; + } +#if defined(EMBB_COMPILER_MSVC) + vsprintf_s(msg_buffer, sizeof(msg_buffer), message, argp); + sprintf_s(buffer, sizeof(buffer), "[%s] - [%s] %s", + channel_str, log_level_str, msg_buffer); + embb_log_global_log_function(log_context, buffer); +#elif defined(EMBB_COMPILER_GNUC) + vsnprintf(msg_buffer, sizeof(msg_buffer), message, argp); + snprintf(buffer, sizeof(buffer), "[%s] - [%s] %s", + channel_str, log_level_str, msg_buffer); + embb_log_global_log_function(log_context, buffer); +#else + embb_log_global_log_function(log_context, "["); + embb_log_global_log_function(log_context, channel_str); + embb_log_global_log_function(log_context, "] - ["); + embb_log_global_log_function(log_context, log_level_str); + embb_log_global_log_function(log_context, "] "); + /* no secure formatting possible, sorry */ + embb_log_global_log_function(log_context, message); +#endif + } +} + +void embb_log_write( + char const * channel, + embb_log_level_t log_level, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, log_level, message, argp); + va_end(argp); +} + +#ifdef EMBB_DEBUG +void embb_log_trace( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_TRACE, message, argp); + va_end(argp); +} + +void embb_log_info( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_INFO, message, argp); + va_end(argp); +} +#endif + +void embb_log_warning( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_WARNING, message, argp); + va_end(argp); +} + +void embb_log_error( + char const * channel, + char const * message, + ...) { + va_list argp; + va_start(argp, message); + embb_log_write_internal(channel, EMBB_LOG_LEVEL_ERROR, message, argp); + va_end(argp); +} diff --git a/base_c/src/memory_allocation.c b/base_c/src/memory_allocation.c new file mode 100644 index 0000000..4e2e878 --- /dev/null +++ b/base_c/src/memory_allocation.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include + +#ifdef EMBB_DEBUG + +static embb_atomic_long embb_bytes_allocated = { 0 }; + +enum { + // Make the marking unlikely to be something else + INVALID_ALLOCATION = 0x91919191, + ALIGNED_ALLOCATION = 0x99AABB11, + UNALIGNED_ALLOCATION = 0x11AABB99 +}; + +void* embb_alloc(size_t bytes) { + size_t bytes_to_allocate = 2 * sizeof(size_t)+bytes; + void* allocated = malloc(bytes_to_allocate); + + if (allocated == NULL) + return NULL; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)bytes_to_allocate); + + size_t* x_as_size_type = (size_t*)allocated; + + x_as_size_type[1] = UNALIGNED_ALLOCATION; + x_as_size_type[0] = (size_t)bytes_to_allocate; + + // Return the pointer to the payload + return (void*)((size_t*)allocated + 2); +} + +void embb_free(void * ptr) { + assert(ptr != NULL); + + size_t * alloc_type = (size_t*)ptr - 1; + size_t * bytes_allocated = (size_t*)ptr - 2; + + // Check whether this memory was allocated unaligned + assert((*alloc_type) == UNALIGNED_ALLOCATION); + + (*alloc_type) = (size_t)INVALID_ALLOCATION; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)(0 - (size_t)(*bytes_allocated))); + + free((size_t*)ptr - 2); +} + +void* embb_alloc_aligned(size_t alignment, size_t size) { + // In debug mode, we count the number of allocated bytes to be able to detect + // memory leaks. For that purpose, we allocate more memory than necessary for + // the payload. In the extra memory before the payload, we store a pointer to + // the original allocated block, a flag which indicates whether the allocation + // was aligned, and the number of allocated bytes. This requires additional + // 3*sizeof(size_t) bytes of memory (additional_bytes). + // To get at least n aligned sections and additional_bytes before, we need + // the following number of bytes: + // (n+1)*alignment + (additional_bytes-1) + + // n specifies the number of alignment blocks we need for the payload + size_t n = (size + (alignment - 1)) / alignment; + + // additional_bytes specifies the number of bytes we need to store in addition + // to the payload + size_t additional_bytes = sizeof(size_t) * 3; + + size_t bytes_to_allocate = (n + 1)*alignment + (additional_bytes - 1); + + char* allocated = (char *)malloc(bytes_to_allocate); + + if (allocated == NULL) + return NULL; + + // Get the next aligned pointer + char* x = (char*)(((uintptr_t)(allocated+alignment)) & ~(alignment - 1)); + + // If we do not have enough space before, get the next allocated position + while ((x-allocated) < (long)additional_bytes) + x += alignment; + + // Now check if our allocation algorithm worked correctly + // 1. Enough space for the payload + assert((size_t)((allocated + bytes_to_allocate) - x) >= size); + + // 2. x is aligned + assert(((size_t)x % alignment) == 0); + + // 3. Enough space for the additional information + assert((x - additional_bytes) >= allocated); + + // x is now the first aligned position (this is the return value) + size_t* x_as_size_type = (size_t*)x; + + x_as_size_type[-1] = (size_t)ALIGNED_ALLOCATION; + x_as_size_type[-2] = (size_t)allocated; + x_as_size_type[-3] = bytes_to_allocate; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)bytes_to_allocate); + + return x; +} + +void embb_free_aligned(void* ptr) { + assert(ptr != NULL); + size_t* ptr_conv = (size_t*)ptr; + + // If embb_free_aligned is called, the memory block should have been allocated + // using embb_alloc_aligned. + assert(ptr_conv[-1] == ALIGNED_ALLOCATION); + + ptr_conv[-1] = (size_t)INVALID_ALLOCATION; + + embb_atomic_fetch_and_add_long( + &embb_bytes_allocated, (long)((long)0 - ptr_conv[-3])); + + free((void*)ptr_conv[-2]); +} + +size_t embb_get_bytes_allocated() { + return (size_t)(embb_atomic_load_long(&embb_bytes_allocated)); +} + +#else // EMBB_DEBUG + +void * embb_alloc(size_t bytes) { + return malloc(bytes); +} + +void embb_free(void * ptr) { + assert(ptr != NULL); + free(ptr); +} + +void *embb_alloc_aligned(size_t alignment, size_t size) { + void* malloc_addr = NULL; +#ifdef EMBB_COMPILER_MSVC + /* + * From the Documentation: + * Allocates memory on a specified alignment boundary. + * + * Return: + * A pointer to the memory block that was allocated or NULL if the operation + * failed. The pointer is a multiple of alignment. + */ + malloc_addr = _aligned_malloc(size, alignment); +#elif defined EMBB_COMPILER_GNUC + /* + * From the Documentation: + * The posix_memalign() function shall allocate size bytes aligned on a + * boundary specified by alignment, and shall return a pointer to the + * allocated memory in memptr. The value of alignment shall be a multiple + * of sizeof(void *), that is also a power of two. Upon successful + * completion, the value pointed to by memptr shall be a multiple of + * alignment. + */ + int status = posix_memalign(&malloc_addr, alignment, size); + EMBB_UNUSED(status); +#endif + return malloc_addr; +} + +void embb_free_aligned(void* ptr) { +#ifdef EMBB_COMPILER_MSVC + _aligned_free(ptr); +#else +#ifdef EMBB_COMPILER_GNUC + free(ptr); +#else +#error Unsupported compiler +#endif +#endif +} + +size_t embb_get_bytes_allocated() { + return 0; +} + +#endif + +void *embb_alloc_cache_aligned(size_t size) { + return embb_alloc_aligned(EMBB_CACHE_LINE_SIZE, size); +} diff --git a/base_c/src/mutex.c b/base_c/src/mutex.c new file mode 100644 index 0000000..47ff982 --- /dev/null +++ b/base_c/src/mutex.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include + +#ifdef EMBB_THREADING_WINTHREADS + +int embb_mutex_init(embb_mutex_t* mutex, int type) { + /* Critical sections in Windows are always recursive */ + InitializeCriticalSection(mutex); + EMBB_UNUSED(type); + return EMBB_SUCCESS; +} + +int embb_mutex_lock(embb_mutex_t* mutex) { + EnterCriticalSection(mutex); + return EMBB_SUCCESS; +} + +int embb_mutex_try_lock(embb_mutex_t* mutex) { + BOOL success; + success = TryEnterCriticalSection(mutex); + if (success == FALSE) return EMBB_ERROR; + return EMBB_SUCCESS; +} + +int embb_mutex_unlock(embb_mutex_t* mutex) { + LeaveCriticalSection(mutex); + return EMBB_SUCCESS; +} + +void embb_mutex_destroy(embb_mutex_t* mutex) { + DeleteCriticalSection(mutex); +} + +#endif /* EMBB_THREADING_WINTHREADS */ + +#ifdef EMBB_THREADING_POSIXTHREADS + +int embb_mutex_init(embb_mutex_t* mutex, int type) { + if (type == EMBB_MUTEX_PLAIN) { + if (pthread_mutex_init(mutex, NULL) != 0) return EMBB_ERROR; + } else { + assert(type == EMBB_MUTEX_RECURSIVE); + pthread_mutexattr_t attributes; + if (pthread_mutexattr_init(&attributes) != 0) return EMBB_ERROR; + if (pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) { + return EMBB_ERROR; + } + if (pthread_mutex_init(mutex, &attributes) != 0) return EMBB_ERROR; + if (pthread_mutexattr_destroy(&attributes) != 0) return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_mutex_lock(embb_mutex_t* mutex) { + int result = pthread_mutex_lock(mutex); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_mutex_try_lock(embb_mutex_t* mutex) { + int result = pthread_mutex_trylock(mutex); + if (result == 0) { + return EMBB_SUCCESS; + } + if (result == EBUSY) { + return EMBB_BUSY; + } + return EMBB_ERROR; +} + +int embb_mutex_unlock(embb_mutex_t* mutex) { + int result = pthread_mutex_unlock(mutex); + if (result != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +void embb_mutex_destroy(embb_mutex_t* mutex) { + pthread_mutex_destroy(mutex); +} + +#endif /* EMBB_THREADING_POSIXTHREADS */ diff --git a/base_c/src/thread.c b/base_c/src/thread.c new file mode 100644 index 0000000..6737f2b --- /dev/null +++ b/base_c/src/thread.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include + +unsigned int embb_thread_get_max_count() { + return (unsigned int)(embb_internal_thread_index_max()); +} + +void embb_thread_set_max_count(unsigned int max) { + embb_internal_thread_index_set_max(max); +} + +#ifdef EMBB_THREADING_WINTHREADS + +/** + * Used to wrap client thread start function and argument when calling internal + * thread start function embb_internal_thread_start. + */ +typedef struct embb_internal_thread_arg_t { + embb_thread_start_t func; + void* arg; +} embb_internal_thread_arg_t; + +/** + * Used to offer a consistent thread start function signature. Windows threads + * have a different signature than Pthreads and C11. This internal start + * function for Windows threads just calls the client start function with the + * given argument. + */ +DWORD WINAPI embb_internal_thread_start(LPVOID internalArg) { + int result = ((embb_internal_thread_arg_t*)internalArg)->func( + ((embb_internal_thread_arg_t*)internalArg)->arg); +#if !defined(__cplusplus) + ExitThread((DWORD)result); /* In C, returning the result code doesn't work */ +#else + return (DWORD)result; +#endif +} + +embb_thread_t embb_thread_current() { + embb_thread_t thread; + thread.embb_internal_handle = GetCurrentThread(); + thread.embb_internal_arg = NULL; + return thread; +} + +void embb_thread_yield() { + SwitchToThread(); +} + +int embb_thread_create(embb_thread_t* thread, const embb_core_set_t* core_set, + embb_thread_start_t func, void *arg) { + assert(thread != NULL); + thread->embb_internal_arg = (embb_internal_thread_arg_t*) + embb_alloc(sizeof(embb_internal_thread_arg_t)); + if (thread->embb_internal_arg == NULL) return EMBB_NOMEM; + thread->embb_internal_arg->func = func; + thread->embb_internal_arg->arg = arg; + + thread->embb_internal_handle = CreateThread( + 0, /* no security */ + 0, /* default stack size */ + embb_internal_thread_start, /* entry function */ + (LPVOID)thread->embb_internal_arg, /* parameters */ + 0, /* no creation arguments */ + 0); /* no system thread ID */ + if (thread->embb_internal_handle == NULL) { + return EMBB_ERROR; + } + + if (core_set != NULL) { /* Set thread affinity, if a core set is given */ + DWORD_PTR core_mask = 0; + DWORD bit_mask = 1; + assert(embb_core_count_available() < 64); + for (unsigned int i = 0; i < embb_core_count_available(); i++) { + if (embb_core_set_contains(core_set, i)) { + core_mask |= bit_mask; + } + bit_mask <<= 1; + } + if (SetThreadAffinityMask(thread->embb_internal_handle, core_mask) + == (DWORD_PTR)NULL) { + return EMBB_ERROR; + } + } + + return EMBB_SUCCESS; +} + +int embb_thread_join(embb_thread_t* thread, int* result_code) { + BOOL success; + DWORD result; + result = WaitForSingleObject(thread->embb_internal_handle, INFINITE); + embb_free(thread->embb_internal_arg); + if (result != WAIT_OBJECT_0) { + /* WAIT_OBJECT_0 indicates successful waiting */ + return EMBB_ERROR; + } + if (result_code != NULL) { /* != NULL means the client wants a result code */ + if (GetExitCodeThread(thread->embb_internal_handle, &result) != 0) { + *result_code = (int)result; + } else { + *result_code = 0; /* Error on obtaining result code */ + return EMBB_ERROR; + } + } + success = CloseHandle(thread->embb_internal_handle); + if (success == FALSE) { + return EMBB_ERROR; + } + /*return embb_internal_thread_counter_try_decrement();*/ + return EMBB_SUCCESS; +} + +int embb_thread_equal(const embb_thread_t* lhs, const embb_thread_t* rhs) { + embb_thread_id_t idLhs = GetThreadId(lhs->embb_internal_handle); + embb_thread_id_t idRhs = GetThreadId(rhs->embb_internal_handle); + if (idLhs == idRhs) { + return 1; + } + return 0; +} + +#endif /* EMBB_THREADING_WINTHREADS */ + +#ifdef EMBB_THREADING_POSIXTHREADS + +#ifdef EMBB_HAS_GLIB_CPU +#include +#endif + +#ifdef EMBB_HAS_HEADER_SYSINFO +#include /* Used to get number of processors */ +#endif /* EMBB_HAS_HEADER_SYSINFO */ + +/** + * Used to wrap client thread start function and argument when calling internal + * thread start function embb_internal_thread_start. + */ +typedef struct embb_internal_thread_arg_t { + embb_thread_start_t func; + void* arg; + int result; +} embb_internal_thread_arg_t; + +/** + * Used to offer a consistent thread start function signature. POSIX threads + * have a different signature than C11 threads. This internal start function + * for POSIX threads just calls the client start function with the given + * argument. + */ +void* embb_internal_thread_start(void* internalArg) { + ((embb_internal_thread_arg_t*)internalArg)->result = + ((embb_internal_thread_arg_t*)internalArg)->func( + ((struct embb_internal_thread_arg_t*)internalArg)->arg); + return NULL; +} + +embb_thread_t embb_thread_current() { + embb_thread_t thread; + thread.embb_internal_handle = pthread_self(); + thread.embb_internal_arg = NULL; + return thread; +} + +void embb_thread_yield() { + pthread_yield(); +} + +int embb_thread_create(embb_thread_t* thread, const embb_core_set_t* core_set, + embb_thread_start_t func, void* arg) { + pthread_attr_t attr; /* Used to set thread affinities */ + int status = pthread_attr_init(&attr); + if (status != 0) return EMBB_ERROR; + if (core_set != NULL) { +#ifdef EMBB_HAS_GLIB_CPU + assert(embb_core_count_available() < CPU_SETSIZE && + "Core sets on Linux systems are only supported up to CPU_SETSIZE " + "processors!"); + cpu_set_t cpuset; + CPU_ZERO(&cpuset); /* Disable all processors */ + for (unsigned int i = 0; i < embb_core_count_available(); i++) { + if (embb_core_set_contains(core_set, i)) { + CPU_SET(i, &cpuset); + } + } + status = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); + if (status != 0) return EMBB_ERROR; +#else /* EMBB_HAS_GLIB_CPU */ + embb_log_write("base_c", EMBB_LOG_LEVEL_WARNING, "Could not set thread " + "affinity, since no implementation available!\n"); +#endif /* else EMBB_HAS_GLIB_CPU */ + } + + /* Dynamic allocation of thread arguments. Freed on call of join. */ + thread->embb_internal_arg = (embb_internal_thread_arg_t*) + embb_alloc(sizeof(embb_internal_thread_arg_t)); + thread->embb_internal_arg->func = func; + thread->embb_internal_arg->arg = arg; + + status = pthread_create( + &(thread->embb_internal_handle), /* pthread handle */ + &attr, /* additional attributes, + e.g., affinities */ + embb_internal_thread_start, /* thread start function */ + (void*)(thread->embb_internal_arg)); /* arguments to thread start func. */ + if (status != 0) return EMBB_ERROR; + + pthread_attr_destroy(&attr); + if (status != 0) return EMBB_ERROR; + return EMBB_SUCCESS; +} + +int embb_thread_join(embb_thread_t* thread, int *result_code) { + int status = 0; + status = pthread_join(thread->embb_internal_handle, NULL); + if (result_code != NULL) { + *result_code = thread->embb_internal_arg->result; + } + embb_free(thread->embb_internal_arg); + if (status != 0) { + return EMBB_ERROR; + } + return EMBB_SUCCESS; +} + +int embb_thread_equal(const embb_thread_t* lhs, const embb_thread_t* rhs) { + return pthread_equal(lhs->embb_internal_handle, rhs->embb_internal_handle); +} + +#endif /* EMBB_THREADING_POSIXTHREADS */ diff --git a/base_c/src/thread_specific_storage.c b/base_c/src/thread_specific_storage.c new file mode 100644 index 0000000..a925462 --- /dev/null +++ b/base_c/src/thread_specific_storage.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include + +int embb_tss_create(embb_tss_t* tss) { + assert(tss != NULL); + tss->size = embb_thread_get_max_count(); + tss->values = (void**) embb_alloc_cache_aligned(tss->size * sizeof(void*)); + if (tss->values == NULL) { + return EMBB_NOMEM; + } + for (unsigned int i = 0; i < tss->size; i++) { + tss->values[i] = NULL; + } + return EMBB_SUCCESS; +} + +int embb_tss_set(embb_tss_t* tss, void* value) { + assert(tss != NULL); + unsigned int index = 0; + int status = embb_internal_thread_index(&index); + if ((status != EMBB_SUCCESS) || (index >= tss->size)) { + return EMBB_ERROR; + } + tss->values[index] = value; + return EMBB_SUCCESS; +} + +void* embb_tss_get(const embb_tss_t* tss) { + assert(tss != NULL); + unsigned int index = 0; + int status = embb_internal_thread_index(&index); + if ((status != EMBB_SUCCESS) || (index >= tss->size)) { + return NULL; + } + return tss->values[index]; +} + +void embb_tss_delete(embb_tss_t* tss) { + assert(tss != NULL); + embb_free_aligned(tss->values); +} diff --git a/base_c/src/time.c b/base_c/src/time.c new file mode 100644 index 0000000..04f3968 --- /dev/null +++ b/base_c/src/time.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +void embb_time_now(embb_time_t* time) { + embb_time_in(time, embb_duration_zero()); +} + +int embb_time_compare(const embb_time_t* lhs, const embb_time_t* rhs) { + assert(lhs != NULL); + assert(rhs != NULL); + assert(lhs->nanoseconds < 1000000000); + assert(rhs->nanoseconds < 1000000000); + + if (lhs->seconds > rhs->seconds) { + return 1; + } else if (lhs->seconds < rhs->seconds) { + return -1; + } else { /* Seconds are equal */ + if (lhs->nanoseconds > rhs->nanoseconds) { + return 1; + } else if (lhs->nanoseconds < rhs->nanoseconds) { + return -1; + } + } + return 0; +} + + +#ifdef EMBB_THREADING_WINTHREADS + +int embb_time_in(embb_time_t* time, const embb_duration_t* duration) { + assert(time != NULL); + assert(duration != NULL); + /* Get system time */ + SYSTEMTIME system_time; + GetLocalTime(&system_time); + /* Convert system time to file time */ + FILETIME file_time; + SystemTimeToFileTime(&system_time, &file_time); /* Has 100 nanosec. ticks */ + /* Convert file time to windows unsigned large integer union type */ + ULARGE_INTEGER win_time; + win_time.LowPart = file_time.dwLowDateTime; + win_time.HighPart = file_time.dwHighDateTime; + /* Convert windows time type to EMBB type */ + time->seconds = win_time.QuadPart / 10000000; + time->nanoseconds = win_time.QuadPart % 10000000; + /* Add duration to time */ + int carry = (int)((time->nanoseconds + duration->nanoseconds) / 1000000000); + if (time->seconds + duration->seconds + carry > EMBB_TIME_MAX_SECONDS) { + return EMBB_OVERFLOW; + } + time->seconds += duration->seconds + carry; + time->nanoseconds += duration->nanoseconds - carry * 1000000000; + return EMBB_SUCCESS; +} + +#endif /* EMBB_THREADING_WINTHREADS */ + + +#ifdef EMBB_THREADING_POSIXTHREADS + +int embb_time_in(embb_time_t* time, const embb_duration_t* duration) { + assert(time != NULL); + assert(duration != NULL); + struct timespec unix_time; + clock_gettime(CLOCK_REALTIME, &unix_time); + time->seconds = unix_time.tv_sec; + time->nanoseconds = unix_time.tv_nsec; + int carry = (time->nanoseconds + duration->nanoseconds) / 1000000000; + if (time->seconds + duration->seconds + carry > EMBB_TIME_MAX_SECONDS) { + return EMBB_OVERFLOW; + } + time->seconds += duration->seconds + carry; + time->nanoseconds += duration->nanoseconds - carry * 1000000000; + return EMBB_SUCCESS; +} + +#endif /* EMBB_THREADING_POSIXTHREADS */ + diff --git a/base_c/test/README.txt b/base_c/test/README.txt new file mode 100644 index 0000000..14524d6 --- /dev/null +++ b/base_c/test/README.txt @@ -0,0 +1,4 @@ +There are no direct tests for the C implementation of atomic operations, since +these are extensively tested via the C++ wrappers. See files atomic_tests.h +and atomic_tests.cc in folder embb\base_cpp\test. + diff --git a/base_c/test/alloc_test.cc b/base_c/test/alloc_test.cc new file mode 100644 index 0000000..9ac33ee --- /dev/null +++ b/base_c/test/alloc_test.cc @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +AllocTest::AllocTest() + : allocated_(partest::TestSuite::GetDefaultNumThreads()) { + CreateUnit("Non-aligned") + .Add(&AllocTest::TestNonAlignedAlloc, this, + partest::TestSuite::GetDefaultNumThreads()) + .Post(&AllocTest::PostNonAligned, this); + + CreateUnit("Aligned") + .Add(&AllocTest::TestAlignedAlloc, this, + partest::TestSuite::GetDefaultNumThreads()) + .Post(&AllocTest::PostAligned, this); + + CreateUnit("Cache aligned") + .Add(&AllocTest::TestCacheAlignedAlloc, this, + partest::TestSuite::GetDefaultNumThreads()) + .Post(&AllocTest::PostCacheAligned, this); + + CreateUnit("AccessAllocatedMemory") + .Add(&AllocTest::TestAccessAllocatedMemory, this) + .Post(&AllocTest::PostAccessAllocatedMemory, this); + + CreateUnit("Mixed").Add(&AllocTest::TestMixedAllocs, this); +} + +void AllocTest::TestAccessAllocatedMemory() { + static const unsigned int memory_allocation_iterations = 1000; + + ::std::vector allocs_aligned; + ::std::vector allocs_unaligned; + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + allocs_aligned.push_back(embb_alloc_cache_aligned(8)); + allocs_unaligned.push_back(embb_alloc(8)); + } + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + memcpy(allocs_aligned[i], &i, sizeof(int)); + memcpy(allocs_unaligned[i], &i, sizeof(int)); + } + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + unsigned int alloc_aligned_test; + unsigned int alloc_unaligned_test; + memcpy(&alloc_aligned_test, allocs_aligned[i], sizeof(i)); + memcpy(&alloc_unaligned_test, allocs_unaligned[i], sizeof(i)); + + PT_EXPECT_EQ(alloc_aligned_test, i); + if (alloc_aligned_test != i) { + PT_ASSERT(false); + } + if (alloc_unaligned_test != i) { + PT_ASSERT(false); + } + } + + for (unsigned int i = 0; i != memory_allocation_iterations; ++i) { + embb_free(allocs_unaligned[i]); + embb_free_aligned(allocs_aligned[i]); + } +} + +void AllocTest::PostAccessAllocatedMemory() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestNonAlignedAlloc() { + size_t thread_num = + partest::TestSuite::GetCurrentThreadID(); + assert(thread_num < allocated_.size()); + allocated_[thread_num] = NULL; + allocated_[thread_num] = embb_alloc(1); + PT_EXPECT_NE(allocated_[thread_num], static_cast(NULL)); + embb_free(allocated_[thread_num]); +} + +void AllocTest::PostNonAligned() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestAlignedAlloc() { + size_t thread_num = + partest::TestSuite::GetCurrentThreadID(); + assert(thread_num < allocated_.size()); + allocated_[thread_num] = NULL; + allocated_[thread_num] = embb_alloc_aligned(2*sizeof(void*), 1); + PT_EXPECT_NE(allocated_[thread_num], static_cast(NULL)); + embb_free_aligned(allocated_[thread_num]); +} + +void AllocTest::PostAligned() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestCacheAlignedAlloc() { + size_t thread_num = + partest::TestSuite::GetCurrentThreadID(); + assert(thread_num < allocated_.size()); + allocated_[thread_num] = NULL; + allocated_[thread_num] = embb_alloc_cache_aligned(1); + PT_EXPECT_NE(allocated_[thread_num], static_cast(NULL)); + embb_free_aligned(allocated_[thread_num]); +} + +void AllocTest::PostCacheAligned() { + size_t left = embb_get_bytes_allocated(); + PT_EXPECT_EQ(left, (size_t)0); +} + +void AllocTest::TestMixedAllocs() { + size_t expected = 0; + size_t allocated = 0; + + // Plain memory allocation + void* plain = NULL; + plain = embb_alloc(2); + PT_EXPECT_NE(plain, static_cast(NULL)); + allocated = embb_get_bytes_allocated(); +#ifdef EMBB_DEBUG + expected += 2 + 2*sizeof(size_t); +#endif // else EMBB_DEBUG + PT_EXPECT_EQ(allocated, expected); + + // Aligned memory allocation + void* aligned = NULL; + aligned = embb_alloc_aligned(2*sizeof(void*), 2); + PT_EXPECT_NE(aligned, static_cast(NULL)); + allocated = embb_get_bytes_allocated(); +#ifdef EMBB_DEBUG + expected += (1 + 1) * 2 * sizeof(void*) + 3 * sizeof(size_t) - 1; +#endif // else EMBB_DEBUG + PT_EXPECT_EQ(allocated, expected); + + // Cache-aligned memory allocation + void* cache_aligned = NULL; + cache_aligned = embb_alloc_cache_aligned(2); + PT_EXPECT_NE(cache_aligned, static_cast(NULL)); + allocated = embb_get_bytes_allocated(); +#ifdef EMBB_DEBUG + expected += (1 + 1) * EMBB_CACHE_LINE_SIZE + 3 * sizeof(size_t) - 1; +#endif // else EMBB_DEBUG + PT_EXPECT_EQ(allocated, expected); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/alloc_test.h b/base_c/test/alloc_test.h new file mode 100644 index 0000000..77ca14b --- /dev/null +++ b/base_c/test/alloc_test.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_ALLOC_TEST_H_ +#define BASE_C_TEST_ALLOC_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class AllocTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + AllocTest(); + + private: + /** + * Tests non-aligned memory allocation and freeing with several threads. + */ + void TestNonAlignedAlloc(); + void PostNonAligned(); + + /** + * Tests aligned memory allocation and freeing with several threads. + */ + void TestAlignedAlloc(); + void PostAligned(); + + /** + * Tests cache aligned memory allocation and freeing with several threads. + */ + void TestCacheAlignedAlloc(); + void PostCacheAligned(); + + /** + * Tests mixed memory allocation in detail using a single thread. + */ + void TestMixedAllocs(); + + /** + * Tests access to allocated memory using a single thread. + */ + void TestAccessAllocatedMemory(); + void PostAccessAllocatedMemory(); + + /** + * Stores the pointer to the allocated memory for each thread. + */ + std::vector allocated_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif /* BASE_C_TEST_ALLOC_TEST_H_ */ diff --git a/base_c/test/condition_var_test.cc b/base_c/test/condition_var_test.cc new file mode 100644 index 0000000..ceb0830 --- /dev/null +++ b/base_c/test/condition_var_test.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014, 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 + +namespace embb { +namespace base { +namespace test { + +ConditionVarTest::ConditionVarTest() +:num_threads_(partest::TestSuite::GetDefaultNumThreads()) { + embb_counter_init(&counter_); + embb_condition_init(&cond_notify_); + embb_mutex_init(&mutex_cond_notify_, EMBB_MUTEX_PLAIN); + embb_condition_init(&cond_wait_); + embb_mutex_init(&mutex_cond_wait_, EMBB_MUTEX_PLAIN); + + CreateUnit("Timed wait timouts") + .Add(&ConditionVarTest::TestTimedWaitTimeouts, this); + if (num_threads_ >= 2) { + CreateUnit("Condition Notify Test") + .Add(&ConditionVarTest::TestNotify, this, num_threads_); + } else { + std::cout << "Warning: Condition Notify Test needs a minimum of two threads" + << std::endl; + } +} + +void ConditionVarTest::TestNotify() { + size_t threadID = partest::TestSuite::GetCurrentThreadID(); + + if (threadID != 0) { + embb_mutex_lock(&mutex_cond_notify_); + embb_counter_increment(&counter_); + embb_condition_wait(&cond_notify_, &mutex_cond_notify_); + embb_counter_increment(&counter_); + embb_mutex_unlock(&mutex_cond_notify_); + } else { + embb_duration_t duration = EMBB_DURATION_INIT; + embb_duration_set_milliseconds(&duration, 1); + + while (embb_counter_get(&counter_) + < static_cast(num_threads_-1)) + {} // all threads entered critical section + embb_mutex_lock(&mutex_cond_notify_); + embb_mutex_unlock(&mutex_cond_notify_); + // All threads called wait on the condition (Even last thread) + + embb_counter_init(&counter_); + + embb_condition_notify_one(&cond_notify_); + embb_mutex_lock(&mutex_cond_wait_); + embb_condition_wait_for(&cond_wait_, &mutex_cond_wait_, &duration); + while (embb_counter_get(&counter_) == 0) + {} //if hangs here signal has not succeded + PT_ASSERT_EQ_MSG(embb_counter_get(&counter_), static_cast(1), + "Only one thread notified"); + + embb_condition_notify_all(&cond_notify_); + + embb_condition_wait_for(&cond_wait_, &mutex_cond_wait_, &duration); + + while (embb_counter_get(&counter_) != + static_cast(num_threads_-1)) + {} // If this hangs then not all threads were notified. + + embb_mutex_unlock(&mutex_cond_wait_); + embb_mutex_destroy(&mutex_cond_wait_); + embb_mutex_destroy(&mutex_cond_notify_); + embb_condition_destroy(&cond_wait_); + embb_condition_destroy(&cond_notify_); + } +} + + +void ConditionVarTest::TestTimedWaitTimeouts() { + // Set up data structures + embb_condition_t cond; + embb_mutex_t mutex; + embb_condition_init(&cond); + embb_mutex_init(&mutex, EMBB_MUTEX_PLAIN); + embb_time_t time; + embb_duration_t duration = EMBB_DURATION_INIT; + + // 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); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Wait for a future timepoint + status = embb_duration_set_milliseconds(&duration, 1); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_time_in(&time, &duration); // Time now + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_condition_wait_until(&cond, &mutex, &time); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Wait for a zero duration + status = embb_duration_set_milliseconds(&duration, 0); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_condition_wait_for(&cond, &mutex, &duration); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Wait for some duration + status = embb_duration_set_milliseconds(&duration, 1); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_condition_wait_for(&cond, &mutex, &duration); + PT_EXPECT_EQ(status, EMBB_TIMEDOUT); + + // Tear down data structures + embb_mutex_unlock(&mutex); + embb_mutex_destroy(&mutex); + embb_condition_destroy(&cond); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/condition_var_test.h b/base_c/test/condition_var_test.h new file mode 100644 index 0000000..7fb1428 --- /dev/null +++ b/base_c/test/condition_var_test.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_CONDITION_VAR_TEST_H_ +#define BASE_C_TEST_CONDITION_VAR_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class ConditionVarTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ConditionVarTest(); + + private: + /** + * Tests timeout of condition wait until and wait for methods. + */ + void TestTimedWaitTimeouts(); + + /** + * Tests if notify works correctly. + * Each thread waits on the same lock. + */ + void TestNotify(); + + size_t num_threads_; + embb_counter_t counter_; + embb_condition_t cond_notify_; + embb_mutex_t mutex_cond_notify_; + embb_condition_t cond_wait_; + embb_mutex_t mutex_cond_wait_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif /* BASE_C_TEST_CONDITION_VAR_TEST_H_ */ diff --git a/base_c/test/core_set_test.cc b/base_c/test/core_set_test.cc new file mode 100644 index 0000000..3b59922 --- /dev/null +++ b/base_c/test/core_set_test.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, 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 +#include + +namespace embb { +namespace base { +namespace test { + +CoreSetTest::CoreSetTest() { + CreateUnit("Test all").Add(&CoreSetTest::Test, this); +} + +void CoreSetTest::Test() { + embb_core_set_t set; + unsigned int available_cores = embb_core_count_available(); + unsigned int zero_cores = 0; + // Test setting all cores + embb_core_set_init(&set, 0); + unsigned int cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, zero_cores); + embb_core_set_init(&set, 1); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, available_cores); + + // Test setting individual cores + embb_core_set_init(&set, 0); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, zero_cores); + for (unsigned int i = 0; i < available_cores; i++) { + embb_core_set_add(&set, i); + int is_set = embb_core_set_contains(&set, i); + PT_EXPECT_EQ(is_set, 1); + if (i < available_cores - 1) { + is_set = embb_core_set_contains(&set, i+1); + PT_EXPECT_EQ(is_set, 0); + } + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, i+1); + } + + // Test logical & and | operations + embb_core_set_t set2; + embb_core_set_init(&set, 0); + embb_core_set_init(&set2, 1); + embb_core_set_intersection(&set, &set2); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, zero_cores); + embb_core_set_union(&set, &set2); + cores = embb_core_set_count(&set); + PT_EXPECT_EQ(cores, available_cores); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git a/base_c/test/core_set_test.h b/base_c/test/core_set_test.h new file mode 100644 index 0000000..43a73b8 --- /dev/null +++ b/base_c/test/core_set_test.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_CORE_SET_TEST_H_ +#define BASE_C_TEST_CORE_SET_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class CoreSetTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + CoreSetTest(); + + private: + /** + * Tests all functionalities. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif /* BASE_C_TEST_CORE_SET_TEST_H_ */ diff --git a/base_c/test/counter_test.cc b/base_c/test/counter_test.cc new file mode 100644 index 0000000..e1cdf27 --- /dev/null +++ b/base_c/test/counter_test.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +namespace embb { +namespace base { +namespace test { + +CounterTest::CounterTest() { + CreateUnit("Single threaded API test").Add(&CounterTest::TestBase, this); + CreateUnit(); +} + +void CounterTest::TestBase() { + embb_counter_t counter; + embb_counter_init(&counter); + + unsigned int value = embb_counter_get(&counter); + PT_EXPECT_EQ(value, static_cast(0)); + value = embb_counter_increment(&counter); + PT_EXPECT_EQ(value, static_cast(0)); + value = embb_counter_get(&counter); + PT_EXPECT_EQ(value, static_cast(1)); + value = embb_counter_decrement(&counter); + PT_EXPECT_EQ(value, static_cast(1)); + value = embb_counter_get(&counter); + PT_EXPECT_EQ(value, static_cast(0)); + embb_counter_destroy(&counter); +} + +CounterTest::TestStress::TestStress() + : TestUnit("Stress test for incrementing and decrementing"), counter_() { + size_t num_threads = partest::TestSuite::GetDefaultNumThreads(); + size_t num_iterations = partest::TestSuite::GetDefaultNumIterations(); + Pre(&TestStress::Init, this); + Add(&TestStress::TestCounterIncrement, this, num_threads, num_iterations); + Add(&TestStress::TestCounterDecrement, this, num_threads, num_iterations); + Post(&TestStress::CheckAndDestroyCounter, this); +} + +void CounterTest::TestStress::CheckAndDestroyCounter() { + unsigned int value = embb_counter_get(&counter_); + PT_EXPECT_EQ(value, static_cast(0)); + embb_counter_destroy(&counter_); +} + +} // namespace test +} // namespace base +} // namespace embb + + diff --git a/base_c/test/counter_test.h b/base_c/test/counter_test.h new file mode 100644 index 0000000..24e280f --- /dev/null +++ b/base_c/test/counter_test.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_COUNTER_TEST_H_ +#define BASE_C_TEST_COUNTER_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for C counter functionality. + */ +class CounterTest : public partest::TestCase { + public: + /** + * Adds test units. + */ + CounterTest(); + + private: + /** + * Checks the correct return values of inc., dec., and get methods. + */ + void TestBase(); + + /** + * Test repeated incrementing and decrement by several threads. + */ + class TestStress : public partest::TestUnit { + public: + /** + * Adds test methods to unit. + */ + TestStress(); + + private: + /** + * Inits the counter. + */ + void Init() { + embb_counter_init(&counter_); + } + /** + * Increases the counter. + */ + void TestCounterIncrement() { + embb_counter_increment(&counter_); + } + + /** + * Decreases the counter. + */ + void TestCounterDecrement() { + embb_counter_decrement(&counter_); + } + + /** + * Checks the value of the counter and deletes the counter. + */ + void CheckAndDestroyCounter(); + + /** + * Counter used in tests. + */ + embb_counter_t counter_; + }; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif /* BASE_C_TEST_COUNTER_TEST_H_ */ diff --git a/base_c/test/duration_test.cc b/base_c/test/duration_test.cc new file mode 100644 index 0000000..061fe67 --- /dev/null +++ b/base_c/test/duration_test.cc @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +DurationTest::DurationTest() { + CreateUnit("Compare durations").Add(&DurationTest::TestCompare, this); + CreateUnit("Set zero durations").Add(&DurationTest::TestSetZero, this); + CreateUnit("Set and get").Add(&DurationTest::TestSetAndGet, this); + CreateUnit("Add").Add(&DurationTest::TestAdd, this); +} + +void DurationTest::TestCompare() { + embb_duration_t lhs = EMBB_DURATION_INIT; + embb_duration_t rhs = EMBB_DURATION_INIT; + + int result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 0); + + lhs.seconds = 1; + lhs.nanoseconds = 0; + rhs.seconds = 0; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 1); + + lhs.seconds = 0; + lhs.nanoseconds = 0; + rhs.seconds = 1; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, -1); + + lhs.seconds = 0; + lhs.nanoseconds = 1; + rhs.seconds = 0; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 1); + + lhs.seconds = 0; + lhs.nanoseconds = 0; + rhs.seconds = 0; + rhs.nanoseconds = 1; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, -1); + + lhs.seconds = 1; + lhs.nanoseconds = 0; + rhs.seconds = 1; + rhs.nanoseconds = 0; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 0); + + lhs.seconds = 0; + lhs.nanoseconds = 1; + rhs.seconds = 0; + rhs.nanoseconds = 1; + result = embb_duration_compare(&lhs, &rhs); + PT_EXPECT_EQ(result, 0); +} + +void DurationTest::TestSetZero() { + PT_EXPECT_EQ(embb_duration_zero()->seconds, + static_cast(0)); + PT_EXPECT_EQ(embb_duration_zero()->nanoseconds, + static_cast(0)); + + embb_duration_t duration; + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_seconds(&duration, 0); + int result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); + + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_milliseconds(&duration, 0); + result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); + + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_microseconds(&duration, 0); + result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); + + duration.seconds = 1; + duration.nanoseconds = 1; + embb_duration_set_nanoseconds(&duration, 0); + result = embb_duration_compare(&duration, embb_duration_zero()); + PT_EXPECT_EQ(result, 0); +} + +void DurationTest::TestSetAndGet() { + // No underflows are detected here, to be done + + embb_duration_t duration = EMBB_DURATION_INIT; + + unsigned long long ticks = 0; + unsigned long long min_ticks = 0; + embb_duration_as_nanoseconds(embb_duration_min(), &min_ticks); + int result = embb_duration_set_nanoseconds(&duration, min_ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks); + result = embb_duration_as_microseconds(&duration, &ticks); + if (min_ticks / 1000 == 0 || min_ticks % 1000 != 0) { + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + } else { + PT_EXPECT_EQ(ticks, min_ticks / 1000); + } + result = embb_duration_as_milliseconds(&duration, &ticks); + if (min_ticks / 1000000 == 0 || min_ticks % 1000000 != 0) { + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + } else { + PT_EXPECT_EQ(ticks, min_ticks / 1000000); + } + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + + ticks = 0; + embb_duration_as_microseconds(embb_duration_min(), &min_ticks); + result = embb_duration_set_microseconds(&duration, min_ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks*1000); + result = embb_duration_as_microseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks); + result = embb_duration_as_milliseconds(&duration, &ticks); + if (min_ticks / 1000 == 0 || min_ticks % 1000 != 0) { + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + } else { + PT_EXPECT_EQ(ticks, min_ticks / 1000); + } + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + + ticks = 0; + embb_duration_as_milliseconds(embb_duration_min(), &min_ticks); + result = embb_duration_set_milliseconds(&duration, min_ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks*1000000); + result = embb_duration_as_microseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks*1000); + result = embb_duration_as_milliseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, min_ticks); + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_UNDERFLOW); + + ticks = 0; + result = embb_duration_set_seconds(&duration, 1); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_as_nanoseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1000000000)); + result = embb_duration_as_microseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1000000)); + result = embb_duration_as_milliseconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1000)); + result = embb_duration_as_seconds(&duration, &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(ticks, static_cast(1)); + + ticks = 0; + result = embb_duration_as_nanoseconds(embb_duration_min(), &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_GT(ticks, static_cast(0)); + + ticks = 0; + result = embb_duration_as_seconds(embb_duration_max(), &ticks); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_NE(ticks, static_cast(0)); + + embb_duration_as_seconds(embb_duration_max(), &ticks); + ticks++; + result = embb_duration_set_seconds(&duration, ticks); + PT_EXPECT_EQ(result, EMBB_OVERFLOW); +} + +void DurationTest::TestAdd() { + embb_duration_t lhs = EMBB_DURATION_INIT; + embb_duration_t rhs = EMBB_DURATION_INIT; + + int result = embb_duration_add(&lhs, &rhs); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(embb_duration_compare(&lhs, embb_duration_zero()), 0); + + result = embb_duration_set_milliseconds(&rhs, 1); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + result = embb_duration_add(&lhs, &rhs); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(embb_duration_compare(&lhs, &rhs), 0); + + embb_duration_set_seconds(&lhs, 0); + embb_duration_set_milliseconds(&rhs, 1100); + result = embb_duration_add(&lhs, &rhs); + PT_EXPECT_EQ(result, EMBB_SUCCESS); + PT_EXPECT_EQ(lhs.seconds, static_cast(1)); + PT_EXPECT_EQ(lhs.nanoseconds, static_cast(100000000)); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git a/base_c/test/duration_test.h b/base_c/test/duration_test.h new file mode 100644 index 0000000..7afb2f4 --- /dev/null +++ b/base_c/test/duration_test.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_DURATION_TEST_H_ +#define BASE_C_TEST_DURATION_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for Base C duration type and methods. + * + * These tests access the internal of embb_duration_t, which is necessary to + * get a starting point without dependencies on not-tested methods. It is + * discouraged in real usage, due to possible changes to the type. + */ +class DurationTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + DurationTest(); + + private: + /** + * Tests compare method. + */ + void TestCompare(); + + /** + * Tests the zero duration and setting a duration to zero with all set methods. + */ + void TestSetZero(); + + /** + * Tests setting and getting values. + */ + void TestSetAndGet(); + + /** + * Tests adding durations. + */ + void TestAdd(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif /* BASE_C_TEST_DURATION_TEST_H_ */ diff --git a/base_c/test/main.cc b/base_c/test/main.cc new file mode 100644 index 0000000..557f641 --- /dev/null +++ b/base_c/test/main.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PT_MAIN("Base C") { + embb_log_set_log_level(EMBB_LOG_LEVEL_WARNING); + unsigned int max_threads = + static_cast(2 * partest::TestSuite::GetDefaultNumThreads()); + embb_thread_set_max_count(max_threads); + + PT_RUN(embb::base::test::AllocTest); + PT_RUN(embb::base::test::DurationTest); + PT_RUN(embb::base::test::TimeTest); + PT_RUN(embb::base::test::CounterTest); + PT_RUN(embb::base::test::MutexTest); + PT_RUN(embb::base::test::ThreadIndexTest); + PT_RUN(embb::base::test::CoreSetTest); + PT_RUN(embb::base::test::ConditionVarTest); + PT_RUN(embb::base::test::ThreadTest); + PT_RUN(embb::base::test::ThreadSpecificStorageTest); +} diff --git a/base_c/test/mutex_test.cc b/base_c/test/mutex_test.cc new file mode 100644 index 0000000..3b22d96 --- /dev/null +++ b/base_c/test/mutex_test.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +namespace embb { +namespace base { +namespace test { + +MutexTest::MutexTest() : counter_(0), + number_threads_(partest::TestSuite::GetDefaultNumThreads()), + number_iterations_(partest::TestSuite::GetDefaultNumIterations()) { + embb_mutex_init(&mutex_, EMBB_MUTEX_PLAIN); + //embb_thread_set_max_count(number_threads_); + CreateUnit("Protected counter") + .Pre(&MutexTest::PreMutexInc, this) + .Add(&MutexTest::TestMutexInc, this, number_threads_, number_iterations_) + .Post(&MutexTest::PostMutexInc, this); + CreateUnit("Recursive mutex") + .Add(&MutexTest::TestRecursiveMutex, this); +} + +void MutexTest::PreMutexInc() { + counter_ = 0; +} + +void MutexTest::TestMutexInc() { + embb_mutex_lock(&mutex_); + ++counter_; + embb_mutex_unlock(&mutex_); +} + +void MutexTest::PostMutexInc() { + PT_EXPECT_EQ(counter_, number_iterations_ * number_threads_); + embb_mutex_destroy(&mutex_); +} + +void MutexTest::TestRecursiveMutex() { + embb_mutex_t mutex; + int status = embb_mutex_init(&mutex, EMBB_MUTEX_RECURSIVE); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + int number = 5; + for (int i = 0; i < number; i++) { + status = embb_mutex_lock(&mutex); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + for (int i = 0; i < number; i++) { + status = embb_mutex_unlock(&mutex); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + embb_mutex_destroy(&mutex); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/mutex_test.h b/base_c/test/mutex_test.h new file mode 100644 index 0000000..57abf28 --- /dev/null +++ b/base_c/test/mutex_test.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_MUTEX_TEST_H_ +#define BASE_C_TEST_MUTEX_TEST_H_ + +#include +#include +#include + + +namespace embb { +namespace base { +namespace test { +/** + * Provides tests for class Mutex. + */ +class MutexTest : public partest::TestCase { + public: + /** + * Constructs the test case and adds test units. + */ + MutexTest(); + + private: + /** + * Prepares TestMutexIncCpp. + */ + void PreMutexInc(); + /** + * Tests mutex locking and unlocking to protect shared counter. + */ + void TestMutexInc(); + /** + * Checks and tears down TestMutexIncCpp. + */ + void PostMutexInc(); + + /** + * Tests the multiple locking and unlocking of a recursive mutex. + */ + void TestRecursiveMutex(); + + /** + * Mutex for tests. + */ + embb_mutex_t mutex_; + + /** + * Shared counter to check effectiveness of mutex. + */ + size_t counter_; + + /** + * Number of threads used to run tests. + */ + size_t number_threads_; + + /** + * Number of times the test method is called by each thread. + */ + size_t number_iterations_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_C_TEST_MUTEX_TEST_H_ diff --git a/base_c/test/thread_index_test.cc b/base_c/test/thread_index_test.cc new file mode 100644 index 0000000..d7db983 --- /dev/null +++ b/base_c/test/thread_index_test.cc @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace test { + +ThreadIndexTest::ThreadIndexTest() + : number_threads_(partest::TestSuite::GetDefaultNumThreads()) { + CreateUnit("Test 0 indices").Add(&ThreadIndexTest::Test0, this); + CreateUnit("Test 1 index").Add(&ThreadIndexTest::Test1, this); + CreateUnit("Test N indices").Add(&ThreadIndexTest::TestN, this, 1); +} + +void ThreadIndexTest::Test0() { + embb_internal_thread_index_reset(); + unsigned int old_max = embb_thread_get_max_count(); + embb_internal_thread_index_set_max(0); + embb_thread_t thread; + bool index_available = false; + int status = embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + embb_internal_thread_index_set_max(old_max); +} + +void ThreadIndexTest::Test1() { + embb_internal_thread_index_reset(); + unsigned int old_max = embb_thread_get_max_count(); + embb_internal_thread_index_set_max(1); + { + embb_thread_t thread; + bool index_available = true; + int status = + embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + } + { + embb_thread_t thread; + bool index_available = false; + int status = + embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + } + embb_internal_thread_index_set_max(old_max); +} + +embb_atomic_int flag = { 1 }; + +void ThreadIndexTest::TestN() { + embb_internal_thread_index_reset(); + unsigned int old_max = embb_thread_get_max_count(); + embb_internal_thread_index_set_max( + static_cast(number_threads_)); + embb_thread_t* threads = new embb_thread_t[number_threads_]; + + embb_atomic_store_int(&flag, 0); + for (size_t i = 0; i < number_threads_; i++) { + bool index_available = true; + int status = embb_thread_create(threads + i, NULL, ThreadStart, + &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + embb_atomic_store_int(&flag, 1); + for (size_t i = 0; i < number_threads_; i++) { + int status = embb_thread_join(threads + i, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + embb_thread_t thread; + bool index_available = false; + int status = embb_thread_create(&thread, NULL, ThreadStart, &index_available); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + embb_thread_join(&thread, NULL); + delete[] threads; + embb_internal_thread_index_set_max(old_max); +} + +int ThreadStart(void* arg) { + assert(arg != NULL); + unsigned int index = UINT_MAX; + while (embb_atomic_load_int(&flag) == 0) { embb_thread_yield(); } + int status = embb_internal_thread_index(&index); + if (*static_cast(arg) == true) { + PT_EXPECT_EQ(status, EMBB_SUCCESS); + PT_EXPECT_NE(index, UINT_MAX); + PT_EXPECT_LT(index, partest::TestSuite::GetDefaultNumThreads()); + } else { + PT_EXPECT_EQ(status, EMBB_ERROR); + PT_EXPECT_EQ(index, UINT_MAX); + } + // Do it a second time + status = embb_internal_thread_index(&index); + if (*static_cast(arg) == true) { + PT_EXPECT_EQ(status, EMBB_SUCCESS); + PT_EXPECT_NE(index, UINT_MAX); + PT_EXPECT_LT(index, partest::TestSuite::GetDefaultNumThreads()); + } else { + PT_EXPECT_EQ(status, EMBB_ERROR); + PT_EXPECT_EQ(index, UINT_MAX); + } + return 0; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/thread_index_test.h b/base_c/test/thread_index_test.h new file mode 100644 index 0000000..579ad28 --- /dev/null +++ b/base_c/test/thread_index_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_THREAD_INDEX_TEST_H_ +#define BASE_C_TEST_THREAD_INDEX_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class ThreadIndexTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ThreadIndexTest(); + + private: + /** + * Tests 0 available indices. + */ + void Test0(); + + /** + * Tests 1 available index. + */ + void Test1(); + + /** + * Tests number_thread_ available indices and starting threads at same time. + */ + void TestN(); + + private: + /** + * Configurable number of threads (and indices) used in TestN(). + */ + size_t number_threads_; +}; + +/** + * Thread start method, performs checks with retrieving thread index. + * + * An own thread start is used to ensure that the thread does not yet have an + * index. This is necessary, since partest assigns thread indices before the + * threads are calling the user test functions (when using EMBB for it). + */ +int ThreadStart(void* arg); + +} // namespace test +} // namespace base +} // namespace embb + + + +#endif /* BASE_C_TEST_THREAD_INDEX_TEST_H_ */ diff --git a/base_c/test/thread_specific_storage_test.cc b/base_c/test/thread_specific_storage_test.cc new file mode 100644 index 0000000..0194e6c --- /dev/null +++ b/base_c/test/thread_specific_storage_test.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +ThreadSpecificStorageTest::ThreadSpecificStorageTest() + : tss_(), number_threads_(partest::TestSuite::GetDefaultNumThreads()) { + embb_tss_create(&tss_); + CreateUnit("API") + .Add(&ThreadSpecificStorageTest::Test, this, number_threads_, 1) + .Post(&ThreadSpecificStorageTest::Post, this); +} + +ThreadSpecificStorageTest::~ThreadSpecificStorageTest() { + embb_tss_delete(&tss_); +} + +void ThreadSpecificStorageTest::Test() { + size_t rank = partest::TestSuite::GetCurrentThreadID(); + void* value = embb_tss_get(&tss_); + if (value == NULL) { + int status = embb_tss_set(&tss_, new size_t(rank)); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } else { + size_t stored_rank = *static_cast(value); + PT_EXPECT_EQ(rank, stored_rank); + } +} + +void ThreadSpecificStorageTest::Post() { + size_t sum_ranks = 0; + for (size_t i = 0; i < embb_thread_get_max_count(); i++) { + void* value = tss_.values[i]; + if (value != NULL) { + sum_ranks += *static_cast(value); + delete static_cast(value); + } + } + size_t expected_sum = 0; + for (size_t i = 0; i < number_threads_; i++) { + expected_sum += i; + } + PT_EXPECT_EQ(sum_ranks, expected_sum); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/thread_specific_storage_test.h b/base_c/test/thread_specific_storage_test.h new file mode 100644 index 0000000..c524b57 --- /dev/null +++ b/base_c/test/thread_specific_storage_test.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ +#define BASE_C_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class ThreadSpecificStorageTest : public partest::TestCase { + public: + /** + * Adds test methods and sets up data structures. + */ + ThreadSpecificStorageTest(); + + ~ThreadSpecificStorageTest(); + + private: + /** + * Test. + */ + void Test(); + void Post(); + + embb_tss_t tss_; + + size_t number_threads_; +}; + +} // namespace test +} // namespace base +} // namespace embb + + + +#endif /* BASE_C_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ */ diff --git a/base_c/test/thread_test.cc b/base_c/test/thread_test.cc new file mode 100644 index 0000000..d27a87e --- /dev/null +++ b/base_c/test/thread_test.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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 +#include + +namespace embb { +namespace base { +namespace test { + +ThreadTest::ThreadTest() { + CreateUnit("Starting and joining") + .Add(&ThreadTest::TestStartingAndJoining, this, + partest::TestSuite::GetDefaultNumThreads()); + CreateUnit("Affinities") + .Add(&ThreadTest::TestThreadAffinities, this, 1, + partest::TestSuite::GetDefaultNumIterations()); +} + +void ThreadTest::TestStartingAndJoining() { + { // Run thread with no argument and do not retrieve return value + embb_thread_t thread; + int status = embb_thread_create(&thread, NULL, ThreadStartFunction, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + status = embb_thread_join(&thread, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + { // Run thread with no argument and do retrieve return value + embb_thread_t thread; + int status = embb_thread_create(&thread, NULL, ThreadStartFunction, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + int return_value = -1; + status = embb_thread_join(&thread, &return_value); + PT_EXPECT_EQ(return_value, 0); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } + { // Run thread with argument and do retrieve return value + embb_thread_t thread; + int arg = 1; + int status = embb_thread_create(&thread, NULL, ThreadStartFunction, &arg); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + int return_value = -1; + status = embb_thread_join(&thread, &return_value); + PT_EXPECT_EQ(return_value, 1); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + } +} + +void ThreadTest::TestThreadAffinities() { + embb_core_set_t core_set; + embb_core_set_init(&core_set, 1); + embb_thread_t thread; + int status = + embb_thread_create(&thread, &core_set, ThreadStartFunction, NULL); + embb_thread_join(&thread, NULL); + PT_EXPECT_EQ(status, EMBB_SUCCESS); +} + +int ThreadStartFunction(void* arg) { + int return_value = 0; + if (arg != NULL) { + return_value = *static_cast(arg); + } + return return_value; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/thread_test.h b/base_c/test/thread_test.h new file mode 100644 index 0000000..743494c --- /dev/null +++ b/base_c/test/thread_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_THREAD_TEST_H_ +#define BASE_C_TEST_THREAD_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class ThreadTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ThreadTest(); + + private: + /** + * Tests starting and joining threads. + */ + void TestStartingAndJoining(); + + /** + * Tests setting thread affinities. + * + * Is executed number_iteration times, since a former bug occurred only + * every few times on Windows. + */ + void TestThreadAffinities(); +}; + +/** + * Thread start function used in tests. + * + * \return Zero, if arg is NULL, otherwise arg converted to int. + */ +int ThreadStartFunction(void* arg); + +} // namespace test +} // namespace base +} // namespace embb + +#endif /* BASE_C_TEST_THREAD_TEST_H_ */ diff --git a/base_c/test/time_test.cc b/base_c/test/time_test.cc new file mode 100644 index 0000000..d0f07b2 --- /dev/null +++ b/base_c/test/time_test.cc @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + + +TimeTest::TimeTest() { + CreateUnit("Time in duration").Add(&TimeTest::TestTimeInDuration, this); +} + +void TimeTest::TestTimeInDuration() { + embb_time_t time; + + int status = embb_time_in(&time, embb_duration_min()); + PT_EXPECT_EQ(status, EMBB_SUCCESS); + + status = embb_time_in(&time, embb_duration_max()); + PT_EXPECT_EQ(status, EMBB_SUCCESS); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_c/test/time_test.h b/base_c/test/time_test.h new file mode 100644 index 0000000..3529ad1 --- /dev/null +++ b/base_c/test/time_test.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, 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 BASE_C_TEST_TIME_TEST_H_ +#define BASE_C_TEST_TIME_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class TimeTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + TimeTest(); + + private: + /** + * Tests time in duration method. + */ + void TestTimeInDuration(); +}; + +} // namespace test +} // namespace base +} // namespace embb + + + +#endif /* BASE_C_TEST_TIME_TEST_H_ */ diff --git a/base_cpp/CMakeLists.txt b/base_cpp/CMakeLists.txt new file mode 100644 index 0000000..ea5a1f7 --- /dev/null +++ b/base_cpp/CMakeLists.txt @@ -0,0 +1,48 @@ +project (project_embb_base_cpp) + +## CODE FILE DETECTION +# +# Fetch all header and source files for C, C++, and test build separately +file(GLOB_RECURSE EMBB_BASE_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_BASE_CPP_HEADERS "include/embb/*.h") +if (BUILD_TESTS STREQUAL ON) + file(GLOB_RECURSE EMBB_BASE_TEST_SOURCES "test/*.cc" "test/*.h") +endif() + +# Create header file from input file +configure_file("include/embb/base/internal/cmake_config.h.in" + "include/embb/base/internal/cmake_config.h") + +# Execute the GroupSources macro +include(../CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include/embb/base) +GroupSourcesMSVC(src) +if (BUILD_TESTS STREQUAL ON) + GroupSourcesMSVC(test) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ) + +## BUILD TARGETS +# +add_library (embb_base_cpp ${EMBB_BASE_CPP_SOURCES} ${EMBB_BASE_CPP_HEADERS}) +target_link_libraries(embb_base_cpp embb_base_c) +if (BUILD_TESTS STREQUAL ON) + include_directories(test/ + ${CMAKE_CURRENT_BINARY_DIR}/../partest/include + ) + add_executable (embb_base_cpp_test ${EMBB_BASE_TEST_SOURCES}) + target_link_libraries(embb_base_cpp_test partest embb_base_cpp embb_base_c + ${compiler_libs}) + CopyBin(BIN embb_base_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_base_cpp DESTINATION lib) diff --git a/base_cpp/include/embb/base/atomic.h b/base_cpp/include/embb/base/atomic.h new file mode 100644 index 0000000..684fecc --- /dev/null +++ b/base_cpp/include/embb/base/atomic.h @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_ATOMIC_H_ +#define EMBB_BASE_ATOMIC_H_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace embb { +namespace base { +#ifdef DOXYGEN + +/** + * \defgroup CPP_BASE_ATOMIC Atomic + * %Atomic operations. + * + * \ingroup CPP_BASE + */ + +/** + * Class representing atomic variables. + * + * The current implementation guarantees sequential consistency (full fences) + * for all atomic operations. Relaxed memory models may be added in the future. + * + * \tparam BaseType Underlying type + * \ingroup CPP_BASE_ATOMIC + */ +template +class Atomic { + public: + /** + * Default constructor. + * + * Constructs an atomic variable holding an uninitialized value. + * + * \waitfree + * + * \see Atomic(BaseType) + */ + Atomic(); + + /** + * Valued-based constructor. + * + * Constructs an atomic variable holding the passed value. + * + * \param val Initial value + * + * \waitfree + * + * \note There is intentionally no copy constructor, since two different + * memory locations cannot be manipulated atomically. + * + * \see Atomic() + */ + explicit Atomic(BaseType val); + + /** + * Assignment operator. + * + * Assigns the passed value to the object. + * + * \waitfree + * + * \param val The value to assign + * + * \return A shallow copy of this object + */ + BaseType operator=(BaseType val); + + /** + * Type conversion. + * + * Returns the value of the object. Equivalent to Load(). + * + * \waitfree + * + * \return Stored value + * + * \see Load() + */ + operator BaseType() const; + + /** + * Predicate representing support for arithmetic operations. + * + * Returns \c true if type \c BaseType supports arithmetic operations, + * otherwise \c false. Only integers and non-void pointers support + * arithmetic operations. + * + * \waitfree + * + * \return Boolean value indicating support for arithmetic operations + * + * \see IsInteger(), IsPointer() + */ + bool IsArithmetic() const; + + /** + * Predicate representing integers. + * + * Returns \c true if \c BaseType is an integer type, otherwise \c false. + * + * \waitfree + * + * \return Boolean value indicating whether \c BaseType is an integer + * + * \see IsArithmetic(), IsPointer() + */ + bool IsInteger() const; + + /** + * Predicate representing pointers. + * + * Returns \c true if \c BaseType is a non-void pointer type, otherwise + * \c false. + * + * \waitfree + * + * \return Boolean value indicating whether \c BaseType is a non-void pointer + * type + * + * \see IsArithmetic(), IsInteger() + */ + bool IsPointer() const; + + /** + * Store operation. + * + * Stores the passed value in the object. Equivalent to assignment operator, + * except that \c Store does not return anything. + * + * \waitfree + * + * \param val Value to be stored + * + * \see Load() + */ + void Store(BaseType val); + + /** + * Load operation. + * + * Loads and returns the stored value. Equivalent to type conversion. + * + * \waitfree + * + * \return Stored value + * + * \see Store() + */ + BaseType Load() const; + + /** + * Swap operation. + * + * Stores the given value in the object and returns the old value. + * + * \waitfree + * + * \param val New value + * + * \return Old value + * + * \see CompareAndSwap() + */ + BaseType Swap(BaseType val); + + /** + * Compare-and-Swap operation (CAS). + * + * Stores \c desired if the current value is equal to \c expected. + * Otherwise, stores the current value in \c expected. + * + * \waitfree + * + * \param expected Expected value + * \param desired Desired value + * + * \return \c true if CAS succeeded, otherwise \c false + * + * \see Swap() + */ + bool CompareAndSwap(BaseType& expected, BaseType desired); + + /** @name Arithmetic members + * + * The following members are only available if \c BaseType supports arithmetic + * operations (integer and non-void pointer types). + * + */ + + /**@{*/ + + /** + * Fetch-and-Add operation. + * + * Adds the passed value and returns the old value. + * + * \waitfree + * + * \param val Addend + * + * \return Old value + * + * \see FetchAndSub() + */ + BaseType FetchAndAdd(BaseType val); + + /** + * Fetch-and-Sub operation. + * + * Subtracts the passed value and returns the old value. + * + * \waitfree + * + * \param val Subtrahend + * + * \return Old value + * + * \see FetchAndAdd() + */ + BaseType FetchAndSub(BaseType val); + + /** + * Post-increment operation. + * + * Increments the value and returns the old value. + * + * \waitfree + * + * \return Old value + * + * \see operator++() + */ + BaseType operator++(int); + + /** + * Post-decrement operation. + * + * Decrements the value and returns the old value. + * + * \waitfree + * + * \return Old value + * + * \see operator--() + */ + BaseType operator--(int); + + /** + * Pre-increment operation. + * + * Increments the value and returns the new value. + * + * \waitfree + * + * \return New value + * + * \see operator++(int) + */ + BaseType operator++(); + + /** + * Pre-decrement operation. + * + * Decrements the value and returns the new value. + * + * \waitfree + * + * \return New value + * + * \see operator--(int) + */ + BaseType operator--(); + + /** + * Assignment by sum operation. + * + * Adds the passed value and returns the new value. + * + * \param val Addend + * \return New value + * + * \waitfree + * + * \see operator-=() + */ + BaseType operator+=(BaseType val); + + /** + * Assignment by difference operation. + * + * Subtracts the passed value and returns the new value. + * + * \param val Subtrahend + * \return New value + * + * \waitfree + * + * \see operator+=() + */ + BaseType operator-=(BaseType val); + + /**@}*/ + + /** @name Integer members + * + * The following members are only available if \c BaseType is an integer type. + * + */ + + /**@{*/ + + /** + * Assignment by bitwise AND. + * + * Stores the result of the bitwise AND in the current object. + * Does not return anything, since this cannot be implemented + * atomically on all architectures. + * + * \waitfree + * + * \param val Second operand of bitwise AND + * + * \see operator|=(), operator^=() + */ + void operator&=(BaseType val); + + /** + * Assignment by bitwise OR. + * + * Stores the result of the bitwise OR in the current object. + * Does not return anything, since this cannot be implemented + * atomically on all architectures. + * + * \waitfree + * + * \param val Second operand of bitwise OR + * + * \see operator&=(), operator^=() + */ + void operator|=(BaseType val); + + /** + * Assignment by bitwise XOR. + * + * Stores the result of the bitwise XOR in the current object. + * Does not return anything, since this cannot be implemented + * atomically on all architectures. + * + * \param val Second operand of bitwise XOR + * + * \waitfree + * + * \see operator&=(), operator|=() + */ + void operator^=(BaseType val); + + /**@}*/ + + /** @name Pointer members + * + * The following members are only available if \c BaseType is a non-void + * pointer type. + * + */ + + /**@{*/ + + /** + * Structure dereference operation. + * + * Used to access an element of an instance of a class or a structure + * pointed to by the stored pointer. + * + * \return Stored pointer + * + * \waitfree + * + * \see operator*() + */ + BaseType* operator->(); + + /** + * Dereference operation. + * + * Used to access the object pointed to by the stored pointer. + * + * \return Reference to the object + * + * \waitfree + * + * \see operator->() + */ + BaseType& operator*(); + + /**@}*/ +}; + +#else + +/** + * Generic implementation that provides basic functionality. + * See \c embb::base::AtomicBase for more information. + * + * \tparam BaseType Underlying type + */ +template +class Atomic : public embb::base::internal::atomic::AtomicBase < BaseType > { + public: + /** + * Constructs an atomic variable holding an uninitialized value. + */ + Atomic() : embb::base::internal::atomic::AtomicBase() {} + + /** + * Constructs an atomic variable holding the passed value. + * + * \param val The value to assign. + */ + explicit Atomic(BaseType val) : embb::base::internal::atomic:: + AtomicBase(val) {} + + /** + * Assignment operator. + * + * \param val The value to assign. + * + * \return A shallow copy of this object. + */ + BaseType operator=(BaseType val) { + return embb::base::internal::atomic::AtomicBase:: + operator=(val); + } +}; + +/** +* Specialization for non-void pointer types. +* See \c embb::base::internal::atomic::AtomicPointer for more information. +* +* \tparam BaseType Type of the objects pointed to +*/ +template +class Atomic : public embb::base::internal::atomic:: + AtomicPointer < BaseType, ptrdiff_t, sizeof(BaseType*) > { + public: + Atomic() : embb::base::internal::atomic:: + AtomicPointer() {} + Atomic(BaseType* p) : embb::base::internal::atomic:: + AtomicPointer(p) {} + + BaseType* operator=(BaseType* p) { + return embb::base::internal::atomic:: + AtomicPointer::operator=(p); + } +}; + +/** +* Specialization for void pointer types. +* See \c embb::base::internal::atomic::AtomicBase for more information. +* Unlike the specialization for non-void pointer types, this class +* does not permit dereferencing, incrementation, etc. +*/ +template<> +class Atomic : public embb::base::internal::atomic::AtomicBase < void* > { + public: + Atomic() : embb::base::internal::atomic::AtomicBase() {} + explicit Atomic(void* p) : embb::base::internal::atomic::AtomicBase(p) + {} + + void* operator=(void* p) { + return embb::base::internal::atomic::AtomicBase::operator=(p); + } +}; + +/** +* Specializations for integers. +* See \c embb::base::internal::atomic::AtomicInteger for more information. +*/ +#define __EMBB_ATOMIC_INTEGER_SPECIALIZATION(T) \ +template<> \ +class Atomic: public embb::base::internal::atomic::AtomicInteger { \ + public: \ + \ + Atomic() : embb::base::internal::atomic::AtomicInteger() {} \ + explicit Atomic(T val) : embb::base::internal::atomic::AtomicInteger(val) \ + {} \ + \ + T operator=(T val) { return embb::base::internal::atomic::AtomicInteger::\ + operator=(val); } \ + \ +} + +// Specializations for integers +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(signed char); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(unsigned char); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(signed short); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(unsigned short); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(signed int); +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(unsigned int); + +#ifdef EMBB_ARCH_X86_64 +__EMBB_ATOMIC_INTEGER_SPECIALIZATION(size_t); +#endif + +#endif +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_ATOMIC_H_ diff --git a/base_cpp/include/embb/base/base.h b/base_cpp/include/embb/base/base.h new file mode 100644 index 0000000..bac7338 --- /dev/null +++ b/base_cpp/include/embb/base/base.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_BASE_H_ +#define EMBB_BASE_BASE_H_ + +/** + * \defgroup CPP C++ Components + * Components written in C++. + */ + +/** + * \defgroup CPP_CONCEPT C++ Concepts + * Concepts for C++ components. + */ + +/** + * \defgroup CPP_BASE Base + * \ingroup CPP + * Platform-independent abstraction layer for multithreading and basic + * operations. + * + * Base C++ is mainly a C++ wrapper around the Base C abstractions. It adds + * additional convenience types and functions that leverage the capabilities of + * C++ such as templates, operator overloading, or RAII paradigms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_BASE_BASE_H_ diff --git a/base_cpp/include/embb/base/condition_variable.h b/base_cpp/include/embb/base/condition_variable.h new file mode 100644 index 0000000..2fc7268 --- /dev/null +++ b/base_cpp/include/embb/base/condition_variable.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_CONDITION_VARIABLE_H_ +#define EMBB_BASE_CONDITION_VARIABLE_H_ + +#include +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_CONDITION Condition Variable + * + * Condition variables for thread synchronization. + * + * \ingroup CPP_BASE + */ + +/** + * Represents a condition variable for thread synchronization. + * + * Provides an abstraction from platform-specific condition variable + * implementations. Condition variables can be waited for with timeouts using + * relative durations and absolute time points. + * + * This class is essentially a wrapper for the underlying C implementation. + * + * \ingroup CPP_BASE_CONDITION + */ +class ConditionVariable { + public: + /** + * Creates a condition variable. + * + * \throws embb::base::ErrorException if initialization failed + * + * \memory Potentially allocates dynamic memory + * + * \notthreadsafe + */ + ConditionVariable(); + + /** + * Wakes up one waiting thread. + * + * \throws embb::base::ErrorException if notification failed + * + * \threadsafe + * + * \see NotifyAll(), Wait() + */ + void NotifyOne(); + + /** + * Wakes up all waiting threads. + * + * \throws embb::base::ErrorException if notification failed + * + * \threadsafe + * + * \see NotifyOne(), Wait() + */ + void NotifyAll(); + + /** + * Releases the lock and waits until the thread is woken up. + * + * \pre The lock has been acquired by the calling thread. + * \post The lock has been re-acquired by the calling thread. + * + * \throws embb::base::ErrorException if waiting failed + * + * \threadsafe + * + * \see NotifyOne(), NotifyAll() + */ + void Wait( + UniqueLock& lock + /**< [IN,OUT] Lock to be released and re-acquired */ + ); + + /** + * Releases the lock and waits until the thread is woken up or the specified + * time point has passed. + * + * \pre The lock has been acquired by the calling thread. + * \post The lock has been re-acquired by the calling thread. + * + * \return \c true if the thread was woken up before the specified time point + * has passed, otherwise \c false. + * + * \throws embb::base::ErrorException if an error occurred + * + * \threadsafe + */ + bool WaitUntil( + UniqueLock& lock, + /**< [IN,OUT] Lock to be released and re-acquired */ + const Time& time + /**< [IN] Absolute time point until which the thread maximally waits */ + ); + + /** + * Releases the lock and waits until the thread is woken up or the specified + * duration has passed. + * + * \pre The lock has been acquired by the calling thread. + * \post The lock has been re-acquired by the calling thread. + * + * \return \c true if the thread was woken up before the specified duration + * has passed, otherwise \c false. + * + * \throws embb::base::ErrorException if an error occurred + * + * \threadsafe + * + * \tparam Tick Type of tick of the duration. See Duration. + */ + template + bool WaitFor( + UniqueLock& lock, + /**< [IN,OUT] Lock to be released and re-acquired */ + const Duration& duration + /**< [IN] Relative time duration the thread maximally waits */ + ); + + private: + /** + * Disables copying and assigment. + */ + ConditionVariable(const ConditionVariable&); + ConditionVariable& operator=(const ConditionVariable&); + + /** + * Holds actual condition variable. + */ + internal::ConditionVariableType condition_var_; +}; + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_CONDITION_VARIABLE_H_ diff --git a/base_cpp/include/embb/base/core_set.h b/base_cpp/include/embb/base/core_set.h new file mode 100644 index 0000000..7857111 --- /dev/null +++ b/base_cpp/include/embb/base/core_set.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_CORE_SET_H_ +#define EMBB_BASE_CORE_SET_H_ + +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_CORESET Core Set + * + * Core sets for thread-to-core affinities + * + * \ingroup CPP_BASE + */ + +/** + * Represents a set of processor cores, used to set thread-to-core affinities. + * + * An instance of this type represents a subset of processor cores. Core sets + * can be used to set thread-to-core affinities. A core in a core set might + * just represent a logical core (hyper-thread), depending on the underlying + * hardware. Each core is identified by a unique integer starting with 0. + * For example, the cores of a quad-core system are represented by the set + * {0,1,2,3}. + * + * This class is essentially a wrapper for the underlying C implementation. + * + * \notthreadsafe + * \ingroup CPP_BASE_CORESET + */ +class CoreSet { + public: + /** + * Returns the number of available processor cores. + * + * If the processor supports hyper-threading, each hyper-thread is treated as + * a separate processor core. + * + * \return Number of cores including hyper-threads + */ + static unsigned int CountAvailable(); + + /** + * Constructs an empty core set. + */ + CoreSet(); + + /** + * Constructs a core set with all or no cores. + */ + explicit CoreSet( + bool value + /**< [IN] \c true includes all cores in the set, \c false excludes all */ + ); + + /** + * Constructs a copy of the specified core set. + */ + CoreSet( + const CoreSet& to_copy + /**< [IN] Core set to copy */ + ); + + /** + * Assigns an existing core set. + * + * \return Reference to \c *this + */ + CoreSet& operator=( + const CoreSet& to_assign + /**< [IN] Core set to assign */ + ); + + /** + * Resets the core set according to the specified value. + */ + void Reset( + bool value + /**< [IN] \c true includes all cores in the set, \c false excludes all */ + ); + + /** + * Adds one core to the core set. + */ + void Add( + unsigned int core + /**< [IN] Core to add (from 0 to number of cores - 1) */ + ); + + /** + * Removes one core from the core set. + */ + void Remove( + unsigned int core + /** [IN] Core to remove (from 0 to number of cores - 1) */ + ); + + /** + * Checks whether the specified core is included in the set. + * + * \return \c true if core is included, otherwise \c false + */ + bool IsContained( + unsigned int core + /**< [IN] Core to check (from 0 to number of cores - 1) */ + ) const; + + /** + * Counts the number of cores in the set. + * + * \return Number of cores in the set + */ + unsigned int Count() const; + + /** + * Intersects this core set with the specified one. + * + * This core set is not modified by the operation. + * + * \return Copy of the result + */ + CoreSet operator&( + const CoreSet& rhs + /** [IN] Core set on right-hand side of intersection operation */ + ) const; + + /** + * Unites this core set with the specified one. + * + * This core set is not modified by the operation. + * + * \return Copy of the result + */ + CoreSet operator|( + const CoreSet& rhs + /** [IN] Core set on right-hand side of union operation */ + ) const; + + /** + * Intersects this core set with the specified one and overwrites this core + * set. + * + * \return Reference to \c *this + */ + CoreSet& operator&=( + const CoreSet& rhs + /** [IN] Core set on right-hand side of intersection operation */ + ); + + /** + * Unites this core set with the specified one an overwrites this core set. + * + * \return Reference to \c *this + */ + CoreSet& operator|=( + const CoreSet& rhs + /** [IN] Core set on right-hand side of union operation */ + ); + + private: + /** + * Internal representation of core set. + */ + embb_core_set_t rep_; + + /** + * Needs access to internal representation to use it with C API. + */ + friend class Thread; +}; + +} // namespace base +} // namespace embb + + + +#endif /* EMBB_BASE_CORE_SET_H_ */ diff --git a/base_cpp/include/embb/base/duration.h b/base_cpp/include/embb/base/duration.h new file mode 100644 index 0000000..c35a0ec --- /dev/null +++ b/base_cpp/include/embb/base/duration.h @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_DURATION_H_ +#define EMBB_BASE_DURATION_H_ + +#include +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_TIMEDURATION Duration and Time + * + * Relative time durations and absolute time points + * + * \ingroup CPP_BASE + */ + +/** + * Represents a relative time duration for a given tick type. + * + * \notthreadsafe + * \tparam Tick Possible tick types are Seconds, Milliseconds, Microseconds, + * Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +template +class Duration { + public: + /** + * Returns duration of length zero. + * \return Duration of length zero + */ + static const Duration& Zero(); + + /** + * Returns duration with maximum ticks representable by implementation. + * + * This value depends on the tick type and on the platform. + * + * \return Reference to duration with maximum value + */ + static const Duration& Max(); + + /** + * Returns duration with minimum ticks representable by implementation. + * + * This value depends on the tick type and on the platform. + * + * \return Reference to duration with minimum value + */ + static const Duration& Min(); + + /** + * Constructs a duration of length zero. + */ + Duration(); + + /** + * Constructs a duration with given number of ticks. + */ + explicit Duration( + unsigned long long ticks + /**< [IN] Number of ticks */ + ); + + /** + * Constructs a duration by copying from an existing duration. + */ + Duration( + const Duration& to_copy + /**< [IN] %Duration to copy */ + ); + + /** + * Assigns an existing duration. + * + * \return Reference to \c *this + */ + Duration& operator=( + const Duration& to_assign + /**< [IN] %Duration to assign */ + ); + + /** + * Returns the number of ticks of the duration. + * \return Number of ticks of the duration + */ + unsigned long long Count() const; + + /** + * Assignment by addition of another duration with same tick type. + * + * \return Reference to \c *this + */ + Duration& operator+=( + const Duration& rhs + /**< [IN] %Duration to add to this duration */ + ); + + private: + /** + * Constructs a duration from the internal representation. + * + * \pre \c duration needs to fit into the duration type + * \throws + */ + Duration( + const embb_duration_t& duration + /**< [IN] %Duration to copy from */ + ); + + /** + * Internal representation from Base C. + */ + embb_duration_t rep_; + + /** + * For accessing rep_ and using Base C functionality. + */ + friend class Time; + + /** + * For accessing rep_ and using Base C functionality. + */ + friend class ConditionVariable; +}; + +/** + * Compares two durations (equality). + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is equal to \c rhs, otherwise \c false + */ +template +bool operator==( + const Duration& lhs, + /**< [IN] Left-hand side of equality operator */ + const Duration& rhs + /**< [IN] Right-hand side of equality operator */ + ) { + return embb_duration_compare(&lhs, &rhs) == 0; +} + +/** + * Compares two durations (inequality). + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is not equal to \c rhs, otherwise \c false + */ +template +bool operator!=( + const Duration& lhs, + /**< [IN] Left-hand side of inequality operator */ + const Duration& rhs + /**< [IN] Right-hand side of inequality operator */ + ) { + return embb_duration_compare(&lhs, &rhs) != 0; +} + +/** + * Compares two durations (less than) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is shorter than \c rhs. + */ +template +bool operator<( + const Duration& lhs, + /**< [IN] Left-hand side of less than operator */ + const Duration& rhs + /**< [IN] Right-hand side of less than operator */ + ) { + return embb_duration_compare(&lhs, &rhs) == -1; +} + +/** + * Compares two durations (greater than) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is longer than \c rhs. + */ +template +bool operator>( + const Duration& lhs, + /**< [IN] Left-hand side of greater than operator */ + const Duration& rhs + /**< [IN] Right-hand side of greater than operator */ + ) { + return embb_duration_compare(&lhs, &rhs) == 1; +} + +/** + * Compares two durations (less than or equal to) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is shorter than or equal to \c rhs. + */ +template +bool operator<=( + const Duration& lhs, + /**< [IN] Left-hand side of less than or equal to operator */ + const Duration& rhs + /**< [IN] Right-hand side of less than or equal to operator */ + ) { + return embb_duration_compare(&lhs, &rhs) < 1; +} + +/** + * Compares two durations (greater than or equal to) + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return \c true if \c lhs is longer than or equal to \c rhs. + */ +template +bool operator>=( + const Duration& lhs, + /**< [IN] Left-hand side of greater than or equal to operator */ + const Duration& rhs + /**< [IN] Right-hand side of greater than or equal to operator */ + ) { + return embb_duration_compare(&lhs, &rhs) > -1; +} + +/** + * Adds two durations + * + * \ingroup CPP_BASE_TIMEDURATION + * + * \return Sum of \c lhs and \c rhs. + */ +template +Duration operator+( + const Duration& lhs, + /**< [IN] Left-hand side of addition operator */ + const Duration& rhs + /**< [IN] Right-hand side of addition operator */ + ) { + return Duration(lhs.Count() + rhs.Count()); +} + +/** + * Base class for ticks. + */ +class Tick { + public: + /** + * Checks the status for under- and overflow and, in such a case, throws an + * exception. + */ + static void CheckExceptions( + int status, + /**< [IN] Status code to check */ + const char* msg + /**< [IN] Exception message if one is thrown */ + ); +}; + +/** + * %Seconds tick for Duration. + * + * \see Milliseconds, Microseconds, Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Seconds : public Tick { + public: + /** + * Sets the ticks as seconds and returns the status of the duration operation. + * + * \return Status code of embb_duration_set_seconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as seconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in seconds for the given duration. + * + * \return Number of second ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of second ticks for the available implementation. + * + * \return Minimum number of second ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of second ticks for the available implementation. + * + * \return Maximum number of second ticks. + */ + static unsigned long long Max(); +}; + +/** + * %Milliseconds tick for Duration. + * + * \see Seconds, Microseconds, Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Milliseconds : public Tick { + public: + /** + * Sets the ticks as milliseconds and returns the status of the duration + * operation. + * + * \return Status code of embb_duration_set_milliseconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as milliseconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in milliseconds for the given duration. + * + * \return Number of millisecond ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of millisecond ticks for the available + * implementation. + * + * \return Minimum number of microsecond ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of millisecond ticks for the available + * implementation. + * + * \return Maximum number of millisecond ticks. + */ + static unsigned long long Max(); +}; + +/** + * %Microseconds tick for Duration. + * + * \see Seconds, Milliseconds, Nanoseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Microseconds : public Tick { + public: + /** + * Sets the ticks as microseconds and returns the status of the duration + * operation. + * + * \return Status code of embb_duration_set_microseconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as microseconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in microseconds for the given duration. + * + * \return Number of microsecond ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of microsecond ticks for the available + * implementation. + * + * \return Minimum number of microsecond ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of microsecond ticks for the available + * implementation. + * + * \return Maximum number of microsecond ticks. + */ + static unsigned long long Max(); +}; + +/** + * %Nanoseconds tick for Duration. + * + * \see Seconds, Milliseconds, Microseconds + * \ingroup CPP_BASE_TIMEDURATION + */ +class Nanoseconds : public Tick { + public: + /** + * Sets the ticks as nanoseconds and returns the status of the duration + * operation. + * + * \return Status code of embb_duration_set_nanoseconds() + */ + static int Set( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Sets the ticks as nanoseconds and calls CheckExceptions() with the status. + */ + static void SetAndCheck( + embb_duration_t& duration, + /**< [OUT] %Duration representation to be considered */ + unsigned long long ticks + /**< [IN] Number of ticks to set */ + ); + + /** + * Returns the number of ticks in nanoseconds for the given duration. + * + * \return Number of nanosecond ticks of \c duration + */ + static unsigned long long Get( + const embb_duration_t& duration + /**< [IN] %Duration representation to be considered */ + ); + + /** + * Returns the minimum number of nanosecond ticks for the available + * implementation. + * + * \return Minimum number of nanosecond ticks. + */ + static unsigned long long Min(); + + /** + * Returns the maximum number of nanosecond ticks for the available + * implementation. + * + * \return Maximum number of nanosecond ticks. + */ + static unsigned long long Max(); +}; + +} // namespace base +} // namespace embb + +#include + +#endif /* EMBB_BASE_DURATION_H_ */ diff --git a/base_cpp/include/embb/base/exceptions.h b/base_cpp/include/embb/base/exceptions.h new file mode 100644 index 0000000..4feb3fc --- /dev/null +++ b/base_cpp/include/embb/base/exceptions.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_EXCEPTIONS_H_ +#define EMBB_BASE_EXCEPTIONS_H_ + +#ifdef EMBB_COMPILER_MSVC +#pragma warning(push) +// Disable warning that exceptions are disabled but try/catch is used. +#pragma warning(disable : 4530) +#endif // EMBB_COMPILER_MSVC + +#include +#include + +#ifdef EMBB_COMPILER_MSVC +#pragma warning(pop) +#endif + +#include +#include + +/** + * \defgroup CPP_BASE_EXCEPTIONS Exception + * + * Exception types. + * + * If exceptions are disabled, i.e., if the library was built without support + * for exceptions, no exceptions will be thrown. Instead, an error message is + * printed to \c stderr and the program exits with the code representing the + * exception. + * + * \ingroup CPP_BASE + */ + +/** + * Macros to be used within EMBB when throwing and catching exceptions. + * + * Example: + * - Throwing an exception: + * EMBB_THROW(NoMemoryException, "Could not create thread."); + * --> If exceptions are disabled, this will write an error to stderr, + * containing the exception's message, and exit the program with the code + * of the exception. + * - Try/catch block: + * EMBB_TRY{ ... things to try ... } + * EMBB_CATCH(ExceptionXYZ& e){ ... things to catch ... } + * --> If exceptions are disabled, this will execute the try block without and + * replace the catch() statement by "if (false)", such that + * the catch block will be compiled but never executed. + */ +#ifdef EMBB_USE_EXCEPTIONS +#define EMBB_TRY try +#define EMBB_THROW(Type, Message) throw Type(Message); +#define EMBB_CATCH(Statement) catch(Statement) +#else /* EMBB_USE_EXCEPTIONS */ +#include +#include +#include +#define EMBB_TRY +/** + * Concatenates the inputs. + */ +#define EMBB_CATCH_VAR_CAT2(X, Y) X##Y +/** + * Is a necessary intermediate macro to create EMBB_CATCH_VAR. + */ +#define EMBB_CATCH_VAR_CAT1(X, Y) EMBB_CATCH_VAR_CAT2(X, Y) +/** + * Defines a unique variable name for each line of code. + * + * The line number is concatenated to a base name. + */ +#define EMBB_CATCH_VAR EMBB_CATCH_VAR_CAT1(embb_catch_var_, __LINE__) +/** + * Replaces catch(xyz) by an if-statement that is always false. + * + * To avoid a compiler warning that the condition is constant, a variable is + * used instead of "false". A unique name for every catch-statement is + * necessary, which is achieved by macro EMBB_CATCH_VAR and helper macros. To + * avoid the unused variable warning, in addition the EMBB_UNUSED macro is + * used. + */ +#define EMBB_CATCH(Statement) \ + int EMBB_CATCH_VAR = false; \ + EMBB_UNUSED(EMBB_CATCH_VAR); \ + if (EMBB_CATCH_VAR) + +/** + * Replaces a throw by an error message and program exit. + */ +#define EMBB_THROW(Type, Message) \ + { \ + Type e(Message); \ + fprintf(stderr, \ + "Exit program due to (not thrown) " #Type ": %s\n", e.what()); \ + exit(e.Code()); \ + } +#endif /* else EMBB_USE_EXCEPTIONS */ + +namespace embb { +namespace base { + +/** + * Abstract base class for exceptions. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class Exception : public std::exception { + public: + /** + * Constructs an exception with a custom message. + */ + explicit Exception( + const char* message + /**< [IN] Error message */ + ) : message_(message) {} + + /** + * Destructs the exception. + */ + virtual ~Exception() throw() {} + + /** + * Constructs an exception by copying from an existing one. + */ + Exception( + const Exception& e + /**< [IN] %Exception to be copied */ + ) : message_(e.message_) {} + + /** + * Assigns an existing exception. + * + * \return Reference to \c *this + */ + Exception& operator=( + const Exception& e + /**< [IN] %Exception to assign */ + ) { + message_ = e.message_; + return *this; + } + + /** + * Returns the error message. + * + * \return Pointer to error message + */ + virtual const char* What() const throw() { + return message_; + } + + /** + * Returns an integer code representing the exception. + * + * \return %Exception code + */ + virtual int Code() const = 0; + + private: + /** + * Holds error message + */ + const char* message_; +}; + +/** + * Indicates lack of memory necessary to allocate a resource. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class NoMemoryException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit NoMemoryException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_NOMEM; } +}; + +/** + * Indicates business (unavailability) of a required resource. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class ResourceBusyException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit ResourceBusyException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_BUSY; } +}; + +/** + * Indicates a numeric underflow. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class UnderflowException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit UnderflowException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_UNDERFLOW; } +}; + +/** + * Indicates a numeric overflow. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class OverflowException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit OverflowException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_OVERFLOW; } +}; + +/** + * Indicates a general error. + * + * \ingroup CPP_BASE_EXCEPTIONS + */ +class ErrorException : public Exception { + public: + /** + * Constructs an exception with the specified message + */ + explicit ErrorException( + const char* message + /**< [IN] Error message */ + ) : Exception(message) {} + virtual int Code() const { return EMBB_ERROR; } +}; + +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_EXCEPTIONS_H_ */ diff --git a/base_cpp/include/embb/base/function.h b/base_cpp/include/embb/base/function.h new file mode 100644 index 0000000..494fe7e --- /dev/null +++ b/base_cpp/include/embb/base/function.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_FUNCTION_H_ +#define EMBB_BASE_FUNCTION_H_ + +/** + * \defgroup CPP_BASE_FUNCTION Function + * %Function wrapper and binding of parameters. + * + * \ingroup CPP_BASE + */ + +namespace embb { +namespace base { + +/** + * Provides placeholders for Function arguments used in Bind() + * + * \ingroup CPP_BASE_FUNCTION + */ +class Placeholder { + public: + class Arg_1 {}; + class Arg_2 {}; + class Arg_3 {}; + class Arg_4 {}; + class Arg_5 {}; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_1 _1; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_2 _2; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_3 _3; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_4 _4; + + /** + * Placeholder variable to be used in Bind() for keeping one argument unbound + */ + static Arg_5 _5; +}; + +} // namespace base +} // namespace embb + +#ifdef DOXYGEN + +namespace embb { +namespace base { + +/** + * Wraps function pointers, member function pointers, and functors with up to + * five arguments. + * + * \ingroup CPP_BASE_FUNCTION + */ +template +class Function { + public: + /** + * Constructor from functor. Uses operator() with return type ReturnType + * and up to five arguments. Copies the functor. + * \memory Allocates memory for the copy of the functor. + */ + template + explicit Function( + ClassType const & obj /**< The functor to wrap. */ + ); + + /** + * Constructor from function pointer with return type ReturnType and up to + * five arguments. + */ + explicit Function( + ReturnType(*func)(...) /**< The function pointer. */ + ); + + /** + * Constructor from object and member function pointer with return type + * ReturnType and up to five arguments. + */ + template + Function( + ClassType & obj, /**< Reference to object. */ + ReturnType(ClassType::*func)(...) + /**< Member function pointer. */ + ); + + /** + * Copy constructor. + */ + Function( + Function const & func /**< The Function to copy. */ + ); + + /** + * Destructor. + */ + ~Function(); + + /** + * Assigns this object a new function pointer. + */ + void operator = ( + ReturnType(*func)(...) /**< The function pointer. */ + ); + + /** + * Assigns this object another Function. + */ + void operator = ( + Function & func /**< The Function. */ + ); + + /** + * Assigns this object a new functor. The functor is copied. + */ + template + void operator = ( + C const & obj /**< The functor. */ + ); + + /** + * Calls the wrapped function with the given parameters. + * \returns A value generated by the wrapped function. + */ + ReturnType operator () (...); +}; + +/** + * Wraps an object and a member function pointer into a Function + * + * \returns Function with same return value and argument syntax as \c func + * \see Function + * \tparam ClassType Class that contains the member function pointed to by \c + * func. + * \tparam ReturnType Return type of member function pointed to by \c func + * \tparam [Arg1,...,Arg5] (Optional) Types of up to five arguments of the + * member function + * \ingroup CPP_BASE_FUNCTION + */ +template +Function MakeFunction( + ClassType& obj, + /**< [IN] Reference to the object with corresponding member function */ + ReturnType(ClassType::*func)([Arg1, ..., Arg5]) + /**< [IN] Member function pointer with up to five optional arguments */ + ); + +/** + * Wraps a function pointer into a Function + * + * \returns Function with same return value and argument syntax as \c func + * \see Function + * \tparam ReturnType Return type of member function pointed to by \c func. + * \tparam [Arg1,...,Arg5] (Optional) Types of up to five arguments of the + * member function + * \ingroup CPP_BASE_FUNCTION + */ +template +Function MakeFunction( + ReturnType(*func)([Arg1, ..., Arg5]) + /**< [IN] Function pointer with up to five optional arguments */ + ); + +/** + * Binds given values as arguments of \c func into a new Function + * + * The new Function has no arguments or one, if Placeholder::_1 is + * given as one of the values. The position of Placeholder::_1 determines which + * argument of \c func is not bound. + * \memory Allocates dynamic memory to hold the parameters. + * \returns Function that uses given values as parameters + * \see Placeholder, Function + * \tparam ReturnType Return type of \c func and parameterless function returned + * \tparam [UnboundArgument] Type of not bound argument of \c func, only present + * when a placeholder is used as value in the bind. + * \tparam Arg1[,...,Arg5] Types of up to five arguments of the values to bind + * \ingroup CPP_BASE_FUNCTION + */ +template +Function Bind( + Function func, + /**< [IN] The Function to bind the values (\c value1, ...) to */ + Arg1 value1, + /**< [IN] At least one and up to five values to bind as arguments of \c func. + Placeholder::_1 can be used instead of one of the values to keep the + corresponding argument of \c func unbound. */ + ... + ); + +} // namespace base +} // namespace embb + +#else // DOXYGEN + +#include +#include +#include +#include +#include +#include + +#endif // DOXYGEN + +#endif // EMBB_BASE_FUNCTION_H_ diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h b/base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h new file mode 100644 index 0000000..399cf5d --- /dev/null +++ b/base_cpp/include/embb/base/internal/atomic/atomic_arithmetic.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_ARITHMETIC_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_ARITHMETIC_H_ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * Subclass that implements arithmetic operations. + * This class provides arithmetic operations for integers and pointers. + * + * \tparam BaseType Underlying type (must be an integral type or a + * non-void pointer type) + * \tparam DifferenceType Difference type (\c ptrdiff_t for pointers, + * otherwise \c BaseType) + * \tparam Stride Stride (in case of pointers, size of the objects + * pointed to, otherwise 1) + */ +template +class AtomicArithmetic : public AtomicBase { + protected: + typedef typename AtomicBase::NativeType NativeType; + + public: + // See base class + AtomicArithmetic(); + // See base class + explicit AtomicArithmetic(BaseType val); + + // See base class + BaseType operator=(BaseType val); + + // See base class + bool IsArithmetic() const; + + // The methods below are documented in atomic.h + BaseType FetchAndAdd(DifferenceType val); + BaseType FetchAndSub(DifferenceType val); + BaseType operator++(int); + BaseType operator--(int); + BaseType operator++(); + BaseType operator--(); + BaseType operator+=(DifferenceType val); + BaseType operator-=(DifferenceType val); +}; + +template +inline AtomicArithmetic::AtomicArithmetic() +: AtomicBase() {} + +template +inline AtomicArithmetic:: +AtomicArithmetic(BaseType val) : AtomicBase(val) {} + +template +inline BaseType +AtomicArithmetic::operator=(BaseType val) { + return AtomicBase::operator=(val); +} + +template +inline bool AtomicArithmetic:: +IsArithmetic() const { + return true; +} + +template +inline BaseType AtomicArithmetic:: +FetchAndAdd(DifferenceType val) { + /* + Despite the (at first sight) complexity of different function variables + and memcpy calls, the compiler is smart enough to optimize this... + A call to a++ (invoking this function), where "a" is an atomic integer + variable, translates to (tested using MSVC): + 00CA3744 mov edx,1 + 00CA3749 lea ecx,[a] + 00CA374C call \embb_internal__atomic_fetch_and_add_4_asm\8 (0CA381Fh) + */ + BaseType return_value; + DifferenceType desired = static_cast(Stride)*val; + + NativeType native_desired; + memcpy(&native_desired, &desired, sizeof(desired)); + + NativeType storage_value = fetch_and_add_implementation:: + fetch_and_add(&this->AtomicValue, native_desired); + + memcpy(&return_value, &storage_value, sizeof(return_value)); + return return_value; +} + +template +inline BaseType AtomicArithmetic:: +FetchAndSub(DifferenceType val) { + return FetchAndAdd(-val); +} + +template +inline BaseType AtomicArithmetic:: +operator++(int) { + return FetchAndAdd(1); +} + +template +inline BaseType AtomicArithmetic:: +operator--(int) { + return FetchAndSub(1); +} + +template +inline BaseType AtomicArithmetic:: +operator++() { + return FetchAndAdd(1) + 1; +} + +template +inline BaseType AtomicArithmetic:: +operator--() { + return FetchAndSub(1) - 1; +} + +template +inline BaseType AtomicArithmetic:: +operator+=(DifferenceType val) { + return FetchAndAdd(val) + val; +} + +template +inline BaseType AtomicArithmetic:: +operator-=(DifferenceType val) { + return FetchAndSub(val) - val; +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_ARITHMETIC_H_ diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_base.h b/base_cpp/include/embb/base/internal/atomic/atomic_base.h new file mode 100644 index 0000000..b44b024 --- /dev/null +++ b/base_cpp/include/embb/base/internal/atomic/atomic_base.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_BASE_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_BASE_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +template +class AtomicBase { + // The copy constructor is undefined, since it cannot be implemented + // atomically. + AtomicBase(const AtomicBase&); + // The assignment operator is undefined, since it cannot be implemented + // atomically. + AtomicBase& operator=(const AtomicBase&); + + protected: + typedef typename embb::base::internal::atomic:: + AtomicTraits:: + NativeType NativeType; + + mutable NativeType AtomicValue; + + public: + /** + * Default constructor. + * Initializes the object with zero. + */ + AtomicBase(); + + /** + * Constructor. + * Initializes the object with the passed value. + * + * \param val Initial value + */ + explicit AtomicBase(BaseType val); + + // The members below are documented in atomic.h + BaseType operator=(BaseType val); + operator BaseType() const; + bool IsLockFree() const; + bool IsArithmetic() const; + bool IsInteger() const; + bool IsPointer() const; + void Store(BaseType val); + BaseType Load() const; + BaseType Swap(BaseType val); + bool CompareAndSwap(BaseType& expected, BaseType desired); +}; + +template +inline AtomicBase::AtomicBase() : AtomicValue(0) { +} + +template +inline AtomicBase::AtomicBase(BaseType val) /*: AtomicValue(val)*/ { + memcpy(&AtomicValue, &val, sizeof(AtomicValue)); +} + +template +inline BaseType AtomicBase::operator=(BaseType val) { + Store(val); + return val; +} + +template +inline AtomicBase::operator BaseType() const { + return Load(); +} + +template +inline bool AtomicBase::IsArithmetic() const { + return false; +} + +template +inline bool AtomicBase::IsInteger() const { + return false; +} + +template +inline bool AtomicBase::IsPointer() const { + return false; +} + +template +inline void AtomicBase::Store(BaseType val) { + NativeType storage_value; + // Justification for using memcpy instead of pointer casts + // or union type punning: + // - with strict aliasing, type punning using pointer casts or + // unions is unsafe + // - memcpy is not slower, as the compiler will optimize it away + // anyway... + memcpy(&storage_value, &val, sizeof(storage_value)); + + store_implementation< NativeType > + ::Store(&AtomicValue, storage_value); +} + +template +inline BaseType AtomicBase::Load() const { + BaseType return_value; + + NativeType storage_value = + load_implementation< NativeType >::Load(&AtomicValue); + + memcpy(&return_value, &storage_value, sizeof(return_value)); + + return return_value; +} + +template +inline BaseType AtomicBase::Swap(BaseType val) { + NativeType storage_value; + BaseType return_value; + + memcpy(&storage_value, &val, sizeof(storage_value)); + + NativeType storage_value2 = swap_implementation< NativeType > + ::Swap(&AtomicValue, storage_value); + + memcpy(&return_value, &storage_value2, sizeof(return_value)); + + return return_value; +} + +template +inline bool AtomicBase:: +CompareAndSwap(BaseType& expected, BaseType desired) { + NativeType native_expected; + NativeType native_desired; + + memcpy(&native_expected, &expected, sizeof(expected)); + memcpy(&native_desired, &desired, sizeof(desired)); + + bool return_val = + (compare_and_swap_implementation:: + compare_and_swap(&AtomicValue, &native_expected, native_desired)) !=0 + ? true : false; + + if (!return_val) + expected = Load(); + + return return_val; +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_BASE_H_ diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_implementation.h b/base_cpp/include/embb/base/internal/atomic/atomic_implementation.h new file mode 100644 index 0000000..ce60555 --- /dev/null +++ b/base_cpp/include/embb/base/internal/atomic/atomic_implementation.h @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_IMPLEMENTATION_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_IMPLEMENTATION_H_ + +#ifdef DOXYGEN +// Omit this file from Doxygen generation. +#else + +#include + +/** + * The macros below are used to generate calls to the base_c implementation. We + * do not implement a macro for each atomic method, as many methods are equal in + * method signature. Therefore, we only define macros for different method + * signatures, which actually define template specializations. The signature is + * reflected in the macro name. For example the prefix + * RET_VAL_PAR1_POINTER_PAR2_VALUE means that the signature of the atomic has + * return type value, the first parameter is a pointer and the second one a + * value. For each set of template specializations, a master template has to be + * defined. This can be done with the EMBB_ATOMIC_GENERAL_TEMPLATE macro. + * Example: + * + * This code... + * ==== + * EMBB_ATOMIC_GENERAL_TEMPLATE(load_implementation) + * EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER (1, + * load_implementation, Load, embb_internal__atomic_load_ + * ==== + * + * ...will generate: + * ==== + * template< typename TYPE > class load_implementation {}; + * + * template<> class load_implementation< EMBB_BASE_BASIC_TYPE_SIZE_1 > { + * public: + * static inline EMBB_BASE_BASIC_TYPE_SIZE_1 Load + * ( EMBB_BASE_BASIC_TYPE_SIZE_1 * par1) { + * return embb_internal__atomic_load_1(par1); + * } + * }; + * ==== + */ + +/** +* \def EMBB_ATOMIC_GENERAL_TEMPLATE +* See general comment on top of file +* \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ +* lib (here) +*/ +#define EMBB_ATOMIC_GENERAL_TEMPLATE(IMPLEMENTATION_CLASS) \ + template< typename TYPE > \ +class IMPLEMENTATION_CLASS \ +{}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1) \ + { \ + return EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1); \ + } \ +}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) par2) \ + { \ + return EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1, par2); \ + } \ +}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline int \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par2, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) par3) \ + { \ + return EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1, par2, par3); \ + } \ +}; + +/** + * \def EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL + * See general comment on top of file + * \param [in] SIZE The size in bytes of the implementation that shall be called + * \param [in] IMPLEMENTATION_CLASS The wrapper class to be defined in the C++ + * lib (here) + * \param [in] IMPLEMENTATION_METHOD The wrapper method that should be defined + * in IMPLEMENTATION_CLASS + * \param [in] IMPLEMENTATION_METHOD The prefix of the method name, as it can be + * found in the base_c implementation. + */ +#define EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL\ +(SIZE, IMPLEMENTATION_CLASS, IMPLEMENTATION_METHOD, NATIVE_PREFIX) \ +template<> \ +class IMPLEMENTATION_CLASS< EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) > \ +{ \ +public: \ + static inline void \ + IMPLEMENTATION_METHOD(\ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) * par1, \ + EMBB_CAT2(EMBB_BASE_BASIC_TYPE_SIZE_, SIZE) par2) \ + { \ + EMBB_CAT2(NATIVE_PREFIX, SIZE)(par1, par2); \ + } \ +}; + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * The macro calls below actually define the atomic implementations using the + * macros defined in this file, as documented above. + */ + +// load_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(load_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(1, load_implementation, Load, embb_internal__atomic_load_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(2, load_implementation, Load, embb_internal__atomic_load_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(4, load_implementation, Load, embb_internal__atomic_load_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER +(8, load_implementation, Load, embb_internal__atomic_load_) +#endif + +// fetch_and_add_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(fetch_and_add_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(1, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(2, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(4, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(8, fetch_and_add_implementation, fetch_and_add, embb_internal__atomic_fetch_and_add_) +#endif + +// store_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(store_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, store_implementation, Store, embb_internal__atomic_store_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, store_implementation, Store, embb_internal__atomic_store_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, store_implementation, Store, embb_internal__atomic_store_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, store_implementation, Store, embb_internal__atomic_store_) +#endif + +// and_assign_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(and_assign_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, and_assign_implementation, and_assign, embb_internal__atomic_and_assign_) +#endif + +// compare_and_swap_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(compare_and_swap_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(1, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(2, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(4, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_INT_PAR1_POINTER_PAR2_POINTER_PAR3_VAL +(8, compare_and_swap_implementation, compare_and_swap, embb_internal__atomic_compare_and_swap_) +#endif + +// or_assign_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(or_assign_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, or_assign_implementation, or_assign, embb_internal__atomic_or_assign_) +#endif + +// xor_assign_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(xor_assign_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(1, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(2, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(4, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VOID_PAR1_POINTER_PAR2_VAL +(8, xor_assign_implementation, xor_assign, embb_internal__atomic_xor_assign_) +#endif + +// swap_implementation +EMBB_ATOMIC_GENERAL_TEMPLATE(swap_implementation) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(1, swap_implementation, Swap, embb_internal__atomic_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(2, swap_implementation, Swap, embb_internal__atomic_swap_) +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(4, swap_implementation, Swap, embb_internal__atomic_swap_) +#ifdef EMBB_64_BIT_ATOMIC_AVAILABLE +EMBB_ATOMIC_IMPLEMENTATION_FOR_SIZE_RET_VAL_PAR1_POINTER_PAR2_VALUE +(8, swap_implementation, Swap, embb_internal__atomic_swap_) +#endif + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // DOXYGEN + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_IMPLEMENTATION_H_ diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_integer.h b/base_cpp/include/embb/base/internal/atomic/atomic_integer.h new file mode 100644 index 0000000..78f19d4 --- /dev/null +++ b/base_cpp/include/embb/base/internal/atomic/atomic_integer.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_INTEGER_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_INTEGER_H_ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * Subclass for integers. + * This class provides bitwise Boolean operations to manipulate integers. + * + * \tparam BaseType Underlying type (must be an integral type) + */ +template +class AtomicInteger : public AtomicArithmetic { + private: + typedef typename AtomicArithmetic:: + NativeType NativeType; + + public: + // See base class + AtomicInteger(); + // See base class + explicit AtomicInteger(BaseType val); + + // See base class + BaseType operator=(BaseType val); + + // See base class + bool IsInteger() const; + + // The methods below are documented in atomic.h + void operator&=(BaseType val); + void operator|=(BaseType val); + void operator^=(BaseType val); +}; + +template +inline AtomicInteger::AtomicInteger() : +AtomicArithmetic() {} + +template +inline AtomicInteger::AtomicInteger(BaseType val) : +AtomicArithmetic(val) {} + +template +inline BaseType AtomicInteger::operator=(BaseType val) { + return AtomicArithmetic::operator=(val); +} + +template +inline bool AtomicInteger::IsInteger() const { + return true; +} + +template +inline void AtomicInteger::operator&=(BaseType val) { + NativeType native_operand; + memcpy(&native_operand, &val, sizeof(val)); + + and_assign_implementation:: + and_assign(&this->AtomicValue, native_operand); +} + +template +inline void AtomicInteger::operator|=(BaseType val) { + NativeType native_operand; + memcpy(&native_operand, &val, sizeof(val)); + + or_assign_implementation:: + or_assign(&this->AtomicValue, native_operand); +} + +template +inline void AtomicInteger::operator^=(BaseType val) { + NativeType native_operand; + memcpy(&native_operand, &val, sizeof(val)); + + xor_assign_implementation:: + xor_assign(&this->AtomicValue, native_operand); +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_INTEGER_H_ diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h b/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h new file mode 100644 index 0000000..26a8aa6 --- /dev/null +++ b/base_cpp/include/embb/base/internal/atomic/atomic_pointer.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_POINTER_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_POINTER_H_ + +#include +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +/** + * Subclass for pointers. + * This class provides dereferencing operators for pointers. + * + * \tparam BaseType Underlying type (must be a non-void pointer type) + */ +template +class AtomicPointer : public AtomicArithmetic { + private: + typedef typename AtomicArithmetic:: + NativeType NativeType; + + public: + // See base class + AtomicPointer(); + // See base class + explicit AtomicPointer(BaseType* ptr); + + // See base class + BaseType* operator=(BaseType* ptr); + + // See base class + bool IsPointer() const; + + // The methods below are documented in atomic.h + BaseType* operator->(); + BaseType& operator*(); +}; + +template +inline AtomicPointer::AtomicPointer() : + AtomicArithmetic() {} + +template +inline AtomicPointer:: + AtomicPointer(BaseType* val) : + AtomicArithmetic(val) {} + +template +inline BaseType* AtomicPointer:: + operator=(BaseType* val) { + return AtomicArithmetic:: + operator=(val); +} + +template +inline bool AtomicPointer:: + IsPointer() const { + return true; +} + +template +inline BaseType* AtomicPointer:: + operator->() { + return this->Load(); +} + +template +inline BaseType& AtomicPointer:: + operator*() { + return *(this->Load()); +} + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_POINTER_H_ diff --git a/base_cpp/include/embb/base/internal/atomic/atomic_utility.h b/base_cpp/include/embb/base/internal/atomic/atomic_utility.h new file mode 100644 index 0000000..3427f0c --- /dev/null +++ b/base_cpp/include/embb/base/internal/atomic/atomic_utility.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_UTILITY_H_ +#define EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_UTILITY_H_ + +#include +#include +#include +#include + +#include + +namespace embb { +namespace base { +namespace internal { +namespace atomic { + +template struct AtomicTraits; + +/** + * The templates below are use to map the size in bytes to the respective + * native type and to define this type. The basic types are defined in the + * base_c component. + */ + +// Specialization for bytes +template<> struct AtomicTraits<1> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_1 NativeType; }; + +// Specialization for half-words +template<> struct AtomicTraits<2> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_2 NativeType; }; + +// Specialization for words +template<> struct AtomicTraits<4> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_4 NativeType; }; + +// Specialization for double-words +template<> struct AtomicTraits<8> + { typedef EMBB_BASE_BASIC_TYPE_SIZE_8 NativeType; }; + +} // namespace atomic +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_ATOMIC_ATOMIC_UTILITY_H_ diff --git a/base_cpp/include/embb/base/internal/cmake_config.h.in b/base_cpp/include/embb/base/internal/cmake_config.h.in new file mode 100644 index 0000000..28059d2 --- /dev/null +++ b/base_cpp/include/embb/base/internal/cmake_config.h.in @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_CPP_INTERNAL_CMAKE_CONFIG_H_ +#define EMBB_BASE_CPP_INTERNAL_CMAKE_CONFIG_H_ + +/* This file is used as input for CMake. CMake creates a file cmake_config.h in + its current build directory under the path builddir/embb/base/internal/. From + there, cmake_config.h can be included as usual using + #include + */ + +/** + * If defined, exceptions are used in the C++ part. Otherwise, error messages + * are written to stderr and the program is exited with corresponding error codes. + * Try blocks are converted to normal code blocks, whereas catch blocks are + * compiled but never executed. + */ +#cmakedefine EMBB_USE_EXCEPTIONS + +#endif /* EMBB_BASE_CPP_INTERNAL_CMAKE_CONFIG_H_ */ \ No newline at end of file diff --git a/base_cpp/include/embb/base/internal/condition_variable-inl.h b/base_cpp/include/embb/base/internal/condition_variable-inl.h new file mode 100644 index 0000000..b3b39ce --- /dev/null +++ b/base_cpp/include/embb/base/internal/condition_variable-inl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_CONDITION_VARIABLE_INL_H_ +#define EMBB_BASE_INTERNAL_CONDITION_VARIABLE_INL_H_ + +namespace embb { +namespace base { + +#include + +template +bool ConditionVariable::WaitFor(UniqueLock& lock, + const Duration& duration) { + int status = embb_condition_wait_for(&condition_var_, &(lock.mutex_->mutex_), + &(duration.rep_)); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Error in ConditionVariable::WaitFor"); + } + if (status == EMBB_TIMEDOUT) return false; + return true; +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_CONDITION_VARIABLE_INL_H_ diff --git a/base_cpp/include/embb/base/internal/config.h b/base_cpp/include/embb/base/internal/config.h new file mode 100644 index 0000000..d076dc3 --- /dev/null +++ b/base_cpp/include/embb/base/internal/config.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_CONFIG_H_ +#define EMBB_BASE_INTERNAL_CONFIG_H_ + +#include +#include + +/* Disable exceptions in STL of MSVC. Leads to errors when used like this!!! */ +/*#if defined(EMBB_COMPILER_MSVC) && !defined(EMBB_USE_EXCEPTIONS) +#define _HAS_EXCEPTIONS 0 +#endif*/ + +#endif // EMBB_BASE_INTERNAL_CONFIG_H_ diff --git a/base_cpp/include/embb/base/internal/duration-inl.h b/base_cpp/include/embb/base/internal/duration-inl.h new file mode 100644 index 0000000..3a8f486 --- /dev/null +++ b/base_cpp/include/embb/base/internal/duration-inl.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_DURATION_INL_H_ +#define EMBB_BASE_INTERNAL_DURATION_INL_H_ + +#include + +namespace embb { +namespace base { + +template +const Duration& Duration::Zero() { + static Duration zero; + return zero; +} + +#ifdef EMBB_COMPILER_MSVC +// Suppress non-thread-safe static initialization warning +// in Max() and Min() +#pragma warning(push) +#pragma warning(disable : 4640) +#endif + +template +const Duration& Duration::Max() { + static Duration maximum(Tick::Max()); + return maximum; +} + +template +const Duration& Duration::Min() { + static Duration minimum(Tick::Min()); + return minimum; +} + +#ifdef EMBB_COMPILER_MSVC +#pragma warning(pop) // Reset warning 4640 +#endif + +template +Duration::Duration() /*: rep_(EMBB_DURATION_INIT)*/ { + rep_.nanoseconds = 0; + rep_.seconds = 0; +} + +template +Duration::Duration(unsigned long long ticks) { + /*: rep_(EMBB_DURATION_INIT) << does not work with vs2012*/ + rep_.nanoseconds = 0; + rep_.seconds = 0; + Tick::SetAndCheck(rep_, ticks); +} + +template +Duration::Duration(const Duration& to_copy) { + /*: rep_(EMBB_DURATION_INIT)*/ + rep_.nanoseconds = 0; + rep_.seconds = 0; + Tick::SetAndCheck(rep_, Tick::Get(to_copy.rep_)); +} + +template +Duration& Duration::operator=(const Duration& to_assign) { + Tick::SetAndCheck(rep_, Tick::Get(to_assign.rep_)); + return *this; +} + +template +unsigned long long Duration::Count() const { + return Tick::Get(rep_); +} + +template +Duration& Duration::operator+=(const Duration& rhs) { + int status = embb_duration_add(&rep_, &(rhs.rep_)); + Tick::CheckExceptions(status, "Add-assign duration"); + return *this; +} + +template +Duration::Duration(const embb_duration_t& duration) : rep_() { + int status = Tick::Set(rep_, 0); + assert(status == EMBB_SUCCESS); + status = embb_duration_add(&rep_, &duration); + assert(status == EMBB_SUCCESS); +} + +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_INTERNAL_DURATION_INL_H_ */ diff --git a/base_cpp/include/embb/base/internal/function0.h b/base_cpp/include/embb/base/internal/function0.h new file mode 100644 index 0000000..cfadde8 --- /dev/null +++ b/base_cpp/include/embb/base/internal/function0.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTION0_H_ +#define EMBB_BASE_INTERNAL_FUNCTION0_H_ + +#include +#include + +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function0 { + public: + virtual ~Function0() {} + virtual R operator () () = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer0 + : public Function0 { + public: + typedef R(*FuncPtrType)(); + explicit FunctionPointer0(FuncPtrType func) : function_(func) {} + virtual R operator () () { + return function_(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer0(function_); + } + + private: + FuncPtrType function_; +}; + +template <> +class FunctionPointer0 + : public Function0 { + public: + typedef void(*FuncPtrType)(); + explicit FunctionPointer0(FuncPtrType func) : function_(func) {} + virtual void operator () () { + function_(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer0(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer0 + : public Function0 { + public: + typedef R(C::*MemFuncPtrType)(); + typedef C & ClassRefType; + MemberFunctionPointer0(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer0(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer0 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () () { + return (object_.*function_)(); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer0(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer0 + : public Function0 { + public: + typedef void(C::*MemFuncPtrType)(); + typedef C & ClassRefType; + MemberFunctionPointer0(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer0(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer0 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () () { + (object_.*function_)(); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer0(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper0 + : public Function0 { + public: + FunctorWrapper0() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper0(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper0(FunctorWrapper0 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper0() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () () { + return (*object_)(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper0(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper0 + : public Function0 { + public: + FunctorWrapper0() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper0(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper0(FunctorWrapper0 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper0() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () () { + (*object_)(); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper0(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function0 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper0(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)()) { + Free(); + function_ = new(storage_) + internal::FunctionPointer0(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper0(obj); + } + explicit Function(R(*func)()) { + function_ = new(storage_) + internal::FunctionPointer0(func); + } + template + Function(C & obj, R(C::*func)()) { + function_ = new(storage_) + internal::MemberFunctionPointer0(obj, func); + } + R operator () () { + return (*function_)(); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer0)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function0(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)()) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)()) { + return Function(func); +} + +// bind to Function0 +template +Function Bind( + C & obj, + R(C::*func)()) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)()) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION0_H_ diff --git a/base_cpp/include/embb/base/internal/function1.h b/base_cpp/include/embb/base/internal/function1.h new file mode 100644 index 0000000..92c2d56 --- /dev/null +++ b/base_cpp/include/embb/base/internal/function1.h @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTION1_H_ +#define EMBB_BASE_INTERNAL_FUNCTION1_H_ + +#include +#include + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function1 { + public: + virtual ~Function1() {} + virtual R operator () (T1) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer1 + : public Function1 { + public: + typedef R(*FuncPtrType)(T1); + explicit FunctionPointer1(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1) { + return function_(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer1(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer1 + : public Function1 { + public: + typedef void(*FuncPtrType)(T1); + explicit FunctionPointer1(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1) { + function_(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer1(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer1 + : public Function1 { + public: + typedef R(C::*MemFuncPtrType)(T1); + typedef C & ClassRefType; + MemberFunctionPointer1(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer1(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer1 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1) { + return (object_.*function_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer1(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer1 + : public Function1 { + public: + typedef void(C::*MemFuncPtrType)(T1); + typedef C & ClassRefType; + MemberFunctionPointer1(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer1(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer1 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1) { + (object_.*function_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer1(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper1 + : public Function1 { + public: + FunctorWrapper1() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper1(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper1(FunctorWrapper1 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper1() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1) { + return (*object_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper1(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper1 + : public Function1 { + public: + FunctorWrapper1() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper1(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper1(FunctorWrapper1 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper1() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1) { + (*object_)(p1); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper1(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound1Functor0 { + public: + Bound1Functor0(Function func, + T1 p1) : function_(func) + , p1_(p1) {} + Bound1Functor0(Bound1Functor0 const & func) : function_(func.function_) + , p1_(func.p1_) {} + R operator() () { + return function_(p1_); + } + + private: + Function function_; + T1 p1_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function1 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper1(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer1(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper1(obj); + } + explicit Function(R(*func)(T1)) { + function_ = new(storage_) + internal::FunctionPointer1(func); + } + template + Function(C & obj, R(C::*func)(T1)) { + function_ = new(storage_) + internal::MemberFunctionPointer1(obj, func); + } + R operator () (T1 p1) { + return (*function_)(p1); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer1)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function1(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1) { + return Function( + internal::Bound1Functor0( + func, p1)); +} + +template +Function Bind(R(*func)(T1), + T1 p1) { + return Bind(Function(func), p1); +} + +template +Function Bind(C & obj, R(C::*func)(T1), + T1 p1) { + return Bind(Function(obj, func), p1); +} + +// bind to Function1 +template +Function Bind( + C & obj, + R(C::*func)(T1), + Placeholder::Arg_1 p1) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1), + Placeholder::Arg_1 p1) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION1_H_ diff --git a/base_cpp/include/embb/base/internal/function2.h b/base_cpp/include/embb/base/internal/function2.h new file mode 100644 index 0000000..a71020a --- /dev/null +++ b/base_cpp/include/embb/base/internal/function2.h @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTION2_H_ +#define EMBB_BASE_INTERNAL_FUNCTION2_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function2 { + public: + virtual ~Function2() {} + virtual R operator () (T1, T2) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer2 + : public Function2 { + public: + typedef R(*FuncPtrType)(T1, T2); + explicit FunctionPointer2(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2) { + return function_(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer2(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer2 + : public Function2 { + public: + typedef void(*FuncPtrType)(T1, T2); + explicit FunctionPointer2(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2) { + function_(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer2(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer2 + : public Function2 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2); + typedef C & ClassRefType; + MemberFunctionPointer2(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer2(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer2 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2) { + return (object_.*function_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer2(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer2 + : public Function2 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2); + typedef C & ClassRefType; + MemberFunctionPointer2(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer2(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer2 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2) { + (object_.*function_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer2(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper2 + : public Function2 { + public: + FunctorWrapper2() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper2(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper2(FunctorWrapper2 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper2() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2) { + return (*object_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper2(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper2 + : public Function2 { + public: + FunctorWrapper2() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper2(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper2(FunctorWrapper2 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper2() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2) { + (*object_)(p1, p2); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper2(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound2Functor0 { + public: + Bound2Functor0(Function func, + T1 p1, T2 p2) : function_(func) + , p1_(p1), p2_(p2) {} + Bound2Functor0(Bound2Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_) {} + R operator() () { + return function_(p1_, p2_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; +}; + +// bind to function1 +template +class Bound2Functor1_Arg1 { + public: + Bound2Functor1_Arg1(Function func + , T2 p2) : function_(func) + , p2_(p2) {} + Bound2Functor1_Arg1(Bound2Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_) {} + R operator() (T1 p1) { + return function_(p1, p2_); + } + + private: + Function function_; + T2 p2_; +}; + +template +class Bound2Functor1_Arg2 { + public: + Bound2Functor1_Arg2(Function func + , T1 p1) : function_(func) + , p1_(p1) {} + Bound2Functor1_Arg2(Bound2Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_) {} + R operator() (T2 p2) { + return function_(p1_, p2); + } + + private: + Function function_; + T1 p1_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function2 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper2(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer2(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper2(obj); + } + explicit Function(R(*func)(T1, T2)) { + function_ = new(storage_) + internal::FunctionPointer2(func); + } + template + Function(C & obj, R(C::*func)(T1, T2)) { + function_ = new(storage_) + internal::MemberFunctionPointer2(obj, func); + } + R operator () (T1 p1, T2 p2) { + return (*function_)(p1, p2); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer2)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function2(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2) { + return Function( + internal::Bound2Functor0( + func, p1, p2)); +} + +template +Function Bind(R(*func)(T1, T2), + T1 p1, T2 p2) { + return Bind(Function(func), p1, p2); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2), + T1 p1, T2 p2) { + return Bind(Function(obj, func), p1, p2); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2) { + return Function( + internal::Bound2Functor1_Arg1(func + , p2)); +} + +template +Function Bind(R(*func)(T1, T2) + , Placeholder::Arg_1 p1, T2 p2) { + return Bind(Function(func), p1, p2); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2) + , Placeholder::Arg_1 p1, T2 p2) { + return Bind(Function(obj, func), p1, p2); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1) { + return Function( + internal::Bound2Functor1_Arg2(func + , p1)); +} + +template +Function Bind(R(*func)(T1, T2) + , T1 p1, Placeholder::Arg_1 p2) { + return Bind(Function(func), p1, p2); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2) + , T1 p1, Placeholder::Arg_1 p2) { + return Bind(Function(obj, func), p1, p2); +} + +// bind to Function2 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION2_H_ diff --git a/base_cpp/include/embb/base/internal/function3.h b/base_cpp/include/embb/base/internal/function3.h new file mode 100644 index 0000000..2000930 --- /dev/null +++ b/base_cpp/include/embb/base/internal/function3.h @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTION3_H_ +#define EMBB_BASE_INTERNAL_FUNCTION3_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function3 { + public: + virtual ~Function3() {} + virtual R operator () (T1, T2, T3) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer3 + : public Function3 { + public: + typedef R(*FuncPtrType)(T1, T2, T3); + explicit FunctionPointer3(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2, T3 p3) { + return function_(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer3(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer3 + : public Function3 { + public: + typedef void(*FuncPtrType)(T1, T2, T3); + explicit FunctionPointer3(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2, T3 p3) { + function_(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer3(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer3 + : public Function3 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2, T3); + typedef C & ClassRefType; + MemberFunctionPointer3(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer3(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer3 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2, T3 p3) { + return (object_.*function_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer3(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer3 + : public Function3 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2, T3); + typedef C & ClassRefType; + MemberFunctionPointer3(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer3(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer3 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2, T3 p3) { + (object_.*function_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer3(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper3 + : public Function3 { + public: + FunctorWrapper3() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper3(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper3(FunctorWrapper3 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper3() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2, T3 p3) { + return (*object_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper3(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper3 + : public Function3 { + public: + FunctorWrapper3() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper3(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper3(FunctorWrapper3 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper3() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2, T3 p3) { + (*object_)(p1, p2, p3); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper3(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound3Functor0 { + public: + Bound3Functor0(Function func, + T1 p1, T2 p2, T3 p3) : function_(func) + , p1_(p1), p2_(p2), p3_(p3) {} + Bound3Functor0(Bound3Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_) {} + R operator() () { + return function_(p1_, p2_, p3_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; +}; + +// bind to function1 +template +class Bound3Functor1_Arg1 { + public: + Bound3Functor1_Arg1(Function func + , T2 p2, T3 p3) : function_(func) + , p2_(p2), p3_(p3) {} + Bound3Functor1_Arg1(Bound3Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_), p3_(func.p3_) {} + R operator() (T1 p1) { + return function_(p1, p2_, p3_); + } + + private: + Function function_; + T2 p2_; + T3 p3_; +}; + +template +class Bound3Functor1_Arg2 { + public: + Bound3Functor1_Arg2(Function func + , T1 p1, T3 p3) : function_(func) + , p1_(p1), p3_(p3) {} + Bound3Functor1_Arg2(Bound3Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_), p3_(func.p3_) {} + R operator() (T2 p2) { + return function_(p1_, p2, p3_); + } + + private: + Function function_; + T1 p1_; + T3 p3_; +}; + +template +class Bound3Functor1_Arg3 { + public: + Bound3Functor1_Arg3(Function func + , T1 p1, T2 p2) : function_(func) + , p1_(p1), p2_(p2) {} + Bound3Functor1_Arg3(Bound3Functor1_Arg3 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_) {} + R operator() (T3 p3) { + return function_(p1_, p2_, p3); + } + + private: + Function function_; + T1 p1_; + T2 p2_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function3 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper3(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2, T3)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer3(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper3(obj); + } + explicit Function(R(*func)(T1, T2, T3)) { + function_ = new(storage_) + internal::FunctionPointer3(func); + } + template + Function(C & obj, R(C::*func)(T1, T2, T3)) { + function_ = new(storage_) + internal::MemberFunctionPointer3(obj, func); + } + R operator () (T1 p1, T2 p2, T3 p3) { + return (*function_)(p1, p2, p3); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer3)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function3(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2, T3)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2, T3)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2, T3 p3) { + return Function( + internal::Bound3Functor0( + func, p1, p2, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3), + T1 p1, T2 p2, T3 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3), + T1 p1, T2 p2, T3 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2, T3 p3) { + return Function( + internal::Bound3Functor1_Arg1(func + , p2, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3) + , Placeholder::Arg_1 p1, T2 p2, T3 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3) + , Placeholder::Arg_1 p1, T2 p2, T3 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1, T3 p3) { + return Function( + internal::Bound3Functor1_Arg2(func + , p1, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3) + , T1 p1, Placeholder::Arg_1 p2, T3 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3) + , T1 p1, Placeholder::Arg_1 p2, T3 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, Placeholder::Arg_1) { + return Function( + internal::Bound3Functor1_Arg3(func + , p1, p2)); +} + +template +Function Bind(R(*func)(T1, T2, T3) + , T1 p1, T2 p2, Placeholder::Arg_1 p3) { + return Bind(Function(func), p1, p2, p3); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3) + , T1 p1, T2 p2, Placeholder::Arg_1 p3) { + return Bind(Function(obj, func), p1, p2, p3); +} + +// bind to Function3 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2, T3), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2, + Placeholder::Arg_3 p3) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2, T3), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2, + Placeholder::Arg_3 p3) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION3_H_ diff --git a/base_cpp/include/embb/base/internal/function4.h b/base_cpp/include/embb/base/internal/function4.h new file mode 100644 index 0000000..d57608d --- /dev/null +++ b/base_cpp/include/embb/base/internal/function4.h @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTION4_H_ +#define EMBB_BASE_INTERNAL_FUNCTION4_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function4 { + public: + virtual ~Function4() {} + virtual R operator () (T1, T2, T3, T4) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer4 + : public Function4 { + public: + typedef R(*FuncPtrType)(T1, T2, T3, T4); + explicit FunctionPointer4(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return function_(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer4(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer4 + : public Function4 { + public: + typedef void(*FuncPtrType)(T1, T2, T3, T4); + explicit FunctionPointer4(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + function_(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer4(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer4 + : public Function4 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2, T3, T4); + typedef C & ClassRefType; + MemberFunctionPointer4(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer4(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer4 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return (object_.*function_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer4(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer4 + : public Function4 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2, T3, T4); + typedef C & ClassRefType; + MemberFunctionPointer4(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer4(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer4 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + (object_.*function_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer4(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper4 + : public Function4 { + public: + FunctorWrapper4() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper4(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper4(FunctorWrapper4 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper4() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return (*object_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper4(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper4 + : public Function4 { + public: + FunctorWrapper4() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper4(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper4(FunctorWrapper4 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper4() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + (*object_)(p1, p2, p3, p4); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper4(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound4Functor0 { + public: + Bound4Functor0(Function func, + T1 p1, T2 p2, T3 p3, T4 p4) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p4_(p4) {} + Bound4Functor0(Bound4Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) + {} + R operator() () { + return function_(p1_, p2_, p3_, p4_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T4 p4_; +}; + +// bind to function1 +template +class Bound4Functor1_Arg1 { + public: + Bound4Functor1_Arg1(Function func + , T2 p2, T3 p3, T4 p4) : function_(func) + , p2_(p2), p3_(p3), p4_(p4) {} + Bound4Functor1_Arg1(Bound4Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) {} + R operator() (T1 p1) { + return function_(p1, p2_, p3_, p4_); + } + + private: + Function function_; + T2 p2_; + T3 p3_; + T4 p4_; +}; + +template +class Bound4Functor1_Arg2 { + public: + Bound4Functor1_Arg2(Function func + , T1 p1, T3 p3, T4 p4) : function_(func) + , p1_(p1), p3_(p3), p4_(p4) {} + Bound4Functor1_Arg2(Bound4Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_), p3_(func.p3_), p4_(func.p4_) {} + R operator() (T2 p2) { + return function_(p1_, p2, p3_, p4_); + } + + private: + Function function_; + T1 p1_; + T3 p3_; + T4 p4_; +}; + +template +class Bound4Functor1_Arg3 { + public: + Bound4Functor1_Arg3(Function func + , T1 p1, T2 p2, T4 p4) : function_(func) + , p1_(p1), p2_(p2), p4_(p4) {} + Bound4Functor1_Arg3(Bound4Functor1_Arg3 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p4_(func.p4_) {} + R operator() (T3 p3) { + return function_(p1_, p2_, p3, p4_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T4 p4_; +}; + +template +class Bound4Functor1_Arg4 { + public: + Bound4Functor1_Arg4(Function func + , T1 p1, T2 p2, T3 p3) : function_(func) + , p1_(p1), p2_(p2), p3_(p3) {} + Bound4Functor1_Arg4(Bound4Functor1_Arg4 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_) {} + R operator() (T4 p4) { + return function_(p1_, p2_, p3_, p4); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function4 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper4(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2, T3, T4)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer4(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper4(obj); + } + explicit Function(R(*func)(T1, T2, T3, T4)) { + function_ = new(storage_) + internal::FunctionPointer4(func); + } + template + Function(C & obj, R(C::*func)(T1, T2, T3, T4)) { + function_ = new(storage_) + internal::MemberFunctionPointer4(obj, func); + } + R operator () (T1 p1, T2 p2, T3 p3, T4 p4) { + return (*function_)(p1, p2, p3, p4); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer4)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function4(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2, T3, T4)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2, T3, T4)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2, T3 p3, T4 p4) { + return Function( + internal::Bound4Functor0( + func, p1, p2, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4), + T1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4), + T1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2, T3 p3, T4 p4) { + return Function( + internal::Bound4Functor1_Arg1(func + , p2, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1, T3 p3, T4 p4) { + return Function( + internal::Bound4Functor1_Arg2(func + , p1, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, Placeholder::Arg_1, T4 p4) { + return Function( + internal::Bound4Functor1_Arg3(func + , p1, p2, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1) { + return Function( + internal::Bound4Functor1_Arg4(func + , p1, p2, p3)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4) { + return Bind(Function(func), p1, p2, p3, p4); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4) { + return Bind(Function(obj, func), p1, p2, p3, p4); +} + +// bind to Function4 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2, T3, T4), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2, + Placeholder::Arg_3 p3, + Placeholder::Arg_4 p4) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2, T3, T4), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2, + Placeholder::Arg_3 p3, + Placeholder::Arg_4 p4) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION4_H_ diff --git a/base_cpp/include/embb/base/internal/function5.h b/base_cpp/include/embb/base/internal/function5.h new file mode 100644 index 0000000..14687f8 --- /dev/null +++ b/base_cpp/include/embb/base/internal/function5.h @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTION5_H_ +#define EMBB_BASE_INTERNAL_FUNCTION5_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +namespace internal { + +template +class Function5 { + public: + virtual ~Function5() {} + virtual R operator () (T1, T2, T3, T4, T5) = 0; + virtual void CopyTo(void* dst) = 0; +}; + +template +class FunctionPointer5 + : public Function5 { + public: + typedef R(*FuncPtrType)(T1, T2, T3, T4, T5); + explicit FunctionPointer5(FuncPtrType func) : function_(func) {} + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return function_(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer5(function_); + } + + private: + FuncPtrType function_; +}; + +template +class FunctionPointer5 + : public Function5 { + public: + typedef void(*FuncPtrType)(T1, T2, T3, T4, T5); + explicit FunctionPointer5(FuncPtrType func) : function_(func) {} + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + function_(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctionPointer5(function_); + } + + private: + FuncPtrType function_; +}; + +template +class MemberFunctionPointer5 + : public Function5 { + public: + typedef R(C::*MemFuncPtrType)(T1, T2, T3, T4, T5); + typedef C & ClassRefType; + MemberFunctionPointer5(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer5(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer5 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return (object_.*function_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer5(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class MemberFunctionPointer5 + : public Function5 { + public: + typedef void(C::*MemFuncPtrType)(T1, T2, T3, T4, T5); + typedef C & ClassRefType; + MemberFunctionPointer5(ClassRefType obj, MemFuncPtrType func) + : object_(obj), function_(func) {} + explicit MemberFunctionPointer5(ClassRefType obj) + : object_(obj), function_(&C::operator()) {} + void operator = (MemberFunctionPointer5 const & memfunc) { + object_ = memfunc.object_; + function_ = memfunc.function_; + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + (object_.*function_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)MemberFunctionPointer5(object_, function_); + } + + private: + ClassRefType object_; + MemFuncPtrType function_; +}; + +template +class FunctorWrapper5 + : public Function5 { + public: + FunctorWrapper5() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper5(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper5(FunctorWrapper5 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper5() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return (*object_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper5(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +template +class FunctorWrapper5 + : public Function5 { + public: + FunctorWrapper5() : object_(NULL), ref_count_(NULL) {} + explicit FunctorWrapper5(C const & obj) { + object_ = Allocation::New(obj); + ref_count_ = Allocation::New >(1); + } + explicit FunctorWrapper5(FunctorWrapper5 const & other) { + object_ = other.object_; + ref_count_ = other.ref_count_; + ++*ref_count_; + } + virtual ~FunctorWrapper5() { + if (0 == --*ref_count_) { + Allocation::Delete(ref_count_); + Allocation::Delete(object_); + } + } + virtual void operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + (*object_)(p1, p2, p3, p4, p5); + } + virtual void CopyTo(void* dst) { + new(dst)FunctorWrapper5(*this); + } + + private: + C * object_; + Atomic * ref_count_; +}; + +// bind to function0 +template +class Bound5Functor0 { + public: + Bound5Functor0(Function func, + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) {} + Bound5Functor0(Bound5Functor0 const & func) : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) + , p5_(func.p5_) {} + R operator() () { + return function_(p1_, p2_, p3_, p4_, p5_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T4 p4_; + T5 p5_; +}; + +// bind to function1 +template +class Bound5Functor1_Arg1 { + public: + Bound5Functor1_Arg1(Function func + , T2 p2, T3 p3, T4 p4, T5 p5) : function_(func) + , p2_(p2), p3_(p3), p4_(p4), p5_(p5) {} + Bound5Functor1_Arg1(Bound5Functor1_Arg1 const & func) + : function_(func.function_) + , p2_(func.p2_), p3_(func.p3_), p4_(func.p4_), p5_(func.p5_) {} + R operator() (T1 p1) { + return function_(p1, p2_, p3_, p4_, p5_); + } + + private: + Function function_; + T2 p2_; + T3 p3_; + T4 p4_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg2 { + public: + Bound5Functor1_Arg2(Function func + , T1 p1, T3 p3, T4 p4, T5 p5) : function_(func) + , p1_(p1), p3_(p3), p4_(p4), p5_(p5) {} + Bound5Functor1_Arg2(Bound5Functor1_Arg2 const & func) + : function_(func.function_) + , p1_(func.p1_), p3_(func.p3_), p4_(func.p4_), p5_(func.p5_) {} + R operator() (T2 p2) { + return function_(p1_, p2, p3_, p4_, p5_); + } + + private: + Function function_; + T1 p1_; + T3 p3_; + T4 p4_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg3 { + public: + Bound5Functor1_Arg3(Function func + , T1 p1, T2 p2, T4 p4, T5 p5) : function_(func) + , p1_(p1), p2_(p2), p4_(p4), p5_(p5) {} + Bound5Functor1_Arg3(Bound5Functor1_Arg3 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p4_(func.p4_), p5_(func.p5_) {} + R operator() (T3 p3) { + return function_(p1_, p2_, p3, p4_, p5_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T4 p4_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg4 { + public: + Bound5Functor1_Arg4(Function func + , T1 p1, T2 p2, T3 p3, T5 p5) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p5_(p5) {} + Bound5Functor1_Arg4(Bound5Functor1_Arg4 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p5_(func.p5_) {} + R operator() (T4 p4) { + return function_(p1_, p2_, p3_, p4, p5_); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T5 p5_; +}; + +template +class Bound5Functor1_Arg5 { + public: + Bound5Functor1_Arg5(Function func + , T1 p1, T2 p2, T3 p3, T4 p4) : function_(func) + , p1_(p1), p2_(p2), p3_(p3), p4_(p4) {} + Bound5Functor1_Arg5(Bound5Functor1_Arg5 const & func) + : function_(func.function_) + , p1_(func.p1_), p2_(func.p2_), p3_(func.p3_), p4_(func.p4_) {} + R operator() (T5 p5) { + return function_(p1_, p2_, p3_, p4_, p5); + } + + private: + Function function_; + T1 p1_; + T2 p2_; + T3 p3_; + T4 p4_; +}; + +} // namespace internal + + +using embb::base::internal::Nil; + +template +class Function { + public: + typedef internal::Function5 * FuncPtrType; + Function() : function_(NULL) {} + template + explicit Function(C const & obj) { + function_ = new(storage_) + internal::FunctorWrapper5(obj); + } + Function(Function const & func) { + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + ~Function() { + Free(); + } + void operator = (R(*func)(T1, T2, T3, T4, T5)) { + Free(); + function_ = new(storage_) + internal::FunctionPointer5(func); + } + void operator = (Function & func) { + Free(); + func.function_->CopyTo(&storage_[0]); + function_ = reinterpret_cast(&storage_[0]); + } + template + void operator = (C const & obj) { + Free(); + function_ = new(storage_) + internal::FunctorWrapper5(obj); + } + explicit Function(R(*func)(T1, T2, T3, T4, T5)) { + function_ = new(storage_) + internal::FunctionPointer5(func); + } + template + Function(C & obj, R(C::*func)(T1, T2, T3, T4, T5)) { + function_ = new(storage_) + internal::MemberFunctionPointer5(obj, func); + } + R operator () (T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return (*function_)(p1, p2, p3, p4, p5); + } + + private: + char storage_[sizeof( + internal::MemberFunctionPointer5)]; + FuncPtrType function_; + void Free() { + if (NULL != function_) { + function_->~Function5(); + function_ = NULL; + } + } +}; + +// wrap member function +template +Function MakeFunction(C & obj, + R(C::*func)(T1, T2, T3, T4, T5)) { + return Function(obj, func); +} + +// wrap function pointer +template +Function MakeFunction( + R(*func)(T1, T2, T3, T4, T5)) { + return Function(func); +} + +// bind to function0 +template +Function Bind(Function func, + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor0( + func, p1, p2, p3, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5), + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5), + T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +// bind to function1 +template +Function Bind(Function func + , Placeholder::Arg_1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor1_Arg1(func + , p2, p3, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , Placeholder::Arg_1 p1, T2 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, Placeholder::Arg_1, T3 p3, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor1_Arg2(func + , p1, p3, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, Placeholder::Arg_1 p2, T3 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, Placeholder::Arg_1, T4 p4, T5 p5) { + return Function( + internal::Bound5Functor1_Arg3(func + , p1, p2, p4, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, Placeholder::Arg_1 p3, T4 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1, T5 p5) { + return Function( + internal::Bound5Functor1_Arg4(func + , p1, p2, p3, p5)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4, T5 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, Placeholder::Arg_1 p4, T5 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +template +Function Bind(Function func + , T1 p1, T2 p2, T3 p3, T4 p4, Placeholder::Arg_1) { + return Function( + internal::Bound5Functor1_Arg5(func + , p1, p2, p3, p4)); +} + +template +Function Bind(R(*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, T4 p4, Placeholder::Arg_1 p5) { + return Bind(Function(func), p1, p2, p3, p4, p5); +} + +template +Function Bind(C & obj, R(C::*func)(T1, T2, T3, T4, T5) + , T1 p1, T2 p2, T3 p3, T4 p4, Placeholder::Arg_1 p5) { + return Bind(Function(obj, func), p1, p2, p3, p4, p5); +} + +// bind to Function5 +template +Function Bind( + C & obj, + R(C::*func)(T1, T2, T3, T4, T5), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2, + Placeholder::Arg_3 p3, + Placeholder::Arg_4 p4, + Placeholder::Arg_5 p5) { + return Function(obj, func); +} + +template +Function Bind( + R(*func)(T1, T2, T3, T4, T5), + Placeholder::Arg_1 p1, + Placeholder::Arg_2 p2, + Placeholder::Arg_3 p3, + Placeholder::Arg_4 p4, + Placeholder::Arg_5 p5) { + return Function(func); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTION5_H_ diff --git a/base_cpp/include/embb/base/internal/functionT.h b/base_cpp/include/embb/base/internal/functionT.h new file mode 100644 index 0000000..84ef320 --- /dev/null +++ b/base_cpp/include/embb/base/internal/functionT.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_FUNCTIONT_H_ +#define EMBB_BASE_INTERNAL_FUNCTIONT_H_ + +#include + +namespace embb { +namespace base { + +using embb::base::internal::Nil; + +template < + typename, + typename = Nil, + typename = Nil, + typename = Nil, + typename = Nil, + typename = Nil > +class Function; + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_FUNCTIONT_H_ diff --git a/base_cpp/include/embb/base/internal/memory_allocation-inl.h b/base_cpp/include/embb/base/internal/memory_allocation-inl.h new file mode 100644 index 0000000..1c997b6 --- /dev/null +++ b/base_cpp/include/embb/base/internal/memory_allocation-inl.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_MEMORY_ALLOCATION_INL_H_ +#define EMBB_BASE_INTERNAL_MEMORY_ALLOCATION_INL_H_ + +#include + +#include + +namespace embb { +namespace base { + +inline void* Allocatable::operator new(size_t size) { + return Allocation::Allocate(size); +} + +inline void Allocatable::operator delete(void* ptr, size_t) { + Allocation::Free(ptr); +} + +inline void* Allocatable::operator new[](size_t size) { + return Allocation::Allocate(size); +} + +inline void Allocatable::operator delete[](void* ptr, size_t) { + Allocation::Free(ptr); +} + +inline void* CacheAlignedAllocatable::operator new(size_t size) { + return Allocation::AllocateCacheAligned(size); +} + +inline void CacheAlignedAllocatable::operator delete(void* ptr, size_t) { + Allocation::FreeAligned(ptr); +} + +inline void* CacheAlignedAllocatable::operator new[](size_t size) { + return Allocation::AllocateCacheAligned(size); +} + +inline void CacheAlignedAllocatable::operator delete[](void* ptr, size_t) { + Allocation::FreeAligned(ptr); +} + +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_MEMORY_ALLOCATION_INL_H_ diff --git a/base_cpp/include/embb/base/internal/mutex-inl.h b/base_cpp/include/embb/base/internal/mutex-inl.h new file mode 100644 index 0000000..6de19c3 --- /dev/null +++ b/base_cpp/include/embb/base/internal/mutex-inl.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_MUTEX_INL_H_ +#define EMBB_BASE_INTERNAL_MUTEX_INL_H_ + +#include + +namespace embb { +namespace base { + +template +UniqueLock::UniqueLock() : mutex_(NULL), locked_(false) { +} + +template +UniqueLock::UniqueLock(Mutex& mutex) + : mutex_(&mutex), locked_(false) { + mutex_->Lock(); + locked_ = true; +} + +template +UniqueLock::UniqueLock(Mutex& mutex, DeferLockTag) + : mutex_(&mutex), locked_(false) { +} + +template +UniqueLock::UniqueLock(Mutex& mutex, embb::base::TryLockTag) + : mutex_(&mutex), locked_(false) { + locked_ = mutex_->TryLock(); +} + +template +UniqueLock::UniqueLock(Mutex& mutex, AdoptLockTag) + : mutex_(&mutex), locked_(true) { +} + +template +UniqueLock::~UniqueLock() { + if (OwnsLock()) { + mutex_->Unlock(); + } +} + +template +void UniqueLock::Lock() { + if ((mutex_ == NULL) || locked_) { + EMBB_THROW(ErrorException, "Mutex not set or locked"); + } + mutex_->Lock(); + locked_ = true; +} + +template +bool UniqueLock::TryLock() { + if ((mutex_ == NULL) || locked_) { + EMBB_THROW(ErrorException, "Mutex not set or locked"); + } + locked_ = mutex_->TryLock(); + return locked_; +} + +template +void UniqueLock::Unlock() { + if ((mutex_ == NULL) || (!locked_)) { + EMBB_THROW(ErrorException, "Mutex not set or unlocked"); + } + locked_ = false; + mutex_->Unlock(); +} + +template +void UniqueLock::Swap(UniqueLock& other) { + locked_ = other.locked_; + mutex_ = other.Release(); +} + +template +Mutex* UniqueLock::Release() { + Mutex* toRelease = mutex_; + mutex_ = NULL; + locked_ = false; + return toRelease; +} + +template +bool UniqueLock::OwnsLock() const { + assert(!(locked_ && (mutex_ == NULL))); + return locked_; +} + +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_INTERNAL_MUTEX_INL_H_ */ diff --git a/base_cpp/include/embb/base/internal/nil.h b/base_cpp/include/embb/base/internal/nil.h new file mode 100644 index 0000000..60611c1 --- /dev/null +++ b/base_cpp/include/embb/base/internal/nil.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_NIL_H_ +#define EMBB_BASE_INTERNAL_NIL_H_ + +namespace embb { +namespace base { +namespace internal { + +/* + * Internal helper class to mark not specified template parameters. + */ +class Nil {}; + +} // namespace internal +} // namespace base +} // namespace embb + +#endif // EMBB_BASE_INTERNAL_NIL_H_ diff --git a/base_cpp/include/embb/base/internal/platform.h b/base_cpp/include/embb/base/internal/platform.h new file mode 100644 index 0000000..5fe2cbf --- /dev/null +++ b/base_cpp/include/embb/base/internal/platform.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_PLATFORM_H_ +#define EMBB_BASE_INTERNAL_PLATFORM_H_ + +/** + * \file Contains platform-specific includes, typedefs, and defines. + */ + +#include +#include + +#ifdef EMBB_THREADING_WINTHREADS + +namespace embb { +namespace base { +namespace internal { + +typedef embb_thread_t ThreadType; +typedef DWORD IDType; +typedef embb_mutex_t MutexType; +typedef embb_condition_t ConditionVariableType; + +} // namespace internal +} // namespace base +} // namespace embb + +#elif defined EMBB_THREADING_POSIXTHREADS // EMBB_THREADING_WINTHREADS + +namespace embb { +namespace base { +namespace internal { + +typedef embb_thread_t ThreadType; +typedef embb_thread_id_t IDType; +typedef embb_mutex_t MutexType; +typedef embb_condition_t ConditionVariableType; + +} // namespace internal +} // namespace base +} // namespace embb + +#else // EMBB_THREADING_POSIXTHREADS + +#error "No threading platform defined!" + +#endif // else + +#endif // EMBB_BASE_INTERNAL_PLATFORM_H_ diff --git a/base_cpp/include/embb/base/internal/thread-inl.h b/base_cpp/include/embb/base/internal/thread-inl.h new file mode 100644 index 0000000..46e81bd --- /dev/null +++ b/base_cpp/include/embb/base/internal/thread-inl.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_THREAD_INL_H_ +#define EMBB_BASE_INTERNAL_THREAD_INL_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +template +Thread::Thread(Function function) : rep_() { + internal::ThreadClosure* closure = + Allocation::New >(function); + int result = embb_thread_create( + &rep_, + NULL, + internal::ThreadClosure::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +Thread::Thread(CoreSet& core_set, Function function) : rep_() { + typedef internal::ThreadClosure Closure; + Closure* closure = Allocation::New(function); + int result = embb_thread_create( + &rep_, + &core_set.rep_, + internal::ThreadClosure::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +Thread::Thread(Function function, Arg1 arg1) : rep_() { + typedef internal::ThreadClosureArg1 Closure; + Closure* closure = Allocation::New(function, arg1); + int result = embb_thread_create( + &rep_, + NULL, + internal::ThreadClosureArg1::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +Thread::Thread(Function function, Arg1 arg1, Arg2 arg2) : rep_() { + typedef internal::ThreadClosureArg2 Closure; + Closure* closure = Allocation::New(function, arg1, arg2); + int result = embb_thread_create( + &rep_, + NULL, + internal::ThreadClosureArg2::ThreadStart, + static_cast(closure)); + CheckThreadCreationErrors(result, closure); +} + +template +void Thread::CheckThreadCreationErrors(int result, ThreadClosure* closure) { + if (result != EMBB_SUCCESS) { + if (closure != NULL) { + delete closure; + } + const char* message = "Could not create thread."; + if (result == EMBB_NOMEM) { + EMBB_THROW(NoMemoryException, message); + } + EMBB_THROW(ErrorException, message); + } +} + +template +std::basic_ostream& + operator<<(std::basic_ostream& os, Thread::ID id) { + return os << id.id_; +} + +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_INTERNAL_THREAD_INL_H_ */ diff --git a/base_cpp/include/embb/base/internal/thread_closures.h b/base_cpp/include/embb/base/internal/thread_closures.h new file mode 100644 index 0000000..3752b42 --- /dev/null +++ b/base_cpp/include/embb/base/internal/thread_closures.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_THREAD_CLOSURES_H_ +#define EMBB_BASE_INTERNAL_THREAD_CLOSURES_H_ + +#include + +namespace embb { +namespace base { +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. + */ +template +struct ThreadClosure { + Function function_; + + static int ThreadStart(void* arg) { + ThreadClosure *self = static_cast(arg); + self->function_(); + Allocation::Delete(self); + return 0; + } + + explicit ThreadClosure(const Function& function) : function_(function) {} +}; + +/** + * 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. + */ +template +struct ThreadClosureArg1 { + Function function_; + Arg1 arg1_; + + static int ThreadStart(void* arg) { + ThreadClosureArg1 *self = static_cast(arg); + self->function_(self->arg1_); + Allocation::Delete(self); + return 0; + } + + ThreadClosureArg1(const Function& function, const Arg1& arg1) + : function_(function), arg1_(arg1) {} +}; + +/** + * 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. + */ +template +struct ThreadClosureArg2 { + Function function_; + Arg1 arg1_; + Arg2 arg2_; + + static int ThreadStart(void* arg) { + ThreadClosureArg2 *self = static_cast(arg); + self->function_(self->arg1_, self->arg2_); + Allocation::Delete(self); + return 0; + } + + ThreadClosureArg2( + const Function& function, const Arg1& arg1, const Arg2& arg2) + : function_(function), arg1_(arg1), arg2_(arg2) {} +}; + +} // namespace internal +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_INTERNAL_THREAD_CLOSURES_H_ */ diff --git a/base_cpp/include/embb/base/internal/thread_specific_storage-inl.h b/base_cpp/include/embb/base/internal/thread_specific_storage-inl.h new file mode 100644 index 0000000..a2ba513 --- /dev/null +++ b/base_cpp/include/embb/base/internal/thread_specific_storage-inl.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_INTERNAL_THREAD_SPECIFIC_STORAGE_INL_H_ +#define EMBB_BASE_INTERNAL_THREAD_SPECIFIC_STORAGE_INL_H_ + +#include +#include +#include + +#include + +namespace embb { +namespace base { + +template +ThreadSpecificStorage::ThreadSpecificStorage() + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage(Initializer initializer) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage( + Initializer1 initializer1, Initializer2 initializer2) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer1, initializer2); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage( + Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer1, initializer2, + initializer3); + } +} + +template +template +ThreadSpecificStorage::ThreadSpecificStorage( + Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3, Initializer4 initializer4) + : rep_(), usage_flags_(NULL) { + Prepare(); + for (unsigned int i = 0; i < rep_.size; i++) { + rep_.values[i] = Allocation::New(initializer1, initializer2, + initializer3, initializer4); + } +} + +template +ThreadSpecificStorage::~ThreadSpecificStorage() { + for (unsigned int i = 0; i < rep_.size; i++) { + Type* value = static_cast(rep_.values[i]); + assert(value != NULL); + Allocation::Delete(value); + } + embb_tss_delete(&rep_); + Allocation::Free(usage_flags_); +} + +template +Type& ThreadSpecificStorage::Get() { + Type* value = static_cast(embb_tss_get(&rep_)); + if (value == NULL) { + EMBB_THROW(ErrorException, "No thread index could be obtained"); + } + unsigned int thread_index = 0; + int status = embb_internal_thread_index(&thread_index); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + usage_flags_[thread_index] = true; + return *value; +} + +template +const Type& ThreadSpecificStorage::Get() const { + const Type* value = static_cast(embb_tss_get(&rep_)); + if (value == NULL) { + EMBB_THROW(ErrorException, "No thread index could be obtained"); + } + unsigned int thread_index = 0; + int status = embb_internal_thread_index(&thread_index); + assert(status == EMBB_SUCCESS); + usage_flags_[thread_index] = true; + return *value; +} + +template +void ThreadSpecificStorage::Prepare() { + int status = embb_tss_create(&rep_); + if (status == EMBB_NOMEM) { + EMBB_THROW(NoMemoryException, "Not enough memory to allocate " + "thread-specific storage"); + } + usage_flags_ = static_cast( + Allocation::Allocate(sizeof(bool) * rep_.size)); + for (unsigned int i = 0; i < rep_.size; i++) { + usage_flags_[i] = false; + } +} + +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_INTERNAL_THREAD_SPECIFIC_STORAGE_INL_H_ */ diff --git a/base_cpp/include/embb/base/memory_allocation.h b/base_cpp/include/embb/base/memory_allocation.h new file mode 100644 index 0000000..235b481 --- /dev/null +++ b/base_cpp/include/embb/base/memory_allocation.h @@ -0,0 +1,938 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_MEMORY_ALLOCATION_H_ +#define EMBB_BASE_MEMORY_ALLOCATION_H_ + +#include +#include +#include +#include +#include + +#include +#include + +/** + * \defgroup CPP_BASE_MEMORY_ALLOCATION Memory Allocation + * Functions, classes, and allocators for dynamic memory allocation. + * + * \ingroup CPP_BASE + */ + +namespace embb { +namespace base { +/** + * Common (static) functionality for unaligned and aligned memory allocation. + * This class is a wrapper for the functions in + * \ref embb/base/c/memory_allocation.h + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +class Allocation { + public: + /** + * Allocates memory for an instance of type \c Type and default-initializes + * it. + * + * Keeps track of allocated memory in debug mode. + * + * \return Pointer to new instance of type \c Type + * + * \throws embb::base::NoMemoryException if not enough memory is available + * for the given type. + * + * \see Delete() + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \tparam Type %Type of the object to be allocated + */ + template + static Type* New() { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(); + } + + #ifdef DOXYGEN + + /** + * Allocates memory unaligned for an instance of type \c Type and initializes + * it with the specified arguments. + * + * Keeps track of allocated memory in debug mode. + * + * \return Pointer to new instance of type \c Type + * + * \throws embb::base::NoMemoryException if not enough memory is available + * for the given type. + * + * \see Delete() + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \tparam Type %Type of the instance to be allocated + * \tparam Arg1 %Type of (first) constructor argument + */ + template + static Type* New( + Arg1 argument1, + /**< [IN] (First) argument for constructor of \c Type */ + ... + ); + +#else // DOXYGEN + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2, arg3); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2, arg3, arg4); + } + + /** + * See Doxygen documentation dummy above + */ + template + static Type* New(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) { + void* memory = embb_alloc(sizeof(Type)); + if (memory == NULL) EMBB_THROW(NoMemoryException, "When allocating memory"); + return new(memory) Type(arg1, arg2, arg3, arg4, arg5); + } + +#endif // else DOXYGEN + + /** + * Destructs an instance of type \c Type and frees the allocated memory. + * + * \tparam Type %Type of instance to be deleted + */ + template + static void Delete( + Type* to_delete + /**< [IN,OUT] Instance to be deleted */ + ) { + to_delete->~Type(); + embb_free(static_cast(to_delete)); + } + + /** + * Returns the total number of bytes currently allocated. + * + * Wrapper for C function embb_get_bytes_allocated(). + * + * \return Number of currently allocated bytes in debug mode, otherwise 0. + * + * \waitfree + */ + static size_t AllocatedBytes(); + + /** + * Allocates \p size bytes of memory (unaligned). + * + * Wrapper for C function embb_allocate(). + * + * Keeps track of allocated memory in debug mode. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory size+3*sizeof(size_t) bytes in debug mode, otherwise \c + * size bytes + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * Allocation::Free(). + * + * \see AllocateAligned(), AllocateCacheAligned(), Free() + */ + static void* Allocate( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by Allocation::Allocate() for + * some pointer \p ptr. + * + * Wrapper for C function embb_free(). + * + * Keeps track of freed memory in debug mode. + * + * \threadsafe + * + * \see Allocate() + */ + static void Free( + void * ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Allocates \p size bytes of memory with alignment \p alignment. + * + * Wrapper for C function embb_alloc_aligned(). + * + * This function can be used to align objects to certain boundaries such as + * cache lines, memory pages, etc. + * + * Keeps track of allocated memory in debug mode. + * + * It is not required that \p size is a multiple of \p alignment as, e.g., + * for the \c aligned\_alloc function of the C11 Standard. + * + * \pre The alignment has to be power of 2 and a multiple of + * size(void*). + * \post The returned pointer is a multiple of \p alignment. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory Debug mode: Let \c n be the number of aligned cells necessary to + * fit the payload. Then, (n+1)*alignment+3*size_of(size_t)-1 + * bytes are allocated.
Release mode: \c size bytes are requested + * using the functions provided by the operating systems. + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * Allocation::FreeAligned(). + * + * \see Allocate(), AllocateCacheAligned(), FreeAligned() + */ + static void* AllocateAligned( + size_t alignment, + /**< [IN] Alignment in bytes */ + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); + + /** + * Frees memory that has been allocated by Allocation::AllocateAligned() or + * Allocation::AllocateCacheAligned() for some pointer \p ptr. + * + * Wrapper for C function embb_free_aligned(). + * + * Keeps track of freed memory in debug mode. + * + * \threadsafe + * + * \see AllocateAligned(), AllocateCacheAligned() + */ + static void FreeAligned( + void * ptr + /**< [IN,OUT] Pointer to memory block to be freed */ + ); + + /** + * Allocates \p size bytes of cache-aligned memory. + * + * Wrapper for C function embb_alloc_cache_aligned(). + * + * Specialized version of Allocation::AllocateAligned(). The alignment is + * chosen automatically (usually 64 bytes). + * + * Keeps track of allocated memory in debug mode. + * + * \post The returned pointer is a multiple of the cache line size. + * + * \return NULL in case of failure, otherwise address of allocated memory + * block. + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory See Allocation::AllocateAligned() + * + * \threadsafe + * + * \note Memory allocated using this function must be freed using + * Allocation::FreeAligned(). + * + * \see Allocate(), AllocateAligned(), FreeAligned() + */ + static void* AllocateCacheAligned( + size_t size + /**< [IN] Size of memory block to be allocated in bytes */ + ); +}; + +/** + * Overloaded new/delete operators. + * + * Classes that derive from this class will use the EMBB methods for dynamic + * allocation and deallocation of memory (Allocation::Allocate() and + * Allocation::Free()). In debug mode, memory consumption is tracked in order to + * detect memory leaks. + * + * \see CacheAlignedAllocatable + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +class Allocatable { + public: + /** + * New operator. + * + * Allocates \c size bytes of memory. Must not be called directly! + * + * \return Pointer to allocated block of memory + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \threadsafe + * + * \memory See Allocation::Allocate() + * + * \see operator delete() + */ + static void* operator new( + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Delete operator. + * + * Deletes \c size bytes of memory pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + * + * \see operator new() + */ + static void operator delete( + void* ptr, + /**< [IN,OUT] Pointer to memory block to be freed */ + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Array new operator. + * + * Allocates an array of \c size bytes. Must not be called directly! + * + * \remark Note that the global new[], calling this function, might + * return a different address. This is stated in the standard + * (5.3.4 New [expr.new]): + * \remark "A new-expression passes the amount of space requested to the + * allocation function as the first argument of type std::size_t. + * That argument shall be no less than the size of the object being + * created; it may be greater than the size of the object being + * created only if the object is an array." + * \remark So, even if the returned pointer of this function is aligned, + * the pointer to the array returned by global new[] need not be. + * For example, when using GCC 4.8.3 (64 bit), the size of the array + * is kept in the first 8 bytes of the allocated memory. + * + * \return Pointer to allocated block of memory + * + * \threadsafe + * + * \memory See Allocation::Allocate() + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \see operator delete[]() + */ + static void* operator new[]( + size_t size + /**< [IN] Size of the array in bytes*/ + ); + + /** + * Array delete operator. + * + * Deletes array of \c size bytes pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + * + * \see operator new[]() + */ + static void operator delete[]( + void* ptr, + /**< [IN,OUT] Pointer to the array to be freed */ + size_t size + /**< [IN] Size of the array in bytes */ + ); +}; + +/** + * Overloaded new/delete operators. + * + * Classes that derive from this class will use the EMBB methods for dynamic, + * cache-aligned allocation and deallocation of memory + * (Allocation::AllocateCacheAligned() and Allocation::FreeAligned()). + * In debug mode, memory consumption is tracked in order to detect memory leaks. + * + * \note When using the new[] operator, not each object in the array is aligned, + * but only the constructed array as a whole. + * + * \see Allocatable + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +class CacheAlignedAllocatable { + public: + /** + * New operator. + * + * Allocates \c size bytes of memory. Must not be called directly! + * + * \return Pointer to allocated block of memory + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \threadsafe + * + * \memory See Allocation::AllocateCacheAligned() + * + * \see operator delete() + */ + static void* operator new( + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Delete operator. + * + * Deletes \c size bytes of memory pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + */ + static void operator delete( + void* ptr, + /**< [IN,OUT] Pointer to memory block to be freed */ + size_t size + /**< [IN] Size of the memory block in bytes */ + ); + + /** + * Array new operator. + * + * Allocates an array of \c size bytes. Must not be called directly! + * + * \return Pointer to allocated block of memory + * + * \throws embb::base::NoMemoryException if not enough memory is available. + * + * \memory See Allocation::AllocateCacheAligned() + * + * \threadsafe + * + * \see operator delete[]() + */ + static void* operator new[]( + size_t size + /**< [IN] size of bytes to allocate for the array*/ + ); + + /** + * Array delete operator. + * + * Deletes array of \c size bytes pointed to by \c ptr. Must not be called + * directly! + * + * \threadsafe + * + * \see operator new[]() + */ + static void operator delete[]( + void* ptr, + /**< [IN,OUT] Pointer to the array to be freed */ + size_t size + /**< [IN] Size of the array in bytes */ + ); +}; + +/* + * Forward declaration + */ +template +class Allocator; + +/* + * Specialization for void + */ +template <> +class Allocator < void > { + public: + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef Allocator other; + }; +}; + +/** + * %Allocator according to the C++ standard. + * + * For memory allocation and deallocation, + * embb::base::Allocation::Allocate() and + * embb::base::Allocation::Free() are used, respectively. + * + * In debug mode, leak checking is active. The function + * embb::base::Allocation::AllocatedBytes() returns the number of + * currently allocated bytes. + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +template +class Allocator { + public: + /** Quantity of elements type */ + typedef size_t size_type; + + /** Difference between two pointers type */ + typedef ptrdiff_t difference_type; + + /** Pointer to element type */ + typedef T* pointer; + + /** Pointer to constant element type */ + typedef const T* const_pointer; + + /** Reference to element type */ + typedef T& reference; + + /** Reference to constant element type */ + typedef const T& const_reference; + + /** Element type */ + typedef T value_type; + + /** + * Rebind allocator to type U + */ + template struct rebind { + /** Type to rebind to */ + typedef Allocator other; + }; + + /** + * Constructs allocator object + */ + Allocator() throw() {} + + /** + * Copies allocator object + */ + Allocator( + const Allocator& + /**< [IN] Other allocator object */ + ) throw() {} + + /** + * Constructs allocator object + * + * Allows construction from allocators for different types (rebind) + */ + template Allocator( + const Allocator& + /**< [IN] Other allocator object*/ + ) + throw() {} + + /** + * Destructs allocator object + */ + ~Allocator() throw() {} + + /** + * Gets address of an object + * + * \return Address of object + * + * \waitfree + */ + pointer address( + reference x + /**< [IN] Reference to object */ + ) const { + return &x; + } + + /** + * Gets address of a constant object + * + * \return Address of object + * + * \waitfree + */ + const_pointer address( + const_reference x + /**< [IN] Reference to constant object */ + ) const { + return &x; + } + + /** + * Allocates but doesn't initialize storage for elements of type T + * + * \threadsafe + * + * \return Pointer to allocated storage + * + * \memory See Allocation::Allocate() + */ + pointer allocate( + size_type n, + /**< [IN] Number of elements to allocate */ + const void* = 0 + /**< [IN] Optional pointer previously obtained from allocate */ + ) { + if (n > max_size()) + EMBB_THROW(embb::base::NoMemoryException, + "Amount of requested memory too high"); + return reinterpret_cast + (embb::base::Allocation::Allocate(n*sizeof(value_type))); + } + + /** + * Deallocates storage of destroyed elements. + * + * \threadsafe + */ + void deallocate( + pointer p, + /**< [IN,OUT] Pointer to allocated storage */ + size_type + ) { + embb::base::Allocation::Free(p); + } + + /** + * %Allocation maximum + * + * \return Maximum number of elements that can be allocated + * + *\waitfree + */ + size_type max_size() const throw() { + return std::numeric_limits::max() / sizeof(value_type); + } + + /** + * Initializes elements of allocated storage with specified value. + * + * \threadsafe + */ + void construct( + pointer p, + /**< [IN,OUT] Pointer to allocated storage */ + const value_type& val + /**< [IN] Value */ + ) { + new(p)value_type(val); + } + + /** + * Destroys elements of initialized storage. + * + * \threadsafe + */ + void destroy( + pointer p + /**< [IN,OUT] Pointer to allocated storage*/ + ) { + EMBB_UNUSED(p); + p->~value_type(); + } + + private: + /* + * \threadsafe + */ + Allocator& operator=(const Allocator&); +}; + +/* + * Forward declaration + */ +template +class AllocatorCacheAligned; + +/* + * Specialization for void + */ +template <> +class AllocatorCacheAligned < void > { + public: + typedef void* pointer; + typedef const void* const_pointer; + typedef void value_type; + + template struct rebind { + typedef AllocatorCacheAligned other; + }; +}; + +/** + * %Allocator according to the C++ standard. Allocates memory cache-aligned. + * + * For memory allocation and deallocation, + * embb::base::Allocation::AllocateCacheAligned() and + * embb::base::Allocation::FreeAligned() are used, respectively. + * + * In debug mode, leak checking is active. The function + * embb::base::Allocation::AllocatedBytes() returns the number of + * currently allocated bytes. + * + * \ingroup CPP_BASE_MEMORY_ALLOCATION + */ +template< typename T > +class AllocatorCacheAligned : public Allocator < T > { + public: + /** Quantity of elements type */ + typedef size_t size_type; + + /** Difference between two pointers type */ + typedef ptrdiff_t difference_type; + + /** Pointer to element type */ + typedef T* pointer; + + /** Pointer to constant element type */ + typedef const T* const_pointer; + + /** Reference to element type */ + typedef T& reference; + + /** Reference to constant element type */ + typedef const T& const_reference; + + /** Element type */ + typedef T value_type; + + /** + * Rebind allocator to type U + */ + template struct rebind { + /** Type to rebind to */ + typedef Allocator other; + }; + + /** + * Constructs allocator object + */ + AllocatorCacheAligned() throw() { } + + /** + * Copies allocator object + */ + AllocatorCacheAligned( + const AllocatorCacheAligned& a + /**< [IN] Other allocator object */ + ) throw() + : Allocator < T >(a) { } + + /** + * Constructs allocator object + * + * Allows construction from allocators for different types (rebind) + */ + template + AllocatorCacheAligned( + const AllocatorCacheAligned& + /**< [IN] Other allocator object*/ + ) throw() { } + + /** + * Destructs allocator object + */ + ~AllocatorCacheAligned() throw() { } + + /** + * Allocates but doesn't initialize storage for elements of type T + * + * \threadsafe + * + * \return Pointer to allocated storage + * + * \memory see Allocation::Allocate() + */ + pointer allocate( + size_type n, + /**< [IN] Number of elements to allocate */ + const void* = 0 + /**< [IN] Optional pointer previously obtained from allocate */ + ) { + if (n > this->max_size()) + EMBB_THROW(embb::base::NoMemoryException, + "Amount of requested memory too high"); + return reinterpret_cast + (embb::base::Allocation::AllocateCacheAligned(n*sizeof(value_type))); + } + + /** + * Deallocates storage of destroyed elements. + * + * \threadsafe + */ + void deallocate( + pointer p, + /**< [IN,OUT] Pointer to allocated storage */ + size_type + ) { + embb::base::Allocation::FreeAligned(p); + } + // Inherit everything else. +}; + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool operator==(const Allocator&, const Allocator&) { + return true; +} + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool operator==(const Allocator&, const Allocator&) { + return true; +} + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool operator!=(const Allocator&, const Allocator&) { + return false; +} + +/* + * Comparison operator for Allocator objects. + * Not for manual use. + */ +template +inline bool +operator!=(const Allocator&, const Allocator&) { + return false; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator==( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return true; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator==( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return true; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator!=( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return false; +} + +/* + * Comparison operator for AllocatorCacheAligned objects. + * Not for manual use. + */ +template +inline bool operator!=( + const AllocatorCacheAligned&, const AllocatorCacheAligned&) { + return false; +} +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_MEMORY_ALLOCATION_H_ diff --git a/base_cpp/include/embb/base/mutex.h b/base_cpp/include/embb/base/mutex.h new file mode 100644 index 0000000..61ca2f5 --- /dev/null +++ b/base_cpp/include/embb/base/mutex.h @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_MUTEX_H_ +#define EMBB_BASE_MUTEX_H_ + +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_MUTEX Mutex and Lock + * + * Mutexes and locks for thread synchronization. + * + * \ingroup CPP_BASE + */ + +/** + * Pre-declaration for friending. + */ +class ConditionVariable; + +namespace internal { + +/** + * Provides main functionality for mutexes. + */ +class MutexBase { + public: + /** + * Creates internal representation. + * + * \notthreadsafe + */ + explicit MutexBase( + int mutex_type + /**< [IN] Mutex type as used in embb_mutex_init(). */ + ); + + /** + * Destroys internal representation. + */ + virtual ~MutexBase() = 0; + + /** + * Waits until the mutex can be locked and locks it. + * + * \pre The mutex is not locked by the current thread or is of type + * RecursiveMutex. + * \post The mutex is locked + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \post If successful, the mutex is locked + * \return \c true if mutex could be locked, otherwise \c false + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks a locked mutex. + * + * \pre The mutex is locked by the current thread + * \post The mutex is unlocked if the number of unlock operations has reached + * the number of lock operations + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + + private: + /** + * Holds the actual mutex. + */ + internal::MutexType mutex_; + + /** + * For access to native implementation type. + */ + friend class embb::base::ConditionVariable; +}; + +} // namespace internal + +/** + * Non-recursive, exclusive mutex. + * + * Mutexes of this type cannot be locked recursively, that is, multiple times + * by the same thread with unlocking it in between. Moreover, it cannot be + * copied or assigned. + * + * \see RecursiveMutex + * \ingroup CPP_BASE_MUTEX + */ +class Mutex : public internal::MutexBase { + public: + /** + * Creates a mutex which is in unlocked state. + * + * \memory Potentially allocates dynamic memory + * \notthreadsafe + */ + Mutex(); + +#ifdef DOXYGEN + + /** + * Waits until the mutex can be locked and locks it. + * + * \pre The mutex is not locked by the current thread. + * \post The mutex is locked + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \pre The mutex is not locked by the current thread. + * \post If successful, the mutex is locked. + * \return \c true if the mutex could be locked, otherwise \c false. + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks the mutex. + * + * \pre The mutex is locked by the current thread + * \post The mutex is unlocked + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + +#endif // DOXYGEN + + private: + /** + * Disables copy construction and assignment. + */ + Mutex(const Mutex&); + Mutex& operator=(const Mutex&); + + /** + * For access to native implementation type. + */ + friend class ConditionVariable; +}; + + +/** + * Recursive, exclusive mutex. + * + * Mutexes of this type can be locked recursively, that is, multiple times by + * the same thread without unlocking it in between. It is unlocked only, if the + * number of unlock operations has reached the number of previous lock + * operations by the same thread. It cannot be copied or assigned. + * + * \see Mutex + * \ingroup CPP_BASE_MUTEX + */ +class RecursiveMutex : public internal::MutexBase { + public: + /** + * Creates a mutex which is in unlocked state. + * + * \memory Potentially allocates dynamic memory + * \notthreadsafe + */ + RecursiveMutex(); + +#ifdef DOXYGEN + + /** + * Waits until the mutex can be locked and locks it. + * + * \post The mutex is locked + * \threadsafe + * \see TryLock(), Unlock() + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \post If successful, the given mutex is locked. + * \return \c true if the mutex could be locked, otherwise \c false. + * \threadsafe + * \see Lock(), Unlock() + */ + bool TryLock(); + + /** + * Unlocks a locked mutex. + * + * \pre The mutex is locked by the current thread. + * \post The mutex is unlocked if the number of unlock operations has reached + * the number of lock operations. + * \threadsafe + * \see Lock(), TryLock() + */ + void Unlock(); + +#endif // DOXYGEN + + private: + /** + * Disables copy construction and assignment. + */ + RecursiveMutex(const RecursiveMutex&); + RecursiveMutex& operator=(const RecursiveMutex&); +}; + + +/** + * Scoped lock (according to the RAII principle) using a mutex. + * + * The mutex is locked on construction and unlocked on leaving the scope of the + * lock. + * + * \tparam Mutex Used mutex type + * \see Mutex, UniqueLock + * \ingroup CPP_BASE_MUTEX + */ +template +class LockGuard { + public: + /** + * Creates the lock and locks the mutex. + * + * \pre The given mutex is unlocked + * \notthreadsafe + */ + explicit LockGuard( + Mutex& mutex + /**< [IN] Mutex to be guarded */ + ) : mutex_(mutex) { + mutex_.Lock(); + } + + /** + * Unlocks the mutex. + */ + ~LockGuard() { + mutex_.Unlock(); + } + + private: + /** + * Holds reference to mutex realizing the lock. + */ + Mutex& mutex_; + + /** + * Disable copy construction and assignment. + */ + LockGuard(const LockGuard&); + LockGuard& operator=(const LockGuard&); +}; + +/** + * \name UniqueLock Tag Variables + * \{ + */ + +/** + * Tag type for deferred UniqueLock construction. + * + * Use the defer_lock variable in constructor calls. + */ +struct DeferLockTag {}; + +/** + * Tag variable for deferred UniqueLock construction. + * + * \ingroup CPP_BASE_MUTEX + */ +const DeferLockTag defer_lock = DeferLockTag(); + +/** + * Tag type for try-lock UniqueLock construction. + * + * Use the try_lock variable in constructor calls. + */ +struct TryLockTag {}; + +/** + * Tag variable for try-lock UniqueLock construction. + * + * \ingroup CPP_BASE_MUTEX + */ +const TryLockTag try_lock = TryLockTag(); + +/** + * Tag type for adopt UniqueLock constructor. + * + * Use the adopt_lock variable in constructor calls. + */ +struct AdoptLockTag {}; + +/** + * Tag variable for adopt UniqueLock construction. + * + * \ingroup CPP_BASE_MUTEX + */ +const AdoptLockTag adopt_lock = AdoptLockTag(); + +/** \} */ + +/** + * Flexible ownership wrapper for a mutex. + * + * Provides exception controlled locking of a mutex with non-recursive semantics, + * that gives more flexibility than LockGuard but also has slightly increased + * memory and processing overhead. Each instance of a UniqueLock can be used by + * one thread only! + * + * \notthreadsafe + * \see Mutex, LockGuard + * \tparam Mutex Used mutex type + * \ingroup CPP_BASE_MUTEX + */ +template +class UniqueLock { + public: + /** + * Creates a lock without assigned mutex. + * + * A mutex can be assigned to the lock using the method Swap(). + */ + UniqueLock(); + + /** + * Creates a lock from an unlocked mutex and locks it. + * + * \pre \c mutex is unlocked + */ + explicit UniqueLock( + Mutex& mutex + /**< [IN] Mutex to be managed. */ + ); + + /** + * Creates a lock from an unlocked mutex without locking it. + * + * \pre \c mutex is unlocked + */ + UniqueLock( + Mutex& mutex, + /**< [IN] Mutex to be managed */ + DeferLockTag + /**< [IN] Tag to select correct constructor */ + ); + + /** + * Creates a lock from an unlocked mutex and tries to lock it. + * + * \pre \c mutex is unlocked + */ + UniqueLock( + Mutex& mutex, + /**< [IN] Mutex to be managed */ + TryLockTag + /**< [IN] Tag to select correct constructor */ + ); + + /** + * Creates a lock from an already locked mutex. + * + * \pre \c mutex is locked + */ + UniqueLock( + Mutex& mutex, + /**< [IN] Mutex to be managed */ + AdoptLockTag + /**< [IN] Tag to select correct constructor */ + ); + + /** + * Unlocks the mutex if owned. + */ + ~UniqueLock(); + + /** + * Waits until the mutex is unlocked and locks it. + * + * \throws ErrorException, if no mutex is set or it is locked + */ + void Lock(); + + /** + * Tries to lock the mutex and returns immediately. + * + * \return \c true if the mutex could be locked, otherwise \c false. + * \throws ErrorException, if no mutex is set + */ + bool TryLock(); + + /** + * Unlocks the mutex. + * + * \throws ErrorException, if no mutex is set or it is not locked + */ + void Unlock(); + + /** + * Transfers ownership of a mutex to this lock. + */ + void Swap( + UniqueLock& other + /**< [IN/OUT] Lock from which ownership shall be transferred */ + ); + + /** + * Gives up ownership of the mutex and returns a pointer to it. + * + * \return A pointer to the owned mutex or NULL, if no mutex was owned + */ + Mutex* Release(); + + /** + * Checks whether the mutex is owned and locked. + * + * \return \c true if mutex is locked, otherwise \c false. + */ + bool OwnsLock() const; + + private: + /** + * Holds reference to mutex realizing the lock. + */ + Mutex* mutex_; + + /** + * Stores information about whether the unique lock has locked the mutex. + */ + bool locked_; + + /** + * Disable copy construction and assignment. + */ + UniqueLock(const UniqueLock&); + UniqueLock& operator=(const UniqueLock&); + + /** + * For access to native implementation type. + */ + friend class embb::base::ConditionVariable; +}; + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_MUTEX_H_ diff --git a/base_cpp/include/embb/base/thread.h b/base_cpp/include/embb/base/thread.h new file mode 100644 index 0000000..5951747 --- /dev/null +++ b/base_cpp/include/embb/base/thread.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_THREAD_H_ +#define EMBB_BASE_THREAD_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +/** + * \defgroup CPP_BASE_THREAD Thread + * + * Threads supporting thread-to-core affinities. + * + * \ingroup CPP_BASE + */ + +/** + * Represents a thread of execution. + * + * Provides an abstraction from platform-specific threading implementations to + * create, manage, and join threads of execution. Support for thread-to-core + * affinities is given on thread creation by using the core set functionality. + * + * This class is essentially a wrapper for the underlying C implementation. + * + * \ingroup CPP_BASE_THREAD + */ +class Thread { + public: + /** + * Unique %ID of a thread that can be compared with other IDs. + */ + class ID { + public: + /** + * Constructs an empty (invalid) thread %ID. + */ + ID() : id_() {} + + private: + /** + * Constructs an %ID from an IDType instance. Is done by the thread. + */ + explicit ID(internal::IDType id) : id_(id) {} + + /** + * Holds the actual %ID representation. + */ + internal::IDType id_; + + /** + * A thread needs to set its ID on start. + */ + friend class Thread; + + /** + * The streaming operator needs to access the internal %ID representation. + */ + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, Thread::ID id); + + /** + * Comparison operators need to access the internal ID representation. + */ + friend bool operator==(Thread::ID lhs, Thread::ID rhs); + friend bool operator!=(Thread::ID lhs, Thread::ID rhs); + }; + + /** + * Returns the maximum number of threads handled by EMB2. + * + * See embb_thread_get_max_count() for a description of the semantics. + * + * \return Maximum number of threads + * + * \lockfree + * \see SetThreadsMaxCount() + */ + static unsigned int GetThreadsMaxCount(); + + /** + * Sets the maximum number of threads handled by EMB2. + * + * \notthreadsafe + * \see GetThreadsMaxCount() + */ + static void SetThreadsMaxCount( + unsigned int max_count + /**< [IN] Maximum number of threads */ + ); + + /** + * Returns the %ID of the current thread. + * + * The %ID is only valid within the calling thread. + * + * \return %ID of the calling thread + * + * \threadsafe + */ + static ID CurrentGetID(); + + /** + * Reschedule the current thread for later execution. + * + * This is only a request, the realization depends on the implementation and + * the scheduler employed by the operating system. + * + * \threadsafe + */ + static void CurrentYield(); + + /** + * Creates and runs a thread with zero-argument start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \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 + */ + template + explicit Thread( + Function function + /**< [IN] Callable (without arguments, must be copyable) */ + ); + + /** + * Creates and runs a thread with zero-argument start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \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 + */ + 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) */ + ); + + /** + * Creates and runs a thread with one-argument thread start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \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 Argument Type of argument + */ + template + Thread( + Function function, + /**< [IN] Callable (with one argument, must be copyable) */ + Arg arg + /**< [IN] Argument for function (must be copyable) */ + ); + + /** + * Creates and runs a thread with two-argument thread start function. + * + * \note If the function is passed as a temporary object when creating a + * thread, this might be interpreted as a function declaration ("most vexing + * parse"). C++11 resolves this by using curly braces for initialization. + * + * \throws NoMemoryException if not enough memory is available + * \throws ErrorException in case of another error + * \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 Arg1 Type of first argument + * \tparam Arg2 Type of second argument + */ + template + Thread( + Function function, + /**< [IN] Callable (with two arguments, must be copyable) */ + Arg1 arg1, + /**< [IN] First argument for function (must be copyable) */ + Arg2 arg2 + /**< [IN] Second argument for function (must be copyable) */ + ); + + /** + * Waits until the thread has finished execution. + * + * \pre The thread has been created but not yet been joined. + * \post The thread has finished execution and dynamic memory allocated during + * creation has been freed. + * \notthreadsafe + */ + void Join(); + + /** + * Returns the thread %ID. + * + * \return %ID of the thread + * + * \threadsafe + */ + ID GetID(); + + private: + /** + * Performs error checks and frees resources from thread constructor. + * + * \tparam ThreadClosure Type of thread closure + */ + template + void CheckThreadCreationErrors( + int result, + /**< [IN] Result value of creating thread with C API */ + ThreadClosure* closure + /**< [IN] Closure used when creating thread */ + ); + + /** + * Disables copying and assignment. + */ + Thread(const Thread&); + Thread& operator=(const Thread&); + + /** + * Holds native implementation thread handle. + */ + internal::ThreadType rep_; +}; + +/** + * Compares two thread IDs for equality. + * + * \return \c true if thread IDs are equivalent, otherwise \c false + * + * \ingroup CPP_BASE_THREAD + */ +bool operator==( + Thread::ID lhs, + /**< [IN] Left-hand side of equality sign */ + Thread::ID rhs + /**< [IN] Right-hand side of equality sign */ + ); + +/** + * Compares two thread IDs for inequality. + * + * \return \c true if thread IDs are not equivalent, otherwise \c false + * + * \ingroup CPP_BASE_THREAD + */ +bool operator!=( + Thread::ID lhs, + /**< [IN] Left-hand side of inequality sign */ + Thread::ID rhs + /**< [IN] Left-hand side of inequality sign */ + ); + +/** + * Writes thread %ID to stream. + * + * \return Reference to the stream + * + * \ingroup CPP_BASE_THREAD + */ +template +std::basic_ostream& + operator<<( + std::basic_ostream& os, + /**< [IN/OUT] Stream to which thread %ID is written */ + Thread::ID id + /**< [IN] %Thread %ID to be written */ + ); + +} // namespace base +} // namespace embb + +#include + +#endif // EMBB_BASE_THREAD_H_ diff --git a/base_cpp/include/embb/base/thread_specific_storage.h b/base_cpp/include/embb/base/thread_specific_storage.h new file mode 100644 index 0000000..812fe42 --- /dev/null +++ b/base_cpp/include/embb/base/thread_specific_storage.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_THREAD_SPECIFIC_STORAGE_H_ +#define EMBB_BASE_THREAD_SPECIFIC_STORAGE_H_ + +#include +#include +#include + +namespace embb { +namespace base { + +namespace test { + /** + * Pre-declaration for friending. + */ + class ThreadSpecificStorageTest; +} + +/** + * \defgroup CPP_BASE_TSS Thread-Specific Storage + * + * %Thread specific storage. + * + * \ingroup CPP_BASE + */ + +/** + * Represents thread-specific storage (TSS). + * + * Provides for each thread a separate slot storing an object of the given type. + * + * \tparam Type Type of the objects + * \ingroup CPP_BASE_TSS + */ +template +class ThreadSpecificStorage { + public: + /** + * Creates the TSS and default initializes all slots. + * + * \throws NoMemoryException if not enough memory is available to allocate + * the TSS slots + * \memory Dynamically allocates embb::base::Thread::GetThreadsMaxCount() + * pointers and slots of the TSS type + * \notthreadsafe + */ + ThreadSpecificStorage(); + +#ifdef DOXYGEN + /** + * Creates the TSS and initializes all slots with up to four constructor + * arguments. + * + * The TSS objects are created by calling their constructor as follows: + * Type(initializer1, ...). + * + * \throws NoMemoryException if not enough memory is available to allocate + * the TSS slots + * \memory Dynamically allocates embb::base::Thread::GetThreadsMaxCount() + * pointers and slots of the TSS type + * \notthreadsafe + */ + template + explicit ThreadSpecificStorage( + Initializer1 initializer1, + /**< [IN] First argument for constructor\n ... */ + ... + ); +#else + /** + * See description of Doxygen variadic constructor above. + */ + template + explicit ThreadSpecificStorage(Initializer initializer); + + /** + * See description of Doxygen variadic constructor above. + */ + template + ThreadSpecificStorage(Initializer1 initializer1, Initializer2 initializer2); + + /** + * See description of Doxygen variadic constructor above. + */ + template + ThreadSpecificStorage(Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3); + + /** + * See description of Doxygen variadic constructor above. + */ + template + ThreadSpecificStorage(Initializer1 initializer1, Initializer2 initializer2, + Initializer3 initializer3, Initializer4 initializer4); +#endif // else defined(DOXYGEN) + + /** + * Destroys the TSS and frees allocated memory for the TSS slots. + */ + ~ThreadSpecificStorage(); + + /** + * Returns a reference to the TSS slot of the current thread. + * + * \return Reference to TSS slot + * + * \throws embb::base::ErrorException if the maximum number of threads has + * been exceeded + * \lockfree + * \see Get() const + */ + Type& Get(); + + /** + * Returns a const reference to the TSS slot of the current thread. + * + * \return Constant reference to TSS slot + * + * \throws embb::base::ErrorException if the maximum number of threads has + * been exceeded + * \lockfree + * \see Get() + */ + const Type& Get() const; + + private: + /** + * Common construction code. + */ + void Prepare(); + + /** + * Representation of TSS implemented in Base C. + */ + embb_tss_t rep_; + + /** + * Flags to indicate the usage of a TSS value. Are set on first access. + */ + bool* usage_flags_; + + /** + * To allow white-box tests. + */ + friend class test::ThreadSpecificStorageTest; +}; + +} // namespace base +} // namespace embb + +#include + +#endif /* EMBB_BASE_THREAD_SPECIFIC_STORAGE_H_ */ diff --git a/base_cpp/include/embb/base/time.h b/base_cpp/include/embb/base/time.h new file mode 100644 index 0000000..1a36470 --- /dev/null +++ b/base_cpp/include/embb/base/time.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, 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 EMBB_BASE_TIME_H_ +#define EMBB_BASE_TIME_H_ + +#include +#include + +namespace embb { +namespace base { + +/** + * Represents an absolute time point. + * + * \ingroup CPP_BASE_TIMEDURATION + */ +class Time { + public: + /** + * Constructs an instance representing the current point of time. + * + * \notthreadsafe + */ + Time(); + + /** + * Constructs an instance representing the current point of time plus + * \c duration. + * + * \notthreadsafe + * \see Duration + * \tparam Tick Type of tick of the Duration + */ + template + explicit Time( + const Duration& duration + /**< [IN] %Duration added to the current point of time. */ + ) : rep_() { + embb_time_in(&rep_, &(duration.rep_)); + } + + private: + /** + * Representation of the absolute time point. + */ + embb_time_t rep_; + + /** + * Needs to access the internal time representation. + */ + friend class ConditionVariable; +}; + +} // namespace base +} // namespace embb + +#endif /* EMBB_BASE_TIME_H_ */ diff --git a/base_cpp/src/condition_variable.cc b/base_cpp/src/condition_variable.cc new file mode 100644 index 0000000..b03de2c --- /dev/null +++ b/base_cpp/src/condition_variable.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 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 +#include + +namespace embb { +namespace base { + +ConditionVariable::ConditionVariable() : condition_var_() { + int status = embb_condition_init(&condition_var_); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable could not be initialized"); + } +} + +void ConditionVariable::NotifyOne() { + int status = embb_condition_notify_one(&condition_var_); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: NotifyOne failed"); + } +} + +void ConditionVariable::NotifyAll() { + int status = embb_condition_notify_all(&condition_var_); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: NotifyAll failed"); + } +} + +void ConditionVariable::Wait(UniqueLock& lock) { + int status = embb_condition_wait(&condition_var_, &(lock.mutex_->mutex_)); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: Wait failed"); + } +} + +bool ConditionVariable::WaitUntil(UniqueLock& lock, const Time& time) { + int status = embb_condition_wait_until( + &condition_var_, &(lock.mutex_->mutex_), &(time.rep_)); + if (status == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Condition variable: WaitUntil failed"); + } + if (status == EMBB_TIMEDOUT) return false; + return true; +} + +} // namespace base +} // namespace embb diff --git a/base_cpp/src/core_set.cc b/base_cpp/src/core_set.cc new file mode 100644 index 0000000..7aafca4 --- /dev/null +++ b/base_cpp/src/core_set.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include + +namespace embb { +namespace base { + +unsigned int CoreSet::CountAvailable() { + return embb_core_count_available(); +} + +CoreSet::CoreSet() : rep_() { + embb_core_set_init(&rep_, 0); +} + +CoreSet::CoreSet(bool value) : rep_() { + int value_converted = value == true ? 1 : 0; + embb_core_set_init(&rep_, value_converted); +} + +CoreSet::CoreSet(const CoreSet& to_copy) : rep_() { + embb_core_set_init(&rep_, 0); + embb_core_set_union(&rep_, &(to_copy.rep_)); +} + +CoreSet& CoreSet::operator=(const CoreSet& to_assign) { + embb_core_set_init(&rep_, 0); + embb_core_set_union(&rep_, &(to_assign.rep_)); + return *this; +} + +void CoreSet::Reset(bool value) { + int value_converted = value ? 1 : 0; + embb_core_set_init(&rep_, value_converted); +} + +void CoreSet::Add(unsigned int core) { + embb_core_set_add(&rep_, core); +} + +void CoreSet::Remove(unsigned int core) { + embb_core_set_remove(&rep_, core); +} + +bool CoreSet::IsContained(unsigned int core) const { + return embb_core_set_contains(&rep_, core) != 0; +} + +unsigned int CoreSet::Count() const { + return embb_core_set_count(&rep_); +} + +CoreSet CoreSet::operator&(const CoreSet& rhs) const { + CoreSet result(*this); + embb_core_set_intersection(&(result.rep_), &(rhs.rep_)); + return result; +} + +CoreSet CoreSet::operator|(const CoreSet& rhs) const { + CoreSet result(*this); + embb_core_set_union(&(result.rep_), &(rhs.rep_)); + return result; +} + +CoreSet& CoreSet::operator&=(const CoreSet& rhs) { + embb_core_set_intersection(&rep_, &(rhs.rep_)); + return *this; +} + +CoreSet& CoreSet::operator|=(const CoreSet& rhs) { + embb_core_set_union(&rep_, &(rhs.rep_)); + return *this; +} + +} // namespace base +} // namespace embb diff --git a/base_cpp/src/duration.cc b/base_cpp/src/duration.cc new file mode 100644 index 0000000..3ede886 --- /dev/null +++ b/base_cpp/src/duration.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { + +void Tick::CheckExceptions(int status, const char* msg) { + switch (status) { + case EMBB_SUCCESS: return; + case EMBB_OVERFLOW: EMBB_THROW(OverflowException, msg); + case EMBB_UNDERFLOW: EMBB_THROW(UnderflowException, msg); + default: EMBB_THROW(ErrorException, msg); + } +} + +int Seconds::Set(embb_duration_t& duration, unsigned long long ticks) { + return embb_duration_set_seconds(&duration, ticks); +} + +void Seconds::SetAndCheck(embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from seconds"); +} + +unsigned long long Seconds::Get(const embb_duration_t& duration) { + unsigned long long ticks = 0; + int status = embb_duration_as_seconds(&duration, &ticks); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long Seconds::Min() { + return 1; +} + +unsigned long long Seconds::Max() { + return EMBB_DURATION_MAX_SECONDS; +} + +int Milliseconds::Set(embb_duration_t& duration, unsigned long long ticks) { + return embb_duration_set_milliseconds(&duration, ticks); +} + +void Milliseconds::SetAndCheck( + embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from milliseconds"); +} + +unsigned long long Milliseconds::Get(const embb_duration_t& duration) { + unsigned long long ticks = 0; + int status = embb_duration_as_milliseconds(&duration, &ticks); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long Milliseconds::Min() { +#if EMBB_DURATION_MIN_NANOSECONDS > 1000000 + assert(EMBB_DURATION_MIN_NANOSECONDS % 1000000 == 0); + return EMBB_DURATION_MIN_NANOSECONDS / 1000000; +#endif + return 1; +} + +unsigned long long Milliseconds::Max() { +#if EMBB_DURATION_MAX_SECONDS < ULLONG_MAX / 1000 + return ULLONG_MAX; +#else + return EMBB_DURATION_MAX_SECONDS * 1000; +#endif +} + +int Microseconds::Set(embb_duration_t& duration, unsigned long long ticks) { + return embb_duration_set_microseconds(&duration, ticks); +} + +void Microseconds::SetAndCheck( + embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from microseconds"); +} + +unsigned long long Microseconds::Get(const embb_duration_t& duration) { + unsigned long long ticks = 0; + + int status = embb_duration_as_microseconds(&duration, &ticks); + + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long Microseconds::Min() { +#if EMBB_DURATION_MIN_NANOSECONDS > 1000 + assert(EMBB_DURATION_MIN_NANOSECONDS % 1000 == 0); + return EMBB_DURATION_MIN_NANOSECONDS / 1000; +#endif + return 1; +} + +unsigned long long Microseconds::Max() { +#if EMBB_DURATION_MAX_SECONDS < ULLONG_MAX / 1000000 + return ULLONG_MAX; +#else + return EMBB_DURATION_MAX_SECONDS * 1000000; +#endif +} + +int Nanoseconds::Set(embb_duration_t& duration, unsigned long long ticks) { + return embb_duration_set_nanoseconds(&duration, ticks); +} + +void Nanoseconds::SetAndCheck( + embb_duration_t& duration, unsigned long long ticks) { + int status = Set(duration, ticks); + CheckExceptions(status, "Setting duration from microseconds"); +} + +unsigned long long Nanoseconds::Get(const embb_duration_t& duration) { + unsigned long long ticks = 0; + int status = embb_duration_as_nanoseconds(&duration, &ticks); + assert(status == EMBB_SUCCESS); + EMBB_UNUSED_IN_RELEASE(status); + return ticks; +} + +unsigned long long Nanoseconds::Min() { + return EMBB_DURATION_MIN_NANOSECONDS; +} + +unsigned long long Nanoseconds::Max() { +#if EMBB_DURATION_MAX_SECONDS < ULLONG_MAX / 1000000000 + return ULLONG_MAX; +#else + return EMBB_DURATION_MAX_SECONDS * 1000000000; +#endif +} + +} // namespace base +} // namespace embb diff --git a/base_cpp/src/function.cc b/base_cpp/src/function.cc new file mode 100644 index 0000000..f48eaf9 --- /dev/null +++ b/base_cpp/src/function.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014, 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 + +namespace embb { +namespace base { + +Placeholder::Arg_1 Placeholder::_1; + +} // namespace base +} // namespace embb diff --git a/base_cpp/src/functionN.lua b/base_cpp/src/functionN.lua new file mode 100644 index 0000000..15811c9 --- /dev/null +++ b/base_cpp/src/functionN.lua @@ -0,0 +1,527 @@ +-- Copyright (c) 2014, 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. + +max_args = 5 + +function make_functionN(arg_cnt) + local file = io.open("function"..arg_cnt..".h", "w") + + local include_guard = "EMBB_BASE_INTERNAL_FUNCTION"..arg_cnt.."_H_" + + local templ_param = "" + local param_list_a = "" + local param_list_c = "" + local param_list = "" + for i = 1, arg_cnt, 1 do + if i > 1 then + templ_param = templ_param..", " + param_list_a = param_list_a..", " + param_list_c = param_list_c..", " + param_list = param_list..", " + end + templ_param = templ_param.."typename T"..i + param_list_a = param_list_a.."T"..i + param_list_c = param_list_c.."T"..i.." p"..i + param_list = param_list.."p"..i + end + + local param_list_a_nil = param_list_a + for i = arg_cnt+1, max_args, 1 do + if i > 1 then + param_list_a_nil = param_list_a_nil..", " + end + param_list_a_nil = param_list_a_nil.."Nil" + end + + local CR_templ + local C_templ + local R_templ + local void_templ + local R_param_list_a + local void_param_list_a + if arg_cnt > 0 then + CR_templ = "template " + C_templ = "template " + R_templ = "template " + void_templ = "template <"..templ_param..">" + R_param_list_a = "R, "..param_list_a + void_param_list_a = "void, "..param_list_a + else + CR_templ = "template " + C_templ = "template " + R_templ = "template " + void_templ = "template <>" + R_param_list_a = "R" + void_param_list_a = "void" + end + + local FunctionN = "Function"..arg_cnt + local FunctionPointerN = "FunctionPointer"..arg_cnt + local MemberFunctionPointerN = "MemberFunctionPointer"..arg_cnt + local FunctorWrapperN = "FunctorWrapper"..arg_cnt + + + file:write("#ifndef "..include_guard.."\n") + file:write("#define "..include_guard.."\n") + file:write("\n") + + file:write("#include \n") + file:write("#include \n") + file:write("\n") + file:write("#include \n") + file:write("#include \n") + file:write("#include \n") + file:write("#include \n") + if arg_cnt > 0 then + file:write("#include \n") + end + if arg_cnt > 1 then + file:write("#include \n") + end + file:write("\n") + + file:write("namespace embb {\n") + file:write("namespace base {\n") + file:write("\n") + file:write("namespace internal {\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("class "..FunctionN.." {\n") + file:write(" public:\n") + file:write(" virtual ~"..FunctionN.."() {}\n") + file:write(" virtual R operator () ("..param_list_a..") = 0;\n") + file:write(" virtual void CopyTo(void* dst) = 0;\n") + file:write("};\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("class "..FunctionPointerN.."\n : public "..FunctionN.."<"..R_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef R(*FuncPtrType)("..param_list_a..");\n") + file:write(" explicit "..FunctionPointerN.."(FuncPtrType func) : function_(func) {}\n") + file:write(" virtual R operator () ("..param_list_c..") {\n") + file:write(" return function_("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctionPointerN.."(function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" FuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(void_templ.."\n") + file:write("class "..FunctionPointerN.."<"..void_param_list_a..">\n : public "..FunctionN.."<"..void_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef void(*FuncPtrType)("..param_list_a..");\n") + file:write(" explicit "..FunctionPointerN.."(FuncPtrType func) : function_(func) {}\n") + file:write(" virtual void operator () ("..param_list_c..") {\n") + file:write(" function_("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctionPointerN.."(function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" FuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("class "..MemberFunctionPointerN.."\n : public "..FunctionN.."<"..R_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef R(C::*MemFuncPtrType)("..param_list_a..");\n") + file:write(" typedef C & ClassRefType;\n") + file:write(" "..MemberFunctionPointerN.."(ClassRefType obj, MemFuncPtrType func)\n : object_(obj), function_(func) {}\n") + file:write(" explicit "..MemberFunctionPointerN.."(ClassRefType obj)\n : object_(obj), function_(&C::operator()) {}\n") + file:write(" void operator = ("..MemberFunctionPointerN.." const & memfunc) {\n") + file:write(" object_ = memfunc.object_;\n") + file:write(" function_ = memfunc.function_;\n") + file:write(" }\n") + file:write(" virtual R operator () ("..param_list_c..") {\n") + file:write(" return (object_.*function_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..MemberFunctionPointerN.."(object_, function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" ClassRefType object_;\n") + file:write(" MemFuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(C_templ.."\n") + file:write("class "..MemberFunctionPointerN.."\n : public "..FunctionN.."<"..void_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" typedef void(C::*MemFuncPtrType)("..param_list_a..");\n") + file:write(" typedef C & ClassRefType;\n") + file:write(" "..MemberFunctionPointerN.."(ClassRefType obj, MemFuncPtrType func)\n : object_(obj), function_(func) {}\n") + file:write(" explicit "..MemberFunctionPointerN.."(ClassRefType obj)\n : object_(obj), function_(&C::operator()) {}\n") + file:write(" void operator = ("..MemberFunctionPointerN.." const & memfunc) {\n") + file:write(" object_ = memfunc.object_;\n") + file:write(" function_ = memfunc.function_;\n") + file:write(" }\n") + file:write(" virtual void operator () ("..param_list_c..") {\n") + file:write(" (object_.*function_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..MemberFunctionPointerN.."(object_, function_);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" ClassRefType object_;\n") + file:write(" MemFuncPtrType function_;\n") + file:write("};\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("class "..FunctorWrapperN.."\n : public "..FunctionN.."<"..R_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" "..FunctorWrapperN.."() : object_(NULL), ref_count_(NULL) {}\n") + file:write(" explicit "..FunctorWrapperN.."(C const & obj) {\n") + file:write(" object_ = Allocation::New(obj);\n") + file:write(" ref_count_ = Allocation::New >(1);\n") + file:write(" }\n") + file:write(" explicit "..FunctorWrapperN.."("..FunctorWrapperN.." const & other) {\n") + file:write(" object_ = other.object_;\n") + file:write(" ref_count_ = other.ref_count_;\n") + file:write(" ++*ref_count_;\n") + file:write(" }\n") + file:write(" virtual ~"..FunctorWrapperN.."() {\n") + file:write(" if (0 == --*ref_count_) {\n") + file:write(" Allocation::Delete(ref_count_);\n") + file:write(" Allocation::Delete(object_);\n") + file:write(" }\n") + file:write(" }\n") + file:write(" virtual R operator () ("..param_list_c..") {\n") + file:write(" return (*object_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctorWrapperN.."(*this);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" C * object_;\n") + file:write(" Atomic * ref_count_;\n") + file:write("};\n") + file:write("\n") + + file:write(C_templ.."\n") + file:write("class "..FunctorWrapperN.."\n : public "..FunctionN.."<"..void_param_list_a.."> {\n") + file:write(" public:\n") + file:write(" "..FunctorWrapperN.."() : object_(NULL), ref_count_(NULL) {}\n") + file:write(" explicit "..FunctorWrapperN.."(C const & obj) {\n") + file:write(" object_ = Allocation::New(obj);\n") + file:write(" ref_count_ = Allocation::New >(1);\n") + file:write(" }\n") + file:write(" explicit "..FunctorWrapperN.."("..FunctorWrapperN.." const & other) {\n") + file:write(" object_ = other.object_;\n") + file:write(" ref_count_ = other.ref_count_;\n") + file:write(" ++*ref_count_;\n") + file:write(" }\n") + file:write(" virtual ~"..FunctorWrapperN.."() {\n") + file:write(" if (0 == --*ref_count_) {\n") + file:write(" Allocation::Delete(ref_count_);\n") + file:write(" Allocation::Delete(object_);\n") + file:write(" }\n") + file:write(" }\n") + file:write(" virtual void operator () ("..param_list_c..") {\n") + file:write(" (*object_)("..param_list..");\n") + file:write(" }\n") + file:write(" virtual void CopyTo(void* dst) {\n") + file:write(" new(dst)"..FunctorWrapperN.."(*this);\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" C * object_;\n") + file:write(" Atomic * ref_count_;\n") + file:write("};\n") + file:write("\n") + + -- bind to function0 + if arg_cnt > 0 then + local param_list_ = "" + for i = 1, arg_cnt, 1 do + if i > 1 then + param_list_ = param_list_..", " + end + param_list_ = param_list_.."p"..i.."_" + end + file:write("// bind to function0\n") + file:write(R_templ.."\n") + file:write("class Bound"..arg_cnt.."Functor0 {\n") + file:write(" public:\n") + file:write(" Bound"..arg_cnt.."Functor0(Function<"..R_param_list_a.."> func,\n "..param_list_c..") : function_(func)\n ") + for i = 1, arg_cnt, 1 do + file:write(", p"..i.."_(p"..i..")") + end + file:write(" {}\n") + file:write(" Bound"..arg_cnt.."Functor0(Bound"..arg_cnt.."Functor0 const & func) : function_(func.function_)\n ") + for i = 1, arg_cnt, 1 do + file:write(", p"..i.."_(func.p"..i.."_)") + if i == 4 then + file:write("\n ") + end + end + file:write(" {}\n") + file:write(" R operator() () {\n") + file:write(" return function_("..param_list_..");\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" Function<"..R_param_list_a.."> function_;\n") + for i = 1, arg_cnt, 1 do + file:write(" T"..i.." p"..i.."_;\n") + end + file:write("};\n") + file:write("\n") + end + + -- bind to function1 + if arg_cnt > 1 then + file:write("// bind to function1\n") + for arg = 1, arg_cnt, 1 do + local param_list_ = "" + local param_list_c_ = "" + for i = 1, arg_cnt, 1 do + if i > 1 then + param_list_ = param_list_..", " + end + param_list_ = param_list_.."p"..i + if i ~= arg then + param_list_ = param_list_.."_" + param_list_c_ = param_list_c_..", T"..i.." p"..i + end + end + file:write(R_templ.."\n") + file:write("class Bound"..arg_cnt.."Functor1_Arg"..arg.." {\n") + file:write(" public:\n") + file:write(" Bound"..arg_cnt.."Functor1_Arg"..arg.."(Function<"..R_param_list_a.."> func\n "..param_list_c_..") : function_(func)\n ") + for i = 1, arg_cnt, 1 do + if i ~= arg then + file:write(", p"..i.."_(p"..i..")") + end + end + file:write(" {}\n") + file:write(" Bound"..arg_cnt.."Functor1_Arg"..arg.."(Bound"..arg_cnt.."Functor1_Arg"..arg.." const & func)\n : function_(func.function_)\n ") + for i = 1, arg_cnt, 1 do + if i ~= arg then + file:write(", p"..i.."_(func.p"..i.."_)") + end + end + file:write(" {}\n") + file:write(" R operator() (T"..arg.." p"..arg..") {\n") + file:write(" return function_("..param_list_..");\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" Function<"..R_param_list_a.."> function_;\n") + for i = 1, arg_cnt, 1 do + if i ~= arg then + file:write(" T"..i.." p"..i.."_;\n") + end + end + file:write("};\n") + file:write("\n") + end + end + + file:write("} // namespace internal\n") + file:write("\n") + + file:write("\n") + file:write("using embb::base::internal::Nil;\n") + file:write("\n") + + file:write(R_templ.."\n") + if arg_cnt < max_args then + file:write("class Function {\n") + else + file:write("class Function {\n") + end + file:write(" public:\n") + file:write(" typedef internal::"..FunctionN.."<"..R_param_list_a.."> * FuncPtrType;\n") + file:write(" Function() : function_(NULL) {}\n") + file:write(" template \n") + file:write(" explicit Function(C const & obj) {\n") + file:write(" function_ = new(storage_)\n internal::"..FunctorWrapperN.."(obj);\n") + file:write(" }\n") + file:write(" Function(Function const & func) {\n") + file:write(" func.function_->CopyTo(&storage_[0]);\n") + file:write(" function_ = reinterpret_cast(&storage_[0]);\n") + file:write(" }\n") + file:write(" ~Function() {\n") + file:write(" Free();\n") + file:write(" }\n") + file:write(" void operator = (R(*func)("..param_list_a..")) {\n") + file:write(" Free();\n") + file:write(" function_ = new(storage_)\n internal::"..FunctionPointerN.."<"..R_param_list_a..">(func);\n") + file:write(" }\n") + file:write(" void operator = (Function & func) {\n") + file:write(" Free();\n") + file:write(" func.function_->CopyTo(&storage_[0]);\n") + file:write(" function_ = reinterpret_cast(&storage_[0]);\n") + file:write(" }\n") + file:write(" template \n") + file:write(" void operator = (C const & obj) {\n") + file:write(" Free();\n") + file:write(" function_ = new(storage_)\n internal::"..FunctorWrapperN.."(obj);\n") + file:write(" }\n") + file:write(" explicit Function(R(*func)("..param_list_a..")) {\n") + file:write(" function_ = new(storage_)\n internal::"..FunctionPointerN.."<"..R_param_list_a..">(func);\n") + file:write(" }\n") + file:write(" template \n") + file:write(" Function(C & obj, R(C::*func)("..param_list_a..")) {\n") + file:write(" function_ = new(storage_)\n internal::"..MemberFunctionPointerN.."(obj, func);\n") + file:write(" }\n") + file:write(" R operator () ("..param_list_c..") {\n") + file:write(" return (*function_)("..param_list..");\n") + file:write(" }\n") + file:write("\n") + file:write(" private:\n") + file:write(" char storage_[sizeof(\n internal::"..MemberFunctionPointerN..")];\n") + file:write(" FuncPtrType function_;\n") + file:write(" void Free() {\n") + file:write(" if (NULL != function_) {\n") + file:write(" function_->~"..FunctionN.."();\n") + file:write(" function_ = NULL;\n") + file:write(" }\n") + file:write(" }\n") + file:write("};\n") + file:write("\n") + + -- wrap member function + file:write("// wrap member function\n") + file:write(CR_templ.."\n") + file:write("Function<"..R_param_list_a.."> MakeFunction(C & obj,\n R(C::*func)("..param_list_a..")) {\n") + file:write(" return Function<"..R_param_list_a..">(obj, func);\n") + file:write("}\n") + file:write("\n") + + -- wrap function pointer + file:write("// wrap function pointer\n") + file:write(R_templ.."\n") + file:write("Function<"..R_param_list_a.."> MakeFunction(\n R(*func)("..param_list_a..")) {\n") + file:write(" return Function<"..R_param_list_a..">(func);\n") + file:write("}\n") + file:write("\n") + + -- bind to function0 + if arg_cnt > 0 then + file:write("// bind to function0\n") + + file:write(R_templ.."\n") + file:write("Function Bind(Function<"..R_param_list_a.."> func,\n "..param_list_c..") {\n") + file:write(" return Function(\n internal::Bound"..arg_cnt.."Functor0<"..R_param_list_a..">(\n func, "..param_list.."));\n") + file:write("}\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("Function Bind(R(*func)("..param_list_a.."),\n "..param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("Function Bind(C & obj, R(C::*func)("..param_list_a.."),\n "..param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(obj, func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + end + + -- bind to function1 + if arg_cnt > 1 then + file:write("// bind to function1\n") + for arg = 1, arg_cnt, 1 do + local target_arg = "T"..arg + local bound_param_list_a = "" + local bound_param_list_c = "" + local bound_param_list = "" + for i = 1, arg_cnt, 1 do + if i == arg then + bound_param_list_c = bound_param_list_c..", Placeholder::Arg_1 p"..i + bound_param_list_a = bound_param_list_a..", Placeholder::Arg_1" + else + bound_param_list_c = bound_param_list_c..", T"..i.." p"..i + bound_param_list_a = bound_param_list_a..", T"..i.." p"..i + bound_param_list = bound_param_list..", p"..i + end + end + + file:write(R_templ.."\n") + file:write("Function Bind(Function<"..R_param_list_a.."> func\n "..bound_param_list_a..") {\n") + file:write(" return Function(\n internal::Bound"..arg_cnt.."Functor1_Arg"..arg.."<"..R_param_list_a..">(func\n "..bound_param_list.."));\n") + file:write("}\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("Function Bind(R(*func)("..param_list_a..")\n "..bound_param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + + file:write(CR_templ.."\n") + file:write("Function Bind(C & obj, R(C::*func)("..param_list_a..")\n "..bound_param_list_c..") {\n") + file:write(" return Bind(Function<"..R_param_list_a..">(obj, func), "..param_list..");\n") + file:write("}\n") + file:write("\n") + end + end + + -- bind to functionN + file:write("// bind to "..FunctionN.."\n") + + local bound_param_list_c = "" + for i = 1, arg_cnt, 1 do + bound_param_list_c = bound_param_list_c..",\n Placeholder::Arg_"..i.." p"..i + end + + file:write(CR_templ.."\n") + file:write("Function<"..R_param_list_a.."> Bind(\n C & obj,\n R(C::*func)("..param_list_a..")"..bound_param_list_c..") {\n") + file:write(" return Function<"..R_param_list_a..">(obj, func);\n") + file:write("}\n") + file:write("\n") + + file:write(R_templ.."\n") + file:write("Function<"..R_param_list_a.."> Bind(\n R(*func)("..param_list_a..")"..bound_param_list_c..") {\n") + file:write(" return Function<"..R_param_list_a..">(func);\n") + file:write("}\n") + file:write("\n") + + file:write("} // namespace base\n") + file:write("} // namespace embb\n") + file:write("\n") + + file:write("#endif // "..include_guard.."\n") + + file:flush() + file:close() +end + +for i = 0, max_args, 1 do + make_functionN(i) +end diff --git a/base_cpp/src/memory_allocation.cc b/base_cpp/src/memory_allocation.cc new file mode 100644 index 0000000..69337b1 --- /dev/null +++ b/base_cpp/src/memory_allocation.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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 +#include + +size_t embb::base::Allocation::AllocatedBytes() { + return embb_get_bytes_allocated(); +} + +void* embb::base::Allocation::Allocate(size_t size) { + void* alloc; + if ((alloc = embb_alloc(size)) == 0) { + EMBB_THROW(embb::base::NoMemoryException, "Could not allocate memory"); + } + return alloc; +} + +void embb::base::Allocation::Free(void * ptr) { + embb_free(ptr); +} + +void* embb::base::Allocation::AllocateAligned(size_t alignment, size_t size) { + void* alloc; + if ((alloc = embb_alloc_aligned(alignment, size)) == 0) { + EMBB_THROW(embb::base::NoMemoryException, "Could not allocate memory"); + } + return alloc; +} + +void embb::base::Allocation::FreeAligned(void * ptr) { + embb_free_aligned(ptr); +} + +void* embb::base::Allocation::AllocateCacheAligned(size_t size) { + void* alloc; + if ((alloc = embb_alloc_cache_aligned(size)) == 0) { + EMBB_THROW(embb::base::NoMemoryException, "Could not allocate memory"); + } + return alloc; +} diff --git a/base_cpp/src/mutex.cc b/base_cpp/src/mutex.cc new file mode 100644 index 0000000..7fcffde --- /dev/null +++ b/base_cpp/src/mutex.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014, 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 +#include + +namespace embb { +namespace base { +namespace internal { + +MutexBase::MutexBase(int mutex_type) : mutex_() { + embb_mutex_init(&mutex_, mutex_type); +} + +MutexBase::~MutexBase() { + embb_mutex_destroy(&mutex_); +} + +void MutexBase::Lock() { + embb_mutex_lock(&mutex_); +} + +bool MutexBase::TryLock() { + int result = embb_mutex_try_lock(&mutex_); + return result == EMBB_SUCCESS; +} + +void MutexBase::Unlock() { + embb_mutex_unlock(&mutex_); +} + +} // namespace internal + +Mutex::Mutex() : MutexBase(EMBB_MUTEX_PLAIN) { +} + +RecursiveMutex::RecursiveMutex() : MutexBase(EMBB_MUTEX_RECURSIVE) { +} + +} // namespace base +} // namespace embb + + + diff --git a/base_cpp/src/thread.cc b/base_cpp/src/thread.cc new file mode 100644 index 0000000..c5a125b --- /dev/null +++ b/base_cpp/src/thread.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { + +unsigned int Thread::GetThreadsMaxCount() { + return embb_thread_get_max_count(); +} + +void Thread::SetThreadsMaxCount(unsigned int max_count) { + embb_thread_set_max_count(max_count); +} + +Thread::ID Thread::CurrentGetID() { +#if defined EMBB_THREADING_WINTHREADS + return Thread::ID(GetCurrentThreadId()); +#else + return Thread::ID(embb_thread_current().embb_internal_handle); +#endif +} + +void Thread::CurrentYield() { + embb_thread_yield(); +} + +void Thread::Join() { + if (embb_thread_join(&rep_, NULL) == EMBB_ERROR) { + EMBB_THROW(ErrorException, "Could not join thread."); + } +} + +Thread::ID Thread::GetID() { +#if defined EMBB_THREADING_WINTHREADS + return Thread::ID(GetCurrentThreadId()); +#elif defined EMBB_THREADING_POSIXTHREADS + return Thread::ID(rep_.embb_internal_handle); +#endif +} + +bool operator==(Thread::ID lhs, Thread::ID rhs) { +#if defined EMBB_THREADING_WINTHREADS + return lhs.id_ == rhs.id_; +#else + return pthread_equal(lhs.id_, rhs.id_) != 0; +#endif +} + +bool operator!=(Thread::ID lhs, Thread::ID rhs) { +#if defined EMBB_THREADING_WINTHREADS + return lhs.id_ != rhs.id_; +#else + return pthread_equal(lhs.id_, rhs.id_) == 0; +#endif +} + +} // namespace base +} // namespace embb diff --git a/base_cpp/src/time.cc b/base_cpp/src/time.cc new file mode 100644 index 0000000..d4fb449 --- /dev/null +++ b/base_cpp/src/time.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { + +Time::Time() : rep_() { + embb_time_now(&rep_); +} + +} // namespace base +} // namespace embb + + diff --git a/base_cpp/test/atomic_test.cc b/base_cpp/test/atomic_test.cc new file mode 100644 index 0000000..fc0445a --- /dev/null +++ b/base_cpp/test/atomic_test.cc @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +size_t AtomicTest::numIterations_; + +void AtomicTest::TestStressLoadStore::barrier(int thread) { + const int my_sense(thread_sense[thread]); + if (--count == 0) { + const int tmp(z.Load()); + PT_EXPECT(tmp == 1 || tmp == 2); + x.Store(0); + y.Store(0); + z.Store(0); + count.Store(4); + sense.Store(my_sense); + } else { + while (sense.Load() != my_sense) {} + } + thread_sense[thread] = !my_sense; +} + +void AtomicTest::TestStressLoadStore::write_x() { + x.Store(1); + barrier(0); +} + +void AtomicTest::TestStressLoadStore::write_y() { + y.Store(1); + barrier(1); +} + +void AtomicTest::TestStressLoadStore::read_x_then_y() { + while (!x.Load()) {} + if (y.Load()) + z++; + barrier(2); +} + +void AtomicTest::TestStressLoadStore::read_y_then_x() { + while (!y.Load()) {} + if (x.Load()) + z++; + barrier(3); +} + +void AtomicTest::TestStressLoadStore::Init() { +} + +void AtomicTest::TestStressLoadStore::CheckAndDestroy() { +} + +AtomicTest::TestStressLoadStore::TestStressLoadStore( + size_t number_threads, size_t number_iterations) + : TestUnit("Load/Store Stress test for Atomics"), x(0), y(0), z(0), + count(4), sense(0) { + PT_ASSERT(number_threads == 1); + thread_sense[0] = 1; + thread_sense[1] = 1; + thread_sense[2] = 1; + thread_sense[3] = 1; + Pre(&TestStressLoadStore::Init, this); + Add(&TestStressLoadStore::write_x, this, + number_threads, number_iterations); + Add(&TestStressLoadStore::write_y, this, + number_threads, number_iterations); + Add(&TestStressLoadStore::read_x_then_y, this, + number_threads, number_iterations); + Add(&TestStressLoadStore::read_y_then_x, this, + number_threads, number_iterations); + Post(&TestStressLoadStore::CheckAndDestroy, this); +} + +AtomicTest::TestStressProduceConsume::TestStressProduceConsume( + size_t number_threads, size_t number_iterations) + : TestUnit("Produce/Consume Stress test for Atomics"), flag(0), + counter_producer(0), counter_consumer(0) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressProduceConsume::Init, this); + + Add(&TestStressProduceConsume::produce, this, + number_threads, number_iterations); + Add(&TestStressProduceConsume::consume, this, + number_threads, number_iterations); + + Post(&TestStressProduceConsume::CheckAndDestroy, this); +} + +void AtomicTest::TestStressProduceConsume::produce() { + counter_consumer++; + while (flag) {} + prod_cons_value = static_cast(counter_consumer); + while (flag.Swap(true)) {} +} + +void AtomicTest::TestStressProduceConsume::consume() { + counter_producer++; + while (flag == 0) {} + PT_EXPECT(prod_cons_value == static_cast(counter_producer)); + flag = 0; +} + +void AtomicTest::TestStressProduceConsume::Init() { +} + +void AtomicTest::TestStressProduceConsume::CheckAndDestroy() { +} + +AtomicTest::TestStressIncrementDecrement::TestStressIncrementDecrement( + size_t number_threads, size_t number_iterations) + : TestUnit("Increment/Decrement Stress test for Atomics"), inc_dec_value(0) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressIncrementDecrement::Init, this); + + Add(&TestStressIncrementDecrement::increment, this, + number_threads, number_iterations); + Add(&TestStressIncrementDecrement::decrement, this, + number_threads, number_iterations); + + Post(&TestStressIncrementDecrement::CheckAndDestroy, this); +} + +void AtomicTest::TestStressIncrementDecrement::decrement() { + inc_dec_value--; +} + +void AtomicTest::TestStressIncrementDecrement::increment() { + inc_dec_value++; +} + +void AtomicTest::TestStressIncrementDecrement::Init() { +} + +void AtomicTest::TestStressIncrementDecrement::CheckAndDestroy() { + // Increment and decrement operations must neutralize each other + PT_EXPECT(inc_dec_value == 0); +} + + + +AtomicTest::TestStressSwap::TestStressSwap( + size_t number_threads, size_t number_iterations) + : TestUnit("Swap Stress test for Atomics"), swap1_counter(1) + , swap2_counter(2) { + bitsets[0] = std::bitset(); + bitsets[1] = std::bitset(); + bitsets[2] = std::bitset(); + + PT_ASSERT(number_threads == 1); + Pre(&TestStressSwap::Init, this); + + Add(&TestStressSwap::swap1, this, + number_threads, number_iterations); + Add(&TestStressSwap::swap2, this, + number_threads, number_iterations); + + Post(&TestStressSwap::CheckAndDestroy, this); +} + +void AtomicTest::TestStressSwap::Init() { +} + +void AtomicTest::TestStressSwap::CheckAndDestroy() { + // Each element is read at most once + bitsets[2] = bitsets[0]; + bitsets[2] &= bitsets[1]; + PT_EXPECT(bitsets[2].none()); + bitsets[2] = bitsets[0]; + bitsets[2] |= bitsets[1]; + // All elements except one are read (push, pop, push and so on... the last + // push is not read), however this must not always be the same element. + PT_EXPECT(bitsets[2].count() == + static_cast(AtomicTest::GetNumberOfIterations() * 2)); +} + +void AtomicTest::TestStressSwap::swap1() { + const int j(swap_value.Swap(swap1_counter)); + PT_EXPECT(!(bitsets[0].test(static_cast(j)))); + bitsets[0].set(static_cast(j)); + swap1_counter += 2; +} + +void AtomicTest::TestStressSwap::swap2() { + const int j(swap_value.Swap(swap2_counter)); + PT_EXPECT(!(bitsets[1].test(static_cast(j)))); + bitsets[1].set(static_cast(j)); + swap2_counter += 2; +} + +AtomicTest::TestStressCompareAndSwap::TestStressCompareAndSwap( + size_t number_threads, size_t number_iterations) + : TestUnit("Compare and Swap Stress test for Atomics"), swap1_counter(1) + , swap2_counter(2) { + PT_ASSERT(number_threads == 1); + Pre(&TestStressCompareAndSwap::Init, this); + + Add(&TestStressCompareAndSwap::compare_and_swap1, this, + number_threads, number_iterations); + Add(&TestStressCompareAndSwap::compare_and_swap2, this, + number_threads, number_iterations); + + Post(&TestStressCompareAndSwap::CheckAndDestroy, this); +} + +void AtomicTest::TestStressCompareAndSwap::Init() { +} + +void AtomicTest::TestStressCompareAndSwap::CheckAndDestroy() { + // Each element is read at most once + bitsets[2] = bitsets[0]; + bitsets[2] &= bitsets[1]; + PT_EXPECT(bitsets[2].none()); + bitsets[2] = bitsets[0]; + bitsets[2] |= bitsets[1]; + // All elements except one are read (push, pop, push and so on... the last + // push is not read), however this must not always be the same element. + PT_EXPECT(bitsets[2].count() == + static_cast(AtomicTest::GetNumberOfIterations() * 2)); +} + +void AtomicTest::TestStressCompareAndSwap::compare_and_swap1() { + int j(0); + while (!swap_value.CompareAndSwap(j, swap1_counter)) {} + PT_EXPECT(!(bitsets[0].test(static_cast(j)))); + bitsets[0].set(static_cast(j)); + swap1_counter += 2; +} + +void AtomicTest::TestStressCompareAndSwap::compare_and_swap2() { + int j(0); + while (!swap_value.CompareAndSwap(j, swap2_counter)) {} + PT_EXPECT(!(bitsets[1].test(static_cast(j)))); + bitsets[1].set(static_cast(j)); + swap2_counter += 2; +} + + + +AtomicTest::AtomicTest() { + numIterations_ = partest::TestSuite::GetDefaultNumIterations(); + PT_ASSERT_LT_MSG(numIterations_, + static_cast(ATOMIC_TESTS_ITERATIONS), "Maximum allowed iterations"); + CreateUnit("BasicTestsSingleThreaded") + .Add(&AtomicTest::BasicTests, this); + CreateUnit(static_cast(1), numIterations_); + CreateUnit(static_cast(1), numIterations_); + CreateUnit(static_cast(1), + numIterations_); + CreateUnit(static_cast(1), numIterations_); + CreateUnit(static_cast(1), numIterations_); +} + +typedef enum { RED, GREEN, BLUE } colors_t; + +void AtomicTest::BasicTests() { + //embb::base::Atomic b; // Boolean + embb::base::Atomic c; // Enumeration + embb::base::Atomic v; // Void pointer + embb::base::Atomic i; // Integer + embb::base::Atomic n; // Non-void pointer + + //template specializations + //PT_EXPECT(!b.IsArithmetic() && !b.IsInteger() && !b.IsPointer()); + PT_EXPECT(!c.IsArithmetic() && !c.IsInteger() && !c.IsPointer()); + PT_EXPECT(!v.IsArithmetic() && !v.IsInteger() && !v.IsPointer()); + PT_EXPECT(i.IsArithmetic() && i.IsInteger() && !i.IsPointer()); + PT_EXPECT(n.IsArithmetic() && !n.IsInteger() && n.IsPointer()); + + + // Constructors + PT_EXPECT(c == RED); + colors_t d(GREEN); + PT_EXPECT(d == GREEN); + // Assignment + PT_EXPECT((c = GREEN) == GREEN); + PT_EXPECT(c == GREEN); + // Swap + PT_EXPECT(c.Swap(BLUE) == GREEN); + PT_EXPECT(c == BLUE); + // Compare-and-swap + + d = RED; + PT_EXPECT(!c.CompareAndSwap(d, GREEN)); + PT_EXPECT(d == BLUE); + PT_EXPECT(c.CompareAndSwap(d, GREEN)); + PT_EXPECT(c == GREEN); + + //Arithmetic opertions... + PT_EXPECT(i == 0); + // Fetch-and-add + PT_EXPECT(i.FetchAndAdd(10) == 0); + PT_EXPECT(i == 10); + // Fetch-and-sub + PT_EXPECT(i.FetchAndSub(5) == 10); + PT_EXPECT(i == 5); + // Increment (postfix) + PT_EXPECT(i++ == 5); + PT_EXPECT(i == 6); + // Decrement (postfix) + PT_EXPECT(i-- == 6); + PT_EXPECT(i == 5); + // Increment (prefix) + PT_EXPECT(++i == 6); + PT_EXPECT(i == 6); + // Decrement (prefix) + PT_EXPECT(--i == 5); + PT_EXPECT(i == 5); + // Addition + PT_EXPECT((i += 10) == 15); + PT_EXPECT(i == 15); + // Subtraction + PT_EXPECT((i -= 10) == 5); + PT_EXPECT(i == 5); + + //Boolean operations... + // And + i = 0; + i &= 0; + PT_EXPECT(i == 0); + i &= 1; + PT_EXPECT(i == 0); + i = 1; + i &= 1; + PT_EXPECT(i == 1); + // Or + i = 1; + i |= 0; + PT_EXPECT(i == 1); + i |= 1; + PT_EXPECT(i == 1); + i = 0; + i |= 0; + PT_EXPECT(i == 0); + // Xor + i = 0; + i ^= 0; + PT_EXPECT(i == 0); + i ^= 1; + PT_EXPECT(i == 1); + + //Pointers...; + n = NULL; + // Stride + PT_EXPECT((uintptr_t)++n == sizeof(int)); + // Dereferencing + n = new int(0x13579BDF); + PT_EXPECT(*n == 0x13579BDF); + delete n; + + // Scalar values + embb::base::Atomic *j = new embb::base::Atomic(); + PT_EXPECT(*j == 0); + int *k = new int(0); + PT_EXPECT(j->CompareAndSwap(*k, 0x13579BDF)); + PT_EXPECT(*j == 0x13579BDF); + delete j; + delete k; +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_cpp/test/atomic_test.h b/base_cpp/test/atomic_test.h new file mode 100644 index 0000000..4e7d74f --- /dev/null +++ b/base_cpp/test/atomic_test.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_ATOMIC_TEST_H_ +#define BASE_CPP_TEST_ATOMIC_TEST_H_ + +#include +#include +#include +#include + +//we need that statically, otherwise we cannot use bitset in test +#define ATOMIC_TESTS_ITERATIONS 10000 + +namespace embb { +namespace base { +namespace test { + +class AtomicTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + AtomicTest(); + + static size_t GetNumberOfIterations() { + return numIterations_; + } + + private: + static size_t numIterations_; + void BasicTests(); + + + class TestStressLoadStore : public partest::TestUnit { + public: + TestStressLoadStore(size_t number_threads, size_t number_iterations); + + private: + void barrier(int thread); + void write_x(); + void write_y(); + void read_x_then_y(); + void read_y_then_x(); + + embb::base::Atomic x, y; + embb::base::Atomic z; + + // A sense-reversing barrier + // See M. Herlihy and N. Shavit. The Art of Multiprocessor Programming. + // Morgan Kaufmann, 2008. + + embb::base::Atomic count; + embb::base::Atomic sense; + + // Do not use a std::vector here, since std::vector may pack + // multiple elements into a single word. As a result, they cannnot be + // modified independently by multiple threads. + bool thread_sense[4]; + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressProduceConsume : public partest::TestUnit { + public: + TestStressProduceConsume(size_t number_threads, size_t number_iterations); + + private: + size_t prod_cons_value; + embb::base::Atomic flag; + int counter_producer; + int counter_consumer; + + void produce(); + void consume(); + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressIncrementDecrement : public partest::TestUnit { + public: + TestStressIncrementDecrement(size_t number_threads, size_t number_iterations); + + private: + embb::base::Atomic inc_dec_value; + + void increment(); + void decrement(); + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressSwap : public partest::TestUnit { + public: + TestStressSwap(size_t number_threads, size_t number_iterations); + + private: + std::bitset bitsets[3]; + embb::base::Atomic swap_value; + + int swap1_counter; + int swap2_counter; + + void swap1(); + void swap2(); + + void Init(); + void CheckAndDestroy(); + }; + + class TestStressCompareAndSwap : public partest::TestUnit { + public: + TestStressCompareAndSwap(size_t number_threads, size_t number_iterations); + + private: + std::bitset bitsets[3]; + embb::base::Atomic swap_value; + + int swap1_counter; + int swap2_counter; + + void compare_and_swap1(); + void compare_and_swap2(); + void Init(); + void CheckAndDestroy(); + }; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_ATOMIC_TEST_H_ diff --git a/base_cpp/test/condition_var_test.cc b/base_cpp/test/condition_var_test.cc new file mode 100644 index 0000000..52900e0 --- /dev/null +++ b/base_cpp/test/condition_var_test.cc @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +#include +#include + + +namespace embb { +namespace base { +namespace test { + +ConditionVarTest::ConditionVarTest() + : num_threads_(partest::TestSuite::GetDefaultNumThreads()) { + embb_counter_init(&counter_); + CreateUnit("Timed wait timouts") + .Add(&ConditionVarTest::TestTimedWaitTimeouts, this); + + if (num_threads_ >= 2) { + CreateUnit("Condition Notify Test") + .Add(&ConditionVarTest::TestNotify, this, num_threads_); + } else { + std::cout << + "Warning: Condition Notify Test needs a minimum of two threads" + << std::endl; + } +} + +void ConditionVarTest::TestTimedWaitTimeouts() { + // Set up data structures + ConditionVariable cond; + Mutex mutex; + UniqueLock lock(mutex); + + // Wait for now tests already passed time point + bool success = cond.WaitUntil(lock, Time()); + PT_EXPECT_EQ(success, false); + + // Wait for a future timepoint + success = cond.WaitUntil(lock, Time(Duration(1))); + PT_EXPECT_EQ(success, false); + + // Wait for a zero duration + success = cond.WaitFor(lock, Duration()); + PT_EXPECT_EQ(success, false); + + // Wait for some duration + success = cond.WaitFor(lock, Duration(1)); + PT_EXPECT_EQ(success, false); +} + +void ConditionVarTest::TestNotify() { + size_t threadID = partest::TestSuite::GetCurrentThreadID(); + + if (threadID != 0) { + UniqueLock lock(mutex_cond_notify_); + embb_counter_increment(&counter_); + cond_notify_.Wait(lock); + embb_counter_increment(&counter_); + } else { + while (embb_counter_get(&counter_) < + static_cast(num_threads_-1)) + {} // all threads entered critical section + UniqueLock lock_notify(mutex_cond_notify_); + lock_notify.Unlock(); + // All threads called wait on the condition (Even last thread) + + embb_counter_init(&counter_); + UniqueLock lock_wait(mutex_cond_wait_); + + cond_notify_.NotifyOne(); + + cond_wait_.WaitUntil(lock_wait, Time(Duration(1))); + while (embb_counter_get(&counter_) == 0) + {} // If hangs here signal has not succeeded + PT_ASSERT_EQ_MSG(embb_counter_get(&counter_), + static_cast(1), "Only 1 thread notified"); + cond_notify_.NotifyAll(); + + cond_wait_.WaitUntil(lock_wait, Time(Duration(2))); + + while (embb_counter_get(&counter_) != + static_cast(num_threads_-1)) + {} // If this hangs then not all threads were notified. + } +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git a/base_cpp/test/condition_var_test.h b/base_cpp/test/condition_var_test.h new file mode 100644 index 0000000..7cacd42 --- /dev/null +++ b/base_cpp/test/condition_var_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_CONDITION_VAR_TEST_H_ +#define BASE_CPP_TEST_CONDITION_VAR_TEST_H_ + +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +class ConditionVarTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + ConditionVarTest(); + + private: + /** + * Tests timeout of condition wait until and wait for methods. + */ + void TestTimedWaitTimeouts(); + + /** + * Tests if notify works correctly when each thread waits on the same lock. + */ + void TestNotify(); + + size_t num_threads_; + embb_counter_t counter_; + ConditionVariable cond_notify_; + Mutex mutex_cond_notify_; + ConditionVariable cond_wait_; + Mutex mutex_cond_wait_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_CONDITION_VAR_TEST_H_ diff --git a/base_cpp/test/core_set_test.cc b/base_cpp/test/core_set_test.cc new file mode 100644 index 0000000..5c9b9a3 --- /dev/null +++ b/base_cpp/test/core_set_test.cc @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 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 +#include + +namespace embb { +namespace base { +namespace test { + +CoreSetTest::CoreSetTest() { + CreateUnit("Test all").Add(&CoreSetTest::Test, this); +} + +void CoreSetTest::Test() { + unsigned int cores = CoreSet::CountAvailable(); + PT_EXPECT_GT(cores, static_cast(0)); + { // Default construction + CoreSet set; + PT_EXPECT_EQ(set.Count(), (size_t)0); + } + { // One argument construction with no cores set + CoreSet set(false); + PT_EXPECT_EQ(set.Count(), (size_t)0); + } + { // One argument construction with all cores set + CoreSet set(true); + PT_EXPECT_EQ(set.Count(), cores); + } + { // Copy construction + CoreSet set(true); + CoreSet copy(set); + PT_EXPECT_EQ(copy.Count(), set.Count()); + } + { // Assignment + CoreSet set(true); + CoreSet assigned = set; + PT_EXPECT_EQ(assigned.Count(), set.Count()); + } + { // Resetting all cores + CoreSet set; + set.Reset(true); + PT_EXPECT_EQ(set.Count(), cores); + set.Reset(false); + PT_EXPECT_EQ(set.Count(), (size_t)0); + } + { // Adding, removing, and checking single cores + CoreSet set; + for (unsigned int i = 0; i < cores; i++) { + set.Add(i); + bool is_contained = set.IsContained(i); + PT_EXPECT_EQ(is_contained, true); + if (i < cores - 1) { + is_contained = set.IsContained(i+1); + PT_EXPECT_EQ(is_contained, false); + } + PT_EXPECT_EQ(set.Count(), i+1); + } + for (unsigned int i = 0; i < cores; i++) { + set.Remove(i); + bool is_contained = set.IsContained(i); + PT_EXPECT_EQ(is_contained, false); + PT_EXPECT_EQ(set.Count(), cores - i - 1); + } + } + { // Logical operators + CoreSet lhs(false); + CoreSet rhs(true); + PT_EXPECT_EQ((lhs & rhs).Count(), (size_t)0); + PT_EXPECT_EQ((lhs | rhs).Count(), cores); + lhs &= rhs; + PT_EXPECT_EQ(lhs.Count(), (size_t)0); + lhs |= rhs; + PT_EXPECT_EQ(lhs.Count(), rhs.Count()); + } +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git a/base_cpp/test/core_set_test.h b/base_cpp/test/core_set_test.h new file mode 100644 index 0000000..c8274ff --- /dev/null +++ b/base_cpp/test/core_set_test.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_CORE_SET_TEST_H_ +#define BASE_CPP_TEST_CORE_SET_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for Core and CoreSet. + */ +class CoreSetTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + CoreSetTest(); + + private: + /** + * Tests all functionalities. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_CORE_SET_TEST_H_ diff --git a/base_cpp/test/duration_test.cc b/base_cpp/test/duration_test.cc new file mode 100644 index 0000000..cb12add --- /dev/null +++ b/base_cpp/test/duration_test.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 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 + +namespace embb { +namespace base { +namespace test { + +DurationTest::DurationTest() { + CreateUnit("Seconds").Add(&DurationTest::Test, this); + CreateUnit("Milliseconds").Add(&DurationTest::Test, this); + CreateUnit("Microseconds").Add(&DurationTest::Test, this); + CreateUnit("Nanoseconds").Add(&DurationTest::Test, this); +} + +} // namespace test +} // namespace base +} // namespace embb + + + diff --git a/base_cpp/test/duration_test.h b/base_cpp/test/duration_test.h new file mode 100644 index 0000000..34dbb51 --- /dev/null +++ b/base_cpp/test/duration_test.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_DURATION_TEST_H_ +#define BASE_CPP_TEST_DURATION_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class DurationTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + DurationTest(); + + private: + /** + * Test second ticks. + */ + template + void Test(); +}; + +template +void DurationTest::Test() { + Duration duration; + PT_EXPECT_EQ(duration.Count(), (size_t)0); + + unsigned long long min_ticks = Duration::Min().Count(); + + duration += Duration(min_ticks); + PT_EXPECT_EQ(duration.Count(), min_ticks); + + duration += duration + duration; + PT_EXPECT_EQ(duration.Count(), min_ticks * 3); + + Duration duration_copy(duration); + PT_EXPECT_EQ(duration_copy.Count(), min_ticks * 3); + + Duration duration_assigned = duration; + PT_EXPECT_EQ(duration_assigned.Count(), min_ticks * 3); +} + +} // namespace test +} // namespace base +} // namespace embb + + +#endif // BASE_CPP_TEST_DURATION_TEST_H_ diff --git a/base_cpp/test/main.cc b/base_cpp/test/main.cc new file mode 100644 index 0000000..2fb4dbe --- /dev/null +++ b/base_cpp/test/main.cc @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PT_MAIN("Base C++") { + unsigned int max_threads = + static_cast(2 * partest::TestSuite::GetDefaultNumThreads()); + embb_thread_set_max_count(max_threads); + PT_RUN(embb::base::test::CoreSetTest); + PT_RUN(embb::base::test::DurationTest); + PT_RUN(embb::base::test::ConditionVarTest); + PT_RUN(embb::base::test::MutexTest); + PT_RUN(embb::base::test::ThreadSpecificStorageTest); + PT_RUN(embb::base::test::AtomicTest); + PT_RUN(embb::base::test::MemoryAllocationTest); + PT_RUN(embb::base::test::ThreadTest); +} diff --git a/base_cpp/test/memory_allocation_test.cc b/base_cpp/test/memory_allocation_test.cc new file mode 100644 index 0000000..188a83d --- /dev/null +++ b/base_cpp/test/memory_allocation_test.cc @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014, 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 +#include + +namespace embb { +namespace base { +namespace test { + +MemoryAllocationTest::MemoryAllocationTest() { + CreateUnit("ClassAllocationTest") + .Add(&MemoryAllocationTest::ClassAllocationTest, this); + CreateUnit("AllocatorTest").Add(&MemoryAllocationTest::AllocatorTest, this); +} + +void MemoryAllocationTest::AllocatorTest() { + static const int alloc_iterations = 10000; + + ::std::vector < DummyClassForAllocatorTests, + embb::base::AllocatorCacheAligned< DummyClassForAllocatorTests > > + aligned_allocating_vector; + + ::std::vector < DummyClassForAllocatorTests, + embb::base::Allocator< DummyClassForAllocatorTests > > + unaligned_allocating_vector; + + for (unsigned int i = 0; + i != static_cast(alloc_iterations); + ++i) { + DummyClassForAllocatorTests d; + d.A = 0xF10000 | i; + d.F = 0xF20000 | i; + aligned_allocating_vector.push_back(d); + unaligned_allocating_vector.push_back(d); + } + + for (unsigned int i = 0; + i != static_cast(alloc_iterations); + ++i) { + unsigned int A = 0xF10000 | i; + unsigned int F = 0xF20000 | i; + PT_ASSERT_EQ(aligned_allocating_vector[i].A, A); + PT_ASSERT_EQ(aligned_allocating_vector[i].F, F); + PT_ASSERT_EQ(unaligned_allocating_vector[i].A, A); + PT_ASSERT_EQ(unaligned_allocating_vector[i].F, F); + } +} + +void MemoryAllocationTest::ClassAllocationTest() { + ::std::vector< DummyClassForAlignedAllocation* > aligned_allocs; + ::std::vector< DummyClassForUnalignedAllocation* > unaligned_allocs; + + size_t expected = 0; + + static const unsigned int alloc_iterations = 10; + + for (unsigned int i = 0; i != alloc_iterations; ++i) { + aligned_allocs.push_back( new DummyClassForAlignedAllocation ); + //write something + aligned_allocs[i]->b = 0xF000 | i; +#ifdef EMBB_DEBUG + size_t n = (sizeof(DummyClassForAlignedAllocation) + + (EMBB_CACHE_LINE_SIZE - 1)) / EMBB_CACHE_LINE_SIZE; + expected += (n + 1)*EMBB_CACHE_LINE_SIZE + (sizeof(size_t) * 3 - 1); +#endif // else EMBB_DEBUG + + //check that the memory is aligned! + PT_ASSERT_EQ((uintptr_t)aligned_allocs[i] % EMBB_CACHE_LINE_SIZE, + (uintptr_t)0); + } + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + //delete! + expected = 0; + for (unsigned int i = 0; i != alloc_iterations; ++i) { + //check if written correctly + PT_ASSERT_EQ(aligned_allocs[i]->b, 0xF000 | i); + delete aligned_allocs[i]; + } + + //everything should be deleted + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + for (unsigned int i = 0; i != alloc_iterations; ++i) { + unaligned_allocs.push_back(new DummyClassForUnalignedAllocation); + //write something + unaligned_allocs[i]->b = 0xA000 | i; + + +#ifdef EMBB_DEBUG + expected += sizeof(DummyClassForUnalignedAllocation) + 2 * sizeof(size_t); +#endif // else EMBB_DEBUG + } + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + //delete! + expected = 0; + for (unsigned int i = 0; i != alloc_iterations; ++i) { + //check if written correctly + PT_ASSERT_EQ(unaligned_allocs[i]->b, 0xA000 | i); + delete unaligned_allocs[i]; + } + + //everything should be deleted + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + + DummyClassForAlignedAllocation * aligned_allocated = + new DummyClassForAlignedAllocation[alloc_iterations]; +#ifdef EMBB_DEBUG + size_t n = (sizeof(DummyClassForAlignedAllocation)*alloc_iterations + + (EMBB_CACHE_LINE_SIZE - 1)) / EMBB_CACHE_LINE_SIZE; + expected += (n + 1)*EMBB_CACHE_LINE_SIZE + (sizeof(size_t) * 3 - 1); +#endif // else EMBB_DEBUG + + // This assert does _not_ hold, but is left for documentation. + // It is not guaranteed that the pointer to the array is aligned. + // See the documentation of the overloaded new[] operator in + // class MemoryAllocation. + // PT_ASSERT_EQ((uintptr_t)aligned_allocated % EMBB_CACHE_LINE_SIZE, 0); + + //delete! + expected = 0; + delete[] aligned_allocated; + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); + + DummyClassForUnalignedAllocation * unaligned_allocated = + new DummyClassForUnalignedAllocation[alloc_iterations]; +#ifdef EMBB_DEBUG + expected += sizeof(DummyClassForUnalignedAllocation)*alloc_iterations + + 2 * sizeof(size_t); +#endif // else EMBB_DEBUG + + PT_ASSERT(embb::base::Allocation::AllocatedBytes() >= expected); + + //delete! + expected = 0; + delete[] unaligned_allocated; + + PT_ASSERT_EQ(embb::base::Allocation::AllocatedBytes(), expected); +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_cpp/test/memory_allocation_test.h b/base_cpp/test/memory_allocation_test.h new file mode 100644 index 0000000..f93cf4f --- /dev/null +++ b/base_cpp/test/memory_allocation_test.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_MEMORY_ALLOCATION_TEST_H_ +#define BASE_CPP_TEST_MEMORY_ALLOCATION_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +class MemoryAllocationTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + MemoryAllocationTest(); + + private: + void ClassAllocationTest(); + void AllocatorTest(); + + class DummyClassForAllocatorTests { + public: + unsigned int A; + double B; + long C; + long long D; + long E; + unsigned int F; + }; + + class DummyClassForAlignedAllocation + : public embb::base::CacheAlignedAllocatable { + public: + int a; + size_t b; + }; + + class DummyClassForUnalignedAllocation + : public embb::base::Allocatable { + public: + int a; + size_t b; + }; +}; + +} // namespace test +} // namespace base +} // namespace embb + + +#endif // BASE_CPP_TEST_MEMORY_ALLOCATION_TEST_H_ diff --git a/base_cpp/test/mutex_test.cc b/base_cpp/test/mutex_test.cc new file mode 100644 index 0000000..879ff6a --- /dev/null +++ b/base_cpp/test/mutex_test.cc @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +MutexTest::MutexTest() : mutex_(), counter_(0), + number_threads_(partest::TestSuite::GetDefaultNumThreads()), + number_iterations_(partest::TestSuite::GetDefaultNumIterations()) { + CreateUnit("Mutex protected counter") + .Pre(&MutexTest::PreMutexCount, this) + .Add(&MutexTest::TestMutexCount, this, number_threads_, + number_iterations_) + .Post(&MutexTest::PostMutexCount, this); + CreateUnit("Recursive mutex") + .Add(&MutexTest::TestRecursiveMutex, this); + CreateUnit("Lock guard protected counter") + .Pre(&MutexTest::PreLockGuardCount, this) + .Add(&MutexTest::TestLockGuardCount, this, number_threads_, + number_iterations_) + .Post(&MutexTest::PostLockGuardCount, this); + CreateUnit("Unique Lock").Add(&MutexTest::TestUniqueLock, this); +} + +void MutexTest::PreMutexCount() { + counter_ = 0; +} + +void MutexTest::TestMutexCount() { + mutex_.Lock(); + ++counter_; + mutex_.Unlock(); +} + +void MutexTest::PostMutexCount() { + PT_EXPECT_EQ((size_t)counter_, number_iterations_ * number_threads_); +} + +void MutexTest::TestRecursiveMutex() { + embb::base::RecursiveMutex mutex; + int number = 5; + for (int i = 0; i < number; i++) { + mutex.Lock(); + bool obtained = mutex.TryLock(); + PT_EXPECT_EQ(obtained, true); + } + for (int i = 0; i < number; i++) { + mutex.Unlock(); + mutex.Unlock(); + } +} + +void MutexTest::PreLockGuardCount() { + counter_ = 0; +} + +void MutexTest::TestLockGuardCount() { + LockGuard guard(mutex_); + ++counter_; +} + +void MutexTest::PostLockGuardCount() { + PT_EXPECT_EQ((size_t)counter_, number_iterations_ * number_threads_); +} + +void MutexTest::TestUniqueLock() { +#ifdef EMBB_USE_EXCEPTIONS + bool exception_thrown = false; +#endif + { // Test standard usage and releasing + UniqueLock lock(mutex_); + PT_EXPECT_EQ(lock.OwnsLock(), true); + +#ifdef EMBB_USE_EXCEPTIONS + // Locked lock should not by re-lockable + EMBB_TRY { + lock.Lock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); + + // Locked lock should not by re-try-lockable + EMBB_TRY { + lock.TryLock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); +#endif // EMBB_USE_EXCEPTIONS + + lock.Unlock(); + PT_EXPECT_EQ(lock.OwnsLock(), false); + +#ifdef EMBB_USE_EXCEPTIONS + // Unlocked lock should not by re-unlockable + exception_thrown = false; + EMBB_TRY { + lock.Unlock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); +#endif // EMBB_USE_EXCEPTIONS + + lock.Lock(); + PT_EXPECT_EQ(lock.OwnsLock(), true); + + lock.Release()->Unlock(); + PT_EXPECT_EQ(lock.OwnsLock(), false); + +#ifdef EMBB_USE_EXCEPTIONS + // Released lock should not by lockable + exception_thrown = false; + EMBB_TRY { + lock.Lock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); + + // Released lock should not by try-lockable + exception_thrown = false; + EMBB_TRY { + lock.TryLock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); + + // Released lock should not by unlockable + exception_thrown = false; + EMBB_TRY { + lock.Unlock(); + } + EMBB_CATCH(ErrorException&) { + exception_thrown = true; + } + PT_EXPECT_EQ(exception_thrown, true); +#endif // EMBB_USE_EXCEPTIONS + } + + { // Test deferred lock construction + UniqueLock<> lock(mutex_, embb::base::defer_lock); + PT_EXPECT_EQ(lock.OwnsLock(), false); + } + + { // Test try-lock construction + UniqueLock<> lock(mutex_, embb::base::try_lock); + PT_EXPECT_EQ(lock.OwnsLock(), true); + } + + { // Test adopt lock construction + mutex_.Lock(); + UniqueLock<> lock(mutex_, embb::base::adopt_lock); + PT_EXPECT_EQ(lock.OwnsLock(), true); + } + + { // Test lock swapping + UniqueLock<> lock1; + UniqueLock<> lock2(mutex_); + PT_EXPECT_EQ(lock1.OwnsLock(), false); + PT_EXPECT_EQ(lock2.OwnsLock(), true); + lock1.Swap(lock2); + PT_EXPECT_EQ(lock1.OwnsLock(), true); + PT_EXPECT_EQ(lock2.OwnsLock(), false); + } +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_cpp/test/mutex_test.h b/base_cpp/test/mutex_test.h new file mode 100644 index 0000000..dc21c8d --- /dev/null +++ b/base_cpp/test/mutex_test.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_MUTEX_TEST_H_ +#define BASE_CPP_TEST_MUTEX_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { +/** + * Provides tests for class Mutex. + */ +class MutexTest : public partest::TestCase { + public: + /** + * Constructs the test case and adds test units. + */ + MutexTest(); + + private: + /** + * Uses Mutex to realize multi-threaded counting. + */ + void PreMutexCount(); + void TestMutexCount(); + void PostMutexCount(); + + /** + * Tests the multiple locking and unlocking of a recursive mutex. + */ + void TestRecursiveMutex(); + + /** + * Uses LockGuard to realize multi-threaded counting. + */ + void PreLockGuardCount(); + void TestLockGuardCount(); + void PostLockGuardCount(); + + /** + * Tests the different states of a UniqueLock. + */ + void TestUniqueLock(); + + /** + * Mutex for tests. + */ + embb::base::Mutex mutex_; + + /** + * Shared counter to check effectiveness of mutex. + */ + int counter_; + + /** + * Number of threads used to run tests. + */ + size_t number_threads_; + + /** + * Number of times the test method is called by each thread. + */ + size_t number_iterations_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_MUTEX_TEST_H_ diff --git a/base_cpp/test/thread_specific_storage_test.cc b/base_cpp/test/thread_specific_storage_test.cc new file mode 100644 index 0000000..5fc8141 --- /dev/null +++ b/base_cpp/test/thread_specific_storage_test.cc @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +namespace embb { +namespace base { +namespace test { + +ThreadSpecificStorageTest::ThreadSpecificStorageTest() + : number_threads_(partest::TestSuite::GetDefaultNumThreads()), tss_() { + PT_EXPECT_GT(Thread::GetThreadsMaxCount(), number_threads_); + CreateUnit("Internal representation") + .Add(&ThreadSpecificStorageTest::TestInternalRepresentation, this, + 1, partest::TestSuite::GetDefaultNumIterations()); + CreateUnit("Multiple TSS variables") + .Add(&ThreadSpecificStorageTest::TestMultipleTSSVariables, this); + CreateUnit("TestConstructors") + .Add(&ThreadSpecificStorageTest::TestConstructors, this); +} + +void ThreadSpecificStorageTest::TestInternalRepresentation() { + embb_internal_thread_index_reset(); + size_t num_threads = partest::TestSuite::GetDefaultNumThreads(); + embb::base::Thread** threads = new embb::base::Thread*[num_threads]; + for (size_t i = 0; i < num_threads; i++) { + threads[i] = new embb::base::Thread( + ThreadSpecificStorageTest::TestInternalRepresentationSetGet, + &tss_, i); + } + for (size_t i = 0; i < num_threads; i++) { + threads[i]->Join(); + delete threads[i]; + } + delete[] threads; + + // Check results + int sum = 0; + for (size_t i = 0; i < tss_.rep_.size; i++) { + if (i < num_threads) { + PT_EXPECT_EQ(tss_.usage_flags_[i], true); + void* value = tss_.rep_.values[i]; + sum += *static_cast(value); + } else { + PT_EXPECT_EQ(tss_.usage_flags_[i], false); + } + } + int expected_sum = 0; + for (size_t i = 0; i < num_threads; i++) { + expected_sum += static_cast(i); + } + PT_EXPECT_EQ(sum, expected_sum); +} + +void ThreadSpecificStorageTest::TestInternalRepresentationSetGet( + ThreadSpecificStorage* tss, size_t rank) { + assert(tss != NULL); + PT_EXPECT_NE(tss->rep_.values, static_cast(NULL)); + PT_EXPECT_EQ(embb_thread_get_max_count(), tss->rep_.size); + PT_EXPECT_LT(rank, tss->rep_.size); + for (unsigned int i = 0; i < tss->rep_.size; i++) { + PT_EXPECT_NE(tss->rep_.values[i], static_cast(NULL)); + } + tss->Get() = rank; + for (size_t i = 0; i < 10; i++) { + size_t stored_rank = tss->Get(); + PT_EXPECT_EQ(rank, stored_rank); + if (rank != stored_rank) break; + } +} + +void ThreadSpecificStorageTest::TestMultipleTSSVariables() { + ThreadSpecificStorage tss2; + PT_EXPECT_NE(&(tss_.Get()), &(tss2.Get())); +} + +void ThreadSpecificStorageTest::TestConstructors() { + { + ThreadSpecificStorage tss(1); + PT_EXPECT_EQ(tss.Get().var, 1); + } + { + ThreadSpecificStorage tss(1, 2); + PT_EXPECT_EQ(tss.Get().var1, 1); + PT_EXPECT_EQ(tss.Get().var2, 2); + } + { + ThreadSpecificStorage tss(1, 2, 3); + PT_EXPECT_EQ(tss.Get().var1, 1); + PT_EXPECT_EQ(tss.Get().var2, 2); + PT_EXPECT_EQ(tss.Get().var3, 3); + } + { + ThreadSpecificStorage tss(1, 2, 3, 4); + PT_EXPECT_EQ(tss.Get().var1, 1); + PT_EXPECT_EQ(tss.Get().var2, 2); + PT_EXPECT_EQ(tss.Get().var3, 3); + PT_EXPECT_EQ(tss.Get().var4, 4); + } +} + +} // namespace test +} // namespace base +} // namespace embb diff --git a/base_cpp/test/thread_specific_storage_test.h b/base_cpp/test/thread_specific_storage_test.h new file mode 100644 index 0000000..f6c93e7 --- /dev/null +++ b/base_cpp/test/thread_specific_storage_test.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ +#define BASE_CPP_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ + +#include +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests tls implementation. + */ +class ThreadSpecificStorageTest : public partest::TestCase { + public: + /** + * Adds test units. + */ + explicit ThreadSpecificStorageTest(); + + private: + /** + * Tests the correctness of the internal representation. + */ + void TestInternalRepresentation(); + static void TestInternalRepresentationSetGet( + ThreadSpecificStorage* tss, + size_t rank); + + /** + * Test basic functionality. + */ + void TestMultipleTSSVariables(); + + /** + * Type to test TSS constructors with initializers. + */ + struct OneArgumentConstructorType { + explicit OneArgumentConstructorType(int arg) : var(arg) {} + int var; + }; + /** + * Type to test TSS constructors with initializers. + */ + struct TwoArgumentConstructorType { + TwoArgumentConstructorType(int arg1, int arg2) : var1(arg1), var2(arg2) {} + int var1, var2; + }; + /** + * Type to test TSS constructors with initializers. + */ + struct ThreeArgumentConstructorType { + ThreeArgumentConstructorType(int arg1, int arg2, int arg3) + : var1(arg1), var2(arg2), var3(arg3) {} + int var1, var2, var3; + }; + /** + * Type to test TSS constructors with initializers. + */ + struct FourArgumentConstructorType { + FourArgumentConstructorType(int arg1, int arg2, int arg3, int arg4) + : var1(arg1), var2(arg2), var3(arg3), var4(arg4) {} + int var1, var2, var3, var4; + }; + + /** + * Tests the different constructors. + */ + void TestConstructors(); + + /** + * Used to differentiate between used and unused TSS slots. + */ + size_t number_threads_; + + /** + * TSS used in tests. + */ + ThreadSpecificStorage tss_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_THREAD_SPECIFIC_STORAGE_TEST_H_ diff --git a/base_cpp/test/thread_test.cc b/base_cpp/test/thread_test.cc new file mode 100644 index 0000000..6412b7a --- /dev/null +++ b/base_cpp/test/thread_test.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +const double ThreadTest::eps_ = 1e-15; + +ThreadTest::ThreadTest() + : number_threads_(partest::TestSuite::GetDefaultNumThreads()) { + CreateUnit("Starting and joining") + .Pre(&ThreadTest::PreStartingAndJoining, this) + .Add(&ThreadTest::TestStartingAndJoining, this, number_threads_); + CreateUnit("Affinities") + .Add(&ThreadTest::TestThreadAffinities, this); +} + +void ThreadTest::PreStartingAndJoining() { + embb::base::Thread::SetThreadsMaxCount( + static_cast(6 * (number_threads_+1))); +} + +void ThreadTest::TestStartingAndJoining() { + // Static member start methods + embb::base::Thread thread1(ThreadTest::StaticThreadStart); + int arg1 = 2; + embb::base::Thread thread2(ThreadTest::StaticThreadStartArg1, arg1); + double arg2 = 3.0; + embb::base::Thread thread3(ThreadTest::StaticThreadStartArg2, arg1, arg2); + + // Non-static member start methods with functor + MemberStart start4(&ThreadTest::ThreadStart, this); + embb::base::Thread thread4(start4); + + std::string arg3("StringArgument"); + MemberStartArg1 start5(&ThreadTest::ThreadStartArg1, + this, arg3); + embb::base::Thread thread5(start5); + + std::vector arg4; + arg4.push_back(1); + arg4.push_back(2); + double arg5 = 5.0; + MemberStartArg2, double&), + const std::vector, double&> start6( + &ThreadTest::ThreadStartArg2, this, arg4, arg5); + embb::base::Thread thread6(start6); + + thread1.Join(); + thread2.Join(); + thread3.Join(); + thread4.Join(); + thread5.Join(); + thread6.Join(); +} + +void ThreadTest::TestThreadAffinities() { + embb::base::CoreSet core_set(true); + bool success = true; + EMBB_TRY { + embb::base::Thread thread(core_set, StaticThreadStart); + thread.Join(); + } + EMBB_CATCH(embb::base::Exception&) { + success = false; + } + PT_EXPECT_EQ(success, true); +} + +void ThreadTest::StaticThreadStart() { +} + +void ThreadTest::StaticThreadStartArg1(int arg1) { + PT_EXPECT_EQ(arg1, 2); +} + +void ThreadTest::StaticThreadStartArg2(int arg1, double arg2) { + PT_EXPECT_EQ(arg1, 2); + PT_EXPECT_GT(arg2 - 3.0 + eps_, 0.0); +} + +void ThreadTest::ThreadStart() { +} + +void ThreadTest::ThreadStartArg1(const std::string& arg1) { + PT_EXPECT_EQ(arg1, std::string("StringArgument")); +} + +void ThreadTest::ThreadStartArg2(std::vector arg1, double& arg2) { + PT_EXPECT_EQ(arg1[0], 1); + PT_EXPECT_EQ(arg1[1], 2); + PT_EXPECT_GT(arg2 - 5.0 + eps_, 0.0); +} + +} // namespace test +} // namespace base +} // namespace embb + diff --git a/base_cpp/test/thread_test.h b/base_cpp/test/thread_test.h new file mode 100644 index 0000000..6f59ce9 --- /dev/null +++ b/base_cpp/test/thread_test.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_THREAD_TEST_H_ +#define BASE_CPP_TEST_THREAD_TEST_H_ + +#include +#include +#include + +namespace embb { +namespace base { +namespace test { + +/** + * Provides tests for class embb::base::Thread. + */ +class ThreadTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + explicit ThreadTest(); /**< + number of threads concurrently running test methods */ + + private: + /** + * Member thread start functor without arguments. + */ + template + struct MemberStart { + Function f_; + ThreadTest* self_; + MemberStart(Function f, ThreadTest* self) : f_(f), self_(self) {} + void operator()() { (self_->*f_)(); } + }; + + /** + * Member thread start functor with one argument. + */ + template + struct MemberStartArg1 { + Function f_; + ThreadTest* self_; + Arg1 arg1_; + MemberStartArg1(Function f, ThreadTest* self, Arg1 arg1) + : f_(f), self_(self), arg1_(arg1) {} + void operator()() { (self_->*f_)(arg1_); } + private: + /** + * Deactivated to avoid MSVC warning "could not create ..." + */ + MemberStartArg1& operator=(const MemberStartArg1&); + }; + + /** + * Member thread start functor with two arguments. + */ + template + struct MemberStartArg2 { + Function f_; + ThreadTest* self_; + Arg1 arg1_; + Arg2 arg2_; + MemberStartArg2(Function f, ThreadTest* self, Arg1 arg1, Arg2 arg2) + : f_(f), self_(self), arg1_(arg1), arg2_(arg2) {} + void operator()() { (self_->*f_)(arg1_, arg2_); } + private: + /** + * Deactivated to avoid MSVC warning "could not create ..." + */ + MemberStartArg2& operator=(const MemberStartArg2&); + }; + + /** + * Sets required number of maximal threads. + */ + void PreStartingAndJoining(); + + /** + * Tests starting and joining threads in different start methods. + */ + void TestStartingAndJoining(); + + /** + * Tests setting thread affinities. + */ + void TestThreadAffinities(); + + /** + * Static member thread start method without arguments. + */ + static void StaticThreadStart(); + + /** + * Static member thread start method with one parameter. + */ + static void StaticThreadStartArg1(int arg1); + + /** + * Static member thread start method with two parameters. + */ + static void StaticThreadStartArg2(int arg1, double arg2); + + /** + * Member thread start method without arguments. + */ + void ThreadStart(); + + /** + * Member thread start method with one argument. + */ + void ThreadStartArg1(const std::string& arg1); + + /** + * Member thread start method with two arguments. + */ + void ThreadStartArg2(std::vector arg1, double& arg2); + + /** + * Number of threads used to run (some) of the tests. + */ + size_t number_threads_; + + /** + * Floating point comparison absolute difference. + */ + static const double eps_; +}; + +} // namespace test +} // namespace base +} // namespace embb + +#endif // BASE_CPP_TEST_THREAD_TEST_H_ diff --git a/base_cpp/test/time_test.h b/base_cpp/test/time_test.h new file mode 100644 index 0000000..41fc157 --- /dev/null +++ b/base_cpp/test/time_test.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, 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 BASE_CPP_TEST_TIME_TEST_H_ +#define BASE_CPP_TEST_TIME_TEST_H_ + +#include + +namespace embb { +namespace base { +namespace test { + +class TimeTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + TimeTest(); + + private: + /** + * Test second ticks. + */ + void Test(); +}; + +} // namespace test +} // namespace base +} // namespace embb + + +#endif // BASE_CPP_TEST_TIME_TEST_H_ diff --git a/containers_cpp/CMakeLists.txt b/containers_cpp/CMakeLists.txt new file mode 100644 index 0000000..1ca0ccf --- /dev/null +++ b/containers_cpp/CMakeLists.txt @@ -0,0 +1,33 @@ +project (project_embb_containers_cpp) + +file(GLOB_RECURSE EMBB_CONTAINERS_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_CONTAINERS_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_CONTAINERS_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(../CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_CONTAINERS_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_CONTAINERS_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include) + +add_library (embb_containers_cpp ${EMBB_CONTAINERS_CPP_SOURCES} ${EMBB_CONTAINERS_CPP_HEADERS}) +target_link_libraries(embb_containers_cpp embb_base_cpp) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_containers_cpp_test ${EMBB_CONTAINERS_CPP_TEST_SOURCES}) + target_link_libraries(embb_containers_cpp_test embb_containers_cpp + partest embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_containers_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/embb + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_containers_cpp DESTINATION lib) diff --git a/containers_cpp/include/embb/containers/containers.h b/containers_cpp/include/embb/containers/containers.h new file mode 100644 index 0000000..0c7a25b --- /dev/null +++ b/containers_cpp/include/embb/containers/containers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_CONTAINERS_H_ +#define EMBB_CONTAINERS_CONTAINERS_H_ + +/** + * \defgroup CPP_CONTAINERS Containers + * \ingroup CPP + * Concurrent data structures, mainly containers + */ + +#include +#include +#include +#include +#include +#include + +#endif // EMBB_CONTAINERS_CONTAINERS_H_ diff --git a/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h b/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h new file mode 100644 index 0000000..2877554 --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/hazard_pointer-inl.h @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ + +namespace embb { +namespace containers { +namespace internal { +template< typename ElementT > +FixedSizeList::FixedSizeList(size_t max_size) : + max_size(max_size), + size(0) { + elementsArray = + new ElementT[max_size]; +} + +template< typename ElementT > +inline size_t FixedSizeList::GetSize() const { + return size; +} + +template< typename ElementT > +inline size_t FixedSizeList::GetMaxSize() const { + return max_size; +} + +template< typename ElementT > +inline void FixedSizeList::clear() { + size = 0; +} + +template< typename ElementT > +typename FixedSizeList::iterator +FixedSizeList::begin() const { + return &elementsArray[0]; +} + +template< typename ElementT > +typename FixedSizeList::iterator +FixedSizeList::end() const { + return &elementsArray[size]; +} + +template< typename ElementT > +FixedSizeList< ElementT > & +FixedSizeList::operator= (const FixedSizeList & other) { + size = 0; + + if (max_size < other.size) { + EMBB_THROW(embb::base::ErrorException, "Copy target to small"); + } + + for (const_iterator it = other.begin(); it != other.end(); ++it) { + PushBack(*it); + } + return *this; +} + +template< typename ElementT > +bool FixedSizeList::PushBack(ElementT const el) { + if (size + 1 > max_size) { + return false; + } + elementsArray[size] = el; + size++; + return true; +} + +template< typename ElementT > +FixedSizeList::~FixedSizeList() { + delete[] elementsArray; +} + +template< typename GuardType > +bool HazardPointerThreadEntry::IsActive() { + return (is_active == 1); +} + +template< typename GuardType > +bool HazardPointerThreadEntry::TryReserve() { + int expected = 0; + return is_active.CompareAndSwap(expected, 1); +} + +template< typename GuardType > +void HazardPointerThreadEntry::Deactivate() { + is_active = 0; +} + +template< typename GuardType > +size_t HazardPointerThreadEntry::GetRetiredCounter() { + return retired_list.GetSize(); +} + +template< typename GuardType > +FixedSizeList< GuardType >& HazardPointerThreadEntry:: +GetRetired() { + return retired_list; +} + +template< typename GuardType > +FixedSizeList< GuardType >& HazardPointerThreadEntry:: +GetRetiredTemp() { + return retired_list_temp; +} + +template< typename GuardType > +FixedSizeList< GuardType >& HazardPointerThreadEntry:: +GetHazardTemp() { + return hazard_pointer_list_temp; +} + +template< typename GuardType > +void HazardPointerThreadEntry:: +SetRetired(internal::FixedSizeList< GuardType > const & retired_list) { + this->retired_list = retired_list; +} + +template< typename GuardType > +HazardPointerThreadEntry:: +HazardPointerThreadEntry(GuardType undefined_guard, int guards_per_thread, + size_t max_size_retired_list) : + undefined_guard(undefined_guard), + guards_per_thread(guards_per_thread), + max_size_retired_list(max_size_retired_list), + retired_list(max_size_retired_list), + retired_list_temp(max_size_retired_list), + hazard_pointer_list_temp(embb::base::Thread::GetThreadsMaxCount() * + guards_per_thread) { + // Initialize guarded pointer list + guarded_pointers = static_cast*> + (embb::base::Allocation::Allocate( + sizeof(embb::base::Atomic)*guards_per_thread)); + + for (int i = 0; i != guards_per_thread; ++i) { + new (static_cast(&guarded_pointers[i])) + embb::base::Atomic(undefined_guard); + } +} + +template< typename GuardType > +HazardPointerThreadEntry::~HazardPointerThreadEntry() { + for (int i = 0; i != guards_per_thread; ++i) { + guarded_pointers[i].~Atomic(); + } + + embb::base::Allocation::Free(guarded_pointers); +} + +template< typename GuardType > +GuardType HazardPointerThreadEntry::GetGuard(int pos) const { + return guarded_pointers[pos]; +} + +template< typename GuardType > +void HazardPointerThreadEntry::AddRetired(GuardType pointerToGuard) { + retired_list.PushBack(pointerToGuard); +} + +template< typename GuardType > +void HazardPointerThreadEntry:: +GuardPointer(int guardNumber, GuardType pointerToGuard) { + guarded_pointers[guardNumber] = pointerToGuard; +} + +template< typename GuardType > +void HazardPointerThreadEntry::SetActive(bool active) { + if (active == true) + is_active = 1; + else + is_active = 0; +} + +template< typename GuardType > +unsigned int HazardPointer< GuardType >::GetCurrentThreadIndex() { + unsigned int thread_index; + int return_val = embb_internal_thread_index(&thread_index); + + if (return_val != EMBB_SUCCESS) + EMBB_THROW(embb::base::ErrorException, "Could not get thread id!"); + + return thread_index; +} +template< typename GuardType > +bool HazardPointer< GuardType >::IsThresholdExceeded() { + double retiredCounterLocThread = + static_cast(GetHazardPointerElementForCurrentThread(). + GetRetiredCounter()); + + return (retiredCounterLocThread >= + RETIRE_THRESHOLD * + static_cast(active_hazard_pointer)* + static_cast(guards_per_thread)); +} + +template< typename GuardType > +size_t HazardPointer< GuardType >::GetActiveHazardPointers() { + return active_hazard_pointer; +} +template< typename GuardType > +typename HazardPointer< GuardType >::HazardPointerThreadEntry_t & +HazardPointer< GuardType >::GetHazardPointerElementForCurrentThread() { + // For each thread, there is a slot in the hazard pointer array. + // Initially, the active flag of a hazard pointer entry is false. + // Only the respective thread changes the flag from true to false. + // This means that the current thread tells that he is about to + // stop operating, and the others are responsible for his retired + // list. + + // int expected = false; + HazardPointerThreadEntry_t* current_thread_entry = + &hazard_pointer_thread_entry_array[GetCurrentThreadIndex()]; + + // If not active, activate it + if (!current_thread_entry->IsActive()) { + current_thread_entry->SetActive(true); + active_hazard_pointer++; + } + + return hazard_pointer_thread_entry_array[GetCurrentThreadIndex()]; +} + +template< typename GuardType > +void HazardPointer< GuardType >::HelpScan() { + // This is a little bit different than in the paper. In the paper, + // the retired nodes from other threads are added to our retired list. + // To be able to give a bound on memory consumption, we execute scan + // for those threads, without moving elements. The effect shall be + // the same. + + for (size_t i = 0; i != hazard_pointers; ++i) { + // Try to find non active lists... + if (!hazard_pointer_thread_entry_array[i].IsActive() && + hazard_pointer_thread_entry_array[i].TryReserve()) { + // Here: grab retired things, first check if there are any... + if (hazard_pointer_thread_entry_array[i].GetRetiredCounter() > 0) { + Scan(&hazard_pointer_thread_entry_array[i]); + } + + // We are done, mark it as deactivated again + hazard_pointer_thread_entry_array[i].Deactivate(); + } + } +} + +template< typename GuardType > +void HazardPointer< GuardType >:: +Scan(HazardPointerThreadEntry_t* currentHazardPointerEntry) { + // In this function, we compute the intersection between local retired + // pointers and all hazard pointers. This intersection cannot be deleted and + // forms the new local retired pointers list. + // It is assumed that the union of all retired pointers contains no two + // pointers with the same value. However, the union of all hazard guards + // might. + + // Here, we store the temporary hazard pointers. We have to store them, + // as iterating multiple time over them might be expensive, as this + // atomic array is shared between threads. + currentHazardPointerEntry->GetHazardTemp().clear(); + + // Get all active hazard pointers! + for (unsigned int i = 0; i != hazard_pointers; ++i) { + // Only consider guards of active threads + if (hazard_pointer_thread_entry_array[i].IsActive()) { + // For each guard in an hazard pointer entry + for (int pos = 0; pos != guards_per_thread; ++pos) { + GuardType guard = hazard_pointer_thread_entry_array[i].GetGuard(pos); + + // UndefinedGuard means not guarded + if (guard == undefined_guard) + continue; + + currentHazardPointerEntry->GetHazardTemp().PushBack(guard); + } + } + } + + currentHazardPointerEntry->GetRetiredTemp().clear(); + + // Sort them, we will do a binary search on each entry from the retired list + std::sort( + currentHazardPointerEntry->GetHazardTemp().begin(), + currentHazardPointerEntry->GetHazardTemp().end()); + + for ( + EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME FixedSizeList< GuardType >::iterator + it = currentHazardPointerEntry->GetRetired().begin(); + it != currentHazardPointerEntry->GetRetired().end(); ++it) { + if (false == ::std::binary_search( + currentHazardPointerEntry->GetHazardTemp().begin(), + currentHazardPointerEntry->GetHazardTemp().end(), *it)) { + this->free_guard_callback(*it); + } else { + currentHazardPointerEntry->GetRetiredTemp().PushBack(*it); + } + } + currentHazardPointerEntry->SetRetired( + currentHazardPointerEntry->GetRetiredTemp()); +} + +template< typename GuardType > +size_t HazardPointer< GuardType >::GetRetiredListMaxSize() const { + return static_cast(RETIRE_THRESHOLD * + static_cast(embb::base::Thread::GetThreadsMaxCount()) * + static_cast(guards_per_thread)) + 1; +} + +template< typename GuardType > +HazardPointer< GuardType >::HazardPointer( + embb::base::Function free_guard_callback, + GuardType undefined_guard, int guards_per_thread) : + undefined_guard(undefined_guard), + guards_per_thread(guards_per_thread), + active_hazard_pointer(0), + free_guard_callback(free_guard_callback) { + hazard_pointers = embb::base::Thread::GetThreadsMaxCount(); + + hazard_pointer_thread_entry_array = static_cast( + embb::base::Allocation::Allocate(sizeof(HazardPointerThreadEntry_t) * + hazard_pointers)); + + for (size_t i = 0; i != hazard_pointers; ++i) { + new (static_cast(&(hazard_pointer_thread_entry_array[i]))) + HazardPointerThreadEntry_t(undefined_guard, guards_per_thread, + GetRetiredListMaxSize()); + } +} + +template< typename GuardType > +HazardPointer< GuardType >::~HazardPointer() { + for (size_t i = 0; i != hazard_pointers; ++i) { + hazard_pointer_thread_entry_array[i].~HazardPointerThreadEntry_t(); + } + + embb::base::Allocation::Free(static_cast < void* > + (hazard_pointer_thread_entry_array)); +} + +template< typename GuardType > +void HazardPointer< GuardType >::DeactivateCurrentThread() { + HazardPointerThreadEntry_t* current_thread_entry = + &hazard_pointer_thread_entry_array[GetCurrentThreadIndex()]; + + // Deactivating a non-active hazard pointer entry has no effect! + if (!current_thread_entry->IsActive()) { + return; + } else { + current_thread_entry->SetActive(false); + active_hazard_pointer--; + } +} + +template< typename GuardType > +void HazardPointer< GuardType >::GuardPointer(int guardPosition, + GuardType guardedElement) { + GetHazardPointerElementForCurrentThread().GuardPointer( + guardPosition, guardedElement); +} + +template< typename GuardType > +void HazardPointer< GuardType >::EnqueuePointerForDeletion( + GuardType guardedElement) { + GetHazardPointerElementForCurrentThread().AddRetired(guardedElement); + if (IsThresholdExceeded()) { + HazardPointerThreadEntry_t* currentHazardPointerEntry = + &GetHazardPointerElementForCurrentThread(); + Scan(currentHazardPointerEntry); + + // Help deactivated threads to clean their retired nodes. + HelpScan(); + } +} + +template +const double embb::containers::internal::HazardPointer:: + RETIRE_THRESHOLD = 1.25f; +} // namespace internal +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/hazard_pointer.h b/containers_cpp/include/embb/containers/internal/hazard_pointer.h new file mode 100644 index 0000000..1d3ded3 --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/hazard_pointer.h @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ +#define EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ + +#include +#include +#include +#include +#include +#include + +#if defined(EMBB_COMPILER_MSVC) +#define EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME +#else +#define EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME typename +#endif + +namespace embb { +namespace containers { +namespace internal { +/** + * A list with fixed size, implemented as an array. Replaces std::vector that + * was used in previous hazard pointer implementation. + * + * Provides iterators, so we can apply algorithms from the STL. + * + * \tparam ElementT Type of the elements contained in the list. + */ +template< typename ElementT > +class FixedSizeList { + private: + /** + * Capacity of the list + */ + size_t max_size; + + /** + * Size of the list + */ + size_t size; + + /** + * Pointer to the array containing the list + */ + ElementT* elementsArray; + + /** + * Copy constructor not implemented. Would require dynamic memory allocation. + */ + FixedSizeList( + const FixedSizeList & + /**< [IN] Other list */); + + public: + /** + * Definition of an iterator + */ + typedef ElementT * iterator; + + /** + * Definition of a const iterator + */ + typedef const ElementT * const_iterator; + + /** + * Constructor, initializes list with given capacity + */ + FixedSizeList( + size_t max_size + /**< [IN] Capacity of the list */); + + /** + * Gets the current size of the list + * + * \return Size of the list + */ + inline size_t GetSize() const; + + /** + * Gets the capacity of the list + * + * \return The capacity of the list + */ + inline size_t GetMaxSize() const; + + /** + * Removes all elements from the list without changing the capacity + */ + inline void clear(); + + /** + * Iterator pointing to the first element + * + * \return Begin iterator + */ + iterator begin() const; + + /** + * Iterator pointing beyond the last element + * + * \return End iterator + */ + iterator end() const; + + /** + * Copies the elements of another list to this list. The capacity of + * this list has to be greater than or equal to the size of the other list. + */ + FixedSizeList & operator=( + const FixedSizeList & other + /**< [IN] Other list */); + + /** + * Appends an element to the end of the list + * + * \return \c false if the operation was not successful because the list is + * full, otherwise \c true. + */ + bool PushBack( + ElementT const el + /**< [IN] Element to append to the list */); + + /** + * Destructs the list. + */ + ~FixedSizeList(); +}; + +/** + * Hazard pointer entry for a single thread. Holds the actual guards that + * determine if the current thread is about to use the guarded pointer. + * Guarded pointers are protected and not deleted. + * + * Moreover, the retired list for this thread is contained. It determines + * the pointers that have been allocated from this thread, but are not used + * anymore by this thread. However, another thread could have a guard on it, + * so the pointer cannot be deleted immediately. + * + * For the scan operation, the intersection of the guarded pointers from all + * threads and the retired list has to be computed. For this computation, we + * need thread local temporary lists which are also contained here. + * + * \tparam GuardType The type of guard, usually a pointer. + */ +template< typename GuardType > +class HazardPointerThreadEntry { + private: + /** + * Value of the undefined guard (means that no guard is set). + */ + GuardType undefined_guard; + + /** + * The number of guards per thread. Determines the size of the guard array. + */ + int guards_per_thread; + + /** + * The capacity of the retired list. It is determined by number of guards, + * retired threshold, and maximum number of threads. + */ + size_t max_size_retired_list; + + /** + * Set to true if the current thread is active. Is used for a thread to + * signal that it is leaving. If a thread has left, the other threads are + * responsible for cleaning up its retired list. + */ + embb::base::Atomic< int > is_active; + + /** + * The guarded pointer of this thread, has size \c guard_per_thread. + */ + embb::base::Atomic< GuardType >* guarded_pointers; + + /** + * The retired list of this thread, contains pointer that shall be released + * when no thread holds a guard on it anymore. + */ + FixedSizeList< GuardType > retired_list; + + /** + * Temporary retired list, has same capacity as \c retired_list, It is used to + * compute the intersection of all guards and the \c retired list. + */ + FixedSizeList< GuardType > retired_list_temp; + + /** + * Temporary guards list. Used to compute the intersection of all guards and + * the \c retired_list. + */ + FixedSizeList< GuardType > hazard_pointer_list_temp; + + /** + * HazardPointerThreadEntry shall not be copied + */ + HazardPointerThreadEntry(const HazardPointerThreadEntry&); + + /** + * HazardPointerThreadEntry shall not be assigned + */ + HazardPointerThreadEntry & operator= (const HazardPointerThreadEntry&); + + public: + /** + * Checks if current thread is active (with respect to participating in hazard + * pointer management) + * + * \return \c true if the current thread is active, otherwise \c false. + */ + bool IsActive(); + + /** + * Tries to set the active flag to true (atomically). Used if the current + * thread is not active anymore as lock for another thread to help cleaning + * up hazard pointer. + * + * \return \c true if this thread was successful setting the active flag, + * otherwise \c false. + */ + bool TryReserve(); + + /** + * Deactivates current thread by atomically setting active flag to false. + */ + void Deactivate(); + + /** + * Gets the count of current retired pointer for the current thread. + * + * \return Count of current retired pointer + */ + size_t GetRetiredCounter(); + + /** + * Gets the retired list. + * + * \return Reference to \c retired_list + */ + FixedSizeList< GuardType >& GetRetired(); + + /** + * Gets the temporary retired list. + * + * \return Reference to \c retired_list_temp + */ + FixedSizeList< GuardType >& GetRetiredTemp(); + + /** + * Gets the temporary hazard pointer list. + * + * \return Reference to \c hazard_pointer_list_temp + */ + FixedSizeList< GuardType >& GetHazardTemp(); + + /** + * Sets the retired list. + */ + void SetRetired( + embb::containers::internal::FixedSizeList< GuardType > const & retired_list + /**< [IN] Retired list */); + + /** + * Constructor + */ + HazardPointerThreadEntry( + GuardType undefined_guard, + /**< [IN] Value of the undefined guard (e.g. NULL) */ + int guards_per_thread, + /**< [IN] Number of guards per thread */ + size_t max_size_retired_list + /**< [IN] The capacity of the retired list(s) */); + + /** + * Destructor + * + * Deallocate lists + */ + ~HazardPointerThreadEntry(); + + /** + * Gets the guard at the specified position. + * Positions are numbered, beginning with 0. + */ + GuardType GetGuard( + int pos + /**< [IN] Position of the guard */) const; + + /** + * Adds pointer to the retired list + */ + void AddRetired( + GuardType pointerToGuard + /**< [IN] Guard to retire */); + + /** + * Guards pointer + */ + void GuardPointer( + int guardNumber, + /**< [IN] Position of guard */ + GuardType pointerToGuard + /**<[IN] Pointer to guard */); + + /** + * Sets the current thread active, i.e., announce that the thread + * participates in managing hazard pointer. + */ + void SetActive( + bool active + /**<[IN] \c true for active, \c false for inactive */); +}; + +/** + * HazardPointer implementation as presented in: + * + * Maged M. Michael. "Hazard pointers: Safe memory reclamation for lock-free + * objects." IEEE Transactions on Parallel and Distributed Systems, 15.6 (2004) + * : 491-504. + * + * In contrast to the original implementation, our implementation only uses + * fixed-size memory. There is a safe upper limit, hazard pointer are guaranteed + * to not consume more memory. Memory is allocated solely at initialization. + * + * Hazard pointers solve the ABA problem for lock-free algorithms. Before + * accessing a pointer, threads announce that they want to access this pointer + * and then check if the pointer is still valid. This announcement is done by + * placing a guard. It is guaranteed that the pointer is not reused until all + * threads remove their guards to this pointer. Objects, these pointers are + * pointing to, can therefore not be deleted directly. Instead, these pointers + * are put into a list for later deletion (retired list). Regularly, this list + * is processed to check which pointers can be deleted. If a pointer can be + * deleted, a callback function provided by the user is called. The user can + * then, e.g., free the respective object, so that the pointer can be safely + * reused. + */ +template< typename GuardType > +class HazardPointer { + private: + /** + * Concrete hazard pointer entry type + */ + typedef HazardPointerThreadEntry < GuardType > + HazardPointerThreadEntry_t; + + /** + * The guard value denoting "not guarding" + */ + GuardType undefined_guard; + + /** + * The capacity of the retired list (safe upper bound for retired list size) + */ + int retired_list_max_size; + + /** + * Guards that can be set per thread + */ + int guards_per_thread; + + /** + * Array of HazardPointerElements. Each thread is assigned to one. + */ + HazardPointerThreadEntry_t* hazard_pointer_thread_entry_array; + + /** + * The threshold, determines at which size of the retired list pointers + * are tried to be deleted. + */ + static const double RETIRE_THRESHOLD; + + /** + * Each thread is assigned a thread index (starting with 0). + * Get the index of the current thread. + */ + static unsigned int GetCurrentThreadIndex(); + + /** + * The number of hazard pointers currently active. + */ + size_t active_hazard_pointer; + + /** + * Count of all hazard pointers. + */ + size_t hazard_pointers; + + /** + * The callback that is triggered when a retired guard can be + * freed. Usually, the user will call a free here. + */ + embb::base::Function free_guard_callback; + + /** + * Checks if the current size of the retired list exceeds the threshold, so + * that each retired guard is checked for being not hazardous anymore. + * + * \return \c true is threshold is exceeded, otherwise \c false. + */ + bool IsThresholdExceeded(); + + /** + * Gets the number of hazard pointe, currently active + * + * \return Number of active hazard pointers + */ + size_t GetActiveHazardPointers(); + + /** + * Gets the hazard pointer entry for the current thread + * + * \return Hazard pointer entry for current thread + */ + HazardPointerThreadEntry_t& + GetHazardPointerElementForCurrentThread(); + + /** + * Threads might leave from participating in hazard pointer management. + * This method helps all those threads processing their retired list. + */ + void HelpScan(); + + /** + * Checks the retired list of a hazard pointer entry for elements of the + * retired list that can be freed, and executes the delete callback for those + * elements. + */ + void Scan( + HazardPointerThreadEntry_t* currentHazardPointerEntry + /**<[IN] Hazard pointer entry that should be checked for elements that + can be deleted*/); + + public: + /** + * Gets the capacity of one retired list + * + * \waitfree + */ + size_t GetRetiredListMaxSize() const; + + /** + * Initializes hazard pointer + * + * \notthreadsafe + * + * \memory + * - Let \c t be the number of maximal threads determined by EMBB + * - Let \c g be the number of guards per thread + * - Let \c x be 1.25*t*g + 1 + * + * We dynamically allocate \c x*(3*t+1) elements of size \c sizeof(void*). + */ + HazardPointer( + embb::base::Function free_guard_callback, + /**<[IN] Callback to the function that shall be called when a retired + guard can be deleted */ + GuardType undefined_guard, + /**<[IN] The guard value denoting "not guarded"*/ + int guards_per_thread + /**<[IN] Number of guards per thread*/); + + /** + * Deallocates lists for hazard pointer management. Note that no objects + * currently in the retired lists are deleted. This is the responsibility + * of the user. Usually, HazardPointer manages pointers of an object pool. + * After destructing HazardPointer, the object pool is deleted, so that + * everything is properly cleaned up. + */ + ~HazardPointer(); + + /** + * Announces that the current thread stops participating in hazard pointer + * management. The other threads now take care of his retired list. + * + * \waitfree + */ + void DeactivateCurrentThread(); + + /** + * Guards \c guardedElement with the guard at position \c guardPosition + */ + void GuardPointer(int guardPosition, GuardType guardedElement); + /** + * Enqueue a pointer for deletion. It is added to the retired list and + * deleted when no thread accesses it anymore. + */ + void EnqueuePointerForDeletion(GuardType guardedElement); +}; +} // namespace internal +} // namespace containers +} // namespace embb + +#include "./hazard_pointer-inl.h" + +#endif // EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ diff --git a/containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h b/containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h new file mode 100644 index 0000000..9ee96da --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/lock_free_mpmc_queue-inl.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_ + +/* + * The following algorithm uses hazard pointers and a lock-free value pool for + * memory management. For a description of the algorithm, see + * Maged M. Michael and Michael L. Scott. "Simple, fast, and practical + * non-blocking and blocking concurrent queue algorithms". Proceedings of the + * fifteenth annual ACM symposium on principles of distributed computing. + * ACM, 1996. (Figure 1, Page 4) + */ + +namespace embb { +namespace containers { +namespace internal { +template< typename T > +LockFreeMPMCQueueNode::LockFreeMPMCQueueNode() : +next(NULL) { +} + +template< typename T > +LockFreeMPMCQueueNode::LockFreeMPMCQueueNode(T const& element) : + next(NULL), + element(element) { +} + +template< typename T > +embb::base::Atomic< LockFreeMPMCQueueNode< T >* > & + LockFreeMPMCQueueNode::GetNext() { + return next; +} + +template< typename T > +T LockFreeMPMCQueueNode::GetElement() { + return element; +} +} // namespace internal + +template< typename T, typename ValuePool > +void LockFreeMPMCQueue:: +DeletePointerCallback(internal::LockFreeMPMCQueueNode* to_delete) { + objectPool.Free(to_delete); +} + +template< typename T, typename ValuePool > +LockFreeMPMCQueue::~LockFreeMPMCQueue() { + // Nothing to do here, did not allocate anything. +} + +template< typename T, typename ValuePool > +LockFreeMPMCQueue::LockFreeMPMCQueue(size_t capacity) : +capacity(capacity), +// Disable "this is used in base member initializer" warning. +// We explicitly want this. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4355) +#endif + delete_pointer_callback(*this, &LockFreeMPMCQueue::DeletePointerCallback), +#ifdef _MSC_VER +#pragma warning(pop) +#endif + hazardPointer(delete_pointer_callback, NULL, 2), + // Object pool, size with respect to the maximum number of retired nodes not + // eligible for reuse. +1 for dummy node. + objectPool( + hazardPointer.GetRetiredListMaxSize()* + embb::base::Thread::GetThreadsMaxCount() + + capacity + 1) { + // Allocate dummy node to reduce the number of special cases to consider. + internal::LockFreeMPMCQueueNode* dummyNode = objectPool.Allocate(); + // Initially, head and tail point to the dummy node. + head = dummyNode; + tail = dummyNode; +} + +template< typename T, typename ValuePool > +size_t LockFreeMPMCQueue::GetCapacity() { + return capacity; +} + +template< typename T, typename ValuePool > +bool LockFreeMPMCQueue::TryEnqueue(T const& element) { + // Get node from the pool containing element to enqueue. + internal::LockFreeMPMCQueueNode* node = objectPool.Allocate(element); + + // Queue full, cannot enqueue + if (node == NULL) + return false; + internal::LockFreeMPMCQueueNode* my_tail; + for (;;) { + my_tail = tail; + internal::LockFreeMPMCQueueNode* my_tail_next = my_tail->GetNext(); + + hazardPointer.GuardPointer(0, my_tail); + + // Check if pointer is still valid after guarding. + if (my_tail != tail) { + hazardPointer.GuardPointer(0, NULL); + continue; // Hazard pointer outdated, retry + } + + if (my_tail == tail) { + // If the next pointer of the tail node is null, the tail pointer + // points to the last object. We try to set the next pointer of the + // tail node to our new node. + if (my_tail_next == NULL) { + // This fails if the next pointer of the "cached" tail is not null + // anymore, i.e., another thread added a node before we could complete. + if (my_tail->GetNext().CompareAndSwap(my_tail_next, node)) + + // We successfully added our node. Still missing: increase tail pointer. + break; + // The tail pointer points not to the last object, first increase + } else { + // Try to increase the tail pointer. + tail.CompareAndSwap(my_tail, my_tail_next); + } + } + } + // We added our node. Try to update tail pointer. Need not succeed, if we + // fail, another thread will help us. + tail.CompareAndSwap(my_tail, node); + // Release guard + hazardPointer.GuardPointer(0, NULL); + + return true; +} + +template< typename T, typename ValuePool > +bool LockFreeMPMCQueue::TryDequeue(T & element) { + T value; + for (;;) { + internal::LockFreeMPMCQueueNode* my_head = head; + internal::LockFreeMPMCQueueNode* my_tail = tail; + internal::LockFreeMPMCQueueNode* my_head_next = my_head->GetNext(); + + // Head did not change + if (my_head == head) { + // Guard head + hazardPointer.GuardPointer(0, my_head); + + // Check if pointer is still valid after guarding. This check is + // essential, tests really crash if missing + if (my_head != head) { + hazardPointer.GuardPointer(0, NULL); + continue; // Hazard pointer outdated, retry + } + + // Check if pointer is still valid after guarding. This check is + // essential, tests really crash if missing + hazardPointer.GuardPointer(1, my_head_next); + if (my_head_next != my_head->GetNext()) { + hazardPointer.GuardPointer(1, NULL); + continue; // Hazard pointer outdated, retry + } + + if (my_tail == my_head) { + if (my_head_next == NULL) { + // Queue is empty. Release guards and return false. + + hazardPointer.GuardPointer(0, NULL); + hazardPointer.GuardPointer(1, NULL); + + // Queue is empty; + return false; + } + // Tail is not pointing to last element, help to increase + tail.CompareAndSwap(my_head, my_head_next); + } else { + value = my_head_next->GetElement(); + if (head.CompareAndSwap(my_head, my_head_next)) { + // It's our element. Release guard and enqueue my_head for + // deletion and leave. + hazardPointer.GuardPointer(0, NULL); + hazardPointer.GuardPointer(1, NULL); + hazardPointer.EnqueuePointerForDeletion(my_head); + break; + } + } + } + } + element = value; + return true; +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LOCK_FREE_MPMC_QUEUE_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h b/containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h new file mode 100644 index 0000000..1317fdd --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/lock_free_stack-inl.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_ + +/* + * The following algorithm uses hazard pointers and a lock-free value pool for + * memory management. For a description of the algorithm, see + * Maged M. Michael. "Hazard pointers: Safe memory reclamation for lock-free + * objects". IEEE Transactions on Parallel and Distributed Systems, 15.6 (2004): + * 491-504. + */ + +namespace embb { +namespace containers { +namespace internal { + template< typename T > + LockFreeStackNode< T >::LockFreeStackNode(T const& element) : + element(element) { + } + + template< typename T > + void LockFreeStackNode< T >::SetNext(LockFreeStackNode< T >* next) { + this->next = next; + } + + template< typename T > + LockFreeStackNode< T >* LockFreeStackNode< T >::GetNext() { + return next; + } + + template< typename T > + T LockFreeStackNode< T >::GetElement() { + return element; + } +} // namespace internal + +template< typename T, typename ValuePool > +void LockFreeStack< T, ValuePool >:: +DeletePointerCallback(internal::LockFreeStackNode* to_delete) { + objectPool.Free(to_delete); +} + +template< typename T, typename ValuePool > +LockFreeStack< T, ValuePool >::LockFreeStack(size_t capacity) : +capacity(capacity), +// Disable "this is used in base member initializer" warning. +// We explicitly want this. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4355) +#endif + delete_pointer_callback(*this, + &LockFreeStack::DeletePointerCallback), +#ifdef _MSC_VER +#pragma warning(pop) +#endif + hazardPointer(delete_pointer_callback, NULL, 1), + // Object pool, size with respect to the maximum number of retired nodes not + // eligible for reuse: + objectPool( + hazardPointer.GetRetiredListMaxSize()* + embb::base::Thread::GetThreadsMaxCount() + + capacity) { +} + +template< typename T, typename ValuePool > +size_t LockFreeStack< T, ValuePool >::GetCapacity() { + return capacity; +} + +template< typename T, typename ValuePool > +LockFreeStack< T, ValuePool >::~LockFreeStack() { + // Nothing to do here, did not allocate anything. +} + +template< typename T, typename ValuePool > +bool LockFreeStack< T, ValuePool >::TryPush(T const& element) { + internal::LockFreeStackNode* newNode = + objectPool.Allocate(element); + + // Stack full, cannot push + if (newNode == NULL) + return false; + + for (;;) { + internal::LockFreeStackNode* top_cached = top; + newNode->SetNext(top_cached); + if (top.CompareAndSwap(top_cached, newNode)) + return true; + } +} + +template< typename T, typename ValuePool > +bool LockFreeStack< T, ValuePool >::TryPop(T & element) { + internal::LockFreeStackNode* top_cached = top; + for (;;) { + top_cached = top; + + // Stack empty, cannot pop + if (top_cached == NULL) + return false; + + // Guard top_cached + hazardPointer.GuardPointer(0, top_cached); + + // Check if top is still top. If this is the case, it has not been + // retired yet (because before retiring that thing, the retiring thread + // would first have to update the top pointer). If not, we wait for the + // next round. + if (top != top_cached) + continue; + + bool compare_and_swap_suc = top.CompareAndSwap(top_cached, + top_cached->GetNext()); + + if (compare_and_swap_suc) { + break; + } else { + // We continue with the next and can unguard top_cached + hazardPointer.GuardPointer(0, NULL); + } + } + + T data = top_cached->GetElement(); + + // We don't need to read from this reference anymore, unguard it + hazardPointer.GuardPointer(0, NULL); + + hazardPointer.EnqueuePointerForDeletion(top_cached); + + element = data; + return true; +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LOCK_FREE_STACK_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h b/containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h new file mode 100644 index 0000000..3b44b15 --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/lock_free_tree_value_pool-inl.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_LOCK_FREE_TREE_VALUE_POOL_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LOCK_FREE_TREE_VALUE_POOL_INL_H_ + +namespace embb { +namespace containers { +template +int LockFreeTreeValuePool:: +GetSmallestPowerByTwoValue(int value) { + int result = 1; + while (result < value) result <<= 1; + return result; +} + +template +bool LockFreeTreeValuePool::IsLeaf( + int node ) { + if (node >= size - 1 && node <= 2 * size - 1) { + return true; + } + return false; +} + +template +bool LockFreeTreeValuePool:: +IsValid(int node) { + return (node >= 0 && node <= 2 * size - 1); +} + +template +int LockFreeTreeValuePool:: +GetLeftChildIndex(int node) { + int index = 2 * node + 1; + assert(IsValid(index)); + return index; +} + +template +int LockFreeTreeValuePool:: +GetRightChildIndex(int node) { + int index = 2 * node + 2; + assert(IsValid(index)); + return index; +} + +template +int LockFreeTreeValuePool:: +NodeIndexToPoolIndex(int node) { + assert(IsLeaf(node)); + return(node - (size - 1)); +} + +template +int LockFreeTreeValuePool:: +PoolIndexToNodeIndex(int index) { + int node = index + (size - 1); + assert(IsLeaf(node)); + return node; +} + +template +bool LockFreeTreeValuePool:: +IsRoot(int node) { + return(0 == node); +} + +template +int LockFreeTreeValuePool:: +GetParentNode(int node) { + int parent = (node - 1) / 2; + assert(parent >= 0 && parent < size - 1); + return parent; +} + +template +int LockFreeTreeValuePool:: +allocate_rec(int node, T& element) { + // If we are a leaf, we try to allocate a cell using CAS. + if (IsLeaf(node)) { + int pool_index = NodeIndexToPoolIndex(node); + + T expected = pool[pool_index]; + if (expected == Undefined) + return -1; + + if (pool[pool_index].CompareAndSwap(expected, Undefined)) { + element = expected; + return pool_index; + } + + return -1; + } + + int current; + int desired; + // Try to decrement node value. + // This is the point, where the algorithm becomes not wait-free. We have to + // atomically decrement the value in the node iff the result is greater than + // or equal to zero. This cannot be done atomically. + do { + current = tree[node]; + desired = current - 1; + if (desired < 0) + return -1; + } while (!tree[node].CompareAndSwap(current, desired)); + + int leftResult = allocate_rec(GetLeftChildIndex(node), element); + if (leftResult != -1) { + return leftResult; + } + int rightResult = (allocate_rec(GetRightChildIndex(node), element)); + // We are guaranteed to be successful either in the left or the right branch. + // It should not happen that we cannot allocate in either branch. + assert(rightResult != -1); + + return rightResult; +} + +template +void LockFreeTreeValuePool:: +Fill(int node, int elementsToStore, int power2Value) { + if (IsLeaf(node)) + return; + + tree[node] = elementsToStore; + + int postPower2Value = power2Value >> 1; + + // Value fits in left cell, don't bother with right cells + if (elementsToStore <= postPower2Value) { + Fill(GetLeftChildIndex(node), elementsToStore, power2Value >> 1); + } else { + Fill(GetLeftChildIndex(node), + postPower2Value, + postPower2Value); + + Fill(GetRightChildIndex(node), + elementsToStore - postPower2Value, + postPower2Value); + } +} + +template +int LockFreeTreeValuePool:: +Allocate(T & element) { + return allocate_rec(0, element); +} + +template +void LockFreeTreeValuePool:: +Free(T element, int index) { + assert(element != Undefined); + + // Put the element back + pool[index].Store(element); + + assert(index >= 0 && index < size); + int node = PoolIndexToNodeIndex(index); + + while (!IsRoot(node)) { + node = GetParentNode(node); + tree[node].FetchAndAdd(1); + } +} + +template< typename T, T Undefined, class PoolAllocator, class TreeAllocator > +template< typename ForwardIterator > +LockFreeTreeValuePool:: +LockFreeTreeValuePool(ForwardIterator first, ForwardIterator last) { + // Number of elements to store + real_size = static_cast(::std::distance(first, last)); + + // Let k be smallest number so that real_size <= 2^k, size = 2^k + size = GetSmallestPowerByTwoValue(real_size); + + // Size of binary tree without the leaves + tree_size = size - 1; + + // Pool stores elements of type T + pool = poolAllocator.allocate(static_cast(real_size)); + + // Tree holds the counter of not allocated elements + tree = treeAllocator.allocate(static_cast(tree_size)); + + int i = 0; + + // Store the elements from the range + for (ForwardIterator curIter(first); curIter != last; ++curIter) { + pool[i++] = *curIter; + } + + // Initialize the binary tree without leaves (counters) + Fill(0, static_cast(::std::distance(first, last)), size); +} + +template +LockFreeTreeValuePool:: +~LockFreeTreeValuePool() { + poolAllocator.deallocate(pool, static_cast(real_size)); + treeAllocator.deallocate(tree, static_cast(tree_size)); +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LOCK_FREE_TREE_VALUE_POOL_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/object_pool-inl.h b/containers_cpp/include/embb/containers/internal/object_pool-inl.h new file mode 100644 index 0000000..d1af0ee --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/object_pool-inl.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_OBJECT_POOL_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_OBJECT_POOL_INL_H_ + +namespace embb { +namespace containers { +template +ObjectPool:: +ReturningTrueIterator::ReturningTrueIterator(size_t count_value) : +count_value(count_value), + ret_value(true) +{} + +template +typename ObjectPool:: +ReturningTrueIterator::self_type +ObjectPool:: +ReturningTrueIterator::operator++() { + self_type i = *this; + count_value++; + return i; +} + +template +typename ObjectPool:: +ReturningTrueIterator::self_type ObjectPool:: +ReturningTrueIterator::operator++(int junk) { + count_value++; + return *this; +} + +template +typename ObjectPool:: +ReturningTrueIterator::reference ObjectPool:: +ReturningTrueIterator::operator*() { + return ret_value; +} + +template +typename ObjectPool:: +ReturningTrueIterator::pointer ObjectPool:: +ReturningTrueIterator::operator->() { + return &ret_value; +} + +template +bool ObjectPool:: +ReturningTrueIterator::operator==(const self_type& rhs) { + return count_value == rhs.count_value; +} + +template +bool ObjectPool:: +ReturningTrueIterator::operator!=(const self_type& rhs) { + return count_value != rhs.count_value; +} + +template +bool ObjectPool:: +IsContained(const T &obj) const { + if ((&obj < &objects[0]) || (&obj > &objects[capacity - 1])) { + return false; + } else { + return true; + } +} + +template +int ObjectPool:: +GetIndexOfObject(const T &obj) const { + assert(IsContained(obj)); + return(static_cast(&obj - &objects[0])); +} + +template +T* ObjectPool::AllocateRaw() { + int val; + int allocated_index = p.Allocate(val); + if (allocated_index == -1) { + return NULL; + } else { + T* ret_pointer = &(objects[allocated_index]); + + return ret_pointer; + } +} + +template +size_t ObjectPool::GetCapacity() { + return capacity; +} + +template +ObjectPool::ObjectPool(size_t capacity) : +capacity(capacity), + p(ReturningTrueIterator(0), ReturningTrueIterator(capacity)) { + // Allocate the objects (without construction, just get the memory) + objects = objectAllocator.allocate(capacity); +} + +template +void ObjectPool::Free(T* obj) { + int index = GetIndexOfObject(*obj); + obj->~T(); + + p.Free(1, index); +} + +template +T* ObjectPool::Allocate() { + T* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)T(); + + return rawObject; +} + +template +template +T* ObjectPool::Allocate( + Param1 const& param1) { + T* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)T(param1); + + return rawObject; +} + +template +template +T* ObjectPool::Allocate( + Param1 const& param1, Param2 const& param2) { + T* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)T(param1, param2); + + return rawObject; +} + +template +template +T* ObjectPool::Allocate( + Param1 const& param1, Param2 const& param2, + Param3 const& param3) { + T* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)T(param1, param2, param3); + + return rawObject; +} + +template +template +T* ObjectPool::Allocate( + Param1 const& param1, Param2 const& param2, + Param3 const& param3, Param4 const& param4) { + T* rawObject = AllocateRaw(); + if (rawObject != NULL) + new (rawObject)T(param1, param2, param3, param4); + + return rawObject; +} + +template +ObjectPool::~ObjectPool() { + // Deallocate the objects + objectAllocator.deallocate(objects, capacity); +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_OBJECT_POOL_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h b/containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h new file mode 100644 index 0000000..add64b2 --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/wait_free_array_value_pool-inl.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_WAIT_FREE_ARRAY_VALUE_POOL_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_WAIT_FREE_ARRAY_VALUE_POOL_INL_H_ + +namespace embb { +namespace containers { +template +void WaitFreeArrayValuePool:: +Free(T element, int index) { + assert(element != Undefined); + + // Just put back the element + pool[index].Store(element); +} + +template +int WaitFreeArrayValuePool:: +Allocate(T & element) { + for (int i = 0; i != size; ++i) { + T expected; + + // If the memory cell is not available, go ahead + if (Undefined == (expected = pool[i].Load())) + continue; + + // Try to get the memory cell + if (pool[i].CompareAndSwap(expected, Undefined)) { + // When the CAS was successful, this element is ours + element = expected; + return i; + } + } + return -1; +} + +template +template +WaitFreeArrayValuePool:: +WaitFreeArrayValuePool(ForwardIterator first, ForwardIterator last) { + size_t dist = static_cast(std::distance(first, last)); + + size = static_cast(dist); + + // Use the allocator to allocate an array of size dist + pool = allocator.allocate(dist); + + int i = 0; + + // Store the elements of the range + for (ForwardIterator curIter(first); curIter != last; ++curIter) { + pool[i++] = *curIter; + } +} + +template +WaitFreeArrayValuePool::~WaitFreeArrayValuePool() { + allocator.deallocate(pool, (size_t)size); +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_WAIT_FREE_ARRAY_VALUE_POOL_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h b/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h new file mode 100644 index 0000000..318b2ce --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_INTERNAL_WAIT_FREE_SPSC_QUEUE_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_WAIT_FREE_SPSC_QUEUE_INL_H_ + +/* + * The following algorithm is described in: + * Maurice Herlihy and Nir Shavit. "The Art of Multiprocessor Programming." + * Page 46. Morgan Kaufmann, 2008. (original: L. Lamport. "Specifying concurrent + * programs"). + */ + +namespace embb { +namespace containers { +template +WaitFreeSPSCQueue::WaitFreeSPSCQueue(size_t capacity) : +capacity(capacity), + head_index(0), + tail_index(0) { + queue_array = allocator.allocate(capacity); +} + +template +size_t WaitFreeSPSCQueue::GetCapacity() { + return capacity; +} + +template +bool WaitFreeSPSCQueue::TryEnqueue(T const & element) { + if (head_index - tail_index == capacity) + return false; + + queue_array[tail_index % capacity] = element; + this->tail_index++; + return true; +} + +template +bool WaitFreeSPSCQueue::TryDequeue(T & element) { + if (tail_index - head_index == 0) + return false; + + T x = queue_array[head_index % capacity]; + this->head_index++; + element = x; + return true; +} + +template +WaitFreeSPSCQueue::~WaitFreeSPSCQueue() { + allocator.deallocate(queue_array, capacity); +} +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_WAIT_FREE_SPSC_QUEUE_INL_H_ diff --git a/containers_cpp/include/embb/containers/lock_free_mpmc_queue.h b/containers_cpp/include/embb/containers/lock_free_mpmc_queue.h new file mode 100644 index 0000000..b129812 --- /dev/null +++ b/containers_cpp/include/embb/containers/lock_free_mpmc_queue.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_LOCK_FREE_MPMC_QUEUE_H_ +#define EMBB_CONTAINERS_LOCK_FREE_MPMC_QUEUE_H_ + +#include +#include + +#include +#include +#include + +#include +#include + +namespace embb { +namespace containers { +namespace internal { +/** + * Queue node + * + * Single linked lists, contains the element (\c element) and a pointer to the + * next node (\c next). + * + * \tparam T Element type + */ +template< typename T > +class LockFreeMPMCQueueNode { + private: + /** + * Pointer to the next node + */ + embb::base::Atomic< LockFreeMPMCQueueNode< T >* > next; + + /** + * The stored element + */ + T element; + + public: + /** + * Creates a queue node + * + * Explicitly allow uninitialized \c element, used for dummy node + */ + LockFreeMPMCQueueNode(); + + /** + * Creates a queue node + */ + LockFreeMPMCQueueNode( + T const& element + /**< [IN] The element of this queue node */ + ); + + /** + * Returns the next pointer + * + * \return The next pointer + */ + embb::base::Atomic< LockFreeMPMCQueueNode< T >* > & GetNext(); + + /** + * Returns the element held by this node + */ + T GetElement(); +}; +} // namespace internal + +/** + * Lock-free queue for multiple producers and multiple consumers + * + * \concept{CPP_CONCEPTS_QUEUE} + * + * \ingroup CPP_CONTAINERS_QUEUES + * + * \see WaitFreeSPSCQueue + * + * \tparam T Type of the queue elements + * \tparam ValuePool Type of the value pool used as basis for the ObjectPool + * which stores the elements. + */ +template< typename T, + typename ValuePool = embb::containers::LockFreeTreeValuePool < int, 0 > +> +class LockFreeMPMCQueue { + private: + /** + * The capacity of the queue. It is guaranteed that the queue can hold at + * least as many elements, maybe more. + */ + size_t capacity; + // Do not change the ordering of class local variables. + // Important for initialization. + + /** + * Callback to the method that is called by hazard pointers if a pointer is + * not hazardous anymore, i.e., can safely be reused. + */ + embb::base::Function*> + delete_pointer_callback; + + /** + * The hazard pointer object, used for memory management. + */ + embb::containers::internal::HazardPointer*> + hazardPointer; + + /** + * The object pool, used for lock-free memory allocation. + */ + ObjectPool< internal::LockFreeMPMCQueueNode, ValuePool > objectPool; + + /** + * Atomic pointer to the head node of the queue + */ + embb::base::Atomic< internal::LockFreeMPMCQueueNode* > head; + + /** + * Atomic pointer to the tail node of the queue + */ + embb::base::Atomic< internal::LockFreeMPMCQueueNode* > tail; + + /** + * The callback function, used to cleanup non-hazardous pointers. + * \see delete_pointer_callback + */ + void DeletePointerCallback(internal::LockFreeMPMCQueueNode* to_delete); + + public: + /** + * Creates a queue with the specified capacity. + * + * \memory + * Let \c t be the maximum number of threads and \c x be 2.5*t+1. + * Then, x*(3*t+1) elements of size sizeof(void*), \c x + * elements of size sizeof(T), and \c capacity+1 elements of size + * sizeof(T) are allocated. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_QUEUE + */ + LockFreeMPMCQueue( + size_t capacity + /**< [IN] Capacity of the queue */); + + /** + * Destroys the queue. + * + * \notthreadsafe + */ + ~LockFreeMPMCQueue(); + + /** + * Returns the capacity of the queue. + * + * \return Number of elements the queue can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Tries to enqueue an element into the queue. + * + * \return \c true if the element could be enqueued, \c false if the queue is + * full. + * + * \lockfree + * + * \note It might be possible to enqueue more elements into the queue than its + * capacity permits. + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryEnqueue( + T const& element + /**< [IN] Const reference to the element that shall be enqueued */ + ); + + /** + * Tries to dequeue an element from the queue. + * + * \return \c true if an element could be dequeued, \c false if the queue is + * empty. + * + * \lockfree + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryDequeue( + T & element + /**< [IN,OUT] Reference to the dequeued element. Unchanged, if the operation + was not successful. */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_LOCK_FREE_MPMC_QUEUE_H_ diff --git a/containers_cpp/include/embb/containers/lock_free_stack.h b/containers_cpp/include/embb/containers/lock_free_stack.h new file mode 100644 index 0000000..299f61e --- /dev/null +++ b/containers_cpp/include/embb/containers/lock_free_stack.h @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_LOCK_FREE_STACK_H_ +#define EMBB_CONTAINERS_LOCK_FREE_STACK_H_ + +#include +#include +#include +#include +#include + +/** + * \defgroup CPP_CONCEPTS_STACK Stack Concept + * Concept for thread-safe stacks + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * A stack is an abstract data type holding a collection of elements of some + * predetermined type. A stack provides two operations: \c TryPush and + * \c TryPop. \c TryPush tries to add an element to the collection, and + * \c TryPop tries to remove an element from the collection. A stack has LIFO + * (Last-In, First-out) semantics, i.e., the last element added to the + * collection (\c TryPush) is removed first (\c TryPop). The capacity \c cap of + * a stack defines the number of elements it can store (depending on the + * implementation, a stack might store more than \c cap elements, since for + * thread-safe memory management, more memory than necessary for holding \c cap + * elements has to be provided). + * + * \par Requirements + * - Let \c Stack be the stack class + * - Let \c T be the element type of the stack + * - Let \c capacity be a value of type \c size_t + * - Let \c element be a reference to an element of type \c T + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
\code{.cpp} Stack(capacity) \endcodeNothing + * Constructs a stack with capacity \c capacity that holds elements of + * type \c T. + *
\code{.cpp} TryPush(element) \endcode\code{.cpp} bool \endcode + * Tries to push \c element onto the stack. Returns \c false if the stack + * is full, otherwise \c true. + *
\code{.cpp} TryPop(element) \endcode\code{.cpp} bool \endcode + * Tries to pop an element from the stack. Returns \c false if the stack is + * empty, otherwise \c true. In the latter case, the popped element is + * stored in \c element which must be passed by reference. + *
+ * + * \} + */ + +/** + * \defgroup CPP_CONTAINERS_STACKS Stacks + * Concurrent stacks + * + * \ingroup CPP_CONTAINERS + * + * \see CPP_CONCEPTS_STACK + */ + +namespace embb { +namespace containers { +namespace internal { +/** + * Stack node + * + * Single linked list, contains the element (\c element) and a pointer to the + * next node (\c next). + * + * \tparam T Element type + */ +template< typename T > +class LockFreeStackNode { + private: + /** + * Pointer to the next node + */ + LockFreeStackNode< T >* next; + + /** + * The stored element + */ + T element; + + public: + /** + * Creates a stack node + */ + LockFreeStackNode( + T const& element + /**< [IN] The element of this stack node */ + ); + + /** + * Sets the next node + */ + void SetNext( + LockFreeStackNode< T >* next + /**< [IN] Pointer to the next node */ + ); + + /** + * Returns the next pointer + * + * \return The next pointer + */ + LockFreeStackNode< T >* GetNext(); + + /** + * Returns the element held by this node + */ + T GetElement(); +}; +} // namespace internal + +/** + * Lock-free stack + * + * \concept{CPP_CONCEPTS_STACK} + * + * \ingroup CPP_CONTAINERS_STACKS + * + * \tparam T Type of the stack elements + * \tparam ValuePool Type of the value pool used as basis for the ObjectPool + * which stores the elements. + */ +template< typename T, +typename ValuePool = embb::containers::LockFreeTreeValuePool < int, 0 > > +class LockFreeStack { + private: + /** + * The capacity of the stack. It is guaranteed that the stack can hold at + * least as many elements, maybe more. + */ + size_t capacity; + + /** + * Callback to the method that is called by hazard pointers if a pointer is + * not hazardous anymore, i.e., can safely be reused. + */ + embb::base::Function*> + delete_pointer_callback; + + /** + * The hazard pointer object, used for memory management. + */ + internal::HazardPointer*> hazardPointer; + + /** + * The callback function, used to cleanup non-hazardous pointers. + * \see delete_pointer_callback + */ + void DeletePointerCallback(internal::LockFreeStackNode* to_delete); + + /** + * The object pool, used for lock-free memory allocation. + */ + ObjectPool< internal::LockFreeStackNode, ValuePool > objectPool; + + /** + * Atomic pointer to the top node of the stack (element that is popped next) + */ + embb::base::Atomic*> top; + + public: + /** + * Creates a stack with the specified capacity. + * + * \memory + * Let \c t be the maximum number of threads and \c x be 1.25*t+1. + * Then, x*(3*t+1) elements of size sizeof(void*), \c x + * elements of size sizeof(T), and \c capacity elements of size + * sizeof(T) are allocated. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_STACK + */ + LockFreeStack( + size_t capacity + /**< [IN] Capacity of the stack */ + ); + + /** + * Returns the capacity of the stack. + * + * \return Number of elements the stack can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Destroys the stack. + * + * \notthreadsafe + */ + ~LockFreeStack(); + + /** + * Tries to push an element onto the stack. + * + * \return \c true if the element could be pushed, \c false if the stack is + * full. + * + * \lockfree + * + * \note It might be possible to push more elements onto the stack than its + * capacity permits. + * + * \see CPP_CONCEPTS_STACK + */ + bool TryPush( + T const& element + /**< [IN] Const reference to the element that shall be pushed */ + ); + + /** + * Tries to pop an element from the stack. + * + * \return \c true if an element could be popped, \c false if the stack is + * empty. + * + * \lockfree + * + * \see CPP_CONCEPTS_STACK + */ + bool TryPop( + T & element + /**< [IN,OUT] Reference to the popped element. Unchanged, if the operation + was not successful. */ + ); +}; + +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_LOCK_FREE_STACK_H_ diff --git a/containers_cpp/include/embb/containers/lock_free_tree_value_pool.h b/containers_cpp/include/embb/containers/lock_free_tree_value_pool.h new file mode 100644 index 0000000..0f5e149 --- /dev/null +++ b/containers_cpp/include/embb/containers/lock_free_tree_value_pool.h @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_LOCK_FREE_TREE_VALUE_POOL_H_ +#define EMBB_CONTAINERS_LOCK_FREE_TREE_VALUE_POOL_H_ + +#include +#include + +namespace embb { +namespace containers { +/** + * Lock-free value pool using binary tree construction. + * + * \concept{CPP_CONCEPTS_VALUE_POOL} + * + * \ingroup CPP_CONTAINERS_POOLS + * + * \see WaitFreeArrayValuePool + * + * \tparam T Element type (must support atomic operations such as \c int). + * \tparam Undefined Bottom element (cannot be stored in the pool) + * \tparam PoolAllocator Allocator used to allocate the pool array + * \tparam TreeAllocator Allocator used to allocate the array representing the + * binary tree. + */ +template >, +class TreeAllocator = embb::base::Allocator < embb::base::Atomic > +> +class LockFreeTreeValuePool { + /* + * Description of the algorithm: + * + * The binary tree is split into two parts, the leaves and the binary tree + * above the leaves. Example: + * + * b + * b b + * b b b b + * l l l l l l l l + * + * The elements b are the elements "above", the leaves l are the pool + * elements. The elements b are represented by the array tree, the elements l + * be the array pool. + * + * A binary tree for storing n elements has k = 2^j leaves, where j is the + * smallest number such that n <= 2^j holds. The variable with name size + * stores k. The variable tree_size holds the number of elements b, the tree + * above. It is size - 1. The array pool is not constructed with size k, but + * with the real size n, as only the first n elements from the pool array are + * accessed. The tree is used as if elements l and b form a binary tree + * together and would be stored in one array. That makes the algorithm + * easier. + * + * The elements b store the number of not allocated cells below. The pool + * elements l are either not allocated, and store the respective element, or + * are allocated and contain the element Undefined. + * + * A tree storing the elements a,b,c,d,e would therefore look like this: + * + * 8 + * 4 1 + * 2 2 1 0 + * pool[]: a b c d e + * + * tree = {8,4,1,2,2,1,0} + * pool = {'a','b','c','d','e'} + * size = 8 + * tree_size = 7 + * + * The algorithm for allocating an element starts at the root node and + * recursively traverses the tree. It tries to decrement a node (a decrement + * is actually a conditional decrement, i.e., a node is decremented iff the + * result is not less than 0. This is the place, where the algorithm is not + * wait-free anymore, as this cannot be implemented atomically.) and if + * successful, calls itself on the left child, if not successful, on the right + * child. If the algorithm encounters a leaf, it tries to reserve it by doing + * a CAS to Undefined. If that is successful, the element together with its + * pool index are returned. Otherwise, no element is returned. + * + * The algorithm for freeing an element is much more simple. The element is + * stored at the pool position it was allocated from and then, the algorithm + * walks the tree towards the root, thereby increasing the value of each + * visited node. + * + * For future work, the memory consumption could be further reduced by + * "stripping" away unused cells in the binary tree. In the example above, + * that would be cell 0 in the row "2 2 1 0". + */ + private: + // Private constructor + LockFreeTreeValuePool(); + + // Prevent copy-construction + LockFreeTreeValuePool(const LockFreeTreeValuePool&); + + // Prevent assignment + LockFreeTreeValuePool& operator=(const LockFreeTreeValuePool&); + + // See algorithm description above + int size; + + // See algorithm description above + int tree_size; + + // See algorithm description above + int real_size; + + // The tree above the pool + embb::base::Atomic* tree; + + // The actual pool + embb::base::Atomic* pool; + + PoolAllocator poolAllocator; + TreeAllocator treeAllocator; + + /** + * Computes smallest power of two fitting the specified value + * + * \return Let k be the smallest number so that value <= 2^k. Return 2^k. + */ + static int GetSmallestPowerByTwoValue(int value); + + /** + * Checks if a given node is a leaf + * + * \return \c true if node is a leaf, otherwise \c false + */ + bool IsLeaf( + int node + /**< [IN] Node index */ + ); + + /** + * Checks if node is valid + * + * \return \c true if node is valid, otherwise \c false. + */ + bool IsValid( + int node + /**< [IN] Node index */ + ); + + /** + * Gets the index of the left child. + * + * \pre The node has to be valid + * \return Index of the left child + */ + int GetLeftChildIndex( + int node + /**< [IN] Node index */ + ); + + /** + * Gets the index of the right child. + * + * \pre The node has to be valid + * \return Index of the right child + */ + int GetRightChildIndex( + int node + /**< [IN] Node index */ + ); + + /** + * The leaves represent the pool. They are indexed from 0 to pool_size. If + * a node is a leaf, this index is returned. + */ + int NodeIndexToPoolIndex( + int node + /**< [IN] Node index */ + ); + + /** + * Gets the leaf node index for a pool index. + * Inverse function for NodeIndexToPoolIndex. + */ + int PoolIndexToNodeIndex(int index); + + /** + * Checks if node index is root + * + * \return \c true if node index is root element, otherwise \c false + */ + bool IsRoot( + int node + /**< [IN] Node index */ + ); + + /** + * Get the parent node. + * + * \return Index of parent node + */ + int GetParentNode( + int node + /**< [IN] Node index */ + ); + + /** + * Tries to allocate an element starting from node \c node. If successful, + * the index of the node is returned and the allocated element is written to + * \c element. If the allocation is not successful, -1 is returned. + * + * \return Index of allocated element, or -1 if not successful. + * + */ + int allocate_rec( + int node, + /**< [IN] Node index */ + T& element + /**< [IN,OUT] Allocated element, if there is any */ + ); + + /** + * Instead of "freeing" each element for initializing the tree, we do this in + * one pass, as this is much faster. Supposed to be called recursively from + * the root node. Does not initialize the pool elements, only the managing + * binary tree. + */ + void Fill( + int node, + /**< [IN] Node index */ + int elementsToStore, + /**< [IN] Number of elements to be stored in the current branch */ + int power2Value + /**< [IN] Maximum number of elements this branch can hold */ + ); + + public: + /** + * Constructs a pool and fills it with the elements in the specified range. + * + * \memory Let n = \c std::distance(first, last)) and \c k be the + * minimum number such that n <= 2^k holds. Then, + * ((2^k)-1) * sizeof(embb::Atomic) + n*sizeof(embb::Atomic) + * bytes of memory are allocated. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + template + LockFreeTreeValuePool( + ForwardIterator first, + /**< [IN] Iterator pointing to the first element of the range the pool is + filled with */ + ForwardIterator last + /**< [IN] Iterator pointing to the last plus one element of the range the + pool is filled with */ + ); + + /** + * Destructs the pool. + * + * \notthreadsafe + */ + ~LockFreeTreeValuePool(); + + /** + * Allocates an element from the pool. + * + * \return Index of the element if the pool is not empty, otherwise \c -1. + * + * \lockfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + int Allocate( + T & element + /**< [IN,OUT] Reference to the allocated element. Unchanged, if the + operation was not successful. */ + ); + + /** + * Returns an element to the pool. + * + * \note The element must have been allocated with Allocate(). + * + * \lockfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + void Free( + T element, + /**< [IN] Element to be returned to the pool */ + int index + /**< [IN] Index of the element as obtained by Allocate() */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_LOCK_FREE_TREE_VALUE_POOL_H_ diff --git a/containers_cpp/include/embb/containers/object_pool.h b/containers_cpp/include/embb/containers/object_pool.h new file mode 100644 index 0000000..bbc8765 --- /dev/null +++ b/containers_cpp/include/embb/containers/object_pool.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_OBJECT_POOL_H_ +#define EMBB_CONTAINERS_OBJECT_POOL_H_ + +#include +#include + +#include +#include + +namespace embb { +namespace containers { + +/** + * \defgroup CPP_CONTAINERS_POOLS Pools + * Concurrent pools + * + * \ingroup CPP_CONTAINERS + */ + +/** + * Pool for thread-safe management of arbitrary objects. + * + * \ingroup CPP_CONTAINERS_POOLS + * + * \tparam T Element type + * \tparam ValuePool Type of the underlying value pool, determines whether + * the object pool is wait-free or lock-free + * \tparam ObjectAllocator Type of allocator used to allocate objects + */ +template, + class ObjectAllocator = embb::base::Allocator > +class ObjectPool { + private: + /** + * Allocator used to allocate elements of the object pool + */ + ObjectAllocator objectAllocator; + + /** + * Array holding the allocated object + */ + T* objects; + + /** + * Capacity of the object pool + */ + size_t capacity; + + /** + * Underlying value pool + */ + ValuePool p; + + /** + * Helper providing a virtual iterator that just returns true in each + * iteration step. Used for filling the value pool. Implements the normal + * C++ iterator concept. Not further documented here. + */ + class ReturningTrueIterator { + public: + typedef ReturningTrueIterator self_type; + typedef int value_type; + typedef int& reference; + typedef int* pointer; + typedef std::forward_iterator_tag iterator_category; + typedef int difference_type; + explicit ReturningTrueIterator(size_t count_value); + self_type operator++(); + self_type operator++(int junk); + reference operator*(); + pointer operator->(); + bool operator==(const self_type& rhs); + bool operator!=(const self_type& rhs); + + private: + size_t count_value; + int ret_value; + }; + + bool IsContained(const T &obj) const; + int GetIndexOfObject(const T &obj) const; + T* AllocateRaw(); + + public: + /** + * Constructs an object pool with capacity \c capacity. + * + * \memory Allocates \c capacity elements of type \c T. + * + * \notthreadsafe + */ + ObjectPool( + size_t capacity + /**< [IN] Number of elements the pool can hold */ + ); + + /** + * Destructs the pool. + * + * \notthreadsafe + */ + ~ObjectPool(); + + /** + * Returns the capacity of the pool. + * + * \return Number of elements the pool can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Returns an element to the pool. + * + * If the underlying value pool is wait-free/lock-free, this operation is + * also wait-free/lock-free, respectively. + * + * \note The element must have been allocated with Allocate(). + */ + void Free( + T* obj + /**< [IN] Pointer to the object to be freed */ + ); + +#ifdef DOXYGEN + /** + * Allocates an element from the pool. + * + * If the underlying value pool is wait-free/lock-free, this operation is + * also wait-free/lock-free, respectively. + * + * \return Pointer to the allocated object if successful, otherwise \c NULL. + * + * \param ... Arguments of arbitrary type, passed to the object's constructor + */ + T* Allocate(...); +#else + T* Allocate(); + + template + T* Allocate(Param1 const& param1); + + template + T* Allocate(Param1 const& param1, Param2 const& param2); + + template + T* Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3); + + template + T* Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3, Param4 const& param4); + +#endif +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_OBJECT_POOL_H_ diff --git a/containers_cpp/include/embb/containers/wait_free_array_value_pool.h b/containers_cpp/include/embb/containers/wait_free_array_value_pool.h new file mode 100644 index 0000000..1cbd90d --- /dev/null +++ b/containers_cpp/include/embb/containers/wait_free_array_value_pool.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_WAIT_FREE_ARRAY_VALUE_POOL_H_ +#define EMBB_CONTAINERS_WAIT_FREE_ARRAY_VALUE_POOL_H_ + +#include +#include + +namespace embb { +namespace containers { +/** + * \defgroup CPP_CONCEPTS_VALUE_POOL Value Pool Concept + * Concept for thread-safe value pools + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * A value pool is a fixed-size multiset of elements, where each element has a + * unique index. The elements cannot be modified and are given at construction + * time (by providing first/last iterators). A value pool provides two + * operations: \c Allocate and \c Free. \c Allocate removes an element from the + * pool, and \c Free returns an element to the pool. It is only allowed to + * free elements that have previously been allocated. + * + * \par Requirements + * - Let \c Pool be the pool class + * - Let \c T be the element type of the pool. Atomic operations must be + * possible on \c T. + * - Let \c b, d be objects of type \c T + * - Let \c i, j be forward iterators supporting \c std::distance. + * - Let \c c be an object of type \c T& + * - Let \c e be a value of type \c int + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
\code{.cpp} Pool(i, j) \endcode + * Nothing + * Constructs a value pool holding elements of type \c T, where \c b is the + * bottom element. The bottom element cannot be stored in the pool, it is + * exclusively used to mark empty cells. The pool initially contains + * \c std::distance(i, j) elements which are copied during construction from + * the range \c [i, j). A concrete class satisfying the value pool concept + * might provide additional template parameters for specifying allocators. + *
\code{.cpp} Allocate(c) \endcode\c int + * Gets an element from the pool. Returns -1, if no element is available, + * i.e., the pool is empty. Otherwise, returns the index of the element in + * the pool. The value of the pool element is written into reference \c c. + *
\code{.cpp} Free(d, e) \endcode\c voidReturns an element \c d to the pool, where \c e is its index. The + * values of \c d and \c e have to match the values of the previous call to + * \c Allocate. For each allocated element, \c Free must be called exactly + * once.
+ * + * \} + */ + +/** + * Wait-free value pool using array construction + * + * \concept{CPP_CONCEPTS_VALUE_POOL} + * + * \ingroup CPP_CONTAINERS_POOLS + * + * \see LockFreeTreeValuePool + * + * \tparam T Element type (must support atomic operations such as \c int). + * \tparam Undefined Bottom element (cannot be stored in the pool) + * \tparam Allocator Allocator used to allocate the pool array + */ +template > > +class WaitFreeArrayValuePool { + private: + int size; + embb::base::Atomic* pool; + WaitFreeArrayValuePool(); + Allocator allocator; + + // Prevent copy-construction + WaitFreeArrayValuePool(const WaitFreeArrayValuePool&); + + // Prevent assignment + WaitFreeArrayValuePool& operator=(const WaitFreeArrayValuePool&); + + public: + /** + * Constructs a pool and fills it with the elements in the specified range. + * + * \memory Dynamically allocates n*sizeof(embb::base::Atomic) + * bytes, where n = std::distance(first, last) is the number + * of pool elements. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + template + WaitFreeArrayValuePool( + ForwardIterator first, + /**< [IN] Iterator pointing to the first element of the range the pool is + filled with */ + ForwardIterator last + /**< [IN] Iterator pointing to the last plus one element of the range the + pool is filled with */ + ); + + /** + * Destructs the pool. + * + * \notthreadsafe + */ + ~WaitFreeArrayValuePool(); + + /** + * Allocates an element from the pool. + * + * \return Index of the element if the pool is not empty, otherwise \c -1. + * + * \waitfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + int Allocate( + T & element + /**< [IN,OUT] Reference to the allocated element. Unchanged, if the + operation was not successful. */ + ); + + /** + * Returns an element to the pool. + * + * \note The element must have been allocated with Allocate(). + * + * \waitfree + * + * \see CPP_CONCEPTS_VALUE_POOL + */ + void Free( + T element, + /**< [IN] Element to be returned to the pool */ + int index + /**< [IN] Index of the element as obtained by Allocate() */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_WAIT_FREE_ARRAY_VALUE_POOL_H_ diff --git a/containers_cpp/include/embb/containers/wait_free_spsc_queue.h b/containers_cpp/include/embb/containers/wait_free_spsc_queue.h new file mode 100644 index 0000000..ea9e64d --- /dev/null +++ b/containers_cpp/include/embb/containers/wait_free_spsc_queue.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2014, 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 EMBB_CONTAINERS_WAIT_FREE_SPSC_QUEUE_H_ +#define EMBB_CONTAINERS_WAIT_FREE_SPSC_QUEUE_H_ + +#include + +#include +#include +#include +#include +/** + * \defgroup CPP_CONCEPTS_QUEUE Queue Concept + * Concept for thread-safe queues + * + * \ingroup CPP_CONCEPT + * \{ + * \par Description + * A queue is an abstract data type holding a collection of elements of some + * predetermined type. A queue provides two operations: \c TryEnqueue and + * \c TryDequeue. \c TryEnqueue tries to add an element to the collection, and + * \c TryDequeue tries to remove an element from the collection. A queue has + * per-thread FIFO (First-In, First-out) semantics, i.e., if one thread enqueues + * two elements and another thread dequeues these elements, then they appear in + * the same order. The capacity \c cap of a queue defines the number of elements + * it can store (depending on the implementation, a queue might store more than + * \c cap elements, since for thread-safe memory management, more memory than + * necessary for holding \c cap elements has to be provided). + * + * \par Requirements + * - Let \c Queue be the queue class + * - Let \c T be the element type of the queue + * - Let \c capacity be a value of type \c size_t + * - Let \c element be a reference to an element of type \c T + * + * \par Valid Expressions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ExpressionReturn typeDescription
\code{.cpp} Queue(capacity) \endcodeNothing + * Constructs a queue with capacity \c capacity that holds elements of + * type \c T. + *
\code{.cpp} TryEnqueue(element) \endcode\code{.cpp} bool \endcode + * Tries to enqueue \c element into the queue. Returns \c false if the + * queue is full, otherwise \c true. + *
\code{.cpp} TryDequeue(element) \endcode\code{.cpp} bool \endcode + * Tries to dequeue an element from the queue. Returns \c false if the + * queue is empty, otherwise \c true. In the latter case, the dequeued + * element is stored in \c element which must be passed by reference. + *
+ * + * \} + */ + +/** + * \defgroup CPP_CONTAINERS_QUEUES Queues + * Concurrent queues + * + * \see CPP_CONCEPTS_QUEUE + * + * \ingroup CPP_CONTAINERS + * + */ +namespace embb { +namespace containers { +/** + * Wait-free queue for a single producer and a single consumer. + * + * \concept{CPP_CONCEPTS_QUEUE} + * + * \ingroup CPP_CONTAINERS_QUEUES + * + * \see LockFreeMPMCQueue + * + * \tparam T Type of the queue elements + * \tparam Allocator Allocator type for allocating queue elements. + */ +template > +class WaitFreeSPSCQueue { + private: + /** + * Allocator for allocating queue elements + */ + Allocator allocator; + + /** + * Capacity of the queue + */ + size_t capacity; + + /** + * Array holding the queue elements + */ + T* queue_array; + + /** + * Index of the head in the \c queue_array + */ + embb::base::Atomic head_index; + + /** + * Index of the tail in the \c queue_array + */ + embb::base::Atomic tail_index; + + public: + /** + * Creates a queue with the specified capacity. + * + * \memory Allocates \c capacity elements of type \c T. + * + * \notthreadsafe + * + * \see CPP_CONCEPTS_QUEUE + */ + WaitFreeSPSCQueue( + size_t capacity + /**< [IN] Capacity of the queue */ + ); + + /** + * Destroys the queue. + * + * \notthreadsafe + */ + ~WaitFreeSPSCQueue(); + + /** + * Returns the capacity of the queue. + * + * \return Number of elements the queue can hold. + * + * \waitfree + */ + size_t GetCapacity(); + + /** + * Tries to enqueue an element into the queue. + * + * \return \c true if the element could be enqueued, \c false if the queue is + * full. + * + * \waitfree + * + * \note Concurrently enqueueing elements by multiple producers leads to + * undefined behavior. + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryEnqueue( + T const & element + /**< [IN] Const reference to the element that shall be enqueued */ + ); + + /** + * Tries to dequeue an element from the queue. + * + * \return \c true if an element could be dequeued, \c false if the queue is + * empty. + * + * \waitfree + * + * \note Concurrently dequeueing elements by multiple consumers leads to + * undefined behavior. + * + * \see CPP_CONCEPTS_QUEUE + */ + bool TryDequeue( + T & element + /**< [IN,OUT] Reference to the dequeued element. Unchanged, if the operation + was not successful. */ + ); +}; +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_WAIT_FREE_SPSC_QUEUE_H_ diff --git a/containers_cpp/src/dummy.cc b/containers_cpp/src/dummy.cc new file mode 100644 index 0000000..eb34253 --- /dev/null +++ b/containers_cpp/src/dummy.cc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 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. + */ + diff --git a/containers_cpp/test/hazard_pointer_test.cc b/containers_cpp/test/hazard_pointer_test.cc new file mode 100644 index 0000000..6ea7611 --- /dev/null +++ b/containers_cpp/test/hazard_pointer_test.cc @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, 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 "./hazard_pointer_test.h" + +namespace embb { +namespace containers { +namespace test { +HazardPointerTest::HazardPointerTest() : +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4355) +#endif + delete_pointer_callback(*this, &HazardPointerTest::DeletePointerCallback), +#ifdef _MSC_VER +#pragma warning(pop) +#endif +n_threads(static_cast + (partest::TestSuite::GetDefaultNumThreads())) { + n_elements_per_thread = 100; + n_elements = n_threads*n_elements_per_thread; + embb::base::Function < void, embb::base::Atomic* > + delete_pointer_callback( + *this, + &HazardPointerTest::DeletePointerCallback); + + // Kind of timing depending test. But tests exactly what hazard pointers are + // designed for. One thread creates an element, does something, retires it. + // Another thread also accesses this element (passed using a stack), by + // placing a guard it protects this element. If the guard was successfully + // placed, the pointer is not allowed to be deleted until the second thread + // removes this guard. + CreateUnit("HazardPointerTestThatGuardWorks"). + Pre(&HazardPointerTest::HazardPointerTest1_Pre, this). + Add( + &HazardPointerTest::HazardPointerTest1_ThreadMethod, + this, static_cast(n_threads)); +} + +void HazardPointerTest::HazardPointerTest1_Pre() { + embb_internal_thread_index_reset(); + object_pool = new embb::containers::ObjectPool< embb::base::Atomic > + (static_cast(n_elements)); + stack = new embb::containers::LockFreeStack< embb::base::Atomic* > + (static_cast(n_elements)); + hp = new embb::containers::internal::HazardPointer< embb::base::Atomic*> + (delete_pointer_callback, + NULL, + 1); +} + +void HazardPointerTest::HazardPointerTest1_ThreadMethod() { + unsigned int thread_index; + embb_internal_thread_index(&thread_index); + + for (int i = 0; i != n_elements_per_thread; ++i) { + embb::base::Atomic* allocated_object = object_pool->Allocate(0); + + hp->GuardPointer(0, allocated_object); + + bool success = stack->TryPush(allocated_object); + + PT_ASSERT(success == true); + + embb::base::Atomic* allocated_object_from_different_thread; + + int diff_count = 0; + + bool same = false; + bool success_pop; + + while ( + (success_pop = stack->TryPop(allocated_object_from_different_thread)) + == true + && allocated_object_from_different_thread == allocated_object + ) { + //try to make it probable to get an element from a different thread + //however, can be the same. Try 10000 times to get a different element. + if (diff_count++ > 10000) { + same = true; + break; + } + bool success = stack->TryPush(allocated_object_from_different_thread); + PT_ASSERT(success == true); + } + PT_ASSERT(success_pop == true); + allocated_object->Store(1); + hp->EnqueuePointerForDeletion(allocated_object); + + if (!same) { + hp->GuardPointer(0, allocated_object_from_different_thread); + + // if this holds, we were successful in guarding... otherwise we + // were to late, because the pointer has already been added + // to the retired list. + if (*allocated_object_from_different_thread == 0) { + // the pointer must not be deleted here! + vector_mutex.Lock(); + for (std::vector< embb::base::Atomic* >::iterator + it = deleted_vector.begin(); + it != deleted_vector.end(); + ++it) { + PT_ASSERT(*it != allocated_object_from_different_thread); + } + vector_mutex.Unlock(); + } + hp->GuardPointer(0, NULL); + } + } +} + +void HazardPointerTest::DeletePointerCallback +(embb::base::Atomic* to_delete) { + vector_mutex.Lock(); + deleted_vector.push_back(to_delete); + vector_mutex.Unlock(); +} +} // namespace test +} // namespace containers +} // namespace embb diff --git a/containers_cpp/test/hazard_pointer_test.h b/containers_cpp/test/hazard_pointer_test.h new file mode 100644 index 0000000..0ccfb99 --- /dev/null +++ b/containers_cpp/test/hazard_pointer_test.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_HAZARD_POINTER_TEST_H_ +#define CONTAINERS_CPP_TEST_HAZARD_POINTER_TEST_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +class HazardPointerTest : public partest::TestCase { + private: + embb::base::Function*> delete_pointer_callback; + + //used to allocate random stuff, we will just use the pointers, not the + //contents + embb::containers::ObjectPool< embb::base::Atomic >* object_pool; + + //used to move pointer between threads + embb::containers::LockFreeStack< embb::base::Atomic* >* stack; + embb::base::Mutex vector_mutex; + embb::containers::internal::HazardPointer*>* hp; + std::vector< embb::base::Atomic* > deleted_vector; + int n_threads; + int n_elements_per_thread; + int n_elements; + + public: + /** + * Adds test methods. + */ + HazardPointerTest(); + void HazardPointerTest1_Pre(); + void HazardPointerTest1_ThreadMethod(); + void DeletePointerCallback(embb::base::Atomic* to_delete); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_HAZARD_POINTER_TEST_H_ diff --git a/containers_cpp/test/main.cc b/containers_cpp/test/main.cc new file mode 100644 index 0000000..bb4f8ce --- /dev/null +++ b/containers_cpp/test/main.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include "./pool_test.h" +#include "./queue_test.h" +#include "./stack_test.h" +#include "./hazard_pointer_test.h" +#include "./object_pool_test.h" +#include +#include +#include +#include +#include +#include + +#define COMMA , + +PT_MAIN("Data Structures C++") { + unsigned int max_threads = + static_cast(2 * partest::TestSuite::GetDefaultNumThreads()); + embb_thread_set_max_count(max_threads); + + PT_RUN(embb::containers::test::PoolTest< + embb::containers::WaitFreeArrayValuePool >); + PT_RUN(embb::containers::test::PoolTest< + embb::containers::LockFreeTreeValuePool >); + + PT_RUN(embb::containers::test::HazardPointerTest); + + PT_RUN(embb::containers::test::QueueTest< + embb::containers::WaitFreeSPSCQueue >); + + PT_RUN(embb::containers::test::QueueTest< + embb::containers::LockFreeMPMCQueue COMMA true COMMA true >); + + PT_RUN(embb::containers::test::StackTest< + embb::containers::LockFreeStack >); + + PT_RUN(embb::containers::test::ObjectPoolTest + >); + + PT_RUN(embb::containers::test::ObjectPoolTest + >); +} diff --git a/containers_cpp/test/object_pool_test-inl.h b/containers_cpp/test/object_pool_test-inl.h new file mode 100644 index 0000000..87dc77c --- /dev/null +++ b/containers_cpp/test/object_pool_test-inl.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_INL_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +ObjectPoolTest::ObjectPoolTest() : +number_threads_(static_cast + (partest::TestSuite::GetDefaultNumThreads())), +number_iterations_(static_cast + (partest::TestSuite::GetDefaultNumIterations())), +allocations_per_thread(100), +allocations(allocations_per_thread*number_threads_), +objectPool(static_cast(allocations)) { + CreateUnit("ParallelObjectPoolTest"). + Pre(&ObjectPoolTest::ParallelObjectPoolTest_Pre, this). + Add(&ObjectPoolTest::ParallelObjectPoolTest_ThreadMethod, this, + static_cast(number_threads_), + static_cast(number_iterations_)). + Post(&ObjectPoolTest::ParallelObjectPoolTest_Post, this); +} + +template +void ObjectPoolTest::ParallelObjectPoolTest_Pre() { + embb_internal_thread_index_reset(); +} + +template +void ObjectPoolTest::ParallelObjectPoolTest_Post() { + //everything should be freed, we should be able to allocate everything... + ::std::vector allocated; + + for (int i = 0; i != allocations_per_thread * number_threads_; ++i) { + // write number to allocate object, to later check that the objects + // are disjoint. + allocated.push_back(objectPool.Allocate(i)); + } + + for (unsigned int i = 0; + i != static_cast(allocated.size()); ++i) { + // check that objects are disjoint + PT_ASSERT(static_cast(allocated[i]->GetThreadId()) == i); + objectPool.Free(allocated[i]); + } +} + +template +void ObjectPoolTest::ParallelObjectPoolTest_ThreadMethod() { + unsigned int thread_index; + + int return_val = embb_internal_thread_index(&thread_index); + + PT_ASSERT(EMBB_SUCCESS == return_val); + + ::std::vector allocated; + + for (int i = 0; i != allocations_per_thread; ++i) { + // all threads allocate without synchronization + ObjectPoolTestStruct* t = objectPool.Allocate(static_cast + (thread_index)); + allocated.push_back(t); + } + + for (unsigned int i = 0; + i != static_cast(allocations_per_thread); ++i) { + // check that no other thread wrote to the object, we have allocated... + PT_ASSERT(allocated[i]->GetThreadId() == + static_cast(thread_index)); + objectPool.Free(allocated[i]); + } +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_INL_H_ diff --git a/containers_cpp/test/object_pool_test.cc b/containers_cpp/test/object_pool_test.cc new file mode 100644 index 0000000..76da782 --- /dev/null +++ b/containers_cpp/test/object_pool_test.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 "./object_pool_test.h" +namespace embb { +namespace containers { +namespace test { +ObjectPoolTestStruct::ObjectPoolTestStruct(int thread_id) + : thread_id(thread_id) { +} + +int ObjectPoolTestStruct::GetThreadId() { + return thread_id; +} +} // namespace test +} // namespace containers +} // namespace embb diff --git a/containers_cpp/test/object_pool_test.h b/containers_cpp/test/object_pool_test.h new file mode 100644 index 0000000..58df808 --- /dev/null +++ b/containers_cpp/test/object_pool_test.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_H_ +#define CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_H_ + +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +class ObjectPoolTestStruct { + private: + int a; + int b; + int c[40]; + int thread_id; + public: + explicit ObjectPoolTestStruct(int thread_id); + int GetThreadId(); +}; + +template +class ObjectPoolTest : public partest::TestCase { + private: + int number_threads_; + int number_iterations_; + int allocations_per_thread; + int allocations; + embb::containers::ObjectPool objectPool; + + void ParallelObjectPoolTest_Pre(); + void ParallelObjectPoolTest_Post(); + void ParallelObjectPoolTest_ThreadMethod(); + + public: + /** + * Adds test methods. + */ + ObjectPoolTest(); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./object_pool_test-inl.h" +#endif // CONTAINERS_CPP_TEST_OBJECT_POOL_TEST_H_ diff --git a/containers_cpp/test/pool_test-inl.h b/containers_cpp/test/pool_test-inl.h new file mode 100644 index 0000000..90142a2 --- /dev/null +++ b/containers_cpp/test/pool_test-inl.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_POOL_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_POOL_TEST_INL_H_ + +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +const int PoolTest::pool_elements_per_thread = 5; + +template +PoolTest::PoolTest() : + number_threads_(static_cast + (partest::TestSuite::GetDefaultNumThreads())) { + CreateUnit("PoolTestStatic").Add(&PoolTest::PoolTestStatic, this); + + CreateUnit("AllocFreeParallel") + .Pre(&PoolTest::PreAllocFreeParallel, this) + .Add(&PoolTest::AllocFreeParallel, this, + static_cast(number_threads_), + static_cast(10000)) + .Post(&PoolTest::PostAllocFreeParallel, this); +} + +template +void PoolTest::PreAllocFreeParallel() { + std::vector< int > elements; + + for (int i = 0; i != (number_threads_*pool_elements_per_thread); ++i) { + elements.push_back(i + 1); + } + + pool = new ValuePool_t(elements.begin(), elements.end()); +} + +template +void PoolTest::AllocFreeParallel() { + ::std::vector< ::std::pair > allocated; + + for (int i = 0; i != pool_elements_per_thread; ++i) { + int element = 0; + int index = pool->Allocate(element); + + //memory is not allowed to run out + PT_ASSERT(index != -1); + + //we aren't not allowed to get anything that was not contained in the + //beginning + PT_ASSERT(element > 0 && + element <= (number_threads_*pool_elements_per_thread)); + + allocated.push_back(::std::make_pair(element, index)); + } + + for (int i = 0; i != pool_elements_per_thread; ++i) { + for (int j = 0; j != pool_elements_per_thread; ++j) { + if (i == j) continue; + + //we should never get equal elements! + PT_EXPECT(allocated[static_cast(i)] + .first != allocated[static_cast(j)].first); + } + } + + for (int i = 0; i != pool_elements_per_thread; ++i) { + pool->Free(allocated[static_cast(i)].first, + allocated[static_cast(i)].second); + } +} + +template +void PoolTest::PostAllocFreeParallel() { + int poolsize = (number_threads_*pool_elements_per_thread); + + ::std::vector elements; + + for (int i = 0; i != poolsize; ++i) { + int element; + int index = pool->Allocate(element); + + //all elements shall be returned... we shall not run out of mem. + PT_EXPECT(index != -1); + + elements.push_back(element); + } + + ::std::sort(elements.begin(), elements.end()); + //after all threads are finished, the same elements than in the beginning + //shall be contained + for (int i = 0; i != poolsize; ++i) { + PT_EXPECT(elements[static_cast(i)] == i+1); + } + + delete pool; +} + +template +void PoolTest::PoolTestStatic() { + size_t size = 100; + int* arr = new int[size]; + + for (int i = 0; i != static_cast(size); ++i) { + arr[static_cast(i)] = i; + } + + //create pool with bottom element -1, elements 0-(size-1) are added + ValuePool_t ap(arr, arr + size); + + int element; + int index; + std::vector< std::pair > allocated; + + //as long as possible, allocate elements + while ((index = ap.Allocate(element)) != -1) { + //write allocated elements to vector, to be able to + //free again! + allocated.push_back(std::make_pair(element, index)); + } + + std::vector indexes_to_free; + + //determine some elements we want to free + indexes_to_free.push_back(5); + indexes_to_free.push_back(16); + indexes_to_free.push_back(43); + indexes_to_free.push_back(90); + + for (int i = 0; i != static_cast(indexes_to_free.size()); ++i) { + //free those elements + ap.Free(allocated[static_cast( + indexes_to_free[static_cast(i)])].first, + allocated[static_cast( + indexes_to_free[static_cast(i)])] + .second); + } + + //if we allocate again, we should get those elements + for (int i = 0; i != static_cast(indexes_to_free.size()); i++) { + int element, index; + index = ap.Allocate(element); + + PT_EXPECT((index != -1)); + + std::vector::iterator it; + + it = std::find(indexes_to_free.begin(), indexes_to_free.end(), element); + + PT_EXPECT(it != indexes_to_free.end()); + } +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_POOL_TEST_INL_H_ diff --git a/containers_cpp/test/pool_test.h b/containers_cpp/test/pool_test.h new file mode 100644 index 0000000..c7b8e06 --- /dev/null +++ b/containers_cpp/test/pool_test.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_POOL_TEST_H_ +#define CONTAINERS_CPP_TEST_POOL_TEST_H_ + +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +class PoolTest : public partest::TestCase { + public: + /** + * Adds test methods. + */ + PoolTest(); + + private: + int number_threads_; + void PoolTestStatic(); + void AllocFreeParallel(); + void PreAllocFreeParallel(); + void PostAllocFreeParallel(); + + ValuePool_t* pool; + static const int pool_elements_per_thread; +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./pool_test-inl.h" +#endif // CONTAINERS_CPP_TEST_POOL_TEST_H_ diff --git a/containers_cpp/test/queue_test-inl.h b/containers_cpp/test/queue_test-inl.h new file mode 100644 index 0000000..fba5d0b --- /dev/null +++ b/containers_cpp/test/queue_test-inl.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_QUEUE_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_QUEUE_TEST_INL_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +QueueTest::QueueTest() : +n_threads(static_cast + (partest::TestSuite::GetDefaultNumThreads())), + n_iterations(200), + n_queue_elements_per_thread(100), + n_queue_elements(n_queue_elements_per_thread*n_threads), + queueSize(0) { + CreateUnit("QueueTestSingleThreadEnqueueDequeue"). + Pre(&QueueTest::QueueTestSingleThreadEnqueueDequeue_Pre, this). + Add(&QueueTest::QueueTestSingleThreadEnqueueDequeue_ThreadMethod, this). + Post(&QueueTest::QueueTestSingleThreadEnqueueDequeue_Post, this); + + CreateUnit("QueueTestTwoThreadsSingleProducerSingleConsumer"). + Pre(&QueueTest::QueueTestSingleProducedSingleConsumer_Pre, this). + Add(&QueueTest::QueueTestSingleProducedSingleConsumer_ThreadMethod, + this, + 2, + TOTAL_PRODUCE_CONSUME_COUNT). + Post(&QueueTest::QueueTestSingleProducedSingleConsumer_Post, this); + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#endif + if (MultipleProducers == true && + MultipleConsumers == true) { +#ifdef _MSC_VER +#pragma warning(pop) +#endif + CreateUnit("QueueTestMultipleThreadsMultipleProducerMultipleConsumer"). + Pre(&QueueTest::QueueTestMultipleProducerMultipleConsumer_Pre, this). + Add(&QueueTest::QueueTestMultipleProducerMultipleConsumer_ThreadMethod, + this, + static_cast(n_threads), + static_cast(n_iterations)). + Post(&QueueTest::QueueTestMultipleProducerMultipleConsumer_Post, this); + } +} + +template +void QueueTest:: +QueueTestMultipleProducerMultipleConsumer_Pre() { + embb_internal_thread_index_reset(); + queue = new Queue_t(static_cast(n_queue_elements)); + + thread_local_vectors = + new std::vector[static_cast(n_threads)]; + + for (int i = 0; i != n_threads; ++i) { + int offset = n_queue_elements_per_thread * 2; + + for (int i2 = 0; i2 != n_queue_elements_per_thread; ++i2) { + int push_element = i2 + (offset*i); + thread_local_vectors[i].push_back(push_element); + expected_queue_elements.push_back(push_element); + } + } +} + +template +void QueueTest:: +QueueTestMultipleProducerMultipleConsumer_Post() { + std::vector produced; + for (int i = 0; i != n_threads; ++i) { + std::vector& loc_elements = thread_local_vectors[i]; + for (std::vector::iterator it = loc_elements.begin(); + it != loc_elements.end(); + ++it) { + produced.push_back(*it); + } + } + + PT_ASSERT(produced.size() == expected_queue_elements.size()); + + std::sort(expected_queue_elements.begin(), expected_queue_elements.end()); + std::sort(produced.begin(), produced.end()); + + for (unsigned int i = 0; + i != static_cast(produced.size()); ++i) { + PT_ASSERT(expected_queue_elements[i] == produced[i]); + } + + delete[] thread_local_vectors; + delete queue; +} + +template +void QueueTest:: +QueueTestMultipleProducerMultipleConsumer_ThreadMethod() { + unsigned int thread_index; + int return_val = embb_internal_thread_index(&thread_index); + + PT_ASSERT(EMBB_SUCCESS == return_val); + + std::vector& my_elements = thread_local_vectors[thread_index]; + + for (std::vector::iterator it = my_elements.begin(); + it != my_elements.end(); + ++it) { + int enq = *it; + bool success = queue->TryEnqueue(enq); + PT_ASSERT(success == true); + } + + my_elements.clear(); + + for (int i = 0; i != n_queue_elements_per_thread; ++i) { + int dequ; + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == true); + my_elements.push_back(dequ); + } +} + +template +void QueueTest:: +QueueTestSingleProducedSingleConsumer_Pre() { + embb_internal_thread_index_reset(); + + queue = new Queue_t(QUEUE_SIZE); + thread_selector_producer = -1; + produce_count = 0; + consume_count = 0; + consumed_elements.clear(); + produced_elements.clear(); +} + +template +void QueueTest:: +QueueTestSingleProducedSingleConsumer_Post() { + embb_atomic_memory_barrier(); + ::std::sort(consumed_elements.begin(), consumed_elements.end()); + ::std::sort(produced_elements.begin(), produced_elements.end()); + + PT_ASSERT(consumed_elements.size() == produced_elements.size()); + + for (unsigned int i = 0; + i != static_cast(consumed_elements.size()); i++) { + PT_ASSERT(consumed_elements[i] == produced_elements[i]); + } + + delete queue; +} + +template +void QueueTest:: +QueueTestSingleProducedSingleConsumer_ThreadMethod() { + unsigned int thread_index; + int return_val = embb_internal_thread_index(&thread_index); + PT_ASSERT(return_val == EMBB_SUCCESS); + + if (thread_selector_producer == -1) { + int expected = -1; + thread_selector_producer.CompareAndSwap(expected, + static_cast(thread_index)); + while (thread_selector_producer == -1) {} + } + + // we are the producer + if (static_cast(thread_selector_producer.Load()) == + thread_index) { + while (produce_count >= QUEUE_SIZE) {} + + int random_var = rand() % 10000; + bool success = queue->TryEnqueue(random_var); + PT_ASSERT(success == true); + produce_count++; + produced_elements.push_back(random_var); + // we are the consumer + } else { + while (consume_count < TOTAL_PRODUCE_CONSUME_COUNT) { + consume_count++; + + while (produce_count == 0) {} + + int consumed; + bool success = queue->TryDequeue(consumed); + PT_ASSERT(success == true); + produce_count--; + consumed_elements.push_back(consumed); + } + } +} + +template +void QueueTest:: +QueueTestSingleThreadEnqueueDequeue_ThreadMethod() { + for (int i = 0; i != QUEUE_SIZE; ++i) { + bool success = queue->TryEnqueue(i * 133); + PT_ASSERT(success == true); + } + for (int i = 0; i != QUEUE_SIZE; ++i) { + int dequ = -1; + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == true); + PT_ASSERT(dequ == i * 133); + } +} + +template +void QueueTest:: +QueueTestSingleThreadEnqueueDequeue_Pre() { + queue = new Queue_t(QUEUE_SIZE); +} +template +void QueueTest:: +QueueTestSingleThreadEnqueueDequeue_Post() { + delete queue; +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_QUEUE_TEST_INL_H_ diff --git a/containers_cpp/test/queue_test.h b/containers_cpp/test/queue_test.h new file mode 100644 index 0000000..69a3a87 --- /dev/null +++ b/containers_cpp/test/queue_test.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_QUEUE_TEST_H_ +#define CONTAINERS_CPP_TEST_QUEUE_TEST_H_ + +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +class QueueTest : public partest::TestCase { + private: + static const unsigned int QUEUE_SIZE = 100; + static const unsigned int TOTAL_PRODUCE_CONSUME_COUNT = 10000; + int n_threads; + embb::base::Atomic thread_selector_producer; + embb::base::Atomic produce_count; + std::vector consumed_elements; + std::vector produced_elements; + + //for multiple p/c + int n_iterations; + int n_queue_elements_per_thread; + int n_queue_elements; + std::vector expected_queue_elements; + std::vector* thread_local_vectors; + embb::base::Atomic queueSize; + + int consume_count; + Queue_t* queue; + + void QueueTestMultipleProducerMultipleConsumer_Pre(); + void QueueTestMultipleProducerMultipleConsumer_Post(); + void QueueTestMultipleProducerMultipleConsumer_ThreadMethod(); + void QueueTestSingleProducedSingleConsumer_Pre(); + void QueueTestSingleProducedSingleConsumer_Post(); + void QueueTestSingleProducedSingleConsumer_ThreadMethod(); + void QueueTestSingleThreadEnqueueDequeue_ThreadMethod(); + void QueueTestSingleThreadEnqueueDequeue_Pre(); + void QueueTestSingleThreadEnqueueDequeue_Post(); + + public: + QueueTest(); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./queue_test-inl.h" + +#endif // CONTAINERS_CPP_TEST_QUEUE_TEST_H_ diff --git a/containers_cpp/test/stack_test-inl.h b/containers_cpp/test/stack_test-inl.h new file mode 100644 index 0000000..f71dab8 --- /dev/null +++ b/containers_cpp/test/stack_test-inl.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTAINERS_CPP_TEST_STACK_TEST_INL_H_ +#define CONTAINERS_CPP_TEST_STACK_TEST_INL_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +StackTest::StackTest() : +n_threads(static_cast + (partest::TestSuite::GetDefaultNumThreads())), + n_iterations(200), + n_stack_elements_per_thread(100), + n_stack_elements(n_stack_elements_per_thread*n_threads), + stack(static_cast(n_stack_elements)), + stackSize(0) { + CreateUnit("StackTestThreadsPushAndPopToGlobalStack"). + Pre(&StackTest::StackTest1_Pre, this). + Add(&StackTest::StackTest1_ThreadMethod, this, + static_cast(n_threads), + static_cast(n_iterations)). + Post(&StackTest::StackTest1_Post, this); +} + +template +void StackTest::StackTest1_Pre() { + embb_internal_thread_index_reset(); + thread_local_vectors = + new std::vector[static_cast(n_threads)]; + + for (int i = 0; i != n_threads; ++i) { + int offset = n_stack_elements_per_thread * 2; + + for (int i2 = 0; i2 != n_stack_elements_per_thread; ++i2) { + int push_element = i2 + (offset*i); + thread_local_vectors[i].push_back(push_element); + expected_stack_elements.push_back(push_element); + } + } +} + +template +void StackTest::StackTest1_Post() { + std::vector produced; + for (int i = 0; i != n_threads; ++i) { + std::vector& loc_elements = thread_local_vectors[i]; + for (std::vector::iterator it = loc_elements.begin(); + it != loc_elements.end(); + ++it) { + produced.push_back(*it); + } + } + + PT_ASSERT(produced.size() == expected_stack_elements.size()); + + std::sort(expected_stack_elements.begin(), expected_stack_elements.end()); + std::sort(produced.begin(), produced.end()); + + for (unsigned int i = 0; + i != static_cast(produced.size()); ++i) { + PT_ASSERT(expected_stack_elements[i] == produced[i]); + } + + delete[] thread_local_vectors; +} + +template +void StackTest::StackTest1_ThreadMethod() { + unsigned int thread_index; + int return_val = embb_internal_thread_index(&thread_index); + + PT_ASSERT(EMBB_SUCCESS == return_val); + + std::vector& my_elements = thread_local_vectors[thread_index]; + + for (std::vector::iterator it = my_elements.begin(); + it != my_elements.end(); + ++it) { + bool success = stack.TryPush(*it); + PT_ASSERT(success == true); + } + + my_elements.clear(); + + for (int i = 0; i != n_stack_elements_per_thread; ++i) { + int return_elem; + bool success = stack.TryPop(return_elem); + PT_ASSERT(success == true); + my_elements.push_back(return_elem); + } +} +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_STACK_TEST_INL_H_ diff --git a/containers_cpp/test/stack_test.h b/containers_cpp/test/stack_test.h new file mode 100644 index 0000000..b8e25e9 --- /dev/null +++ b/containers_cpp/test/stack_test.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, Siemens AG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef CONTAINERS_CPP_TEST_STACK_TEST_H_ +#define CONTAINERS_CPP_TEST_STACK_TEST_H_ + +#include +#include +#include + +namespace embb { +namespace containers { +namespace test { +template +class StackTest : public partest::TestCase { + private: + int n_threads; + int n_iterations; + int n_stack_elements_per_thread; + int n_stack_elements; + Stack_t stack; + std::vector expected_stack_elements; + std::vector* thread_local_vectors; + embb::base::Atomic stackSize; + + public: + StackTest(); + + void StackTest1_Pre(); + + void StackTest1_Post(); + + void StackTest1_ThreadMethod(); +}; +} // namespace test +} // namespace containers +} // namespace embb + +#include "./stack_test-inl.h" + +#endif // CONTAINERS_CPP_TEST_STACK_TEST_H_ diff --git a/dataflow_cpp/CMakeLists.txt b/dataflow_cpp/CMakeLists.txt new file mode 100644 index 0000000..f5949aa --- /dev/null +++ b/dataflow_cpp/CMakeLists.txt @@ -0,0 +1,36 @@ +project (project_dataflow_cpp) + +file(GLOB_RECURSE EMBB_DATAFLOW_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_DATAFLOW_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_DATAFLOW_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(../CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_DATAFLOW_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_DATAFLOW_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_cpp/include + ) + +add_library (embb_dataflow_cpp ${EMBB_DATAFLOW_CPP_SOURCES} ${EMBB_DATAFLOW_CPP_HEADERS}) +target_link_libraries(embb_dataflow_cpp embb_mtapi_cpp embb_base_cpp embb_mtapi_c embb_base_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_dataflow_cpp_test ${EMBB_DATAFLOW_CPP_TEST_SOURCES}) + target_link_libraries(embb_dataflow_cpp_test embb_mtapi_cpp embb_mtapi_c partest + embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_dataflow_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_dataflow_cpp DESTINATION lib) diff --git a/dataflow_cpp/include/embb/dataflow/dataflow.h b/dataflow_cpp/include/embb/dataflow/dataflow.h new file mode 100644 index 0000000..0b9d98e --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/dataflow.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_DATAFLOW_H_ +#define EMBB_DATAFLOW_DATAFLOW_H_ + +/** + * \defgroup CPP_DATAFLOW Dataflow + * C++ library for parallel, stream-based applications. + * \ingroup CPP + */ + +#define EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY 0 + +#include + +#endif // EMBB_DATAFLOW_DATAFLOW_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/action.h b/dataflow_cpp/include/embb/dataflow/internal/action.h new file mode 100644 index 0000000..79da9e1 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/action.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_ACTION_H_ +#define EMBB_DATAFLOW_INTERNAL_ACTION_H_ + +#include + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class Action { + public: + Action() : node_(NULL), clock_(0), pending_(0) {} + Action(Node * node, int clock) : node_(node), clock_(clock), pending_(2) {} + + void RunSequential() { + pending_ = 1; + node_->Run(clock_); + pending_ = 0; + } + + void RunMTAPI(embb::mtapi::TaskContext & /*context*/) { + pending_ = 1; + node_->Run(clock_); + pending_ = 0; + } + + bool IsPending() const { return pending_ > 0; } + + int GetClock() const { return clock_; } + + private: + Node * node_; + int clock_; + volatile int pending_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_ACTION_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h b/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h new file mode 100644 index 0000000..493baf7 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/clock_listener.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_CLOCK_LISTENER_H_ +#define EMBB_DATAFLOW_INTERNAL_CLOCK_LISTENER_H_ + +namespace embb { +namespace dataflow { +namespace internal { + +class ClockListener { + public: + virtual ~ClockListener() {} + virtual void OnClock(int /*clock*/) = 0; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_CLOCK_LISTENER_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/constant_source.h b/dataflow_cpp/include/embb/dataflow/internal/constant_source.h new file mode 100644 index 0000000..c7fa38d --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/constant_source.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_CONSTANT_SOURCE_H_ +#define EMBB_DATAFLOW_INTERNAL_CONSTANT_SOURCE_H_ + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class ConstantSource + : public Node { + public: + typedef Outputs OutputsType; + + private: + OutputsType outputs_; + Type value_; + + public: + explicit ConstantSource(Type value) : value_(value) {} + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + GetOutput<0>().Send(Signal(clock, value_)); + } + + virtual void Start(int clock) { + Run(clock); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_CONSTANT_SOURCE_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/in.h b/dataflow_cpp/include/embb/dataflow/internal/in.h new file mode 100644 index 0000000..3ac6fc1 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/in.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_IN_H_ +#define EMBB_DATAFLOW_INTERNAL_IN_H_ + +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY +#include +#include +#endif + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Out; + +template +class In { + public: + typedef Signal SignalType; + + In() : connected_(false) {} + + SignalType const & GetSignal(int clock) const { + return values_[clock % Slices]; + } + + Type GetValue(int clock) const { + SignalType const & signal = GetSignal(clock); + if (signal.IsBlank()) + EMBB_THROW(embb::base::ErrorException, + "Signal is blank, cannot get a value.") + return signal.GetValue(); + } + + bool IsConnected() const { return connected_; } + void SetConnected() { connected_ = true; } + + void SetListener(ClockListener * listener) { listener_ = listener; } + + void Clear(int clock) { + const int idx = clock % Slices; + values_[idx].Clear(); + } + + friend class Out; + + private: + SignalType values_[Slices]; + ClockListener * listener_; + bool connected_; +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + SpinLock lock_; + std::vector history_; +#endif + + void Receive(SignalType const & value) { + const int idx = value.GetClock() % Slices; + if (values_[idx].GetClock() >= value.GetClock()) + EMBB_THROW(embb::base::ErrorException, + "Received signal does not increase clock."); + values_[idx] = value; + listener_->OnClock(value.GetClock()); +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + lock_.Lock(); + history_.push_back(value); + lock_.Unlock(); +#endif + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_IN_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/inputs.h b/dataflow_cpp/include/embb/dataflow/internal/inputs.h new file mode 100644 index 0000000..021f261 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/inputs.h @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_INPUTS_H_ +#define EMBB_DATAFLOW_INTERNAL_INPUTS_H_ + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template < + int, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil> +class Inputs; + +template +class Inputs + : public Tuple + , public ClockListener { + public: + void SetListener(ClockListener * /*notify*/) {} + bool AreNoneBlank(int /*clock*/) { return false; } + bool AreAtClock(int /*clock*/) { return true; } + virtual void OnClock(int /*clock*/) {} +}; + +template +class Inputs + : public Tuple, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil, + embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() { + for (int ii = 0; ii < Slices; ii++) + count_[ii] = 1; + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % Slices; + if (count_[idx] == 0) + EMBB_THROW(embb::base::ErrorException, + "All inputs already fired for this clock.") + if (--count_[idx] == 0) { + count_[idx] = 1; + listener_->OnClock(clock); + } + } + private: + embb::base::Atomic count_[Slices]; + ClockListener * listener_; +}; + +template +class Inputs + : public Tuple, In, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() { + for (int ii = 0; ii < Slices; ii++) + count_[ii] = 2; + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % Slices; + if (count_[idx] == 0) + EMBB_THROW(embb::base::ErrorException, + "All inputs already fired for this clock.") + if (--count_[idx] == 0) { + count_[idx] = 2; + listener_->OnClock(clock); + } + } + private: + embb::base::Atomic count_[Slices]; + ClockListener * listener_; +}; + +template +class Inputs + : public Tuple, In, In, + embb::base::internal::Nil, embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() { + for (int ii = 0; ii < Slices; ii++) + count_[ii] = 3; + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + this->template Get<2>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank() || + this->template Get<2>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock) && + (this->template Get<2>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + this->template Get<2>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % Slices; + if (count_[idx] == 0) + EMBB_THROW(embb::base::ErrorException, + "All inputs already fired for this clock.") + if (--count_[idx] == 0) { + count_[idx] = 3; + listener_->OnClock(clock); + } + } + private: + embb::base::Atomic count_[Slices]; + ClockListener * listener_; +}; + +template +class Inputs + : public Tuple, In, In, + In, embb::base::internal::Nil> + , public ClockListener { + public: + Inputs() { + for (int ii = 0; ii < Slices; ii++) + count_[ii] = 4; + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + this->template Get<2>().SetListener(this); + this->template Get<3>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank() || + this->template Get<2>().GetSignal(clock).IsBlank() || + this->template Get<3>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock) && + (this->template Get<2>().GetSignal(clock).GetClock() == clock) && + (this->template Get<3>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + this->template Get<2>().Clear(clock); + this->template Get<3>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % Slices; + if (count_[idx] == 0) + EMBB_THROW(embb::base::ErrorException, + "All inputs already fired for this clock.") + if (--count_[idx] == 0) { + count_[idx] = 4; + listener_->OnClock(clock); + } + } + private: + embb::base::Atomic count_[Slices]; + ClockListener * listener_; +}; + +template +class Inputs + : public Tuple, In, In, + In, In > + , public ClockListener { + public: + Inputs() { + for (int ii = 0; ii < Slices; ii++) + count_[ii] = 5; + } + void SetListener(ClockListener * listener) { + listener_ = listener; + this->template Get<0>().SetListener(this); + this->template Get<1>().SetListener(this); + this->template Get<2>().SetListener(this); + this->template Get<3>().SetListener(this); + this->template Get<4>().SetListener(this); + } + bool AreNoneBlank(int clock) { + return !( + this->template Get<0>().GetSignal(clock).IsBlank() || + this->template Get<1>().GetSignal(clock).IsBlank() || + this->template Get<2>().GetSignal(clock).IsBlank() || + this->template Get<3>().GetSignal(clock).IsBlank() || + this->template Get<4>().GetSignal(clock).IsBlank()); + } + bool AreAtClock(int clock) { + return + (this->template Get<0>().GetSignal(clock).GetClock() == clock) && + (this->template Get<1>().GetSignal(clock).GetClock() == clock) && + (this->template Get<2>().GetSignal(clock).GetClock() == clock) && + (this->template Get<3>().GetSignal(clock).GetClock() == clock) && + (this->template Get<4>().GetSignal(clock).GetClock() == clock); + } + void Clear(int clock) { + this->template Get<0>().Clear(clock); + this->template Get<1>().Clear(clock); + this->template Get<2>().Clear(clock); + this->template Get<3>().Clear(clock); + this->template Get<4>().Clear(clock); + } + virtual void OnClock(int clock) { + const int idx = clock % Slices; + if (count_[idx] == 0) + EMBB_THROW(embb::base::ErrorException, + "All inputs already fired for this clock.") + if (--count_[idx] == 0) { + count_[idx] = 5; + listener_->OnClock(clock); + } + } + private: + embb::base::Atomic count_[Slices]; + ClockListener * listener_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_INPUTS_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/node.h b/dataflow_cpp/include/embb/dataflow/internal/node.h new file mode 100644 index 0000000..fca2700 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/node.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_NODE_H_ +#define EMBB_DATAFLOW_INTERNAL_NODE_H_ + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class Node { + public: + Node() : sched_(NULL) {} + virtual ~Node() {} + virtual bool HasInputs() const { return false; } + virtual bool HasOutputs() const { return false; } + virtual void Run(int clock) = 0; + virtual void Start(int /*clock*/) { + EMBB_THROW(embb::base::ErrorException, + "Nodes are started implicitly."); + } + void SetScheduler(Scheduler * sched) { sched_ = sched; } + + protected: + Scheduler * sched_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_NODE_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/out.h b/dataflow_cpp/include/embb/dataflow/internal/out.h new file mode 100644 index 0000000..df20147 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/out.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_OUT_H_ +#define EMBB_DATAFLOW_INTERNAL_OUT_H_ + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Out { + public: + typedef Signal SignalType; + typedef In InType; + + Out() { + } + + void Send(SignalType const & value) { + for (size_t ii = 0; ii < targets_.size(); ii++) { + targets_[ii]->Receive(value); + } + } + + void Connect(InType & input) { + if (input.IsConnected()) { + EMBB_THROW(embb::base::ErrorException, + "Input is already connected.") + } else { + input.SetConnected(); + targets_.push_back(&input); + } + } + + void operator >> (InType & input) { + Connect(input); + } + + private: + std::vector< InType * > targets_; +}; + + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_OUT_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/outputs.h b/dataflow_cpp/include/embb/dataflow/internal/outputs.h new file mode 100644 index 0000000..9fb26e7 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/outputs.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_OUTPUTS_H_ +#define EMBB_DATAFLOW_INTERNAL_OUTPUTS_H_ + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template < + int, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil, + typename = embb::base::internal::Nil > +class Outputs; + +template +class Outputs + : public Tuple { + public: +}; + +template +class Outputs + : public Tuple, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil, + embb::base::internal::Nil> { + public: +}; + +template +class Outputs + : public Tuple, Out, embb::base::internal::Nil, + embb::base::internal::Nil, embb::base::internal::Nil> { + public: +}; + +template +class Outputs + : public Tuple, Out, Out, + embb::base::internal::Nil, embb::base::internal::Nil> { + public: +}; + +template +class Outputs + : public Tuple, Out, Out, + Out, embb::base::internal::Nil>{ + public: +}; + +template +class Outputs + : public Tuple, Out, Out, + Out, Out > { + public: +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_OUTPUTS_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/process.h b/dataflow_cpp/include/embb/dataflow/internal/process.h new file mode 100644 index 0000000..0b00b35 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/process.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_PROCESS_H_ +#define EMBB_DATAFLOW_INTERNAL_PROCESS_H_ + +#include +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template class Process; + +template < + int Slices, bool Serial, + typename I1, typename I2, typename I3, typename I4, typename I5, + typename O1, typename O2, typename O3, typename O4, typename O5> +class Process< Slices, Serial, Inputs, + Outputs > + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef Outputs OutputsType; + typedef ProcessExecutor< InputsType, OutputsType > ExecutorType; + typedef typename ExecutorType::FunctionType FunctionType; + + explicit Process(FunctionType function) + : executor_(function) { + input_clock_expected_ = 0; + inputs_.SetListener(this); + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + bool ordered = Serial; + if (ordered) { + // force ordering + while (input_clock_expected_ != clock) embb::base::Thread::CurrentYield(); + } + + executor_.Execute(clock, inputs_, outputs_); + //inputs_.Clear(clock); + + input_clock_expected_ = clock + 1; + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + virtual void OnClock(int clock) { + const int idx = clock % Slices; + if (!inputs_.AreAtClock(clock)) + EMBB_THROW(embb::base::ErrorException, + "Some inputs are not at expected clock.") + action_[idx] = Action(this, clock); + sched_->Spawn(action_[idx]); + } + + private: + InputsType inputs_; + OutputsType outputs_; + ExecutorType executor_; + embb::base::Atomic input_clock_expected_; + Action action_[Slices]; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_PROCESS_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/process_executor.h b/dataflow_cpp/include/embb/dataflow/internal/process_executor.h new file mode 100644 index 0000000..2602dc8 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/process_executor.h @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_PROCESS_EXECUTOR_H_ +#define EMBB_DATAFLOW_INTERNAL_PROCESS_EXECUTOR_H_ + +#include + +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class ProcessExecutor; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + function_( + inputs.template Get<0>().GetValue(clock), + o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + O3 o3; + function_( + inputs.template Get<0>().GetValue(clock), + o1, o2, o3); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + outputs.template Get<2>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + O3 o3; + O4 o4; + function_( + inputs.template Get<0>().GetValue(clock), + o1, o2, o3, o4); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + outputs.template Get<3>().Send(Signal(clock, o4)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + outputs.template Get<2>().Send(Signal(clock)); + outputs.template Get<3>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + O3 o3; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + o1, o2, o3); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + outputs.template Get<2>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + O2 o2; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + outputs.template Get<1>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +template +class ProcessExecutor< Inputs, Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit ProcessExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs, + Outputs & outputs) { + if (inputs.AreNoneBlank(clock)) { + O1 o1; + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + inputs.template Get<3>().GetValue(clock), + o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } else { + outputs.template Get<0>().Send(Signal(clock)); + } + } + + private: + FunctionType function_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_PROCESS_EXECUTOR_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/scheduler.h b/dataflow_cpp/include/embb/dataflow/internal/scheduler.h new file mode 100644 index 0000000..1b0214b --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/scheduler.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SCHEDULER_H_ +#define EMBB_DATAFLOW_INTERNAL_SCHEDULER_H_ + +namespace embb { +namespace dataflow { +namespace internal { + +class Action; + +class Scheduler { + public: + Scheduler() {} + virtual ~Scheduler() {} + virtual void Spawn(Action & action) = 0; + virtual void WaitForSlice(int slice) = 0; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SCHEDULER_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h b/dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h new file mode 100644 index 0000000..54f7c43 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/scheduler_mtapi.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SCHEDULER_MTAPI_H_ +#define EMBB_DATAFLOW_INTERNAL_SCHEDULER_MTAPI_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class SchedulerMTAPI : public Scheduler { + public: + SchedulerMTAPI() { + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + for (int ii = 0; ii < Slices; ii++) { + embb::mtapi::Group & group = node.CreateGroup(); + group_[ii] = &group; + } + } + virtual ~SchedulerMTAPI() { + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + for (int ii = 0; ii < Slices; ii++) { + group_[ii]->WaitAll(MTAPI_INFINITE); + node.DestroyGroup(*group_[ii]); + } + } + virtual void Spawn(Action & action) { + const int idx = action.GetClock() % Slices; + group_[idx]->Spawn(embb::base::MakeFunction(action, &Action::RunMTAPI)); + } + virtual void WaitForSlice(int slice) { + group_[slice]->WaitAll(MTAPI_INFINITE); + } + + private: + embb::mtapi::Group * group_[Slices]; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SCHEDULER_MTAPI_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h b/dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h new file mode 100644 index 0000000..02ea744 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/scheduler_sequential.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SCHEDULER_SEQUENTIAL_H_ +#define EMBB_DATAFLOW_INTERNAL_SCHEDULER_SEQUENTIAL_H_ + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class SchedulerSequential : public Scheduler { + public: + SchedulerSequential() {} + virtual ~SchedulerSequential() {} + virtual void Spawn(Action & action) { + action.RunSequential(); + } + virtual void WaitForSlice(int /*slice*/) {} +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SCHEDULER_SEQUENTIAL_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/select.h b/dataflow_cpp/include/embb/dataflow/internal/select.h new file mode 100644 index 0000000..e2c03c0 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/select.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SELECT_H_ +#define EMBB_DATAFLOW_INTERNAL_SELECT_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Select + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef Outputs OutputsType; + + Select() { + inputs_.SetListener(this); + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + if (GetInput<0>().GetSignal(clock).IsBlank()) { + GetOutput<0>().Send(Signal(clock)); + } else { + bool pred = GetInput<0>().GetValue(clock); + Type val; + if (pred) { + if (GetInput<1>().GetSignal(clock).IsBlank()) { + GetOutput<0>().Send(Signal(clock)); + } else { + val = GetInput<1>().GetValue(clock); + GetOutput<0>().Send(Signal(clock, val)); + } + } else { + if (GetInput<2>().GetSignal(clock).IsBlank()) { + GetOutput<0>().Send(Signal(clock)); + } else { + val = GetInput<2>().GetValue(clock); + GetOutput<0>().Send(Signal(clock, val)); + } + } + } + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + virtual void OnClock(int clock) { + //const int idx = clock % Slices; + if (!inputs_.AreAtClock(clock)) + EMBB_THROW(embb::base::ErrorException, + "Some inputs are not at expected clock.") + Run(clock); + } + + private: + InputsType inputs_; + OutputsType outputs_; + Action action_[Slices]; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SELECT_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/signal.h b/dataflow_cpp/include/embb/dataflow/internal/signal.h new file mode 100644 index 0000000..f28f3f9 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/signal.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SIGNAL_H_ +#define EMBB_DATAFLOW_INTERNAL_SIGNAL_H_ + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Signal { + public: + Signal() : blank_(true), value_(), clock_(-1) {} + Signal(int clock, Type value) : blank_(false), value_(value), clock_(clock) {} + explicit Signal(int clock) : blank_(true), value_(), clock_(clock) {} + Signal(Signal const & other) + : blank_(other.blank_), value_(other.value_), clock_(other.clock_) {} + void operator = (Signal const & rhs) { + lock_.Lock(); + blank_ = rhs.blank_; + value_ = rhs.value_; + clock_ = rhs.clock_; + lock_.Unlock(); + } + int GetClock() const { return clock_; } + bool IsBlank() const { return blank_; } + Type const & GetValue() const { return value_; } + void Clear() { + lock_.Lock(); + blank_ = true; + clock_ = -1; + lock_.Unlock(); + } + + private: + bool blank_; + Type value_; + int clock_; + SpinLock lock_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SIGNAL_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/sink.h b/dataflow_cpp/include/embb/dataflow/internal/sink.h new file mode 100644 index 0000000..f711e11 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/sink.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SINK_H_ +#define EMBB_DATAFLOW_INTERNAL_SINK_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template class Sink; + +template < + int Slices, + typename I1, typename I2, typename I3, typename I4, typename I5> +class Sink< Slices, Inputs > + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef SinkExecutor< InputsType > ExecutorType; + typedef typename ExecutorType::FunctionType FunctionType; + + explicit Sink(FunctionType function) + : executor_(function) { + input_clock_expected_ = 0; + inputs_.SetListener(this); + } + + void SetListener(ClockListener * listener) { + listener_ = listener; + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual void Run(int clock) { + //const int idx = clock % Slices; + + // force ordering + while (input_clock_expected_ != clock) embb::base::Thread::CurrentYield(); + + if (inputs_.AreNoneBlank(clock)) { + executor_.Execute(clock, inputs_); + } + listener_->OnClock(clock); + + input_clock_expected_ = clock + 1; + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + virtual void OnClock(int clock) { + lock_.Lock(); + TrySpawn(clock); + lock_.Unlock(); + } + + private: + InputsType inputs_; + ExecutorType executor_; + embb::base::Atomic input_clock_expected_; + Action action_[Slices]; + ClockListener * listener_; + SpinLock lock_; + + void TrySpawn(int clock) { + const int idx = clock % Slices; + if (!inputs_.AreAtClock(clock)) + EMBB_THROW(embb::base::ErrorException, + "Some inputs are not at expected clock.") + action_[idx] = Action(this, clock); + sched_->Spawn(action_[idx]); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SINK_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/sink_executor.h b/dataflow_cpp/include/embb/dataflow/internal/sink_executor.h new file mode 100644 index 0000000..65dacf3 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/sink_executor.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SINK_EXECUTOR_H_ +#define EMBB_DATAFLOW_INTERNAL_SINK_EXECUTOR_H_ + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class SinkExecutor; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function + FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + inputs.template Get<3>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +template +class SinkExecutor< Inputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SinkExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Inputs & inputs) { + function_( + inputs.template Get<0>().GetValue(clock), + inputs.template Get<1>().GetValue(clock), + inputs.template Get<2>().GetValue(clock), + inputs.template Get<3>().GetValue(clock), + inputs.template Get<4>().GetValue(clock)); + } + + private: + FunctionType function_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SINK_EXECUTOR_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/source.h b/dataflow_cpp/include/embb/dataflow/internal/source.h new file mode 100644 index 0000000..6a925e8 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/source.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SOURCE_H_ +#define EMBB_DATAFLOW_INTERNAL_SOURCE_H_ + +#include +#include + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template class Source; + +template < + int Slices, + typename O1, typename O2, typename O3, typename O4, typename O5> +class Source< Slices, Outputs > + : public Node { + public: + typedef Outputs OutputsType; + typedef SourceExecutor< OutputsType > ExecutorType; + typedef typename ExecutorType::FunctionType FunctionType; + + explicit Source(FunctionType function) + : executor_(function) { + next_clock_ = 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + executor_.Execute(clock, outputs_); + next_clock_++; + } + + virtual void Start(int clock) { + while (clock != next_clock_) embb::base::Thread::CurrentYield(); + const int idx = clock % Slices; + action_[idx] = Action(this, clock); + sched_->Spawn(action_[idx]); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + private: + OutputsType outputs_; + ExecutorType executor_; + Action action_[Slices]; + embb::base::Atomic next_clock_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SOURCE_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/source_executor.h b/dataflow_cpp/include/embb/dataflow/internal/source_executor.h new file mode 100644 index 0000000..6f7b109 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/source_executor.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SOURCE_EXECUTOR_H_ +#define EMBB_DATAFLOW_INTERNAL_SOURCE_EXECUTOR_H_ + +#include + +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class SourceExecutor; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Outputs & outputs) { + O1 o1; + function_(o1); + outputs.template Get<0>().Send(Signal(clock, o1)); + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + function_(o1, o2); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + O3 o3; + function_(o1, o2, o3); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + O3 o3; + O4 o4; + function_(o1, o2, o3, o4); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + outputs.template Get<3>().Send(Signal(clock, o4)); + } + + private: + FunctionType function_; +}; + +template +class SourceExecutor< Outputs > { + public: + typedef embb::base::Function FunctionType; + + explicit SourceExecutor(FunctionType func) : function_(func) {} + + void Execute( + int clock, + Outputs & outputs) { + O1 o1; + O2 o2; + O3 o3; + O4 o4; + O5 o5; + function_(o1, o2, o3, o4, o5); + outputs.template Get<0>().Send(Signal(clock, o1)); + outputs.template Get<1>().Send(Signal(clock, o2)); + outputs.template Get<2>().Send(Signal(clock, o3)); + outputs.template Get<3>().Send(Signal(clock, o4)); + outputs.template Get<4>().Send(Signal(clock, o5)); + } + + private: + FunctionType function_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SOURCE_EXECUTOR_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/spinlock.h b/dataflow_cpp/include/embb/dataflow/internal/spinlock.h new file mode 100644 index 0000000..8b0f275 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/spinlock.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SPINLOCK_H_ +#define EMBB_DATAFLOW_INTERNAL_SPINLOCK_H_ + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +class SpinLock { + public: + SpinLock() : lock_(0) {} + void Lock() { + int expected = 0; + while (!lock_.CompareAndSwap(expected, 1)) { + expected = 0; + } + } + bool TryLock(int spins = 0) { + int expected = 0; + while (spins > 0 && !lock_.CompareAndSwap(expected, 1)) { + expected = 0; + spins--; + } + return spins > 0; + } + void Unlock() { + lock_ = 0; + } + + private: + embb::base::Atomic lock_; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SPINLOCK_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/switch.h b/dataflow_cpp/include/embb/dataflow/internal/switch.h new file mode 100644 index 0000000..6cd2df2 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/switch.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_SWITCH_H_ +#define EMBB_DATAFLOW_INTERNAL_SWITCH_H_ + +#include +#include +#include +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class Switch + : public Node + , public ClockListener { + public: + typedef Inputs InputsType; + typedef Outputs OutputsType; + + Switch() { + inputs_.SetListener(this); + } + + virtual bool HasInputs() const { + return inputs_.Size() > 0; + } + + virtual bool HasOutputs() const { + return outputs_.Size() > 0; + } + + virtual void Run(int clock) { + if (inputs_.AreNoneBlank(clock)) { + bool pred = GetInput<0>().GetValue(clock); + Type val = GetInput<1>().GetValue(clock); + if (pred) { + // signal with value + GetOutput<0>().Send(Signal(clock, val)); + // blank signal + GetOutput<1>().Send(Signal(clock)); + } else { + // blank signal + GetOutput<0>().Send(Signal(clock)); + // signal with value + GetOutput<1>().Send(Signal(clock, val)); + } + } else { + GetOutput<0>().Send(Signal(clock)); + GetOutput<1>().Send(Signal(clock)); + } + } + + InputsType & GetInputs() { + return inputs_; + } + + template + typename TypeAt::Result & GetInput() { + return inputs_.template Get(); + } + + OutputsType & GetOutputs() { + return outputs_; + } + + template + typename TypeAt::Result & GetOutput() { + return outputs_.template Get(); + } + + virtual void OnClock(int clock) { + //const int idx = clock % Slices; + if (!inputs_.AreAtClock(clock)) + EMBB_THROW(embb::base::ErrorException, + "Some inputs are not at expected clock.") + Run(clock); + } + + private: + InputsType inputs_; + OutputsType outputs_; + Action action_[Slices]; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_SWITCH_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/tuple.h b/dataflow_cpp/include/embb/dataflow/internal/tuple.h new file mode 100644 index 0000000..2df36c0 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/tuple.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_TUPLE_H_ +#define EMBB_DATAFLOW_INTERNAL_TUPLE_H_ + +#include + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +class TupleLeaf { + public: + typedef T Type; + + enum { index = Index }; + + enum { count = 1 }; + + TupleLeaf() : value_() {} + + TupleLeaf(TupleLeaf const & other) : value_(other.value_) {} + + void operator = (TupleLeaf const & other) { + value_ = other.value_; + } + + T const & Get() const { + return value_; + } + + T & Get() { + return value_; + } + + private: + T value_; +}; + +template +class TupleLeaf { + public: + typedef embb::base::internal::Nil Type; + + enum { index = Index }; + + enum { count = 0 }; + + embb::base::internal::Nil const & Get() const { + static embb::base::internal::Nil vv; + return vv; + } + + embb::base::internal::Nil & Get() { + static embb::base::internal::Nil vv; + return vv; + } +}; + +template < + typename T1 = embb::base::internal::Nil, + typename T2 = embb::base::internal::Nil, + typename T3 = embb::base::internal::Nil, + typename T4 = embb::base::internal::Nil, + typename T5 = embb::base::internal::Nil> +class Tuple : + public TupleLeaf<0, T1>, + public TupleLeaf<1, T2>, + public TupleLeaf<2, T3>, + public TupleLeaf<3, T4>, + public TupleLeaf<4, T5> { + public: + typedef typename MakeTypeList::Result Types; + typedef typename MakeTypeList< + TupleLeaf<0, T1>, + TupleLeaf<1, T2>, + TupleLeaf<2, T3>, + TupleLeaf<3, T4>, + TupleLeaf<4, T5> >::Result LeafTypes; + + size_t Size() const { + return + TupleLeaf<0, T1>::count + + TupleLeaf<1, T2>::count + + TupleLeaf<2, T3>::count + + TupleLeaf<3, T4>::count + + TupleLeaf<4, T5>::count; + } + + template + typename TypeAt< Types, Index >::Result & Get() { + typename TypeAt< LeafTypes, Index >::Result * leaf = + static_cast::Result *>(this); + return leaf->Get(); + } + + template + typename TypeAt< Types, Index >::Result const & Get() const { + typename TypeAt< LeafTypes, Index >::Result * leaf = + static_cast::Result *>(this); + return leaf->Get(); + } +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_TUPLE_H_ diff --git a/dataflow_cpp/include/embb/dataflow/internal/typelist.h b/dataflow_cpp/include/embb/dataflow/internal/typelist.h new file mode 100644 index 0000000..eb5b286 --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/internal/typelist.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_INTERNAL_TYPELIST_H_ +#define EMBB_DATAFLOW_INTERNAL_TYPELIST_H_ + +#include + +namespace embb { +namespace dataflow { +namespace internal { + +template +struct TypeList { + typedef T Head; + typedef U Tail; +}; + +template < + typename T1 = embb::base::internal::Nil, + typename T2 = embb::base::internal::Nil, + typename T3 = embb::base::internal::Nil, + typename T4 = embb::base::internal::Nil, + typename T5 = embb::base::internal::Nil> +struct MakeTypeList { + private: + typedef typename MakeTypeList::Result TailResult; + + public: + typedef TypeList Result; +}; + +template <> +struct MakeTypeList<> { + typedef embb::base::internal::Nil Result; +}; + +template struct TypeListLength; + +template <> struct TypeListLength { + enum { value = 0 }; +}; + +template +struct TypeListLength > { + enum { value = 1 + TypeListLength::value }; +}; + +template struct TypeAt; + +template +struct TypeAt< TypeList, 0> { + typedef Head Result; +}; + +template +struct TypeAt< TypeList, Index> { + typedef typename TypeAt::Result Result; +}; + +} // namespace internal +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_INTERNAL_TYPELIST_H_ diff --git a/dataflow_cpp/include/embb/dataflow/network.h b/dataflow_cpp/include/embb/dataflow/network.h new file mode 100644 index 0000000..3610ccc --- /dev/null +++ b/dataflow_cpp/include/embb/dataflow/network.h @@ -0,0 +1,884 @@ +/* + * Copyright (c) 2014, 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 EMBB_DATAFLOW_NETWORK_H_ +#define EMBB_DATAFLOW_NETWORK_H_ + +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace embb { +namespace dataflow { + +#ifdef DOXYGEN + +/** + * Represents a set of processes, that are connected by communication channels. + * + * \tparam Slices Number of concurrently processed tokens. + * \ingroup CPP_DATAFLOW + */ +template +class Network { + public: + /** + * Constructs an empty network. + */ + Network() {} + + /** + * Input port class. + */ + template + class In { + }; + + /** + * Output port class. + */ + template + class Out { + public: + /** + * Input port class that can be connected to this output port. + */ + typedef In InType; + + /** + * Connects this output port to the input port \c input. + * If the input port already was connected to a different + * output an ErrorException is thrown. + * \param input The input port to connect to. + */ + void Connect(InType & input); + + /** + * Connects this output port to the input port \c input. + * If the input port already was connected to a different + * output an ErrorException is thrown. + * \param input The input port to connect to. + */ + void operator >> (InType & input); + }; + + /** + * Provides the input port types for a process. + * \tparam T1 Type of first port. + * \tparam T2 Optional type of second port. + * \tparam T3 Optional type of third port. + * \tparam T4 Optional type of fourth port. + * \tparam T5 Optional type of fifth port. + */ + template + struct Inputs { + /** + * Type list used to derive input port types from Index. + * \tparam Index The index of the input port type to query. + */ + template + struct Types { + /** + * Result of an input port type query. + * T_Index is T1 if Index is 0, T2 if Index is 1 and so on. + */ + typedef In Result; + }; + + /** + * \returns Reference to input port at Index. + */ + template + typename Types::Result & Get(); + }; + + /** + * Provides the output port types for a process. + * \tparam T1 Type of first port. + * \tparam T2 Optional type of second port. + * \tparam T3 Optional type of third port. + * \tparam T4 Optional type of fourth port. + * \tparam T5 Optional type of fifth port. + */ + template + struct Outputs { + /** + * Type list used to derive output port types from Index. + * \tparam Index The index of the output port type to query. + */ + template + struct Types { + /** + * Result of an output port type query. + * T_Index is T1 if Index is 0, T2 if Index is 1 and so on. + */ + typedef Out Result; + }; + + /** + * \returns Reference to output port at Index. + */ + template + typename Types::Result & Get(); + }; + + /** + * Generic serial process template. + * + * Implements a generic serial process in the network that may have one to + * four input ports and one to four output ports but no more that five total + * ports. + * Tokens are processed in order. + * + * \see Source, ParallelProcess, Sink, Switch, Select + * + * \tparam Inputs Inputs of the process. + * \tparam Outputs Outputs of the process. + */ + template + class SerialProcess { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function + FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a SerialProcess with a user specified processing function. + * \param function The Function to call to process a token. + */ + explicit SerialProcess(FunctionType function); + + /** + * \returns \c true if the SerialProcess has any inputs, \c false + * otherwise. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns \c true if the SerialProcess has any outputs, \c false + * otherwise. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + }; + + /** + * Adds a new serial process to the network. + * \param proc The process to add. + */ + template + void Add(SerialProcess & proc); + + /** + * Generic parallel process template. + * + * Implements a generic parallel process in the network that may have one to + * four input ports and one to four output ports but no more that five total + * ports. + * Tokens are processed as soon as all inputs for that token are complete. + * + * \see Source, SerialProcess, Sink, Switch, Select + * + * \tparam Inputs Inputs of the process. + * \tparam Outputs Outputs of the process. + */ + template + class ParallelProcess { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function + FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a ParallelProcess with a user specified processing function. + * \param function The Function to call to process a token. + */ + explicit ParallelProcess(FunctionType function); + + /** + * \returns \c true if the ParallelProcess has any inputs, \c false + * otherwise. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns \c true if the ParallelProcess has any outputs, \c false + * otherwise. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + }; + + /** + * Adds a new parallel process to the network. + * \param proc The process to add. + */ + template + void Add(ParallelProcess & proc); + + /** + * Switch process template. + * + * A switch has 2 inputs and 2 outputs. Input port 0 is of type boolean and + * selects to which output port the value of input port 1 of type \c Type + * is sent. If input port 0 is set to true the value goes to output port 0 + * and to output port 1 otherwise. + * Tokens are processed as soon as all inputs for that token are complete. + * + * \see Select + * + * \tparam Type The type of input port 1 and output port 0 and 1. + */ + template + class Switch { + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * \returns Always \c true. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + }; + + /** + * Adds a new switch process to the network. + * \param sw The switch process to add. + */ + template + void Add(Switch & sw); + + /** + * Select process template. + * + * A select has 3 inputs and 1 output. Input port 0 is of type boolean and + * selects which of input port 1 or 2 (of type \c Type) is sent to output + * port 0 (of type \c Type). If input port 0 is set to true the value of + * input port 1 is selected, otherwise the value of input port 2 is taken. + * Tokens are processed as soon as all inputs for that token are complete. + * + * \see Switch + * + * \tparam Type The type of input port 1 and 2 and output port 0. + */ + template + class Select { + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * \returns Always \c true. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + }; + + /** + * Adds a new select process to the network. + * \param sel The select process to add. + */ + template + void Add(Select & sel); + + /** + * Sink process template. + * + * A sink marks the end of a particular processing chain. It can have one to + * five input ports and no output ports. + * Tokens are processed in order by the sink, regardless in which order they + * arrive at the input ports. + * + * \see Source, SerialProcess, ParallelProcess + * + * \tparam I1 Type of first input port. + * \tparam I2 Optional type of second input port. + * \tparam I3 Optional type of third input port. + * \tparam I4 Optional type of fourth input port. + * \tparam I5 Optional type of fifth input port. + */ + template + class Sink { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Input port type list. + */ + typedef Inputs InputsType; + + /** + * Constructs a Sink with a user specified processing function. + * \param function The Function to call to process a token. + */ + explicit Sink(FunctionType function); + + /** + * \returns Always \c true. + */ + virtual bool HasInputs() const; + + /** + * \returns Reference to a list of all input ports. + */ + InputsType & GetInputs(); + + /** + * \returns Input port at Index. + */ + template + typename InputsType::Types::Result & GetInput(); + + /** + * \returns Always \c false. + */ + virtual bool HasOutputs() const; + }; + + /** + * Adds a new sink process to the network. + * \param sink The sink process to add. + */ + template + void Add(Sink & sink); + + /** + * Source process template. + * + * A source marks the start of a processing chain. It can have one to five + * output ports and no input ports. + * Tokens are emitted in order by the source. + * + * \see SerialProcess, ParallelProcess, Sink + * + * \tparam O1 Type of first output port. + * \tparam O2 Optional type of second output port. + * \tparam O3 Optional type of third output port. + * \tparam O4 Optional type of fourth output port. + * \tparam O5 Optional type of fifth output port. + */ + template + class Source { + public: + /** + * Function type to use when processing tokens. + */ + typedef embb::base::Function FunctionType; + + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a Source with a user specified processing function. + * \param function The Function to call to emit a token. + */ + explicit Source(FunctionType function); + + /** + * \returns Always \c false. + */ + virtual bool HasInputs() const; + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at INDEX. + */ + template + typename OutputsType::Types::Result & GetOutput(); + }; + + /** + * Adds a new source process to the network. + * \param source The source process to add. + */ + template + void Add(Source & source); + + /** + * Constant source process template. + * + * A constant source has one output port and emits a constant value given + * at construction time for each token. + * + * \tparam Type The type of output port 0. + */ + template + class ConstantSource { + public: + /** + * Output port type list. + */ + typedef Outputs OutputsType; + + /** + * Constructs a ConstantSource with a value to emit on each token. + * \param value The value to emit. + */ + explicit ConstantSource(Type value); + + /** + * \returns Always \c false. + */ + virtual bool HasInputs() const; + + /** + * \returns Always \c true. + */ + virtual bool HasOutputs() const; + + /** + * \returns Reference to a list of all output ports. + */ + OutputsType & GetOutputs(); + + /** + * \returns Output port at Index. + */ + template + typename OutputsType::Types::Result & GetOutput(); + }; + + /** + * Adds a new constant source process to the network. + * \param source The constant source process to add. + */ + template + void Add(ConstantSource & source); + + /** + * Executes the network for at most \c elements tokens. + * \param elements Maximum number of tokens to process. + */ + void operator () (int elements); +}; + +#else + +template +class Network : public internal::ClockListener { + public: + Network() {} + + template + struct Inputs { + typedef internal::Inputs Type; + }; + + template + struct Outputs { + typedef internal::Outputs Type; + }; + + template class SerialProcess; + + template < + typename I1, typename I2, typename I3, typename I4, typename I5, + typename O1, typename O2, typename O3, typename O4, typename O5> + class SerialProcess< internal::Inputs, + internal::Outputs > + : public internal::Process< Slices, true, + internal::Inputs, + internal::Outputs > { + public: + typedef typename internal::Process< Slices, true, + internal::Inputs, + internal::Outputs >::FunctionType + FunctionType; + explicit SerialProcess(FunctionType function) + : internal::Process< Slices, true, + internal::Inputs, + internal::Outputs >(function) { + //empty + } + }; + + template + void Add(SerialProcess & proc) { + processes_.push_back(&proc); + } + + template class ParallelProcess; + + template < + typename I1, typename I2, typename I3, typename I4, typename I5, + typename O1, typename O2, typename O3, typename O4, typename O5> + class ParallelProcess< internal::Inputs, + internal::Outputs > + : public internal::Process< Slices, false, + internal::Inputs, + internal::Outputs >{ + public: + typedef typename internal::Process< Slices, false, + internal::Inputs, + internal::Outputs >::FunctionType + FunctionType; + explicit ParallelProcess(FunctionType function) + : internal::Process< Slices, false, + internal::Inputs, + internal::Outputs >(function) { + //empty + } + }; + + template + void Add(ParallelProcess & proc) { + processes_.push_back(&proc); + } + + template + class Switch : public internal::Switch { + public: + }; + + template + void Add(Switch & sw) { + processes_.push_back(&sw); + } + + template + class Select : public internal::Select { + public: + }; + + template + void Add(Select & sel) { + processes_.push_back(&sel); + } + + template + class Sink : public internal::Sink > { + public: + typedef typename internal::Sink >::FunctionType FunctionType; + + explicit Sink(FunctionType function) + : internal::Sink >(function) { + //empty + } + }; + + template + void Add(Sink & sink) { + sink.SetListener(this); + sinks_.push_back(&sink); + } + + template + class Source : public internal::Source > { + public: + typedef typename internal::Source >::FunctionType + FunctionType; + + explicit Source(FunctionType function) + : internal::Source >(function) { + //empty + } + }; + + template + void Add(Source & source) { + sources_.push_back(&source); + } + + template + class ConstantSource : public internal::ConstantSource { + public: + explicit ConstantSource(Type value) + : internal::ConstantSource(value) { + //empty + } + }; + + template + void Add(ConstantSource & source) { + sources_.push_back(&source); + } + + void operator () (int elements) { + internal::SchedulerSequential sched_seq; + internal::SchedulerMTAPI sched_mtapi; + internal::Scheduler * sched = &sched_mtapi; + + for (size_t it = 0; it < sources_.size(); it++) + sources_[it]->SetScheduler(sched); + for (size_t it = 0; it < processes_.size(); it++) + processes_[it]->SetScheduler(sched); + for (size_t it = 0; it < sinks_.size(); it++) + sinks_[it]->SetScheduler(sched); + + for (int ii = 0; ii < Slices; ii++) sink_count_[ii] = 0; + + for (int clock = 0; clock < elements; clock++) { + const int idx = clock % Slices; + while (sink_count_[idx] > 0) embb::base::Thread::CurrentYield(); + sched->WaitForSlice(idx); + SpawnClock(clock); + } + + for (int ii = 0; ii < Slices; ii++) { + while (sink_count_[ii] > 0) embb::base::Thread::CurrentYield(); + sched->WaitForSlice(ii); + } + } + + /** + * Internal. + * \internal + * Gets called when a token has reached all sinks and frees up the + * corresponding slot, thus allowing a new token to be emitted. + */ + virtual void OnClock(int clock) { + const int idx = clock % Slices; + const int cnt = --sink_count_[idx]; + if (cnt < 0) + EMBB_THROW(embb::base::ErrorException, + "More sinks than expected signaled reception of given clock.") + } + + private: + std::vector processes_; + std::vector sources_; + std::vector sinks_; + embb::base::Atomic sink_count_[Slices]; +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + std::vector spawn_history_[Slices]; +#endif + + void SpawnClock(int clock) { + const int idx = clock % Slices; +#if EMBB_DATAFLOW_TRACE_SIGNAL_HISTORY + spawn_history_[idx].push_back(clock); +#endif + sink_count_[idx] = static_cast(sinks_.size()); + for (size_t kk = 0; kk < sources_.size(); kk++) { + sources_[kk]->Start(clock); + } + } +}; + +#endif // DOXYGEN + +} // namespace dataflow +} // namespace embb + +#endif // EMBB_DATAFLOW_NETWORK_H_ diff --git a/dataflow_cpp/src/dummy.cc b/dataflow_cpp/src/dummy.cc new file mode 100644 index 0000000..eb34253 --- /dev/null +++ b/dataflow_cpp/src/dummy.cc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 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. + */ + diff --git a/dataflow_cpp/test/dataflow_cpp_test_simple.cc b/dataflow_cpp/test/dataflow_cpp_test_simple.cc new file mode 100644 index 0000000..13c1466 --- /dev/null +++ b/dataflow_cpp/test/dataflow_cpp_test_simple.cc @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#include + +#include + +#include + +typedef embb::dataflow::Network<4> MyNetwork; +typedef MyNetwork::ConstantSource< int > MyConstantSource; +typedef MyNetwork::Source< int > MySource; +typedef MyNetwork::SerialProcess< MyNetwork::Inputs::Type, + MyNetwork::Outputs::Type > MyPred; +typedef MyNetwork::ParallelProcess< MyNetwork::Inputs::Type, + MyNetwork::Outputs::Type > MyFilter; +typedef MyNetwork::ParallelProcess< MyNetwork::Inputs::Type, + MyNetwork::Outputs::Type > MyMult; +typedef MyNetwork::Sink< int > MySink; +typedef MyNetwork::Switch< int > MySwitch; +typedef MyNetwork::Select< int > MySelect; + +#define TEST_COUNT 12 + +embb::base::Atomic source_counter; +int source_array[TEST_COUNT]; + +void sourceFunc(int & out) { + out = source_counter; + + source_array[source_counter] = out; + source_counter++; +} + +embb::base::Atomic pred_counter; +bool pred_array[TEST_COUNT]; + +void predFunc(int const & in, bool & out) { + out = (0 == (in % 2)); + + pred_array[pred_counter] = out; + pred_counter++; +} + +embb::base::Atomic filter_counter; +int filter_array[TEST_COUNT]; + +void filterFunc(int const &in, int & out) { + out = in + 1; + + filter_array[filter_counter] = out; + filter_counter++; +} + +embb::base::Atomic mult_counter; +int mult_array[TEST_COUNT]; + +void multFunc(int const & in_a, int const & in_b, int & out) { + out = in_a * in_b; + + mult_array[mult_counter] = out; + mult_counter++; +} + +template +class ArraySink { + private: + int values_[SIZE]; + int pos_; + + public: + ArraySink() { + Init(); + } + + void Print() { + std::cout << values_[0]; + for (int ii = 1; ii < SIZE; ii++) { + std::cout << ", " << values_[ii]; + } + std::cout << std::endl; + } + + void Init() { + for (int ii = 0; ii < SIZE; ii++) { + values_[ii] = -1; + } + pos_ = 0; + } + + bool Check() { + for (int ii = 0; ii < SIZE; ii++) { + int expected; + if (0 == (ii % 2)) + expected = ii + 1; + else + expected = ii * 4; + if (values_[ii] != expected) + return false; + } + return true; + } + + void Run(int const & in) { + values_[pos_] = in; + pos_++; + } +}; + +SimpleTest::SimpleTest() { + CreateUnit("dataflow_cpp simple test").Add(&SimpleTest::TestBasic, this); +} + +void SimpleTest::TestBasic() { + embb::mtapi::Node::Initialize(1, 1); + + for (int ii = 0; ii < 10000; ii++) { + ArraySink asink; + MyNetwork network; + MyConstantSource constant(4); + MySource source(embb::base::MakeFunction(sourceFunc)); + MyFilter filter(embb::base::MakeFunction(filterFunc)); + MyMult mult(embb::base::MakeFunction(multFunc)); + MySink sink(embb::base::MakeFunction(asink, &ArraySink::Run)); + MyPred pred(embb::base::MakeFunction(predFunc)); + MySwitch sw; + MySelect sel; + + for (int kk = 0; kk < TEST_COUNT; kk++) { + source_array[kk] = -1; + pred_array[kk] = false; + filter_array[kk] = -1; + mult_array[kk] = -1; + } + source_counter = 0; + pred_counter = 0; + mult_counter = 0; + filter_counter = 0; + + filter.HasInputs(); + filter.HasOutputs(); + + source.GetOutput<0>() >> sw.GetInput<1>(); + + source.GetOutput<0>() >> pred.GetInput<0>(); + pred.GetOutput<0>() >> sw.GetInput<0>(); + pred.GetOutput<0>() >> sel.GetInput<0>(); + + sw.GetOutput<0>() >> filter.GetInput<0>(); + filter.GetOutput<0>() >> sel.GetInput<1>(); + + constant.GetOutput<0>() >> mult.GetInput<0>(); + sw.GetOutput<1>() >> mult.GetInput<1>(); + mult.GetOutput<0>() >> sel.GetInput<2>(); + + sel.GetOutput<0>() >> sink.GetInput<0>(); + + network.Add(constant); + network.Add(source); + + network.Add(filter); + network.Add(mult); + + network.Add(pred); + network.Add(sw); + network.Add(sel); + + network.Add(sink); + + network(TEST_COUNT); + + PT_EXPECT(asink.Check()); + } + + embb::mtapi::Node::Finalize(); +} diff --git a/dataflow_cpp/test/dataflow_cpp_test_simple.h b/dataflow_cpp/test/dataflow_cpp_test_simple.h new file mode 100644 index 0000000..35b7471 --- /dev/null +++ b/dataflow_cpp/test/dataflow_cpp_test_simple.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_SIMPLE_H_ +#define DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_SIMPLE_H_ + +#include + +class SimpleTest : public partest::TestCase { + public: + SimpleTest(); + + private: + void TestBasic(); +}; + +#endif // DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_SIMPLE_H_ diff --git a/dataflow_cpp/test/dataflow_cpp_test_tuple.cc b/dataflow_cpp/test/dataflow_cpp_test_tuple.cc new file mode 100644 index 0000000..cd20ee2 --- /dev/null +++ b/dataflow_cpp/test/dataflow_cpp_test_tuple.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#include + +TupleTest::TupleTest() { + CreateUnit("dataflow_cpp tuple test").Add(&TupleTest::TestBasic, this); +} + +void TupleTest::TestBasic() { + embb::dataflow::internal::Tuple test; + test.Get<0>() = 1; + test.Get<1>() = "test"; +} diff --git a/dataflow_cpp/test/dataflow_cpp_test_tuple.h b/dataflow_cpp/test/dataflow_cpp_test_tuple.h new file mode 100644 index 0000000..1eb62bd --- /dev/null +++ b/dataflow_cpp/test/dataflow_cpp_test_tuple.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_TUPLE_H_ +#define DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_TUPLE_H_ + +#include + +class TupleTest : public partest::TestCase { + public: + TupleTest(); + + private: + void TestBasic(); +}; + +#endif // DATAFLOW_CPP_TEST_DATAFLOW_CPP_TEST_TUPLE_H_ diff --git a/dataflow_cpp/test/main.cc b/dataflow_cpp/test/main.cc new file mode 100644 index 0000000..fd36777 --- /dev/null +++ b/dataflow_cpp/test/main.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include + +PT_MAIN("Dataflow C++") { + PT_RUN(SimpleTest); + PT_RUN(TupleTest); +} diff --git a/doc/examples/CMakeLists.txt b/doc/examples/CMakeLists.txt new file mode 100644 index 0000000..cff5224 --- /dev/null +++ b/doc/examples/CMakeLists.txt @@ -0,0 +1,26 @@ +project (project_embb_tutorials) + +file(GLOB_RECURSE EXAMPLES_SOURCES "*.cc" "*.h") + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/ + ${CMAKE_CURRENT_BINARY_DIR}/ + ${CMAKE_CURRENT_SOURCE_DIR}/../../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../mtapi_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../containers_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../algorithms_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../dataflow_cpp/include + ) + +if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "-std=c++11") +endif() + +add_executable(examples ${EXAMPLES_SOURCES}) +target_link_libraries(examples embb_dataflow_cpp embb_algorithms_cpp embb_mtapi_cpp + embb_mtapi_c embb_base_cpp embb_base_c embb_containers_cpp ${compiler_libs}) +CopyBin(BIN examples DEST ${local_install_dir}) diff --git a/doc/examples/algorithms/counting/count-snippet.h b/doc/examples/algorithms/counting/count-snippet.h new file mode 100644 index 0000000..9b3191d --- /dev/null +++ b/doc/examples/algorithms/counting/count-snippet.h @@ -0,0 +1,3 @@ +std::iterator_traits::difference_type count; +using embb::algorithms::Count; +count = Count(range, range + 8, 1); diff --git a/doc/examples/algorithms/counting/count_if-snippet.h b/doc/examples/algorithms/counting/count_if-snippet.h new file mode 100644 index 0000000..68806e8 --- /dev/null +++ b/doc/examples/algorithms/counting/count_if-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::CountIf; +count = CountIf(range, range + 8, + [](const int& value) -> bool { return value > 0; }); diff --git a/doc/examples/algorithms/counting/counting-fragmented.cc b/doc/examples/algorithms/counting/counting-fragmented.cc new file mode 100644 index 0000000..7da2553 --- /dev/null +++ b/doc/examples/algorithms/counting/counting-fragmented.cc @@ -0,0 +1,16 @@ +#include + +/** + * Example using embb::algorithms::Count and CountIf. + * + * Counting elements of a range that fulfill certain properties. + */ +void RunCounting() { + #include "algorithms/counting/setup-snippet.h" + + #include "algorithms/counting/count-snippet.h" + assert(count == 2); + + #include "algorithms/counting/count_if-snippet.h" + assert(count == 6); +} diff --git a/doc/examples/algorithms/counting/setup-snippet.h b/doc/examples/algorithms/counting/setup-snippet.h new file mode 100644 index 0000000..f488cbd --- /dev/null +++ b/doc/examples/algorithms/counting/setup-snippet.h @@ -0,0 +1 @@ +int range[] = {0, 3, 2, 0, 1, 1, 3, 2}; diff --git a/doc/examples/algorithms/for_each/doubling-snippet.h b/doc/examples/algorithms/for_each/doubling-snippet.h new file mode 100644 index 0000000..9d16397 --- /dev/null +++ b/doc/examples/algorithms/for_each/doubling-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::ForEach; +ForEach(range.begin(), range.end(), + [] (int& to_double) { to_double *= 2; }); diff --git a/doc/examples/algorithms/for_each/doubling_zip-snippet.h b/doc/examples/algorithms/for_each/doubling_zip-snippet.h new file mode 100644 index 0000000..0e5ccc4 --- /dev/null +++ b/doc/examples/algorithms/for_each/doubling_zip-snippet.h @@ -0,0 +1,7 @@ +using embb::algorithms::Zip; +using embb::algorithms::ZipPair; +ForEach(Zip(input_range.begin(), output_range.begin()), + Zip(input_range.end(), output_range.end()), + [] (ZipPair pair) { + pair.Second() = pair.First() * 2; + }); diff --git a/doc/examples/algorithms/for_each/for_each-fragmented.cc b/doc/examples/algorithms/for_each/for_each-fragmented.cc new file mode 100644 index 0000000..b3e09bb --- /dev/null +++ b/doc/examples/algorithms/for_each/for_each-fragmented.cc @@ -0,0 +1,27 @@ +#include +#include +#include + +static void CheckResults(const std::vector& range) { + int i = 1; + for (const int& value : range) { + assert(value == i * 2); + EMBB_UNUSED_IN_RELEASE(value); + i++; + } +} + +/** + * Example using embb::algorithms::ForEach. + * + * Traversing and modifying a sequence with a for-each loop. + */ +void RunForEach() { + #include "stl_for_each/setup-snippet.h" + #include "algorithms/for_each/doubling-snippet.h" + CheckResults(range); + + #include "algorithms/for_each/setup_zip-snippet.h" + #include "algorithms/for_each/doubling_zip-snippet.h" + CheckResults(output_range); +} diff --git a/doc/examples/algorithms/for_each/setup_zip-snippet.h b/doc/examples/algorithms/for_each/setup_zip-snippet.h new file mode 100644 index 0000000..311fb4b --- /dev/null +++ b/doc/examples/algorithms/for_each/setup_zip-snippet.h @@ -0,0 +1,5 @@ +std::vector input_range(5); +for (size_t i=0; i < input_range.size(); i++) { + input_range[i] = static_cast(i) + 1; +} +std::vector output_range(5); diff --git a/doc/examples/algorithms/invoke/invocation-snippet.h b/doc/examples/algorithms/invoke/invocation-snippet.h new file mode 100644 index 0000000..2b27930 --- /dev/null +++ b/doc/examples/algorithms/invoke/invocation-snippet.h @@ -0,0 +1,2 @@ +using embb::algorithms::Invoke; +Invoke(WorkPackageA, WorkPackageB, WorkPackageC); diff --git a/doc/examples/algorithms/invoke/invoke-fragmented.cc b/doc/examples/algorithms/invoke/invoke-fragmented.cc new file mode 100644 index 0000000..042e337 --- /dev/null +++ b/doc/examples/algorithms/invoke/invoke-fragmented.cc @@ -0,0 +1,73 @@ +#include +#include + +#include "algorithms/invoke/packages-snippet.h" + +static int a = 0, b = 0, c = 0; + +void WorkPackageA() { + a++; +} + +void WorkPackageB() { + b++; +} + +void WorkPackageC() { + c++; +} + +int* Partition(int* first, int* last) { + int* pivot = last - 1; + while (first != last) { + while (*first < *pivot) { + ++first; + if (first == last) return first; + } + do { + --last; + if (first == last) return first; + } while (*pivot < *last); + std::swap(*first, *last); + if(pivot == first) { + pivot = last; + } else if (pivot == last) { + pivot = first; + } + ++first; + } + return first; +} + +#include "algorithms/invoke/quick_sort-snippet.h" + +#include "algorithms/invoke/parallel_quick_sort-snippet.h" + +/** + * Example using embb::algorithms::ParallelInvoke() to execute work packages in + * parallel. + */ +void RunInvoke() { + #include "algorithms/invoke/invocation-snippet.h" + assert(a == 1); + assert(b == 1); + assert(c == 1); + + { + int range[] = {2, 5, 3, 1, 4}; + QuickSort(range, range + 5); + for (size_t i = 0; i < 5; i++) { + assert(range[i] == static_cast(i) + 1); + } + } + { + int range[] = {2, 5, 3, 1, 4}; + ParallelQuickSort(range, range + 5); + for (size_t i = 0; i < 5; i++) { + assert(range[i] == static_cast(i) + 1); + } + } +} + + + diff --git a/doc/examples/algorithms/invoke/packages-snippet.h b/doc/examples/algorithms/invoke/packages-snippet.h new file mode 100644 index 0000000..743db1d --- /dev/null +++ b/doc/examples/algorithms/invoke/packages-snippet.h @@ -0,0 +1,3 @@ +void WorkPackageA(); +void WorkPackageB(); +void WorkPackageC(); diff --git a/doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h b/doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h new file mode 100644 index 0000000..f518853 --- /dev/null +++ b/doc/examples/algorithms/invoke/parallel_quick_sort-snippet.h @@ -0,0 +1,7 @@ +void ParallelQuickSort(int* first, int* last) { + if (last - first <= 1) return; + int* mid = Partition(first, last); + using embb::algorithms::Invoke; + Invoke([=](){ParallelQuickSort(first, mid);}, + [=](){ParallelQuickSort(mid, last);}); +} diff --git a/doc/examples/algorithms/invoke/quick_sort-snippet.h b/doc/examples/algorithms/invoke/quick_sort-snippet.h new file mode 100644 index 0000000..9d33c89 --- /dev/null +++ b/doc/examples/algorithms/invoke/quick_sort-snippet.h @@ -0,0 +1,6 @@ +void QuickSort(int* first, int* last) { + if (last - first <= 1) return; + int* mid = Partition(first, last); + QuickSort(first, mid); + QuickSort(mid, last); +} diff --git a/doc/examples/algorithms/reduce/dot_product-snippet.h b/doc/examples/algorithms/reduce/dot_product-snippet.h new file mode 100644 index 0000000..8d6bbf2 --- /dev/null +++ b/doc/examples/algorithms/reduce/dot_product-snippet.h @@ -0,0 +1,9 @@ +using embb::algorithms::Zip; +using embb::algorithms::ZipPair; +int dot_product = Reduce(Zip(range.begin(), second_range.begin()), + Zip(range.end(), second_range.end()), + 0, + std::plus(), + [](const ZipPair& pair) { + return pair.First() * pair.Second(); + }); diff --git a/doc/examples/algorithms/reduce/parallel-snippet.h b/doc/examples/algorithms/reduce/parallel-snippet.h new file mode 100644 index 0000000..b28ad55 --- /dev/null +++ b/doc/examples/algorithms/reduce/parallel-snippet.h @@ -0,0 +1,2 @@ +using embb::algorithms::Reduce; +sum = Reduce(range.begin(), range.end(), 0, std::plus()); diff --git a/doc/examples/algorithms/reduce/range_init-snippet.h b/doc/examples/algorithms/reduce/range_init-snippet.h new file mode 100644 index 0000000..e3a3ee3 --- /dev/null +++ b/doc/examples/algorithms/reduce/range_init-snippet.h @@ -0,0 +1,4 @@ +std::vector range(5); +for (size_t i = 0; i < range.size(); i++) { + range[i] = static_cast(i) + 1; +} diff --git a/doc/examples/algorithms/reduce/reduce-fragmented.cc b/doc/examples/algorithms/reduce/reduce-fragmented.cc new file mode 100644 index 0000000..7019b05 --- /dev/null +++ b/doc/examples/algorithms/reduce/reduce-fragmented.cc @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +/** + * Example using embb::algorithms::Reduce. + * + * Summing up a range of values and more. + */ +void RunReduce() { + #include "algorithms/reduce/range_init-snippet.h" + + #include "algorithms/reduce/sequential-snippet.h" + assert(sum == 1 + 2 + 3 + 4 + 5); + + #include "algorithms/reduce/parallel-snippet.h" + assert(sum == 1 + 2 + 3 + 4 + 5); + + #include "algorithms/reduce/second_range_init-snippet.h" + #include "algorithms/reduce/dot_product-snippet.h" + assert(dot_product == 1*5 + 2*6 + 3*7 + 4*8 + 5*9); + EMBB_UNUSED_IN_RELEASE(dot_product); +} diff --git a/doc/examples/algorithms/reduce/second_range_init-snippet.h b/doc/examples/algorithms/reduce/second_range_init-snippet.h new file mode 100644 index 0000000..0c1f44e --- /dev/null +++ b/doc/examples/algorithms/reduce/second_range_init-snippet.h @@ -0,0 +1,4 @@ +std::vector second_range(5); +for (size_t i = 0; i < range.size(); i++) { + second_range[i] = static_cast(i) + 5; +} diff --git a/doc/examples/algorithms/reduce/sequential-snippet.h b/doc/examples/algorithms/reduce/sequential-snippet.h new file mode 100644 index 0000000..c5cca02 --- /dev/null +++ b/doc/examples/algorithms/reduce/sequential-snippet.h @@ -0,0 +1,4 @@ +int sum = 0; +for (size_t i = 0; i < range.size(); i++) { + sum += range[i]; +} diff --git a/doc/examples/algorithms/scan/prefix_sum-snippet.h b/doc/examples/algorithms/scan/prefix_sum-snippet.h new file mode 100644 index 0000000..38fae7d --- /dev/null +++ b/doc/examples/algorithms/scan/prefix_sum-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::Scan; +Scan(input_range.begin(), input_range.end(), output_range.begin(), + 0, std::plus()); diff --git a/doc/examples/algorithms/scan/scan-fragmented.cc b/doc/examples/algorithms/scan/scan-fragmented.cc new file mode 100644 index 0000000..2fad96b --- /dev/null +++ b/doc/examples/algorithms/scan/scan-fragmented.cc @@ -0,0 +1,29 @@ +#include +#include +#include + +void CheckResults(const std::vector& results) { + assert(results.size() == 5); + assert(results[0] == 1); + assert(results[1] == 3); + assert(results[2] == 6); + assert(results[3] == 10); + assert(results[4] == 15); + EMBB_UNUSED_IN_RELEASE(results); +} + +/** + * Example using embb::algorithms::Scan. + * + * + */ +void RunScan() { + #include "algorithms/scan/setup-snippet.h" + + #include "algorithms/scan/sequential_prefix_sum-snippet.h" + CheckResults(output_range); + + for (size_t i = 0; i < output_range.size(); i++) output_range[i] = 0; + #include "algorithms/scan/prefix_sum-snippet.h" + CheckResults(output_range); +} diff --git a/doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h b/doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h new file mode 100644 index 0000000..4f42a55 --- /dev/null +++ b/doc/examples/algorithms/scan/sequential_prefix_sum-snippet.h @@ -0,0 +1,5 @@ +std::vector output_range(input_range.size()); +output_range[0] = input_range[0]; +for(size_t i = 1; i < input_range.size(); i++) { + output_range[i] = output_range[i-1] + input_range[i]; +} diff --git a/doc/examples/algorithms/scan/setup-snippet.h b/doc/examples/algorithms/scan/setup-snippet.h new file mode 100644 index 0000000..56c9517 --- /dev/null +++ b/doc/examples/algorithms/scan/setup-snippet.h @@ -0,0 +1,5 @@ +std::vector input_range(5); +for (size_t i = 0; i < input_range.size(); i++) { + input_range[i] = static_cast(i) + 1; +} + diff --git a/doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h b/doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h new file mode 100644 index 0000000..7f12c05 --- /dev/null +++ b/doc/examples/algorithms/sorting/merge_sort_preallocated-snippet.h @@ -0,0 +1,3 @@ +using embb::algorithms::MergeSort; +std::vector temporary_range(range.size()); +MergeSort(range.begin(), range.end(), temporary_range.begin()); diff --git a/doc/examples/algorithms/sorting/quick_sort-snippet.h b/doc/examples/algorithms/sorting/quick_sort-snippet.h new file mode 100644 index 0000000..a8eeb9b --- /dev/null +++ b/doc/examples/algorithms/sorting/quick_sort-snippet.h @@ -0,0 +1,2 @@ +using embb::algorithms::QuickSort; +QuickSort(range.begin(), range.end()); diff --git a/doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h b/doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h new file mode 100644 index 0000000..d33307d --- /dev/null +++ b/doc/examples/algorithms/sorting/quick_sort_custom_compare-snippet.h @@ -0,0 +1 @@ +QuickSort(range.begin(), range.end(), std::greater()); diff --git a/doc/examples/algorithms/sorting/range_define-snippet.h b/doc/examples/algorithms/sorting/range_define-snippet.h new file mode 100644 index 0000000..f9eb29e --- /dev/null +++ b/doc/examples/algorithms/sorting/range_define-snippet.h @@ -0,0 +1 @@ +std::vector range; diff --git a/doc/examples/algorithms/sorting/sorting-fragmented.cc b/doc/examples/algorithms/sorting/sorting-fragmented.cc new file mode 100644 index 0000000..e45ba44 --- /dev/null +++ b/doc/examples/algorithms/sorting/sorting-fragmented.cc @@ -0,0 +1,30 @@ +#include +#include + +/** + * Example using embb::algorithms::QuickSort and MergeSort. + * + * Sorting a range of values. + */ +void RunSorting() { + #include "algorithms/sorting/range_define-snippet.h" + range = {4, 2, 3, 5, 1}; + + #include "algorithms/sorting/quick_sort-snippet.h" + for (size_t i = 0; i < range.size(); i++) { + assert(range[i] == static_cast(i) + 1); + } + + #include "algorithms/sorting/quick_sort_custom_compare-snippet.h" + for (size_t i = 0; i < range.size(); i++) { + assert(range[i] == static_cast(range.size() - i)); + } + + #include "algorithms/sorting/merge_sort_preallocated-snippet.h" + for (size_t i = 0; i < range.size(); i++) { + assert(range[i] == static_cast(i) + 1); + } +} + + + diff --git a/doc/examples/containers/object_pool-fragmented.cc b/doc/examples/containers/object_pool-fragmented.cc new file mode 100644 index 0000000..9940735 --- /dev/null +++ b/doc/examples/containers/object_pool-fragmented.cc @@ -0,0 +1,20 @@ +#include +#include +#include + +void RunObjectPoolExample1() +{ + #include "containers/object_pool-snippet.h" +} + +void RunObjectPoolExample2() +{ + #include "containers/object_pool_2-snippet.h" +} + +void RunObjectPoolExamples() +{ + RunObjectPoolExample1(); + RunObjectPoolExample2(); + +} diff --git a/doc/examples/containers/object_pool-snippet.h b/doc/examples/containers/object_pool-snippet.h new file mode 100644 index 0000000..15fe2b0 --- /dev/null +++ b/doc/examples/containers/object_pool-snippet.h @@ -0,0 +1,11 @@ +embb::containers::ObjectPool objPool(5); //@\label{lst:object_pool_lst1:line_create}@ + +int* alloc[5]; + +for (int i = 0; i != 5; ++i) { + alloc[i] = objPool.Allocate(); //@\label{lst:object_pool_lst1:line_allocate}@ +} + +for (int i = 0; i != 5; ++i) { + objPool.Free(alloc[i]); //@\label{lst:object_pool_lst1:line_free}@ +} \ No newline at end of file diff --git a/doc/examples/containers/object_pool_2-snippet.h b/doc/examples/containers/object_pool_2-snippet.h new file mode 100644 index 0000000..db87c42 --- /dev/null +++ b/doc/examples/containers/object_pool_2-snippet.h @@ -0,0 +1,2 @@ +embb::containers::ObjectPool> objPool(5); //@\label{lst:object_pool_lst2:line_create}@ diff --git a/doc/examples/containers/queues-fragmented.cc b/doc/examples/containers/queues-fragmented.cc new file mode 100644 index 0000000..881f766 --- /dev/null +++ b/doc/examples/containers/queues-fragmented.cc @@ -0,0 +1,14 @@ +#include +#include +#include + +void RunQueueExample1() +{ + #include "containers/queues-snippet.h" +} + + +void RunQueueExamples() +{ + RunQueueExample1(); +} diff --git a/doc/examples/containers/queues-snippet.h b/doc/examples/containers/queues-snippet.h new file mode 100644 index 0000000..951f5c3 --- /dev/null +++ b/doc/examples/containers/queues-snippet.h @@ -0,0 +1,15 @@ +embb::containers::LockFreeMPMCQueue queue(10); //@\label{lst:queue_lst1:line_create}@ + +int i, j; +bool result = queue.TryDequeue(i); //@\label{lst:queue_lst1:fail_pop}@ +assert(result == false); + +for (int i = 0; i <= 4; ++i) { //@\label{lst:queue_lst1:loop1}@ + result = queue.TryEnqueue(i); //@\label{lst:queue_lst1:push}@ + assert(result == true); +} + +for (int i = 0; i <= 4; ++i) { //@\label{lst:queue_lst1:loop2}@ + result = queue.TryDequeue(j); //@\label{lst:queue_lst1:pop}@ + assert(result == true && i == j); //@\label{lst:queue_lst1:assert}@ +} \ No newline at end of file diff --git a/doc/examples/containers/stack-fragmented.cc b/doc/examples/containers/stack-fragmented.cc new file mode 100644 index 0000000..3cec916 --- /dev/null +++ b/doc/examples/containers/stack-fragmented.cc @@ -0,0 +1,11 @@ +#include +#include +void RunStackExample1() +{ + #include "containers/stack-snippet.h" +} + +void RunStackExamples() +{ + RunStackExample1(); +} diff --git a/doc/examples/containers/stack-snippet.h b/doc/examples/containers/stack-snippet.h new file mode 100644 index 0000000..dce744f --- /dev/null +++ b/doc/examples/containers/stack-snippet.h @@ -0,0 +1,15 @@ +embb::containers::LockFreeStack stack(10); //@\label{lst:stack_lst1:line_create}@ + +int i, j; +bool result = stack.TryPop(i); //@\label{lst:stack_lst1:fail_pop}@ +assert(result == false); + +for (int i = 0; i <= 4; ++i) {//@\label{lst:stack_lst1:loop1}@ + result = stack.TryPush(i); //@\label{lst:stack_lst1:push}@ + assert(result == true); +} + +for (int i = 4; i >= 0; --i) { //@\label{lst:stack_lst1:loop2}@ + result = stack.TryPop(j); //@\label{lst:stack_lst1:pop}@ + assert(result == true && i == j); //@\label{lst:stack_lst1:assert}@ +} \ No newline at end of file diff --git a/doc/examples/dataflow/dataflow_add-snippet.h b/doc/examples/dataflow/dataflow_add-snippet.h new file mode 100644 index 0000000..70a1157 --- /dev/null +++ b/doc/examples/dataflow/dataflow_add-snippet.h @@ -0,0 +1,3 @@ + nw.Add(read); + nw.Add(replace); + nw.Add(write); diff --git a/doc/examples/dataflow/dataflow_comparator-snippet.h b/doc/examples/dataflow/dataflow_comparator-snippet.h new file mode 100644 index 0000000..3630f18 --- /dev/null +++ b/doc/examples/dataflow/dataflow_comparator-snippet.h @@ -0,0 +1,8 @@ +template +class Comparator { +public: + void Run(const T& a, const T& b, T& x, T& y) { + x = std::min(a,b); + y = std::max(a,b); + } +}; diff --git a/doc/examples/dataflow/dataflow_connect-snippet.h b/doc/examples/dataflow/dataflow_connect-snippet.h new file mode 100644 index 0000000..eac1dfe --- /dev/null +++ b/doc/examples/dataflow/dataflow_connect-snippet.h @@ -0,0 +1,2 @@ + read.GetOutput<0>() >> replace.GetInput<0>(); + replace.GetOutput<0>() >> write.GetInput<0>(); diff --git a/doc/examples/dataflow/dataflow_consumer-snippet.h b/doc/examples/dataflow/dataflow_consumer-snippet.h new file mode 100644 index 0000000..eae4054 --- /dev/null +++ b/doc/examples/dataflow/dataflow_consumer-snippet.h @@ -0,0 +1,9 @@ +template +class Consumer { +public: + void Run(const T& x1, const T& x2, const T& x3, const T& x4) { + if (x1 <= x2 && x2 <= x3 && x3 <= x4) { + // consume values + } + } +}; diff --git a/doc/examples/dataflow/dataflow_declare_add_sources-snippet.h b/doc/examples/dataflow/dataflow_declare_add_sources-snippet.h new file mode 100644 index 0000000..630dedd --- /dev/null +++ b/doc/examples/dataflow/dataflow_declare_add_sources-snippet.h @@ -0,0 +1,20 @@ + Producer + producer1(1), + producer2(2), + producer3(3), + producer4(4); + + Network::Source + source1( + embb::base::MakeFunction(producer1, &Producer::Run) ), + source2( + embb::base::MakeFunction(producer2, &Producer::Run) ), + source3( + embb::base::MakeFunction(producer3, &Producer::Run) ), + source4( + embb::base::MakeFunction(producer4, &Producer::Run) ); + + nw.Add(source1); + nw.Add(source2); + nw.Add(source3); + nw.Add(source4); diff --git a/doc/examples/dataflow/dataflow_declare_replace-snippet.h b/doc/examples/dataflow/dataflow_declare_replace-snippet.h new file mode 100644 index 0000000..e230807 --- /dev/null +++ b/doc/examples/dataflow/dataflow_declare_replace-snippet.h @@ -0,0 +1,5 @@ + Network::ParallelProcess< + Network::Inputs::Type, + Network::Outputs::Type> replace( + embb::base::MakeFunction(ReplaceFunction) + ); diff --git a/doc/examples/dataflow/dataflow_declare_sink-snippet.h b/doc/examples/dataflow/dataflow_declare_sink-snippet.h new file mode 100644 index 0000000..dfdd6b2 --- /dev/null +++ b/doc/examples/dataflow/dataflow_declare_sink-snippet.h @@ -0,0 +1,3 @@ + Network::Sink write( + embb::base::MakeFunction(SinkFunction) + ); diff --git a/doc/examples/dataflow/dataflow_declare_source-snippet.h b/doc/examples/dataflow/dataflow_declare_source-snippet.h new file mode 100644 index 0000000..470db74 --- /dev/null +++ b/doc/examples/dataflow/dataflow_declare_source-snippet.h @@ -0,0 +1,3 @@ + Network::Source read( + embb::base::MakeFunction(SourceFunction) + ); diff --git a/doc/examples/dataflow/dataflow_include-snippet.h b/doc/examples/dataflow/dataflow_include-snippet.h new file mode 100644 index 0000000..ca47769 --- /dev/null +++ b/doc/examples/dataflow/dataflow_include-snippet.h @@ -0,0 +1 @@ +#include diff --git a/doc/examples/dataflow/dataflow_linear-fragmented.cc b/doc/examples/dataflow/dataflow_linear-fragmented.cc new file mode 100644 index 0000000..35c4d09 --- /dev/null +++ b/doc/examples/dataflow/dataflow_linear-fragmented.cc @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +// replace all occurrences of 'what' in 'str' with 'with' +void repl(std::string& str, const std::string &what, + const std::string& with) { + std::string::size_type pos = 0; + while ((pos = str.find(what, pos)) != std::string::npos) { + str.replace(pos, what.length(), with); + pos += with.length(); + } +} + +#include "dataflow/dataflow_include-snippet.h" + +std::stringstream file("hi world!\nhi there!\nhi hi!?\nhi!\n"); +std::string what("hi"); +std::string with("hello"); + +#include "dataflow/dataflow_network-snippet.h" + +#include "dataflow/dataflow_source_function-snippet.h" +#include "dataflow/dataflow_replace_function-snippet.h" +#include "dataflow/dataflow_sink_function-snippet.h" + +void RunDataflowLinear() { +#include "dataflow/dataflow_declare_source-snippet.h" +#include "dataflow/dataflow_declare_replace-snippet.h" +#include "dataflow/dataflow_declare_sink-snippet.h" +#include "dataflow/dataflow_connect-snippet.h" +#include "dataflow/dataflow_add-snippet.h" +#include "dataflow/dataflow_run-snippet.h" +} diff --git a/doc/examples/dataflow/dataflow_network-snippet.h b/doc/examples/dataflow/dataflow_network-snippet.h new file mode 100644 index 0000000..c13ebeb --- /dev/null +++ b/doc/examples/dataflow/dataflow_network-snippet.h @@ -0,0 +1,2 @@ +typedef embb::dataflow::Network<2> Network; +static Network nw; diff --git a/doc/examples/dataflow/dataflow_nonlinear-fragmented.cc b/doc/examples/dataflow/dataflow_nonlinear-fragmented.cc new file mode 100644 index 0000000..511d378 --- /dev/null +++ b/doc/examples/dataflow/dataflow_nonlinear-fragmented.cc @@ -0,0 +1,69 @@ +#include +#include + +#ifdef NDEBUG +#undef assert +#define assert(x) if(!x) std::cout << "assert failed." << std::endl +#endif + +static int SimpleRand(int & seed) { + const int a = 16807; + const int m = 2147483647; + seed = (a * seed) % m; + return seed % 100; +} + +#include "dataflow/dataflow_include-snippet.h" + +#include "dataflow/dataflow_producer-snippet.h" +#include "dataflow/dataflow_comparator-snippet.h" +#include "dataflow/dataflow_consumer-snippet.h" + +#include "dataflow/dataflow_network-snippet.h" + +void RunDataflowNonLinear() { +#include "dataflow/dataflow_declare_add_sources-snippet.h" + + Comparator comparator; + + Network::ParallelProcess< + Network::Inputs::Type, Network::Outputs::Type> + process1( embb::base::MakeFunction(comparator, &Comparator::Run) ), + process2( embb::base::MakeFunction(comparator, &Comparator::Run) ), + process3( embb::base::MakeFunction(comparator, &Comparator::Run) ), + process4( embb::base::MakeFunction(comparator, &Comparator::Run) ), + process5( embb::base::MakeFunction(comparator, &Comparator::Run) ); + + nw.Add(process1); + nw.Add(process2); + nw.Add(process3); + nw.Add(process4); + nw.Add(process5); + + Consumer consumer; + + Network::Sink + sink1(embb::base::MakeFunction(consumer, &Consumer::Run)); + + nw.Add(sink1); + + source1.GetOutput<0>() >> process1.GetInput<0>(); + source2.GetOutput<0>() >> process2.GetInput<0>(); + source3.GetOutput<0>() >> process1.GetInput<1>(); + source4.GetOutput<0>() >> process2.GetInput<1>(); + + process1.GetOutput<0>() >> process3.GetInput<0>(); + process2.GetOutput<0>() >> process3.GetInput<1>(); + process1.GetOutput<1>() >> process4.GetInput<0>(); + process2.GetOutput<1>() >> process4.GetInput<1>(); + + process3.GetOutput<1>() >> process5.GetInput<0>(); + process4.GetOutput<0>() >> process5.GetInput<1>(); + + process3.GetOutput<0>() >> sink1.GetInput<0>(); + process5.GetOutput<0>() >> sink1.GetInput<1>(); + process5.GetOutput<1>() >> sink1.GetInput<2>(); + process4.GetOutput<1>() >> sink1.GetInput<3>(); + + nw(10); +} diff --git a/doc/examples/dataflow/dataflow_producer-snippet.h b/doc/examples/dataflow/dataflow_producer-snippet.h new file mode 100644 index 0000000..92c61c2 --- /dev/null +++ b/doc/examples/dataflow/dataflow_producer-snippet.h @@ -0,0 +1,12 @@ +template +class Producer { + public: + explicit Producer(int seed) : seed_(seed) {} + void Run(T& x) { + // produce a new value x + x = SimpleRand(seed_); + } + + private: + int seed_; +}; diff --git a/doc/examples/dataflow/dataflow_replace_function-snippet.h b/doc/examples/dataflow/dataflow_replace_function-snippet.h new file mode 100644 index 0000000..21e71d9 --- /dev/null +++ b/doc/examples/dataflow/dataflow_replace_function-snippet.h @@ -0,0 +1,4 @@ +void ReplaceFunction(std::string const & istr, std::string & ostr) { + ostr = istr; + repl(ostr, what, with); +} diff --git a/doc/examples/dataflow/dataflow_run-snippet.h b/doc/examples/dataflow/dataflow_run-snippet.h new file mode 100644 index 0000000..a5d17a5 --- /dev/null +++ b/doc/examples/dataflow/dataflow_run-snippet.h @@ -0,0 +1 @@ + nw(4); diff --git a/doc/examples/dataflow/dataflow_sink_function-snippet.h b/doc/examples/dataflow/dataflow_sink_function-snippet.h new file mode 100644 index 0000000..66896ea --- /dev/null +++ b/doc/examples/dataflow/dataflow_sink_function-snippet.h @@ -0,0 +1,3 @@ +void SinkFunction(std::string const & str) { + std::cout << str << std::endl; +} diff --git a/doc/examples/dataflow/dataflow_source_function-snippet.h b/doc/examples/dataflow/dataflow_source_function-snippet.h new file mode 100644 index 0000000..6df637b --- /dev/null +++ b/doc/examples/dataflow/dataflow_source_function-snippet.h @@ -0,0 +1,3 @@ +void SourceFunction(std::string & str) { + std::getline(file, str); +} diff --git a/doc/examples/insert_snippets.py b/doc/examples/insert_snippets.py new file mode 100644 index 0000000..40d70bc --- /dev/null +++ b/doc/examples/insert_snippets.py @@ -0,0 +1,51 @@ +import os +import glob +import shutil +import sys +import string + +filenames = os.listdir(".") +# Work through all files and directories +for filename in filenames: + # If it is a directory, just add content to filenames + if os.path.isdir(filename): + nestedfiles = os.listdir(filename + "/") + for i in range(len(nestedfiles)): + nestedfiles[i] = filename + "/" + nestedfiles[i] + filenames.extend(nestedfiles) + # Otherwise look into file + else: + #sys.stdout.write("Looking at " + filename) + is_fragmented = string.find(filename, "-fragmented.cc") != -1 + if is_fragmented: + in_file = open(filename, 'r+') + out_filename = string.replace(filename, "-fragmented", "") + sys.stdout.write(filename + " -> " + out_filename + "\n") + out_file = open(out_filename, 'w') + for line in in_file: + stripped_line = string.lstrip(line) + # Check if first characters after whitespaces are include + is_include = string.find(stripped_line, "#include") == 0 + # Check if snippet keyword is contained in include + includes_snippet = string.find(line, "-snippet.") != -1 + if is_include and includes_snippet: + # Get number of whitespaces for indentation of snippet + num_whitespaces = len(line) - len(stripped_line) + # Create Whitespace-string for indentation + whitespaces = "" + for i in range(num_whitespaces): + whitespaces += " " + # Get include filename + first_quotes_pos = string.find(line, '"') + last_quotes_pos = string.rfind(line, '"') + snippet_filename = line[first_quotes_pos + 1 : last_quotes_pos] + sys.stdout.write(" + " + snippet_filename + "\n") + snippet_file = open(snippet_filename, 'r') + for snippet_line in snippet_file: + out_file.write(whitespaces + snippet_line) + snippet_file.close() + #out_file.write("\n") + else: + out_file.write(line) + in_file.close() + out_file.close() \ No newline at end of file diff --git a/doc/examples/main.cc b/doc/examples/main.cc new file mode 100644 index 0000000..04f3c57 --- /dev/null +++ b/doc/examples/main.cc @@ -0,0 +1,81 @@ +#include + +void RunMTAPI_C(); +void RunMTAPI_CPP(); +void RunDataflowLinear(); +void RunDataflowNonLinear(); +void RunSTLForEach(); +void RunForEach(); +void RunInvoke(); +void RunSorting(); +void RunReduce(); +void RunCounting(); +void RunScan(); +void RunObjectPoolExamples(); +void RunStackExamples(); +void RunQueueExamples(); + +/** + * Runs all examples and tests their correctness. + */ +int main() { + std::cout << "Running examples ..." << std::endl; + + std::cout << "RunMTAPI_C() ..." << std::endl; + RunMTAPI_C(); + std::cout << "RunMTAPI_C() ... done" << std::endl; + + std::cout << "RunMTAPI_CPP() ..." << std::endl; + RunMTAPI_CPP(); + std::cout << "RunMTAPI_CPP() ... done" << std::endl; + + std::cout << "RunDataflowLinear() ..." << std::endl; + RunDataflowLinear(); + std::cout << "RunDataflowLinear() ... done" << std::endl; + + std::cout << "RunDataflowNonLinear() ..." << std::endl; + RunDataflowNonLinear(); + std::cout << "RunDataflowNonLinear() ... done" << std::endl; + + std::cout << "RunSTLForEach() ..." << std::endl; + RunSTLForEach(); + std::cout << "RunSTLForEach() ... done" << std::endl; + + std::cout << "RunForEach() ..." << std::endl; + RunForEach(); + std::cout << "RunForEach() ... done" << std::endl; + + std::cout << "RunInvoke() ..." << std::endl; + RunInvoke(); + std::cout << "RunInvoke() ... done" << std::endl; + + std::cout << "RunSorting() ... " << std::endl; + RunSorting(); + std::cout << "RunSorting() ... done" << std::endl; + + std::cout << "RunReduce() ... " << std::endl; + RunReduce(); + std::cout << "RunReduce() ... done" << std::endl; + + std::cout << "RunCounting() ..." << std::endl; + RunCounting(); + std::cout << "RunCounting() ... done" << std::endl; + /* + std::cout << "RunScan() ..." << std::endl; + RunScan(); + std::cout << "RunScan() ... done" << std::endl; + */ + std::cout << "RunObjectPoolExamples() ..." << std::endl; + RunObjectPoolExamples(); + std::cout << "RunObjectPoolExamples() ... done" << std::endl; + + std::cout << "RunStackExamples() ..." << std::endl; + RunStackExamples(); + std::cout << "RunStackExamples() ... done" << std::endl; + + std::cout << "RunQueueExamples() ..." << std::endl; + RunQueueExamples(); + std::cout << "RunQueueExamples() ... done" << std::endl; + + std::cout << "Running examples ... done" << std::endl; +} diff --git a/doc/examples/mtapi/mtapi_c-fragmented.cc b/doc/examples/mtapi/mtapi_c-fragmented.cc new file mode 100644 index 0000000..bfc6b82 --- /dev/null +++ b/doc/examples/mtapi/mtapi_c-fragmented.cc @@ -0,0 +1,40 @@ +#include +#include + +#include "mtapi/mtapi_check_status-snippet.h" +#include "mtapi/mtapi_c_domain_node_id-snippet.h" + +static +#include "mtapi/mtapi_c_action_signature-snippet.h" +#include "mtapi/mtapi_c_validate_arguments-snippet.h" +#include "mtapi/mtapi_c_validate_result_buffer-snippet.h" + /* calculate */ +#include "mtapi/mtapi_terminating_condition-snippet.h" + /* first recursive call spawned as task (x = fib(n - 1);) */ +#include "mtapi/mtapi_c_calc_task-snippet.h" + /* second recursive call can be called directly (y = fib(n - 2);) */ +#include "mtapi/mtapi_c_calc_direct-snippet.h" + /* wait for completion */ +#include "mtapi/mtapi_c_wait_task-snippet.h" + /* add the two preceeding numbers */ +#include "mtapi/mtapi_write_back-snippet.h" + +static int fibonacci(int n) { +#include "mtapi/mtapi_c_initialize-snippet.h" +#include "mtapi/mtapi_c_register_action-snippet.h" +#include "mtapi/mtapi_c_start_task-snippet.h" + /* wait for task completion */ + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); +#include "mtapi/mtapi_c_finalize-snippet.h" + return result; +} + +void RunMTAPI_C() { + /* run calculation */ + int result; + result = fibonacci(6); + + /* print result */ + printf("result: %i\n", result); +} diff --git a/doc/examples/mtapi/mtapi_c_action_signature-snippet.h b/doc/examples/mtapi/mtapi_c_action_signature-snippet.h new file mode 100644 index 0000000..80b8a4f --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_action_signature-snippet.h @@ -0,0 +1,9 @@ +void fibonacciActionFunction( + const void* args, + mtapi_size_t arg_size, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context + ) { diff --git a/doc/examples/mtapi/mtapi_c_calc_direct-snippet.h b/doc/examples/mtapi/mtapi_c_calc_direct-snippet.h new file mode 100644 index 0000000..525f7be --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_calc_direct-snippet.h @@ -0,0 +1,7 @@ + int b = n - 2; + int y; + fibonacciActionFunction( + &b, sizeof(int), + &y, sizeof(int), + MTAPI_NULL, 0, + task_context); diff --git a/doc/examples/mtapi/mtapi_c_calc_task-snippet.h b/doc/examples/mtapi/mtapi_c_calc_task-snippet.h new file mode 100644 index 0000000..62b352a --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_calc_task-snippet.h @@ -0,0 +1,15 @@ + int a = n - 1; + int x; + mtapi_task_hndl_t task = mtapi_task_start( + MTAPI_TASK_ID_NONE, /* optional task ID */ + fibonacciJob, /* job */ + (void*)&a, /* arguments passed to action + functions */ + sizeof(int), /* size of arguments */ + (void*)&x, /* result buffer */ + sizeof(int), /* size of result buffer */ + MTAPI_DEFAULT_TASK_ATTRIBUTES, /* task attributes */ + MTAPI_GROUP_NONE, /* optional task group */ + &status /* status out - parameter */ + ); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h b/doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h new file mode 100644 index 0000000..9af8d70 --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_domain_node_id-snippet.h @@ -0,0 +1,5 @@ +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#define FIBONACCI_JOB 1 +mtapi_job_hndl_t fibonacciJob; diff --git a/doc/examples/mtapi/mtapi_c_finalize-snippet.h b/doc/examples/mtapi/mtapi_c_finalize-snippet.h new file mode 100644 index 0000000..21c5bfa --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_finalize-snippet.h @@ -0,0 +1,7 @@ + /* delete action */ + mtapi_action_delete(fibonacciAction, 100, &status); + MTAPI_CHECK_STATUS(status); + + /* finalize the node */ + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_c_initialize-snippet.h b/doc/examples/mtapi/mtapi_c_initialize-snippet.h new file mode 100644 index 0000000..1269342 --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_initialize-snippet.h @@ -0,0 +1,25 @@ + mtapi_status_t status; + + /* initialize node attributes to default values */ + mtapi_node_attributes_t node_attr; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + /* set node type to SMP */ + mtapi_nodeattr_set( + &node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + /* initialize the node */ + mtapi_info_t info; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_c_register_action-snippet.h b/doc/examples/mtapi/mtapi_c_register_action-snippet.h new file mode 100644 index 0000000..a05aaa7 --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_register_action-snippet.h @@ -0,0 +1,17 @@ + /* create action */ + mtapi_action_hndl_t fibonacciAction; + fibonacciAction = mtapi_action_create( + FIBONACCI_JOB, /* action ID, defined by the + application */ + (fibonacciActionFunction), /* action function */ + MTAPI_NULL, /* no shared data */ + 0, /* length of shared data */ + MTAPI_DEFAULT_ACTION_ATTRIBUTES, /* action attributes */ + &status /* status out - parameter */ + ); + MTAPI_CHECK_STATUS(status); + + /* get job */ + mtapi_task_hndl_t task; + fibonacciJob = mtapi_job_get(FIBONACCI_JOB, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_c_start_task-snippet.h b/doc/examples/mtapi/mtapi_c_start_task-snippet.h new file mode 100644 index 0000000..79d005e --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_start_task-snippet.h @@ -0,0 +1,15 @@ + /* start task */ + int result; + task = mtapi_task_start( + MTAPI_TASK_ID_NONE, /* optional task ID */ + fibonacciJob, /* job */ + (void*)&n, /* arguments passed to action + functions */ + sizeof(int), /* size of arguments */ + (void*)&result, /* result buffer */ + sizeof(int), /* size of result buffer */ + MTAPI_DEFAULT_TASK_ATTRIBUTES, /* task attributes */ + MTAPI_GROUP_NONE, /* optional task group */ + &status /* status out - parameter */ + ); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h b/doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h new file mode 100644 index 0000000..44246ab --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_validate_arguments-snippet.h @@ -0,0 +1,12 @@ + /* check size of arguments (in this case we only expect one int + value)*/ + mtapi_status_t status; + if (arg_size != sizeof(int)) { + printf("wrong size of arguments\n"); + mtapi_context_status_set(task_context, MTAPI_ERR_ARG_SIZE, &status); + MTAPI_CHECK_STATUS(status); + return; + } + + /* cast arguments to the desired type */ + int n = *(int*)args; diff --git a/doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h b/doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h new file mode 100644 index 0000000..24e7630 --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_validate_result_buffer-snippet.h @@ -0,0 +1,21 @@ + /* if the caller is not interested in results, result_buffer may be + MTAPI_NULL. Of course, this depends on the application */ + int* result = MTAPI_NULL; + if (result_buffer == MTAPI_NULL) { + mtapi_context_status_set( + task_context, MTAPI_ERR_RESULT_SIZE, &status); + MTAPI_CHECK_STATUS(status); + } else { + /* if results are expected by the caller, check result buffer + size... */ + if (result_buffer_size == sizeof(int)) { + /* ... and cast the result buffer */ + result = (int*)result_buffer; + } else { + printf("wrong size of result buffer\n"); + mtapi_context_status_set( + task_context, MTAPI_ERR_RESULT_SIZE, &status); + MTAPI_CHECK_STATUS(status); + return; + } + } diff --git a/doc/examples/mtapi/mtapi_c_wait_task-snippet.h b/doc/examples/mtapi/mtapi_c_wait_task-snippet.h new file mode 100644 index 0000000..8c3e348 --- /dev/null +++ b/doc/examples/mtapi/mtapi_c_wait_task-snippet.h @@ -0,0 +1,2 @@ + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_check_status-snippet.h b/doc/examples/mtapi/mtapi_check_status-snippet.h new file mode 100644 index 0000000..d22c393 --- /dev/null +++ b/doc/examples/mtapi/mtapi_check_status-snippet.h @@ -0,0 +1,7 @@ +#include +#include +#define MTAPI_CHECK_STATUS(status) \ +if (MTAPI_SUCCESS != status) { \ + embb_log_error("MTAPI C Example", "...error %d\n\n", status); \ + abort(); \ +} diff --git a/doc/examples/mtapi/mtapi_cpp-fragmented.cc b/doc/examples/mtapi/mtapi_cpp-fragmented.cc new file mode 100644 index 0000000..1ad6f37 --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp-fragmented.cc @@ -0,0 +1,38 @@ +#include + +#include + +#include "mtapi/mtapi_check_status-snippet.h" + +static +#include "mtapi/mtapi_cpp_action_signature-snippet.h" + /* get the node instance */ +#include "mtapi/mtapi_cpp_get_node-snippet.h" + /* calculate */ +#include "mtapi/mtapi_terminating_condition-snippet.h" + /* first recursive call spawned as task (x = fib(n - 1);) */ +#include "mtapi/mtapi_cpp_calc_task-snippet.h" + /* second recursive call can be called directly (y = fib(n - 2);) */ +#include "mtapi/mtapi_cpp_calc_direct-snippet.h" + /* wait for completion */ +#include "mtapi/mtapi_cpp_wait_task-snippet.h" + /* add the two preceeding numbers */ +#include "mtapi/mtapi_write_back-snippet.h" + +static +int fibonacci(int n) { + /* get the node instance, the node is initialized automatically */ + embb::mtapi::Node& node = embb::mtapi::Node::GetInstance(); + /* start calculation */ +#include "mtapi/mtapi_cpp_start_task-snippet.h" + /* wait for task completion */ + mtapi_status_t status = task.Wait(MTAPI_INFINITE); + MTAPI_CHECK_STATUS(status); + + return result; +} + +void RunMTAPI_CPP() { + int result = fibonacci(6); + std::cout << "result: " << result << std::endl; +} diff --git a/doc/examples/mtapi/mtapi_cpp_action_signature-snippet.h b/doc/examples/mtapi/mtapi_cpp_action_signature-snippet.h new file mode 100644 index 0000000..f15cd7f --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp_action_signature-snippet.h @@ -0,0 +1,5 @@ +void fibonacciActionFunction( + int n, + int* result, + embb::mtapi::TaskContext & task_context + ) { diff --git a/doc/examples/mtapi/mtapi_cpp_calc_direct-snippet.h b/doc/examples/mtapi/mtapi_cpp_calc_direct-snippet.h new file mode 100644 index 0000000..eb08e75 --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp_calc_direct-snippet.h @@ -0,0 +1,6 @@ + int b = n - 2; + int y; + fibonacciActionFunction( + b, + &y, + task_context); diff --git a/doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h b/doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h new file mode 100644 index 0000000..4362f38 --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp_calc_task-snippet.h @@ -0,0 +1,10 @@ + int a = n - 1; + int x; + embb::mtapi::Task task = node.Spawn( + embb::base::Bind( + embb::base::MakeFunction(fibonacciActionFunction), + a, /* argument */ + &x, /* result */ + embb::base::Placeholder::_1 + ) + ); diff --git a/doc/examples/mtapi/mtapi_cpp_get_node-snippet.h b/doc/examples/mtapi/mtapi_cpp_get_node-snippet.h new file mode 100644 index 0000000..b91d187 --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp_get_node-snippet.h @@ -0,0 +1 @@ + embb::mtapi::Node& node = embb::mtapi::Node::GetInstance(); diff --git a/doc/examples/mtapi/mtapi_cpp_start_task-snippet.h b/doc/examples/mtapi/mtapi_cpp_start_task-snippet.h new file mode 100644 index 0000000..7dddbb3 --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp_start_task-snippet.h @@ -0,0 +1,9 @@ + int result; + embb::mtapi::Task task = node.Spawn( + embb::base::Bind( + embb::base::MakeFunction(fibonacciActionFunction), + n, + &result, + embb::base::Placeholder::_1 + ) + ); diff --git a/doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h b/doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h new file mode 100644 index 0000000..21637fd --- /dev/null +++ b/doc/examples/mtapi/mtapi_cpp_wait_task-snippet.h @@ -0,0 +1,2 @@ + mtapi_status_t status = task.Wait(MTAPI_INFINITE); + MTAPI_CHECK_STATUS(status); diff --git a/doc/examples/mtapi/mtapi_terminating_condition-snippet.h b/doc/examples/mtapi/mtapi_terminating_condition-snippet.h new file mode 100644 index 0000000..7112b48 --- /dev/null +++ b/doc/examples/mtapi/mtapi_terminating_condition-snippet.h @@ -0,0 +1,3 @@ + if (n < 2) { + *result = n; + } else { diff --git a/doc/examples/mtapi/mtapi_write_back-snippet.h b/doc/examples/mtapi/mtapi_write_back-snippet.h new file mode 100644 index 0000000..bca08c7 --- /dev/null +++ b/doc/examples/mtapi/mtapi_write_back-snippet.h @@ -0,0 +1,3 @@ + *result = x + y; + } +} diff --git a/doc/examples/stl_for_each/function-snippet.h b/doc/examples/stl_for_each/function-snippet.h new file mode 100644 index 0000000..d870d31 --- /dev/null +++ b/doc/examples/stl_for_each/function-snippet.h @@ -0,0 +1 @@ +std::for_each(range.begin(), range.end(), &DoubleFunction); diff --git a/doc/examples/stl_for_each/function_define-snippet.h b/doc/examples/stl_for_each/function_define-snippet.h new file mode 100644 index 0000000..0bb8d2b --- /dev/null +++ b/doc/examples/stl_for_each/function_define-snippet.h @@ -0,0 +1,3 @@ +void DoubleFunction(int& to_double) { + to_double *= 2; +} diff --git a/doc/examples/stl_for_each/functor-snippet.h b/doc/examples/stl_for_each/functor-snippet.h new file mode 100644 index 0000000..e282b10 --- /dev/null +++ b/doc/examples/stl_for_each/functor-snippet.h @@ -0,0 +1 @@ +std::for_each(range.begin(), range.end(), DoubleFunctor()); diff --git a/doc/examples/stl_for_each/functor_define-snippet.h b/doc/examples/stl_for_each/functor_define-snippet.h new file mode 100644 index 0000000..d5e34d1 --- /dev/null +++ b/doc/examples/stl_for_each/functor_define-snippet.h @@ -0,0 +1,5 @@ +struct DoubleFunctor { + void operator()(int& to_double) { + to_double *= 2; + } +}; diff --git a/doc/examples/stl_for_each/lambda-snippet.h b/doc/examples/stl_for_each/lambda-snippet.h new file mode 100644 index 0000000..e8c545a --- /dev/null +++ b/doc/examples/stl_for_each/lambda-snippet.h @@ -0,0 +1,2 @@ +std::for_each(range.begin(), range.end(), + [] (int& to_double) { to_double *= 2; }); diff --git a/doc/examples/stl_for_each/manual-snippet.h b/doc/examples/stl_for_each/manual-snippet.h new file mode 100644 index 0000000..22b72cc --- /dev/null +++ b/doc/examples/stl_for_each/manual-snippet.h @@ -0,0 +1,3 @@ +for (size_t i=0; i < range.size(); i++) { + range[i] *= 2; +} diff --git a/doc/examples/stl_for_each/setup-snippet.h b/doc/examples/stl_for_each/setup-snippet.h new file mode 100644 index 0000000..0e78cc8 --- /dev/null +++ b/doc/examples/stl_for_each/setup-snippet.h @@ -0,0 +1,4 @@ +std::vector range(5); +for (size_t i=0; i < range.size(); i++) { + range[i] = static_cast(i) + 1; +} diff --git a/doc/examples/stl_for_each/stl_for_each-fragmented.cc b/doc/examples/stl_for_each/stl_for_each-fragmented.cc new file mode 100644 index 0000000..251c40e --- /dev/null +++ b/doc/examples/stl_for_each/stl_for_each-fragmented.cc @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +#include "stl_for_each/function_define-snippet.h" + +#include "stl_for_each/functor_define-snippet.h" + +static void CheckResults(const std::vector& range) { + int i = 1; + for (const int& value : range) { + assert(value == i * 2); + EMBB_UNUSED_IN_RELEASE(value); + i++; + } +} + +/** + * Example using embb::patterns::ForEach. + * + * Traversing and modifying a sequence with a for-each loop. + */ + +void RunSTLForEach() { + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/manual-snippet.h" + CheckResults(range); + } + + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/function-snippet.h" + CheckResults(range); + } + + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/functor-snippet.h" + CheckResults(range); + } + + { + #include "stl_for_each/setup-snippet.h" + #include "stl_for_each/lambda-snippet.h" + CheckResults(range); + } +} diff --git a/doc/images/embb.jpg b/doc/images/embb.jpg new file mode 100644 index 0000000..c6ecc92 Binary files /dev/null and b/doc/images/embb.jpg differ diff --git a/doc/images/embb.png b/doc/images/embb.png new file mode 100644 index 0000000..b55a4ab Binary files /dev/null and b/doc/images/embb.png differ diff --git a/doc/images/embb.pptx b/doc/images/embb.pptx new file mode 100644 index 0000000..545540e Binary files /dev/null and b/doc/images/embb.pptx differ diff --git a/doc/reference/Doxyfile.in b/doc/reference/Doxyfile.in new file mode 100644 index 0000000..9dd6791 --- /dev/null +++ b/doc/reference/Doxyfile.in @@ -0,0 +1,394 @@ +# Copyright (c) 2014, 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. + +# ============================================================================== +# Project related options +# ============================================================================== + +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "Embedded Multicore Building Blocks V@EMBB_BASE_VERSION_MAJOR@.@EMBB_BASE_VERSION_MINOR@.@EMBB_BASE_VERSION_PATCH@" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = YES +INLINE_INHERITED_MEMB = YES +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@CMAKE_SOURCE_DIR@" +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 3 +ALIASES = "notthreadsafe=\par Concurrency\nNot thread-safe\n" \ + "threadsafe=\par Concurrency\nThread-safe\n" \ + "lockfree=\par Concurrency\nThread-safe and lock-free\n" \ + "waitfree=\par Concurrency\nThread-safe and wait-free\n" \ + "memory=\par Dynamic memory allocation\n" \ + "pre=\par Precondition\n" \ + "post=\par Postcondition\n" \ + "note=\par Note\n" \ + "concept{1}=@ingroup \1\n@par Implemented concepts:\n@ref \1" +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = YES +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 + +# ============================================================================== +# Build related options +# ============================================================================== + +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = NO +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = @CMAKE_SOURCE_DIR@/doc/reference/DoxygenLayout.xml +CITE_BIB_FILES = + +# ============================================================================== +# Options related to warning and progress messages +# ============================================================================== + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +# ============================================================================== +# Options related to input files +# ============================================================================== + +INPUT = "@CMAKE_SOURCE_DIR@/doc/reference/embb.dox" \ + "@CMAKE_SOURCE_DIR@/containers_cpp/include" \ + "@CMAKE_SOURCE_DIR@/dataflow_cpp/include" \ + "@CMAKE_SOURCE_DIR@/algorithms_cpp/include" \ + "@CMAKE_SOURCE_DIR@/mtapi_cpp/include" \ + "@CMAKE_SOURCE_DIR@/base_cpp/include" \ + "@CMAKE_SOURCE_DIR@/mtapi_c/include" \ + "@CMAKE_SOURCE_DIR@/base_c/include" +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h \ + *.cc \ + *.c \ + *.dox +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */test/* \ + */internal/* +EXCLUDE_SYMBOLS = *test* \ + *::internal::* +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = @CMAKE_SOURCE_DIR@/doc +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = + +# ============================================================================== +# Options related to source browsing +# ============================================================================== + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +CLANG_ASSISTED_PARSING = NO +CLANG_OPTIONS = + +# ============================================================================== +# Options related to alphabetical class index +# ============================================================================== + +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = cm + +# ============================================================================== +# Options related to HTML output +# ============================================================================== + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/reference/DoxygenHTMLStyle.css +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = + +# ============================================================================== +# Options related to LaTeX output +# ============================================================================== + +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain + +# ============================================================================== +# Options related to RTF output +# ============================================================================== + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +# ============================================================================== +# Options related to man page output +# ============================================================================== + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO + +# ============================================================================== +# Options related to XML output +# ============================================================================== + +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES + +# ============================================================================== +# Options related to DOCBOOK output +# ============================================================================== + +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook + +# ============================================================================== +# Options for the AutoGen Definitions output +# ============================================================================== + +GENERATE_AUTOGEN_DEF = NO + +# ============================================================================== +# Options related to Perl module output +# ============================================================================== + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +# ============================================================================== +# Options related to preprocessor +# ============================================================================== + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = protected=private \ + DOXYGEN +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +# ============================================================================== +# Options related to external references +# ============================================================================== + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = NO +PERL_PATH = /usr/bin/perl + +# ============================================================================== +# Options related to dot tool +# ============================================================================== + +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = NO +COLLABORATION_GRAPH = NO +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = NO +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = @DOT_PATH@ +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/doc/reference/DoxygenHTMLStyle.css b/doc/reference/DoxygenHTMLStyle.css new file mode 100644 index 0000000..52a9ec0 --- /dev/null +++ b/doc/reference/DoxygenHTMLStyle.css @@ -0,0 +1,3 @@ +div.image img[src="embb.png"]{ + width:500px; +} diff --git a/doc/reference/DoxygenLayout.xml b/doc/reference/DoxygenLayout.xml new file mode 100644 index 0000000..2a5c055 --- /dev/null +++ b/doc/reference/DoxygenLayout.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/reference/embb.dox b/doc/reference/embb.dox new file mode 100644 index 0000000..60ab98d --- /dev/null +++ b/doc/reference/embb.dox @@ -0,0 +1,42 @@ +/** + +\mainpage Overview + +The Embedded Multicore Building Blocks (EMB2) are an easy to +use yet powerful and efficient C/C++ library for the development of +parallel applications. EMB2 has been specifically designed +for embedded systems and the typical requirements that accompany them, +such as real-time capability and constraints on memory consumption. As a +major advantage, low-level operations are hidden in the library which +relieves software developers from the burden of thread management and +synchronization. This not only improves productivity of parallel +software development, but also results in increased reliability and +performance of the applications. + +EMB2 is independent of the hardware architecture (x86, ARM, +...) and runs on various platforms, from small devices to large systems +containing numerous processor cores. It builds on MTAPI, a standardized +programming interface for leveraging task parallelism in embedded +systems containing symmetric or asymmetric multicore processors. A core +feature of MTAPI is low-overhead scheduling of fine-grained tasks among +the available cores during runtime. Unlike existing libraries, +EMB2 supports task priorities, which allows the creation of +soft real-time systems. Additionally, the scheduling strategy can be +optimized for non-functional requirements such as minimal latency and +fairness. + +Besides the task scheduler, EMB2 provides basic parallel +algorithms, concurrent data structures, and skeletons for implementing +stream processing applications (see figure below). These building blocks +are largely implemented in a non-blocking fashion, thus preventing +frequently encountered pitfalls like lock contention, deadlocks, and +priority inversion. As another advantage in real-time systems, the +algorithms and data structures give certain progress guarantees. For +example, wait-free data structures guarantee system-wide progress which +means that every operation completes within a finite number of steps +independently of any other concurrent operations on the same data +structure. + +\image html ../images/embb.png + +*/ diff --git a/doc/reference/index.html b/doc/reference/index.html new file mode 100644 index 0000000..f2713ad --- /dev/null +++ b/doc/reference/index.html @@ -0,0 +1,15 @@ + + + + + + + Page Redirection + + + If you are not redirected automatically, follow the link + + + diff --git a/doc/tutorial/content/algorithms.tex b/doc/tutorial/content/algorithms.tex new file mode 100644 index 0000000..9a52973 --- /dev/null +++ b/doc/tutorial/content/algorithms.tex @@ -0,0 +1,139 @@ +\chapter{Algorithms} +\label{cha:algorithms} + +The \emph{Algorithms} building block of \embb provides high-level constructs for typical parallelization tasks. They are aligned to the functions provided by the C++ Standard Library, but contain additional functionality typical for embedded systems such as task priorities. Although the algorithms can be used in a black-box way, it is good to have a basic understanding of their implementation: The algorithms split computations to be performed in parallel into tasks which are executed by the MTAPI task scheduler (cf. Chapter~\ref{cha:mtapi}). For that purpose, the tasks are stored in queues and mapped to a fixed number of worker threads at runtime. Since MTAPI allocates the necessary data structures during initialization, the maximum number of tasks in flight is fixed. In case one of the algorithms exceeds this limit, an exception is thrown. + +In the following, we look at parallel function invocation (Section~\ref{sec:algorithms_invoke}), sorting (Section~\ref{sec:algorithms_sorting}), counting (Section~\ref{sec:algorithms_counting}), foreach loops (Section~\ref{sec:algorithms_foreach}), reductions (Section~\ref{sec:algorithms_reductions}), and prefix computations (Section~\ref{sec:algorithms_prefix}). + +\section{Function Invocation} +\label{sec:algorithms_invoke} + +%In the previous section, we have considered data parallelism, that is, parallel execution of the same operation on a range of elements. Next, we consider parallel execution of several work packages encapsulated in functions. +Let us start with the parallel execution of several work packages encapsulated in functions. Suppose that the following functions operate on different data sets, and thus are independent of each other: +% +\\\inputlisting{../examples/algorithms/invoke/packages-snippet.h} +% +The functions can be executed in parallel using the \lstinline|ParallelInvoke| construct provided by {\embb}: +% +\\\inputlisting{../examples/algorithms/invoke/invocation-snippet.h} +% +Note that \lstinline|ParallelInvoke| waits until all its arguments have finished execution. + +Next, let us consider a more elaborate example. The following piece of code shows a serial quick sort algorithm which we want to parallelize (do not care about the details of the \lstinline|Partition| function for the moment): +% +\\\inputlisting{../examples/algorithms/invoke/quick_sort-snippet.h} +% +A straightforward approach to parallelize this algorithm is to execute the recursive calls to \lstinline|Quicksort| in parallel. With \lstinline|ParallelInvoke| and lambdas, it is as simple as that: +% +\\\inputlisting{../examples/algorithms/invoke/parallel_quick_sort-snippet.h} +% +The lambdas capture the \lstinline|first|, \lstinline|mid|, and \lstinline|last| pointers to the range to be sorted and forward them to the recursive calls of quick sort. These are executed in parallel, where \lstinline|Invoke| does not return before both have finished execution. The above implementation of parallel quick sort is of course not yet optimal. In particular, the creation of new tasks should be stopped when a certain lower bound on the size of the subranges has been reached. The subranges can then be sorted sequentially in order to reduce the overhead for task creation and management. Fortunately, {\embb} already provides solutions for parallel sorting, which will be covered in the following section. + +\section{Sorting} +\label{sec:algorithms_sorting} + +%As sorting is a prominent problem that can benefit from multicore processors, {\embb} provides ready-to-use algorithms for parallel sorting. +For systems with constraints on memory consumption, the quick sort algorithm provided by \embb is usually the best choice, since it works in-place which means that it does not require additional memory. Considering real-time systems, however, its worst-case runtime of $O(N^2)$, where $N$ is the number of elements to be sorted, can be a problem. For this reason, {\embb} also provides a parallel merge sort algorithm. Merge sort does not work in-place, but has a predictable runtime complexity of $\Theta(N \log N)$. Assume we want to sort a vector of integers: +% +\\\inputlisting{../examples/algorithms/sorting/range_define-snippet.h} +% +Using quick sort, we simply write: +% +\\\inputlisting{../examples/algorithms/sorting/quick_sort-snippet.h} +% +The default invocation of \lstinline|QuickSort| uses \lstinline|std::less| with the iterators' \lstinline|value_type| as comparison operation. As a result, the range is sorted in ascending order. It is possible to provide a custom comparison operation, for example \lstinline|std::greater|, by passing it as a function object to the algorithm. Sorting the elements in descending can be accomplished as follows: +% +\\\inputlisting{../examples/algorithms/sorting/quick_sort_custom_compare-snippet.h} + +The merge sort algorithm comes in two versions. The first version automatically allocates dynamic memory for temporary values when the algorithm is called. Its name is \lstinline|MergeSortAllocate| and it has the same parameters as \lstinline|QuickSort|. To enable the use of merge sort in environments that forbid dynamic memory allocation after initialization, the second version can be called with a pre-allocated temporary range of values: +% +\\\inputlisting{../examples/algorithms/sorting/merge_sort_preallocated-snippet.h} +% +The temporary range can be allocated at any time, e.g., during the initialization phase of the system. + +\section{Counting} +\label{sec:algorithms_counting} + +%Related to the above described summation reductions are the so-called counting operations. +\embb also provides functions for counting the number of elements in a range. Consider a range of integers from 0 to 3: +% +\\\inputlisting{../examples/algorithms/counting/setup-snippet.h} +% +To determine how often a specific value appears within the range, we could simply iterate over it and compare each element with the specified one. The \lstinline|Count| function does this in parallel, where the first two arguments specify the range and the third one the element to be counted: +%have to go through each of them, perform a comparison, and count the elements that compare equal. As in the reduction, the problem here is that a global counter is involved. The counting with equal comparison can be realized using the \lstinline|Count| function as +% +\\\inputlisting{../examples/algorithms/counting/count-snippet.h} +% +For the range given above, we have \lstinline|count == 2|. + +In case the comparison operation is not equality, we can employ the \lstinline|CountIf| function. Here, the third argument is a unary predicate which evaluates to \lstinline|true| for each element to be counted. The following example shows how to count the number of values greater than 0: +% +\\\inputlisting{../examples/algorithms/counting/count_if-snippet.h} + +\section{Foreach Loops} +\label{sec:algorithms_foreach} + +A frequently encountered task in parallel programming is to apply some operation on a range of values, as illustrated in the example of Section~\ref{sec:introduction_function_objects}. In principle, one could apply the operation to all elements in parallel provided that there are no data dependencies. However, this results in unnecessary overhead if the number of elements is greater than the number of available processor cores $p$. A better solution is to partition the range into $p$ blocks and to process the elements of a block sequentially. With the \lstinline|ForEach| construct provided by \embb, users do not have to care about the partitioning, since this is done automatically. Similar to the Standard Library's \lstinline|for_each| function, it is sufficient to pass the operation in form of a function object. The following piece of code shows how to implement the example of Section~\ref{sec:introduction_function_objects} using \embb: +% +\\\inputlisting{../examples/algorithms/for_each/doubling-snippet.h} + +In the above code snippet, the results of the computation overwrite the input. If the input has to be left unchanged, the results must be written to a separate output range. Thus, the operation requires two ranges. {\embb} supports such scenarios by the \lstinline|ZipIterator|, which wraps two iterators into one. Consider the following revised example for doubling the elements of a vector: +% +\\\inputlisting{../examples/algorithms/for_each/setup_zip-snippet.h} +% +Using the \lstinline|Zip| function as convenient way to create a zip iterator, the doubling can be performed as follows: +% +\\\inputlisting{../examples/algorithms/for_each/doubling_zip-snippet.h} +% +The argument to the lambda function is a \lstinline|ZipPair| with the iterators' reference value as template parameters. The elements pointed to by the zip iterator can be accessed via \lstinline|First()| and \lstinline|Second()|, similar to \lstinline|std::pair|. + +\section{Reductions} +\label{sec:algorithms_reductions} + +As mentioned in the previous section, the \lstinline|ForEach| construct requires the loop iterations do be independent of each other. However, this is not always the case. Imagine we want to sum up the values of a range, e.g., a vector of integers: +% +\\\inputlisting{../examples/algorithms/reduce/range_init-snippet.h} +% +Sequentially, this can be done by a simple loop: +% +\\\inputlisting{../examples/algorithms/reduce/sequential-snippet.h} +% +One might be tempted to sum up the elements in parallel using a foreach loop. The problem is that parallel accesses to \lstinline|sum| must be synchronized to avoid race conditions, which in fact sequentializes the loop. A more efficient approach is to compute intermediate sums for each block of the range and to sum them up at the end. For such purposes, {\embb} provides the function \lstinline|Reduce|: +% +\\\inputlisting{../examples/algorithms/reduce/parallel-snippet.h} +% +The third argument to \lstinline|Reduce| is the neutral element of the reduction operation, i.e., the element that does not change the result. In case of addition (\lstinline|std::plus|), the neutral element is 0. If we wanted to compute the product of the vector elements, the neutral element would be 1. + +Next, let us consider the parallel computation of a dot product. Given two input ranges, we want to multiply each pair of input elements and sum up the products. The second input range is given as follows: +% +\\\inputlisting{../examples/algorithms/reduce/second_range_init-snippet.h} +% +The reduction consists of two steps: First, the input ranges are transformed and then, the reduction is performed on the transformed range. For that purpose, the \lstinline|Reduce| function allows to specify a transformation function object. By default, this is the identity functor which does not modify the input range. To implement the dot product, we can use the \lstinline|Zip| function (see Section~\ref{sec:algorithms_foreach}) and a lambda function for computing the transformed range: +% +\\\inputlisting{../examples/algorithms/reduce/dot_product-snippet.h} + +\section{Prefix Computations} +\label{sec:algorithms_prefix} + +Prefix computations (or scans) can be viewed as a generalization of reductions. They transform an input range $x_i \in X$ into an output range $y_i \in Y$ with $i=1,...,n$ such that +\begin{eqnarray*} +y_0 &=& id \cdot x_0 \\ +y_1 &=& y_0 \cdot x_1 \\ +&\vdots& \\ +y_i &=& y_{i-1} \cdot x_i \\ +&\vdots& \\ +y_n &=& y_{n-1} \cdot x_n +\end{eqnarray*} +where $id$ is the identity (neutral element) with respect to the operation $(\cdot): X \times X \rightarrow Y$. As an example, consider the following range: +% +\\\inputlisting{../examples/algorithms/scan/setup-snippet.h} +% +Computing the prefix sums of \lstinline|input_range| sequentially is easy: +% +\\\inputlisting{../examples/algorithms/scan/sequential_prefix_sum-snippet.h} +% +Note the dependency on loop iteration $i-1$ to compute the result in iteration $i$. A special two-pass algorithm is used in the {\embb} function \lstinline|Scan| to perform prefix computations in parallel. Using \lstinline|Scan| to compute the prefix sums, we get: +% +\\\inputlisting{../examples/algorithms/scan/prefix_sum-snippet.h} +% +As in the case of reductions, the neutral element has to be given explicitly. Also, a transformation function can be passed as additional argument to \lstinline|Scan|. The elements of the input range are then transformed before given to the prefix operation. \ No newline at end of file diff --git a/doc/tutorial/content/containers.tex b/doc/tutorial/content/containers.tex new file mode 100644 index 0000000..5cdc0ce --- /dev/null +++ b/doc/tutorial/content/containers.tex @@ -0,0 +1,46 @@ +\chapter{Containers} +\label{cha:containers} + +Containers are essential for storing objects in an organized way. Unfortunately, the containers provided by the C++ Standard Library are not thread-safe. Attempts to read and write elements concurrently may corrupt the stored data. While such undefined behavior can be avoided by synchronizing all accesses using a mutex, this essentially eliminates any parallelism. + +The containers provided by \embb enable a high degree of parallelism by design. They are implemented in a lock-free or wait-free fashion, thus avoiding any blocking operations. This way, multiple threads or tasks may access a container concurrently without suffering from typical side effects like convoying. Wait-free algorithms even guarantee that an operation completes within a bounded number of steps. Consequently, threads are immune to starvation which is critical for real-time systems. + +In embedded systems, memory is often preallocated in the initialization phase to minimize the effort for memory management during operation and to prevent unpredictable out-of-memory errors. \embb containers have a fixed capacity and allocate the required memory at construction time. Consequently, they can be used in safety-critical application, where dynamic memory allocation after initialization is forbidden. + +\section{Object Pools} +\label{sec:containers_object_pools} + +An object pool allocates a fixed number of objects at construction. Objects can then be allocated from the pool and returned for later reuse. When implementing lock-free or wait-free algorithms, the underlying memory allocation scheme also to be lock-free or wait-free, respectively. However, memory allocation functions such as \lstinline|new| and \lstinline|delete| usually do not give any progress guarantees. To solve this problem, \embb provides lock-free and wait-free object pools. + +Listing~\ref{lst:object_pool_lst1} shows an example, where we create in Line~\ref{lst:object_pool_lst1:line_create} an object pool with five objects of type \lstinline|int|. If nothing else is specified, the object-pool uses a wait-free implementation. Then, we allocate five objects from the object pool and store the obtained pointers in a temporary array. The actual allocation takes place in Line~\ref{lst:object_pool_lst1:line_allocate}. After that, we deallocate them in the second loop be calling \lstinline|FreeObject| on each pointer (see Line~\ref{lst:object_pool_lst1:line_free}). + +\lstinputlisting[caption={Object pool -- initialization, allocation and +deallocation},label={lst:object_pool_lst1}]{../examples/containers/object_pool-snippet.h} + +For actually allocating and deallocating objects, the object pool's implementation relies on a value pool which keeps track of the objects in use. If the value pool is implemented in a lock-free manner, the object pool is lock-free as well (analogously for wait-free pools). Currently, \embb provides two value pools: \lstinline|WaitFreeArrayValuePool| and \lstinline|LockFreeTreeValuePool|. Normally (if nothing is specified), the wait-free pool is used. For having a lock-free object pool instead, one has to specify the corresponding value pool to use as additional template parameter. If we replace Line~\ref{lst:object_pool_lst1:line_create} of the previous example with the following lines, the object pool is not wait-free anymore but lock-free (the values are of type +\lstinline|int| and initialized to \lstinline|0|): +% +\lstinputlisting{../examples/containers/object_pool_2-snippet.h} +% +This will result in a speed-up for most applications, but progress guarantees are weaker. + +\section{Stacks} +\label{sec:containers_stacks} + +As the name indicates, the class template \lstinline|LockFreeStack| implements a lock-free stack which stores elements according to the LIFO (Last-In, First-Out) principle. Listing~\ref{lst:stack_lst1} shows a simple example. In Line~\ref{lst:stack_lst1:line_create}, we create a stack of integers with a capacity of 10 elements.\footnote{Due to the necessary over-provisioning of memory in thread-safe memory management, the stack might be able to hold more than 10 elements, but is guaranteed to be able to hold at least 10 elements.} The stack provides two methods, \lstinline|TryPush| and \lstinline|TryPop|, both returning a Boolean value indicating success of the operation: \lstinline|TryPop| returns \lstinline|false| if the stack is empty, and \lstinline|TryPush| returns false if the stack is full. \lstinline|TryPop| returns the element removed from the stack via reference. + +\lstinputlisting[caption={Stack -- initialization, push and +pop},label={lst:stack_lst1}]{../examples/containers/stack-snippet.h} + +In Line~\ref{lst:stack_lst1:fail_pop} of Listing~\ref{lst:stack_lst1}, we try to pop an element from the empty stack, which has to fail. In the for-loop in Line \ref{lst:stack_lst1:loop1}, we fill the stack with \lstinline|int| values 0 $\ldots$ 4. Afterwards, in the loop in Line~\ref{lst:stack_lst1:loop2}, we pop five values (Line~\ref{lst:stack_lst1:pop}) from the stack into variable \lstinline|j|. According to the LIFO semantics, the values are popped in reverse order, i.e., we get the sequence 4 $\ldots$ 0. This is checked by the assertion in Line~\ref{lst:stack_lst1:assert}. + +\section{Queues} +\label{sec:containers_queues} + +There are two FIFO (First-In, First-Out) queue implementations in \embb, \lstinline|LockFreeMPMCQueue| and \lstinline|WaitFreeSPSCQueue|. The former permits multiple producers and multiple consumers (MPMC), whereas the latter is restricted to a single producer and a single consumer (SPSC). The interfaces are the same for both queues. + +Listing~\ref{lst:queue_lst1} shows an example for the \lstinline|LockFreeMPMCQueue|. In Line~\ref{lst:queue_lst1:line_create}, we create a queue with element type \lstinline|int| and a capacity of 10 elements.\footnote{As in case of stacks, the queue may actually hold more than 10 elements.} The Boolean return value of the methods \lstinline|TryEnqueue| and \lstinline|TryDequeue| indicates success (\lstinline|false| if the queue is full or empty, respectively). + +\lstinputlisting[caption={Queue -- initialization, enqueue and dequeue},label={lst:queue_lst1}]{../examples/containers/queues-snippet.h} + +In Line~\ref{lst:queue_lst1:fail_pop} of Listing~\ref{lst:queue_lst1}, we try to dequeue an element from the empty queue, which has to fail. In the for-loop in Line~\ref{lst:queue_lst1:loop1}, we fill the queue with \lstinline|int| values 0 $\ldots$ 4. Afterwards, in the loop in Line~\ref{lst:queue_lst1:loop2}, we dequeue five values (Line~\ref{lst:queue_lst1:pop}) from the queue into variable \lstinline|j|. According to the FIFO semantics, the values are dequeued in the same order as they were enqueued, i.e., we get the sequence 0 $\ldots$ 4. This is checked by the assertion in Line~\ref{lst:queue_lst1:assert}. diff --git a/doc/tutorial/content/dataflow.tex b/doc/tutorial/content/dataflow.tex new file mode 100644 index 0000000..6679d70 --- /dev/null +++ b/doc/tutorial/content/dataflow.tex @@ -0,0 +1,334 @@ +\chapter{Dataflow} +\label{cha:dataflow} + +With the \emph{Dataflow} building block, \embb provides generic skeletons for the development of parallel stream-based applications. These skeletons are based on dataflow process networks (DPNs), a model of computation which is, due to its simplicity and flexibility, widely employed in different domains like digital signal processing and imaging. As a major advantage, DPNs are deterministic which significantly simplifies testing and debugging. This is particularly important in safety-critical systems, where high demands are put on correctness and reliability. Moreover, DPNs are inherently parallel and hence, lend themselves well for execution on a multicore processor. In fact, they can be viewed as a generalization of pipelining, a frequently encountered parallel pattern. + +%\begin{itemize}\setlength{\itemsep}{0pt} +% \item generic programming in the style of STL +% \item efficienct implementation using templates +% \item flexibility through pluggable components +% \item lock-free algorithms and data structures +% \item type safety and const correctness +% \item support for lambda functions and streaming operators +% \item abstraction from underlying hardware +% \item non-linear pipelines with multiple sources/sinks +% \item automatic memory management and load balancing +% \item inplace and non-inplace operations +% \item serial (in-order) and parallel (out-of-order) stages +% \item dynamic control of data streams (conditional execution) +%\end{itemize} + +\section{Linear Pipelines} + +Before we go into detail, we demonstrate the basic concepts of this building block by means of a simple application which finds and replaces strings in a file. Let us start with the sequential implementation. The program shown in Listing~\ref{lst:replace_seq} reads a file line by line and replaces each occurrence of a given string with a new string. The main part consists of the \lstinline|while| loop which performs three steps: + +\begin{enumerate}\setlength{\itemsep}{0pt} + \item read a line from \lstinline|file| and store it in the string \lstinline|str| + \item replace each occurrence of \lstinline|what| in \lstinline|str| with \lstinline|with| + \item write the resulting string to \lstinline|cout| +\end{enumerate} + +\begin{lstlisting}[float,caption={Sequential program for replacing strings in a file},label={lst:replace_seq}] +#include +#include +#include +#include + +using namespace std; + +// replace all ocurrences of 'what' in 'str' with 'with' +void repl(string& str, const string &what, + const string& with) { + string::size_type pos = 0; + while((pos = str.find(what, pos)) != string::npos) { + str.replace(pos, what.length(), with); + pos += with.length(); + } +} + +int main(int argc, char *argv[]) { + // check and read command line arguments + if(argc != 4) { + cerr << "Usage: replace " << endl; + exit(EXIT_FAILURE); + } + const string what(argv[1]), with(argv[2]); + + // open input file + ifstream file(argv[3]); + if(!file) { + cerr << "Cannot open file " << argv[3] << endl; + exit(EXIT_FAILURE); + } + + // read input file line by line and replace strings + string str; + while(getline(file, str)) { + repl(str, what, with); + cout << str << endl; + } + + // close file and exit + file.close(); + exit(EXIT_SUCCESS); +} +\end{lstlisting} + +To run this program on a multicore processor, we may execute the above steps in a pipelined fashion. In this way, a new line can be read from the hard disk while the previous one is still being processed. Likewise, processing a string and writing the result to standard output can be performed in parallel. Thus, the pipeline may consist of three stages as depicted in Figure~\ref{fig:replace_par}. + +\begin{figure}[!htb] +\centering% +\begin{tikzpicture}[box/.style={rectangle, draw, minimum width=2cm, minimum height=0.75cm, anchor=base}, arrow/.style={thick, ->, >=stealth'}] +\node (READ) [box] at (0,0) {}; +\node (REPLACE) [box] at (3,0) {}; +\node (WRITE) [box] at (6,0) {}; +\node (FILE) [draw, cylinder, shape border rotate=90, minimum width=1.5cm, minimum height=1cm, anchor=base] at (0,-1.85) {}; +\node (IO) [draw, trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=1cm, minimum height=1cm, anchor=base] at (6,-1.75) {}; +\node [anchor=mid] at (0,0) {\textbf{read}}; +\node [anchor=mid] at (3,0) {\textbf{process}}; +\node [anchor=mid] at (6,0) {\textbf{write}}; +\draw [arrow] (FILE.north) -- (READ.south); +\draw [arrow] (READ.east) -- (REPLACE.west); +\draw [arrow] (REPLACE.east) -- (WRITE.west); +\draw [arrow] (WRITE.south) -- (IO.north); +\end{tikzpicture} +\caption{Pipeline for replacing strings in a file} +\label{fig:replace_par} +\end{figure} + +This pipeline can be easily implemented using the Dataflow building block. As the first step, we have to include the \lstinline|dataflow.h| header file: +% +\\\inputlisting{../examples/dataflow/dataflow_include-snippet.h} +% +Then, we have to construct a \emph{network}. A network consists of a set of processes that are connected by communication channels. +%\footnote{Pipelines belong to the most simple networks, where the processes are connected in string-like (linear) fashion.} +%\embb provides a class template \lstinline|Network| that can be customized to your needs. For the moment, we are satisfied with the default configuration and omit the template arguments: +% +\\\inputlisting{../examples/dataflow/dataflow_network-snippet.h} +% +As the next step, we have to construct the processes shown in Figure~\ref{fig:replace_par}. The easiest way to construct a process is to wrap the user-defined code in a lambda function and to pass it to the network. The network constructs an object for that process and executes the lambda function whenever new data is available. There are several methods for constructing processes depending on their type. The process \textbf{read} is a \emph{source} process, since it produces data (by reading it from the specified file) but does not consume any data. Source processes are constructed from a function object +% +\\\inputlisting{../examples/dataflow/dataflow_source_function-snippet.h} +% +like this: +% +\\\inputlisting{../examples/dataflow/dataflow_declare_source-snippet.h} +% +%There are a couple of things to mention here. Firstly, +Note the template argument \lstinline|string| to \lstinline|Source|. This tells \embb that the process has exactly one \emph{port} of type \lstinline|string| and that this port is used to transmit data to other processes. The user-defined code can access the ports via the parameters of the function. Thus, each parameter corresponds to exactly one port. In our example, the result of the process is stored in a variable \lstinline|str|, which is passed by reference. +%Secondly, note that sources have to return a Boolean value. If the returned value is \lstinline|true|, the network will continue to execute the corresponding process. Otherwise, it will stop execution of the process and terminate when all previously produced data is processed. + +The replacement of the strings can be done by a \emph{parallel} process, which means that multiple invocations of the process may be executed simultaneously. In general, processes that neither have any side effects nor maintain a state can safely be executed in parallel. This helps to avoid bottlenecks that arise when some processes are faster than others. Suppose, for example, that \textbf{replace} requires up to 50 ms to execute, whereas \textbf{read} and \textbf{write} each requires 10 ms to execute. If only one invocation of \textbf{replace} could be executed at a time, the throughput would be at most 20 elements per second. Since \textbf{replace} is a parallel process, however, the network may start a new invocation every 10 ms. Hence, up to five invocations may be executed in parallel, yielding a throughput of 100 elements per second. To compensate for variations in the runtime of parallel stages, \embb may execute them \emph{out-of-order}. As a result, the order in which the elements of a stream enter and leave parallel stages is not necessarily preserved. In our example, the runtime of \textbf{replace} may vary significantly due to the fact that not all lines have the same length and that the number of replacements depends on the content. However, let us now return to our example. The \textbf{replace} process is constructed from the function +% +\\\inputlisting{../examples/dataflow/dataflow_replace_function-snippet.h} +% +like this: +% +\\\inputlisting{../examples/dataflow/dataflow_declare_replace-snippet.h} +% +The template parameter \lstinline|Network::Inputs| specifies that the process has one port serving as input. Analogously, \lstinline|Network::Outputs| specifies that there is one port serving as output. + +Since the last process (\textbf{write}) does not have any outputs, we make it a \emph{Sink}. Unlike parallel processes, sinks are always executed \emph{in-order}. \embb takes care that the elements are automatically reordered according to their original order in the stream. In this way, the externally visible behavior is preserved even if some parallel stages may be executed out-of-order. The function +% +\\\inputlisting{../examples/dataflow/dataflow_sink_function-snippet.h} +% +is used to construct the sink: +% +\\\inputlisting{../examples/dataflow/dataflow_declare_sink-snippet.h} +% +%In order to avoid that the received string is overwritten accidentally, the parameter \lstinline|str| corresponding to the input port of \lstinline|write| must be constant.\\ + +\emph{Note: If you parallelize your own application using \embb and your compiler emits a lengthy error message containing lots of templates, it is very likely that for at least one process, the ports and their directions do not match the signature of the given function.} + +The network needs to know about the processes declared above, so we add them to our network: +% +\\\inputlisting{../examples/dataflow/dataflow_add-snippet.h} +% + +As the last step, we have to connect the processes (ports). This is straightforward using the C++ stream operator: +% +\\\inputlisting{../examples/dataflow/dataflow_connect-snippet.h} +% +Once all connections have been established, we can start the network: +% +\\\inputlisting{../examples/dataflow/dataflow_run-snippet.h} +% +The integer passed to the network's function operator specifies the maximum number of elements that can be in the network at a time. The number of elements is limited to avoid that the network is flooded with new elements before the previous elements have been processed. In a linear pipeline, for example, this may happen if the source is faster than the sink. In our example, at most four elements may be processed simultaneously: one in the source, one in the sink, and two in the middle stage (see above). Finding an optimal value depends on the application and usually requires some experimentation. In general, large values boost the throughput but also increase the latency. Conversely, small values reduce the latency but may lead to a drop of performance in terms of throughput. + +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. + +% ---------------------------------------------------------------------- +\section{Nonlinear Pipelines} +% ---------------------------------------------------------------------- + +Some applications exhibit a more complex structure than the linear pipeline presented in the previous section. Typical examples are applications where the result of a pipeline stage is used by more than one successor stage. Such pipelines are said to be nonlinear. In principle, every nonlinear pipeline can be transformed to a linear one as depicted in Figure~\ref{fig:linearization}. However, this increases the latency and complicates the implementation due to data that must be passed through intermediate stages. In Figure~\ref{fig:linearization}, for example, the data transferred from stage A to stage C must be passed through stage B in the linearized implementation. + +\begin{figure}[!htb] +\centering% +\begin{tikzpicture} +\matrix[column sep=1cm, row sep=0cm] { + \dpnser{A}{A}; & & \dpnser{D}{D}; \\ + & \dpnser{C}{C}; & \\ + \dpnser{B}{B}; & & \dpnser{E}{E}; \\ +}; +\draw [edgestyle] (A.east) -- (C); +\draw [edgestyle] (B.east) -- (C); +\draw [edgestyle] (C) -- (D.west); +\draw [edgestyle] (C) -- (E.west); +\end{tikzpicture} +\hspace{1cm} +\begin{tikzpicture} +\matrix[column sep=1cm, row sep=0cm] { + \dpnser{A}{A}; & & \dpnser{D}{D}; \\ + & \dpnser{C}{C}; & \\ + \dpnser{B}{B}; & & \dpnser{E}{E}; \\ +}; +\draw [edgestyle] (A) -- (B); +\draw [edgestyle] (B.east) -- (C); +\draw [edgestyle] (C) -- (D.west); +\draw [edgestyle] (D) -- (E); +\end{tikzpicture} +\caption{Nonlinear pipeline and linearized variant} +\label{fig:linearization} +\end{figure} + +Nonlinear pipelines can be implemented as they are using \embb, i.e., there is need not linearize them. As an example, let us consider the implementation of a sorting network. Sorting networks consist of a set of interconnected comparators and are used to sort sequences of data items. As depicted in Figure~\ref{fig:comparator}, each comparator sorts a pair of values by putting the smaller value to one output, and the larger one to the other output. Thus, a comparator can be viewed as a switch that transfers the values at the inputs to the outputs, either directly or by swapping them (cf.~Figure~\ref{fig:example_comparator}). + +\begin{figure}[!htb] +\centering% +\begin{tikzpicture}[comparator/.style={rectangle, draw, minimum width=1cm, minimum height=2cm, anchor=south west}, arrow/.style={thick, ->, >=stealth'}] +\node [comparator] at (1,0) {}; +\node [left] at (0.25,1.5) {$a$}; +\node [left] at (0.25,0.5) {$b$}; +\node [right] at (2.75,1.5) {$\min(a,b)$}; +\node [right] at (2.75,0.5) {$\max(a,b)$}; +\draw [arrow] (0.25,0.5) -- (1,0.5); +\draw [arrow] (0.25,1.5) -- (1,1.5); +\draw [arrow] (2,0.5) -- (2.75,0.5); +\draw [arrow] (2,1.5) -- (2.75,1.5); +\end{tikzpicture} +\caption{Block diagram of a comparator} +\label{fig:comparator} +\end{figure} + +\begin{figure}[!htb] +\centering% +\begin{tikzpicture}[comparator/.style={rectangle, draw, minimum width=1cm, minimum height=2cm, anchor=south west}, line/.style={thick, dashed}, arrow/.style={thick, ->, >=stealth'}] +\node [comparator] at (1,0) {}; +\node [left] at (0.25,1.5) {$1$}; +\node [left] at (0.25,0.5) {$3$}; +\node [right] at (2.75,1.5) {$1$}; +\node [right] at (2.75,0.5) {$3$}; +\draw [arrow] (0.25,0.5) -- (1,0.5); +\draw [arrow] (0.25,1.5) -- (1,1.5); +\draw [arrow] (2,0.5) -- (2.75,0.5); +\draw [arrow] (2,1.5) -- (2.75,1.5); +\draw [line] (1,1.5) -- (2,1.5); +\draw [line] (1,0.5) -- (2,0.5); +\end{tikzpicture} +\hspace{2cm} +\begin{tikzpicture}[comparator/.style={rectangle, draw, minimum width=1cm, minimum height=2cm, anchor=south west}, line/.style={thick, dashed}, arrow/.style={thick, ->, >=stealth'}] +\node [comparator] at (1,0) {}; +\node [left] at (0.25,1.5) {$5$}; +\node [left] at (0.25,0.5) {$2$}; +\node [right] at (2.75,1.5) {$2$}; +\node [right] at (2.75,0.5) {$5$}; +\draw [arrow] (0.25,0.5) -- (1,0.5); +\draw [arrow] (0.25,1.5) -- (1,1.5); +\draw [arrow] (2,0.5) -- (2.75,0.5); +\draw [arrow] (2,1.5) -- (2.75,1.5); +\draw [line] (1,1.5) -- (2,0.5); +\draw [line] (1,0.5) -- (2,1.5); +\end{tikzpicture} +\caption{Example for the operating principle of a comparator} +\label{fig:example_comparator} +\end{figure} + +Figure~\ref{fig:sorting_network} shows a sorting network with four inputs$/$outputs and five comparators. The numbers at the interconnections exemplify a ``run'' of the network. As can be seen from Figure~\ref{fig:sorting_network}, the comparators $C_1-C_4$ "sink" the largest value to the bottom and "float" the smallest value to the top. The final comparator $C_5$ simply sorts out the middle two values. In this way it is guaranteed that the values at the outputs occur in ascending order. %Note that the structure of the network makes it well-suited for sorting continuous streams of data in a pipelined fashion. + +\begin{figure}[!htb] +\centering% +\begin{tikzpicture}[comparator/.style={rectangle, draw, minimum width=1cm, minimum height=2.5cm, anchor=south west}, line/.style={thick}, arrow/.style={thick, ->, >=stealth'}] +\node [comparator, minimum height=4cm] at (1,1.5) {$C_1$}; +\node [comparator, minimum height=4cm] at (3,0) {$C_2$}; +\node [comparator] at (5,3) {$C_3$}; +\node [comparator] at (5,0) {$C_4$}; +\node [comparator] at (7,1.5) {$C_5$}; +\draw [arrow] (0,5) -- (1,5); +\draw [arrow] (2,5) -- (5,5); +\draw [arrow] (6,5) -- (9,5); +\draw [line] (0,3.5) -- (1,3.5); +\draw [arrow] (2,3.5) -- (3,3.5); +\draw [arrow] (4,3.5) -- (5,3.5); +\draw [arrow] (6,3.5) -- (7,3.5); +\draw [arrow] (8,3.5) -- (9,3.5); +\draw [arrow] (0,2) -- (1,2); +\draw [line] (2,2) -- (3,2); +\draw [arrow] (4,2) -- (5,2); +\draw [arrow] (6,2) -- (7,2); +\draw [arrow] (8,2) -- (9,2); +\draw [arrow] (0,0.5) -- (3,0.5); +\draw [arrow] (4,0.5) -- (5,0.5); +\draw [arrow] (6,0.5) -- (9,0.5); +\node [left] at (0,5.0) {$3$}; +\node [left] at (0,3.5) {$2$}; +\node [left] at (0,2.0) {$4$}; +\node [left] at (0,0.5) {$1$}; +\node [above] at (2.5,5.0) {$3$}; +\node [above] at (4.5,3.5) {$1$}; +\node [above] at (2.5,2.0) {$4$}; +\node [above] at (4.5,0.5) {$2$}; +\node [above] at (6.5,3.5) {$3$}; +\node [above] at (6.5,2.0) {$2$}; +\node [right] at (9,5.0) {$1$}; +\node [right] at (9,3.5) {$2$}; +\node [right] at (9,2.0) {$3$}; +\node [right] at (9,0.5) {$4$}; +\end{tikzpicture} +\caption{Sorting network with four inputs$/$outputs and five comparators} +\label{fig:sorting_network} +\end{figure} + +Let us now consider the implementation of the sorting network using \embb. As in the previous example, we need three types of processes: one or more sources that produce a stream of data items, a total number of five processes that implement the comparators, and one or more sinks that consume the sorted sequences. The processes should be generic so that they can be used with different types. For example, in one application we might use the network to sort integers, and in another application to sort floating point values. + +The following Listing shows the implementation of the source processes using classes instead of functions.\footnote{For the sake of brevity, we omit the functionality. A complete implementation can be found in the examples directory.} +% +\\\inputlisting{../examples/dataflow/dataflow_producer-snippet.h} +% +%In order to use an instance of a class as a process, it must be derived from one of the predfined base classes. +The class-based approach has several advantages besides the use of templates: Firstly, the creation of multiple processes is straightforward. Secondly, one can derive other processes from a given base class such as \lstinline|Producer|. Thirdly, it eases migration of existing code. For example, if you want to use an object of an existing class \lstinline|foo| as a process, you might derive a class \lstinline|bar| from \lstinline|foo|. The function operator of \lstinline|bar| then has access to the members provided by \lstinline|foo|. +%Thirdly, it eases migration of existing code by means of multiple inheritance. For example, if you want to use an object of an existing class \lstinline|foo| as a process, you might derive a class \lstinline|bar| from \lstinline|foo| and \lstinline|Network::Source<...>| (or any other process class). The function operator of \lstinline|bar| then has access to the members provided by \lstinline|foo|. + +Each instance of the class \lstinline|Network| maintains a list of source processes that belong to the network. +% When you create a source process using \lstinline|MakeSource|, it is automatically added to this list. Otherwise, you must explicitly add it by a call to \lstinline|Add|. For example, if we want to feed our sorting network \lstinline|nw| with streams of integer values, we may write: +You must explicitly add all processes to the network by a call to \lstinline|Add|. For example, if we want to feed our sorting network \lstinline|nw| with four streams of integer values, we may write: +% +\\\inputlisting{../examples/dataflow/dataflow_declare_add_sources-snippet.h} +% +%This is only necessary for source processes. All other processes are automatically found via a depth-first search starting from the source processes. + +The code for the comparators looks like this: +% +\\\inputlisting{../examples/dataflow/dataflow_comparator-snippet.h} +% +Since the comparators neither have any side effects nor maintain a state, we allow multiple invocations to be executed in parallel. +% by deriving the class \lstinline|comparator| from the base class \lstinline|network<>::parallel|. + +%In the above implementation of \lstinline|Comparator|, the inputs \lstinline|a| and \lstinline|b| are passed by value, which works fine if \lstinline|T| is a scalar type such as an integer or a float. However, since objects are passed by reference for efficiency reasons (unless otherwise specified), we cannot use classes as template argument for \lstinline|comparator|. To solve this problem, the classes \lstinline|in|, \lstinline|out|, and \lstinline|inout| define the parameter type that matches the given data type. As an example, \lstinline|in::parameter_type| is simply \lstinline|int|, while \lstinline|in::parameter_type| yields \lstinline|const foo&| for a class \lstinline|foo|. A fully generic implementation of \lstinline|comparator| is shwon in Listing~\ref{lst:comparator_gen} (the objects of type \lstinline|T| must be comparable in order to compute the minimum and the maximum). +% +%\begin{lstlisting}[caption={Fully generic comparator process},label={lst:comparator_gen}] +%template +%class Comparator { +%public: +% void Run(const T& a, const T& b, T& x, T& y) const { +% x = min(a,b); +% y = max(a,b); +% } +%}; +%\end{lstlisting} + +To check whether the resulting values are sorted, we use a single sink with four inputs: +% +\\\inputlisting{../examples/dataflow/dataflow_consumer-snippet.h} +% +In general, however, we could also have a sink for each output of the sorting network. There is no restriction on the number of sources and sinks a network may have. diff --git a/doc/tutorial/content/introduction.tex b/doc/tutorial/content/introduction.tex new file mode 100644 index 0000000..b98aaca --- /dev/null +++ b/doc/tutorial/content/introduction.tex @@ -0,0 +1,61 @@ +\chapter{Introduction} + +\section{Overview} + +The Embedded Multicore Building Blocks (\embb) are an easy to use yet powerful and efficient C/C++ library for the development of parallel applications. \embb has been specifically designed for embedded systems and the typical requirements that accompany them, such as real-time capability and constraints on memory consumption. As a major advantage, low-level operations are hidden in the library which relieves software developers from the burden of thread management and synchronization. This not only improves productivity of parallel software development, but also results in increased reliability and performance of the applications. + +\embb is independent of the hardware architecture (x86, ARM, ...) and runs on various platforms, from small devices to large systems containing numerous processor cores. It builds on MTAPI, a standardized programming interface for leveraging task parallelism in embedded systems containing symmetric or asymmetric multicore processors. A core feature of MTAPI is low-overhead scheduling of fine-grained tasks among the available cores during runtime. Unlike existing libraries, \embb supports task priorities, which allows the creation of soft real-time systems. Additionally, the scheduling strategy can be optimized for non-functional requirements such as minimal latency and fairness. + +Besides the task scheduler, \embb provides basic parallel algorithms, concurrent data structures, and skeletons for implementing stream processing applications (see Figure~\ref{fig:introduction_embb_components}). These building blocks are largely implemented in a non-blocking fashion, thus preventing frequently encountered pitfalls like lock contention, deadlocks, and priority inversion. As another advantage in real-time systems, the algorithms and data structures give certain progress guarantees. For example, wait-free data structures guarantee system-wide progress which means that every operation completes within a finite number of steps independently of any other concurrent operations on the same data structure. + +\begin{figure}[H] + \centering + \includegraphics[width=0.75\columnwidth]{../images/embb.pdf} + \label{fig:embb_components} + \caption{Main building blocks of \embb} + \label{fig:introduction_embb_components} +\end{figure} + +% TODO +%\section{Key Features and Requirements} + +\section{Outline} + +The purpose of this tutorial is to introduce the basic concepts of \embb and to demonstrate typical application scenarios by means of simple examples. The tutorial is not intended to be complete in the sense that it describes every feature of \embb. For a detailed description of the API, please see the HTML documentation. + +In the next subsection, we briefly describe the concept of function objects which is essential for using \embb. In Chapter~\ref{cha:mtapi}, we then present the task management features of MTAPI. These provide the basis for the algorithms outlined in Chapter~\ref{cha:algorithms} and the dataflow framework presented in Chapter~\ref{cha:dataflow}. + +\section{Functions, Functors, and Lambdas} +\label{sec:introduction_function_objects} + +Throughout this tutorial, we will encounter C++ types which model the C++ concept\footnote{In this context, the term \emph{concept} refers to a named set of requirements on a type.} \lstinline|FunctionObject|. The function object concept comprises function pointer, functor, and lambda types that are callable with suitable arguments by the function call syntax. Given a function object \lstinline|f| and arguments \lstinline|arg1|, \lstinline|arg2|, \lstinline|...|, the expression \lstinline|f(arg1, arg2, ...)| is a valid function invocation. If you are already familiar with function objects, you can safely skip the rest of this section. Otherwise, it might be worth reading it to get an idea of what is meant when talking about a function objects. + +Consider, for example, the transformation of an iterable range of data values. Specifically, consider a vector of integers initialized as follows: +% +\\\inputlisting{../examples/stl_for_each/setup-snippet.h} +% +The range consists of the values (\lstinline|1, 2, 3, 4, 5|) and we now want to double each of them. We could simply get an iterator from the container holding the range, iterate over every element, and multiply it by two: +% +\\\inputlisting{../examples/stl_for_each/manual-snippet.h} +% +The range then contains the values (\lstinline|2, 4, 6, 8, 10|). In order to demonstrate the concept of function objects, we are now going to use the \lstinline|std::for_each| function defined in the \lstinline|algorithm| header of the C++ Standard Library. This function accepts as argument a \lstinline|UnaryFunction|, that is, a function object which takes only one argument. In case of \lstinline|std::for_each|, the argument has to have the same type as the elements in the range, as these are passed to the unary function. In our example, the unary function's task is to double the incoming value. We could define a function for that purpose: +% +\\\inputlisting{../examples/stl_for_each/function_define-snippet.h} +% +Since a function pointer models the concept of function objects, we can simply pass \lstinline|&DoubleFunction| to \lstinline|std::for_each|: +% +\\\inputlisting{../examples/stl_for_each/function-snippet.h} +% +Another possibility is to define a functor +% +\\\inputlisting{../examples/stl_for_each/functor_define-snippet.h} +% +and to pass an instance of this class to \lstinline|std::for_each|: +% +\\\inputlisting{../examples/stl_for_each/functor-snippet.h} +% +Functors as well as the function pointers separate the actual implementation from its place of usage which can be useful if the functionality is needed at different places. In many cases, however, it is advantageous to have the implementation of the function object at the same place as it is used. C++11 provides lambda expressions for that purpose which make our example more concise: +% +\\\inputlisting{../examples/stl_for_each/lambda-snippet.h} +% +Of course, this example is too simple to really benefit from function objects and the algorithms contained in the C++ Standard Library. However, in combination with the parallelization features provided by \embb, function objects are a helpful mechanism to boost productivity. Within this document, whenever a function object or one of its subtypes is required, one can use a function pointer, a functor, or a lambda. For simplicity, we will restrict ourselves to lambdas in subsequent examples, as they are most suitable for this kind of tutorial. diff --git a/doc/tutorial/content/mtapi.tex b/doc/tutorial/content/mtapi.tex new file mode 100644 index 0000000..06ea39e --- /dev/null +++ b/doc/tutorial/content/mtapi.tex @@ -0,0 +1,192 @@ +\chapter{MTAPI} +\label{cha:mtapi} + +Leveraging the power of multicore processors requires to split computations into fine-grained tasks that can be executed in parallel. Threads are usually too heavy-weight for that purpose, since context switches consume a significant amount of time. Moreover, programming with threads is complex and error-prone due to typical pitfalls such as race conditions and deadlocks. To solve these problems, efficient task scheduling techniques have been developed which dynamically distribute the available tasks among a fixed number of worker threads. To reduce overhead, there is usually exactly one worker thread for each processor core. + +While task schedulers are nowadays widely employed, especially in desktop and server applications, they are typically limited to a single operating system running on a homogeneous multicore processor. System-wide task management in heterogeneous embedded systems must be realized explicitly with low-level communication mechanisms. MTAPI~\cite{MTAPI} addresses those issues by providing an API which allows parallel embedded software to be designed in a straightforward way, covering homogeneous and heterogeneous multicore architectures, as well as acceleration units. It abstracts from the hardware details and lets software developers focus on the application. Moreover, MTAPI takes into account typical requirements of embedded systems such as real-time constraints and predictable memory consumption. + +The remainder of this chapter is structured as follows: The next section explains the basic terms and concepts of MTAPI as given in the specification~\cite{MTAPI}. Section~\ref{sec:mtapi_c_interface} describes the C API using a simple example taken from~\cite{MTAPI}. Finally, Section~\ref{sec:mtapi_cpp_interface} outlines the use of MTAPI in C++ applications. Note that the C++ interface is provided by \embb for convenience but it is not part of the standard. + +\section{Foundations} + +\subsection{Domains} + +An MTAPI system is composed of one or more MTAPI domains. An MTAPI domain is a unique system global entity. Each MTAPI domain comprises a set of MTAPI nodes. An MTAPI node may only belong to one MTAPI domain, while an MTAPI domain may contain one or more MTAPI nodes. This allows the programmer to use MTAPI domains as namespaces for all kinds of IDs (e.g., nodes, actions, queues, etc.). + +\subsection{Nodes} + +An MTAPI node is an independent unit of execution, such as a process, thread, thread pool, processor, hardware accelerator, or instance of an operating system. A given MTAPI implementation specifies what constitutes a node for that implementation. + +The intent is to avoid a mixture of node definitions in the same implementation (or in different domains within an implementation). If a node is defined as a unit of execution with its private address space (like a process), then a core with a single unprotected address space OS is equivalent to a node, whereas a core with a virtual memory OS can host multiple nodes. + +On a shared memory SMP processor, a node can be defined as a subset of cores. A quad-core processor, for example, could be divided into two nodes, one node representing three cores and one node representing the fourth core reserved exclusively for certain tasks. The definition of a node is flexible because this allows applications to be written in the most portable fashion supported by the underlying hardware, while at the same time supporting more general-purpose multicore and many-core devices. + +The definition allows portability of software at the interface level (e.g., the functional interface between nodes). However, the software implementation of a particular node cannot (and often should not) necessarily be preserved across a multicore SoC product line (or across product lines from different silicon providers) because a given node's functionality may be provided in different ways, depending on the chosen multicore SoC. + +\subsection{Tasks} + +A task represents the computation associated with the data to be processed. A task is executed concurrently to the code starting the task. The main API functions are \lstinline|mtapi_task_start()| and \lstinline|mtapi_task_wait()|. The semantics are similar to the corresponding thread functions (e.g. \lstinline|pthread_create|$/$\lstinline|pthread_join| in Pthreads). The lifetime of a task is limited; it can be started only once. + +\subsection{Actions} + +In order to cope with heterogeneous systems and computations implemented in hardware, a task is not directly associated with an entry function as it is done in other task-parallel APIs. Instead, it is associated with at least one action object representing the calculation. The association is indirect: one or more actions implement a job, one job is associated with a task. If the action is implemented in software, this is either a function on the same node (which can represent the same processor or core) or a function implemented on a different node that does not share memory with the core starting the task. + +Starting a task consists of three steps: +\begin{enumerate} + \item Create the action object with a job ID (software-implemented actions only). + \item Obtain a job reference. + \item Start the task using the job reference. +\end{enumerate} + +\subsection{Synchronization} + +The basic synchronization mechanism provided with in MTAPI is waiting for task completion. Calling \lstinline|mtapi_task_wait()| with a task handle blocks the current thread or task until the task referenced by the handle has completed. Depending on the implementation, the calling thread can be used for executing other tasks while waiting for the task to be completed. In order to synchronize with a set of tasks, every task can be associated with a task group. The methods \lstinline|mtapi_group_wait_all()| and \lstinline|mtapi_group_wait_any()| wait for a group of tasks or completion of any task in the group, respectively. +%MTAPI only provides synchronization on task granularity. Synchronization inside a task implementation can be done by MCAPI messages, MRAPI synchronization primitives, and the MRAPI memory primitives. If MCAPI or MRAPI implementations are not available, synchronization mechanisms provided by the operating system or a threading library must be used. In this case, the MTAPI implementation must define the consequences of using those mechanisms in the task context. + +\subsection{Queues} + +Queues are used for guaranteeing sequential order of execution of tasks. A common use case is packet processing in the communication domain: for every connection all packets must be processed sequentially, while the packets of different connections can be processed in parallel to each other. + +Sequential execution is accomplished by using a queue for every connection and queuing all packets of one connection into the same queue. In some systems, queues are implemented in hardware, otherwise MTAPI implements software queues. MTAPI is designed for handling thousands of queues that are processed in parallel. + +The procedure for setting up and using a queue is as follows: +\begin{enumerate} + \item Create the action object (software-implemented actions only). + \item Obtain a job reference. + \item Create a queue object and attach the job to the queue (software-implemented queues only). + \item Obtain a queue handle if the queue was created on a different node, or if the queue is hardware-implemented. + \item Use the queue: enqueue the work using the queue. +\end{enumerate} +Another important purpose of queues is that different queues can express different scheduling attributes for the same job. For example, in contrast to order-preserving queues, non-order-preserving queues can be used for load-balancing purposes between different computation nodes. In this case, the queue must be associated with more than one action implementing the same task on different nodes (i.e., different processors or cores implementing different instruction set architectures). If a queue is configured this way, the order will not be preserved. + +\subsection{Attributes} + +Attributes are provided as a means to extend the API. Different implementations may define and support additional attributes beyond those predefined by the API. To promote portability and implementation flexibility, attributes are maintained in an opaque data object that may not be directly examined by the user. Each object (e.g., task, action, queue) has an attributes data object associated with it, and many attributes have a small set of predefined values that must be supported by MTAPI implementations. The user may initialize, get, and set these attributes. For default behavior, it is not necessary to call the initialize, get, and set attribute functions. However, to get non-default behavior, the typical four-step process is: +\begin{enumerate} + \item Declare an attributes object of the \lstinline|mtapi__attributes_t| data type. + \item \lstinline|mtapi_attr_init()|: Returns an attributes object with all attributes set to their default values. + \item \lstinline|mtapi_attr_set()|: (Repeat for all attributes to be set). Assigns a value to the specified attribute of the specified attributes object. + \item \lstinline|mtapi__create()|: Passes the attributes object modified in the previous step as a parameter when creating the object. +\end{enumerate} +At any time, the user can call \lstinline|mtapi__get_attribute()| to query the value of an attribute. After an object has been created, some objects allow to change attributes by calling \lstinline|mtapi__set_attribute()|. + +\section{C Interface} +\label{sec:mtapi_c_interface} + +The calculation of Fibonacci numbers is a simple example for a recursive algorithm that can easily be parallelized. Listing~\ref{lst:mtapi_fibonacci_sequential} shows a sequential version. + +\begin{lstlisting}[frame=none,caption={Sequential program for computing Fibonacci numbers},label={lst:mtapi_fibonacci_sequential}] +int fib(int n) { + int x,y; + if (n < 2) { + return n; + } else { + x = fib(n - 1); + y = fib(n - 2); + return x + y; + } +} + +int fibonacci(int n) { + return fib(n); +} + +void main(void) { + int n = 6; + int result = fibonacci(n); + printf("fib(%i) = %i\n", n, result); +} +\end{lstlisting} + +This algorithm can be parallelized by spawning a task for one of the recursive calls (\lstinline|fib(n - 1)|, for example). When doing this with MTAPI, an action function that represents \lstinline|fib(int n)| is needed. It has the following signature: +% +\\\inputlisting{../examples/mtapi/mtapi_c_action_signature-snippet.h} +% +Within the action function, the arguments should be checked, since the user might supply a buffer that is too small: +% +\\\inputlisting{../examples/mtapi/mtapi_c_validate_arguments-snippet.h} +% +Here, \lstinline|mtapi_context_status_set()| is used to report errors. The error code will be returned by \lstinline|mtapi_task_wait()|. Also, care has to be taken when using the result buffer. The user might not want to use the result and supply a NULL pointer or accidentally a buffer that is too small: +% +\\\inputlisting{../examples/mtapi/mtapi_c_validate_result_buffer-snippet.h} +% +At this point, calculation of the result can commence. First, the terminating condition of the recursion is checked: +% +\\\inputlisting{../examples/mtapi/mtapi_terminating_condition-snippet.h} +% +After that, the first part of the computation is launched as a task using \lstinline|mtapi_task_start()| (the action function is registered with the job \lstinline|FIBONACCI_JOB| in the \lstinline|fibonacci()| function and the resulting handle is stored in the global variable \lstinline|mtapi_job_hndl_t fibonacciJob|): +% +\\\inputlisting{../examples/mtapi/mtapi_c_calc_task-snippet.h} +% +The second part can be executed directly: +% +\\\inputlisting{../examples/mtapi/mtapi_c_calc_direct-snippet.h} +% +Then, completion of the MTAPI task has to be waited for by calling \lstinline|mtapi_task_wait()|: +% +\\\inputlisting{../examples/mtapi/mtapi_c_wait_task-snippet.h} +% +Finally, the results can be added and written into the result buffer: +% +\\\inputlisting{../examples/mtapi/mtapi_write_back-snippet.h} +% + +The \lstinline|fibonacci()| function gets a bit more complicated now. The MTAPI runtime has to be initialized first by (optionally) initializing node attributes and then calling \lstinline|mtapi_initialize()|: +% +\\\inputlisting{../examples/mtapi/mtapi_c_initialize-snippet.h} +% +Then, the action function needs to be associated to a job. By calling \lstinline|mtapi_action_create()|, the action function is registered with the job \lstinline|FIBONACCI_JOB|. The job handle of this job is stored in the global variable \lstinline|mtapi_job_hndl_t fibonacciJob| so that it can be accessed by the action function later on: +% +\\\inputlisting{../examples/mtapi/mtapi_c_register_action-snippet.h} +% +Now that the action is registered with a job, the root task can be started with \lstinline|mtapi_task_start()|: +% +\\\inputlisting{../examples/mtapi/mtapi_c_start_task-snippet.h} +% +%The started task has to be waited for before the result can be returned. +After everything is done, the action is deleted (\lstinline|mtapi_action_delete()|) and the runtime is shut down (\lstinline|mtapi_finalize()|): +% +\\\inputlisting{../examples/mtapi/mtapi_c_finalize-snippet.h} +% + +\section{C++ Interface} +\label{sec:mtapi_cpp_interface} + +\embb provides C++ wrappers for the MTAPI C interface. Using the example from the previous section, the signature of the action function for the C++ interface looks like this: +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_action_signature-snippet.h} +% +First, the node instance needs to be obtained. If the node is not initialized yet, this function will do it. +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_get_node-snippet.h} +% +Checking the arguments and the result buffer is not necessary, since everything is safely typed. However, the terminating condition of the recursion still needs to be checked: +% +\\\inputlisting{../examples/mtapi/mtapi_terminating_condition-snippet.h} +% +After that, the first part of the computation is launched as an MTAPI task using \lstinline|embb::mtapi::Node::Spawn()| (registering an action function with a job is done automatically): +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_calc_task-snippet.h} +% +The second part can be executed directly: +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_calc_direct-snippet.h} +% +Then, completion of the MTAPI task has to be waited for using \lstinline|embb::mtapi::Task::Wait()|: +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_wait_task-snippet.h} +% +Finally, the two parts can be added and written into the result buffer: +% +\\\inputlisting{../examples/mtapi/mtapi_write_back-snippet.h} +% + +The \lstinline|fibonacci()| function also gets simpler compared to the C version. The MTAPI runtime is initialized automatically, only the node instance has to be fetched: +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_get_node-snippet.h} +% +The root task can be started using \lstinline|embb::mtapi::Node::Spawn()| directly, registering with a job is done automatically: +% +\\\inputlisting{../examples/mtapi/mtapi_cpp_start_task-snippet.h} +% +Again, the started task has to be waited for (using \lstinline|embb::mtapi::Task::Wait()|) before the result can be returned. The runtime is shut down automatically in an \lstinline|atexit()| handler. diff --git a/doc/tutorial/content/preface.tex b/doc/tutorial/content/preface.tex new file mode 100644 index 0000000..d5e08eb --- /dev/null +++ b/doc/tutorial/content/preface.tex @@ -0,0 +1,11 @@ +\sectionnonum{Intended Audience} + +This document is intended for software developers who want to get an introduction to \embb and an idea on how to use its functionalities. It is not an API reference. + +\sectionnonum{Objectives} + +The objective of this document is to give an idea on how to use the functionalities of the \embb to solve simple programming problems. + +\sectionnonum{Structure} + +Structure \ No newline at end of file diff --git a/doc/tutorial/customize.tex b/doc/tutorial/customize.tex new file mode 100644 index 0000000..b1876c7 --- /dev/null +++ b/doc/tutorial/customize.tex @@ -0,0 +1,175 @@ + +% =================================== +% document formatting refinement +% =================================== + +% No indents for whole text +\setlength{\parindent}{0pt} + +% improve the line breaking +\setlength{\emergencystretch}{1em} + +% pic width = \textwidth - \chapterpicwidth +\def\chapterpicwidth{0.43} + +% line breaks for URLs +\renewcommand{\UrlBreaks}{\do\/} + + +% +% custom hyphenation +% +\hyphenation{in-stru-men-ta-tion analy-sis con-cur-ren-cy} + + +% =================================== +% settings for the listings package +% =================================== + +\definecolor{lightGray}{RGB}{242,242,242} +\lstset{ + language=C++, + basicstyle=\ttfamily, + columns=fullflexible, + keepspaces=true, % To have spaces monsized + tabsize=2, + frame=none, % = single + breaklines=true, + numbers=left, + numberstyle=\tiny, + escapeinside={//@}{@}, + showstringspaces=false, + resetmargins=true, + xleftmargin=18pt, + xrightmargin=0pt, + %resetmargins=true, + %framextopmargin=0pt + backgroundcolor=\color{lightGray}, + captionpos=b, + %abovecaptionskip=0pt, + %belowcaptionskip=0pt, + %aboveskip=3pt, + %belowskip=0pt, + %framesep=1pt, + numberbychapter=false, +} + +% coloring for listings +\lstdefinestyle{colored}{ + moredelim=[is][\color{blue}]{@}{@}, + moredelim=[is][\color{red}]{|}{|}, +} + +% +% Counter and caption for listings +% + +%\newcounter{lstlisting} % already defined by pkg listing at \begin{document} +\counterwithout{lstlisting}{chapter} + +\newcommand{\inputlisting}[1]{ + \begin{minipage}{\columnwidth} + \lstinputlisting{#1} + \end{minipage} +} + +\newcommand{\lstgname}{Listing} +\newcommand{\listingcaption}[1]% +{% + {% + \refstepcounter{lstlisting} + \noindent\footnotesize{\lstgname~\thelstlisting{:}~#1\hfill} + }% +}% + + + +% =========================== +% cover generation commands +% =========================== + +\def\backcover{ +\genbackpage{ + \textbf{Siemens AG}\newline + Corporate Technology\newline + Multicore Expert Center\newline + \newline + Otto-Hahn-Ring 6\newline + 81739 Muenchen\newline + Germany\newline + \newline + {http://multicore.ct.siemens.de} + } +} +% ====================== +% convenience commands +% ====================== + +\def\chaptername{Chapter} +\def\algname{Algorithm} +\def\sectionname{Section} + +% +% Programming languages and frameworks +% + +\def\qt{Qt} +\def\cE{C11} +\def\cpp{C\raisebox{0.17ex}{\small\textbf{++}}} +\def\cppFootnote{C\raisebox{0.08ex}{\small{++}}} +\def\cppE{{\cpp}11} +\newcommand{\csharp}{% + {\settoheight{\dimen0}{C}C\kern-.05em\hspace{0.5pt}\resizebox{!}{\dimen0}{\raisebox{\depth}{\#}}}} +\def\dotnet{.NET} + +% +% Special characters +% +\def\myCheck{\ding{51}} % check +\def\myCross{\ding{55}} % cross + + +% +% Various stuff +% +\def\time#1{#1} + +\def\markup#1{{\color{NavyBlue}#1}} + +\newcommand{\toolcard}[9]{ + \begin{table} + \caption{Quick card for {#2}} + {\small + \begin{tabular}{>{\bfseries}p{0.295\columnwidth}p{0.605\columnwidth}} + \thickhline + {Tool} & #2 \\ + {Detectable bugs} & #3 \\ + {License} & #4 \\ + {Platforms} & #5 \\ + {Operating systems} & #6 \\ + {\mbox{Languages}} & #7 \\ + {Threading libraries} & {#8} \\ + {Additional facts} & {#9} \\ + \thickhline + \end{tabular} + } + \label{qc:#1} + \end{table} +} + +% table cell that allows line breaks (e.g., for toolcard) +% first (optional) parameter defines alignment (l,c,r) +% second parameter is the content +\def\lbcell[#1]#2{\rule{0pt}{4ex}\shortstack[#1]{#2}} + + +% ======================= +% other (various) stuff +% ======================= + +% +% very custom stuff +% +\def\hb#1{\hbImpl(#1)} +\def\hbRel{\rightarrow} +\def\hbImpl(#1,#2){$#1 \hbRel #2$} diff --git a/doc/tutorial/pics/chicago-square.jpg b/doc/tutorial/pics/chicago-square.jpg new file mode 100644 index 0000000..209fb2f Binary files /dev/null and b/doc/tutorial/pics/chicago-square.jpg differ diff --git a/doc/tutorial/pics/cover/logo_multicore_expert-center_siemens.png b/doc/tutorial/pics/cover/logo_multicore_expert-center_siemens.png new file mode 100644 index 0000000..60189a3 Binary files /dev/null and b/doc/tutorial/pics/cover/logo_multicore_expert-center_siemens.png differ diff --git a/doc/tutorial/pics/cover/sie_logo_layer_petrol_rgb.png b/doc/tutorial/pics/cover/sie_logo_layer_petrol_rgb.png new file mode 100644 index 0000000..37c5395 Binary files /dev/null and b/doc/tutorial/pics/cover/sie_logo_layer_petrol_rgb.png differ diff --git a/doc/tutorial/references.bib b/doc/tutorial/references.bib new file mode 100644 index 0000000..af81734 --- /dev/null +++ b/doc/tutorial/references.bib @@ -0,0 +1,6 @@ +@manual{MTAPI, + title = {Multicore Task Management API (MTAPI) Specification V1.0}, + organization = {The Multicore Association}, + year = 2013, + month = mar +} diff --git a/doc/tutorial/sty/README.txt b/doc/tutorial/sty/README.txt new file mode 100644 index 0000000..d46f535 --- /dev/null +++ b/doc/tutorial/sty/README.txt @@ -0,0 +1,183 @@ +Copyright (C) nemadesign GbR +$Rev: 80 $ +$Date: 2014-09-08 21:22:10 +0200 (Mo, 08. Sep 2014) $ + + + + + +Things to consider +================== +In case of a wrong linf for the 'Bilbiography' +entry, add this before the '\addcontentsline{toc}{...}{...}: + +Example: +-------- BEGIN --------- +\cleardoublepage +\phantomsection +% generate TOC +\addcontentsline{toc}{chapter}{Bibliography} +--------- END ---------- + +New macros +========== +\chapterpictopnum{_TEXT_}{_PIC_}{_WIDTH_}{_LABEL_} +\chapterpictopnonum{_TEXT_}{_PIC_}{_WIDTH_}{_LABEL_} + +Add picture above chapter title + +_TEXT_ : title for the chapter +_PIC_ : image file +_WIDTH_ : relative width of text (1.0 equals text width) +_LABEL_ : name for the label of the chapter + +Examples: +\chapterpicnum{Das Kapitel}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.5} +\chapterpicnonum{text text text}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.3} +______________________________________________________________________________________ + +\chapterpicrightnum{_TEXT_}{_PIC_}{_WIDTH_}{_LABEL_} +\chapterpicrightnonum{_TEXT_}{_PIC_}{_WIDTH_}{_LABEL_} + +Add picture right to chapter title + +_TEXT_ : title for the chapter +_PIC_ : image file +_WIDTH_ : relative width of text (1.0 equals text width) +_LABEL_ : name for the label of the chapter + +Examples: +\chapterpicrightnum{Chapter with Picture}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.5} +\chapterpicrightnonum{Chapter* with Picture}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.4} + +______________________________________________________________________________________ + + +\chapterpicteasernum{_TEXT_}{_PIC_}{_WIDTH_}{_LABEL_}{_TEASE_} +\chapterpicteasernonum{_TEXT_}{_PIC_}{_WIDTH_}{_LABEL_}{_TEASE_} + +Chapter title with picture and teaser text. + +In single column layout: white teaser text in grey box. +In double column layout: grey teaser text in white box. + +_TEXT_ : title for the chapter +_PIC_ : image file +_WIDTH_ : relative width of text (1.0 equals text width) +_LABEL_ : name for the label of the chapter +_TEASE_ : teaser text +Examples: +\chapterpicteasernum{Chapter \& Picture \& Teaser}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.5}{Text} +\chapterpicteasernonum{Chapter* \& Picture \& Teaser}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.4}{Text} + +______________________________________________________________________________________ + +\tableofcontentspic{_TEXT_}{_PIC_}{_WIDTH_} +Add table of contents with picture + +_TEXT_ : title for the chapter +_PIC_ : tmage file +_WIDTH_ : relative width of text (1.0 equals text width) + +Example: +\tableofcontentspic{Contents}{gfx/siemens-logo-wallpaper-1024x597.jpg}{0.5} + +______________________________________________________________________________________ + +\gentitlepage{_PIC_}{_TEXT1_}{_TEXT2_} +Generate a titlepage + +_PIC_ : image file (must be of square dimensions!) +_TEXT1_ : text above title +_TEXT2_ : text for title + +Example: +\gentitlepage{gfx/Infineon-Baseband-Chip-X618.jpg}{Some text here}{Very long title for this Document Over Two Lines} +______________________________________________________________________________________ + +\gentitlepagenopic{_TEXT1_}{_TEXT2_} +Generate a titlepage without a picture + +_TEXT1_ : text above title +_TEXT2_ : text for title + +Example: +\gentitlepagenopic{Some text here}{Very long title for this Document Over Two Lines} +______________________________________________________________________________________ + +\genbackpage{_TEXT_} +Generate a backpage + +_TEXT_ : white text + +Example: +\genbackpage{Siemens AG} + +______________________________________________________________________________________ + +\renewcommand{\shorttitle}{_TEXT_} +Set short title in header + +_TEXT_ : text for short title + +Example: +\renewcommand{\shorttitle}{Short title for paper} +______________________________________________________________________________________ + +\renewcommand{\shortdate}{_TEXT_} +Set date in short title + +_TEXT_ : text for date in short title, default is \today + +Example: +\renewcommand{\shortdate}{\today} +\renewcommand{\shortdate}{11.09.2011} +______________________________________________________________________________________ + +\renewcommand{\copyrightdate}{_TEXT_} +Set date in short title + +_TEXT_ : text for date in copyright line on last page ,default is \today + +Example: +\renewcommand{\copyrightdate}{\today} +\renewcommand{\copyrightdate}{11.09.2011} +______________________________________________________________________________________ + + +Used LaTeX packages +=================== +\usepackage[colorlinks=true, bookmarks=false]{hyperref} +\usepackage[scaled]{uarial} +\usepackage[T1]{fontenc} +%\usepackage[ngerman]{babel} +\usepackage[english]{babel} +\usepackage{amsmath} +\usepackage[pdftex]{graphicx} +\usepackage{multirow} +\usepackage{subfigure} +\usepackage{listings} +\usepackage{courier} +\usepackage{titlesec} +\usepackage{scrpage2} +\usepackage{chngcntr} +\usepackage[table,usenames,dvipsnames]{xcolor} +\usepackage{booktabs} +\usepackage{color} +\usepackage{longtable} +\usepackage{colortbl} +\usepackage{fp} +\usepackage{eso-pic,picture} +\usepackage[absolute]{textpos} +\usepackage{multicol} +\usepackage{titletoc} +\usepackage{moresize} +\usepackage{printlen} +\usepackage{setspace} +\usepackage[colorlinks=true, bookmarks=false]{hyperref} + +These packages are only used in the demo documents: +\usepackage{layout} +\usepackage{lipsum} + + diff --git a/doc/tutorial/sty/siemens_core.sty b/doc/tutorial/sty/siemens_core.sty new file mode 100644 index 0000000..3181d87 --- /dev/null +++ b/doc/tutorial/sty/siemens_core.sty @@ -0,0 +1,208 @@ +% Copyright (C) nemadesign GbR +% $Rev: 56 $: +% $Date: 2014-09-20 13:52:19 +0200 (Sa, 20. Sep 2014) $: + +% This file contains settings and macros which are used in +% all variants for the siemens stylesheets + +\usepackage[scaled]{uarial} % Font is Arial +\fontfamily{phv}\fontseries{m}\fontshape{n}\selectfont % Arial for Headings +\setkomafont{sectioning}{\sffamily\bfseries} +\renewcommand{\familydefault}{\sfdefault} +\usepackage[T1]{fontenc} + +%\usepackage[ngerman]{babel} +\usepackage[english]{babel} +\usepackage{amsmath} +\usepackage[pdftex]{rotating,graphicx} +\usepackage{multirow} +\usepackage{layout} +\usepackage{lipsum} +\usepackage{subfigure} +\usepackage{multicol,listings} +\usepackage{courier} +\usepackage[automark]{scrpage2} + +\usepackage{titlesec} +\usepackage{scrpage2} +\usepackage{chngcntr} +\usepackage[table,usenames,dvipsnames]{xcolor} +\usepackage{booktabs} +\usepackage{color} +\usepackage{longtable} +\usepackage{colortbl} +\usepackage{fp} +\usepackage{eso-pic,picture} +\usepackage[absolute]{textpos} +\usepackage{multicol} +\usepackage{cite} +\usepackage{titletoc} +\usepackage{moresize} +\usepackage{printlen} +\usepackage{setspace} + +% Siemens specific packages +\usepackage[plain]{algorithm} +\usepackage{algorithmic} +\usepackage{array} % for tabular +\usepackage{pifont} % for \myCheck, \myCross +\usepackage[hyphens]{url} +\usepackage[hidelinks]{hyperref} % hyperref without colored links +\usepackage{balance} % for balancing the bibliography +\usepackage{ifthen} + + +% set title and date in headline +\newcommand{\shorttitle}{Default short title} +\newcommand{\shortdate}{\today} +\newcommand{\copyrightdate}{\today} + +% nobody likes Hurenkinder und Schusterjungen +\hyphenpenalty = 500 +\tolerance = 800 +\widowpenalty = 300 +\clubpenalty = 300 +\displaywidowpenalty = 10000 + +% change table background colors +\definecolor{lightgray}{gray}{0.9} +\let\oldtabular\tabular +\let\endoldtabular\endtabular +\renewenvironment{tabular}{\rowcolors{2}{white}{lightgray}\oldtabular}{\endoldtabular} + +% thick hline in tables +\newcommand{\thickhline}{% + \noalign {\ifnum 0=`}\fi \hrule height 1pt + \futurelet \reserved@a \@xhline +} + +% global counters for tables and figures +\counterwithout{figure}{chapter} +\counterwithout{table}{chapter} +\counterwithout{footnote}{chapter} +% left align captions +\usepackage[ +singlelinecheck=false % <-- important +]{caption} + +% No title for TOC +\renewcommand*{\tocbasic@listhead}[1]{% + \typeout{Ueberschrift ``#1'' nicht ausgeben}% +} + + +\pagestyle{scrheadings} + +% set headline and footline +\lehead{} +\lohead{} +\cehead{} +\cohead{} +\rehead{\shorttitle \;| \shortdate} +\rohead{\shorttitle \;| \shortdate} + +\lefoot{Restricted \copyright \; Siemens AG \the\year. All rights reserved.} +\lofoot{Restricted \copyright \; Siemens AG \the\year. All rights reserved.} +\cefoot{} +\cofoot{} +\refoot{\thepage} +\rofoot{\thepage} + +%\pagestyle{scrheadings} +\deftripstyle{myemptypage}{}{}{}{\thepage}{}{} +\KOMAoptions{cleardoublepage=myemptypage} + +% headings and footings for chapter +\renewcommand{\chapterpagestyle}{scrheadings} + +% no gap at beginning of chapter +\renewcommand*{\chapterheadstartvskip}{\vspace*{-\topskip}} + +% small gap at the end of chapter +\renewcommand*{\chapterheadendvskip}{\vspace{3.0\baselineskip}} + + +% color definition +\definecolor{orange}{rgb}{0.92,0.47,0.04} % HEX 235 120 10 +\definecolor{grey1}{rgb}{0.53,0.61,0.67} % HEX 135 155 170 +\definecolor{grey2}{rgb}{0.31,0.39,0.45} % HEX 80 100 115 + +% font style for listings +\lstset{basicstyle=\footnotesize\ttfamily,breaklines=true} +\lstset{framextopmargin=0pt,frame=single} + +% stripped pagewidth for title page +\newcommand{\Pw}{\strip@pt\paperwidth} + + +% sytle for LOF +\addtocontents{lof}{\protect\vspace{0pt}} +\renewcommand*\l@figure{\mdseries\bprot@dottedtocline{1}{0em}{3.0em}} + + + +% shift everything up +\newcommand{\squeezeup}{\vspace{-5mm}} +\titlespacing*{\chapter}{0pt}{0pt}{40pt} + +\addtokomafont{sectioning}{\color{orange}} +\def\thespread{30} +\addtokomafont{chapter}{\fontsize{28pt}{1.0}\linespread{\thespread}\selectfont} +\setkomafont{pagehead}{ +\normalfont\sffamily} +\setkomafont{pagefoot}{ +\normalfont\sffamily} +\setkomafont{pagenumber}{ +\normalfont\sffamily} +\addtokomafont{pagenumber}{\color{grey1}} +\addtokomafont{pagefoot}{\color{grey1}} +\addtokomafont{pagehead}{\color{grey1}} + +% Make nicer table and figure captions +\setkomafont{captionlabel}{\sffamily\bfseries} +\setkomafont{caption}{\sffamily} + +% same identation for all lines in footnotes +\deffootnote{1em}{1em}{\thefootnotemark\ } + +% Set first row for tables to bold text +\newcolumntype{$}{>{\global\let\currentrowstyle\relax}} +\newcolumntype{^}{>{\currentrowstyle}} +\newcommand{\rowstyle}[1]{\gdef\currentrowstyle{#1}% + #1\ignorespaces +} + +% Macros by nemadesign +\setlength{\fboxsep}{0pt} +\setlength\fboxrule{0.1pt} +\newcommand{\moveup}{\vspace{-7mm}} +\newcommand{\movedown}{\vspace{10mm}} +\newcommand{\picgap}{\vspace{8mm}} +\newcommand{\picgapsmall}{\vspace{0mm}} + + +% Macros by Siemens +\newcommand{\printAuthors}[2][\empty] +{ + \clearpage % this is important + \begin{minipage}[b][\textheight]{\textwidth} + \begin{oldtabular}{rp{0.8\columnwidth}} + \multicolumn{2}{l}{Authors} \\ + & #2 + \end{oldtabular} + \ifthenelse{\equal{#1}{\empty}} + {\empty} + { + \begin{oldtabular}{rp{0.8\columnwidth}} + \\ + \multicolumn{2}{l}{Contributors} \\ + & #1 + \end{oldtabular} + } \\ + \end{minipage} + \clearpage % this is important +} + + + + diff --git a/doc/tutorial/sty/siemens_double.sty b/doc/tutorial/sty/siemens_double.sty new file mode 100644 index 0000000..41f72f3 --- /dev/null +++ b/doc/tutorial/sty/siemens_double.sty @@ -0,0 +1,358 @@ +% Copyright (C) nemadesign GbR +% $Rev: 56 $: +% $Date: 2014-09-02 21:28:49 +0200 (Di, 02. Sep 2014) $: + +% definitions for two column style sheet + +\makeatletter + +\usepackage{sty/siemens_core} + +\usepackage{geometry} +\geometry +{ + left=1.8cm, %linker Seitenrand + right=1.8cm, %rechter Seitenrand + top=25mm, %oben der Abstand + bottom=28mm %Unten der Abstand +} + +\renewcommand\section{\@startsection + {section}{1}{0mm} % name, ebene, einzug + {1.5\baselineskip} % vor-abstand + {0.3\baselineskip} % nach-abstand + {\bfseries\sffamily\Large\color{orange}} % layout +} + +\renewcommand\subsection{\@startsection + {subsection}{2}{0mm} + {1.0\baselineskip} + {0.2\baselineskip} + {\bfseries\sffamily\Large\color{grey1}} +} + +\newcommand\sectionnonum{\@startsection + {section}{1}{0mm} + {1.5\baselineskip} + {0.3\baselineskip} + {\normalfont\Large\bfseries\color{orange}}*} + +\newcommand\subsectionnonum{\@startsection + {subsection}{2}{0mm} + {1.0\baselineskip} + {0.2\baselineskip} + {\bfseries\sffamily\Large\color{grey1}}*} + +% TOC without indents +% add space before TOC +\addtocontents{toc}{\protect\vspace{100pt}} +\renewcommand*{\@dotsep}{0.5} +\renewcommand*\l@chapter{\bfseries\bprot@dottedtocline{1}{6em}{3.0em}} +\renewcommand*\l@section{\mdseries\bprot@dottedtocline{2}{6em}{3.0em}} +\renewcommand*\l@subsection{\mdseries\bprot@dottedtocline{3}{6em}{3.0em}} +\renewcommand*\l@subsubsection{\mdseries\bprot@dottedtocline{4}{6em}{3.0em}} + +% gap between columns +\setlength{\columnsep}{12mm} + +% gap between paragraphs +\setlength{\parskip}{0pt} + +\def\chapterpictopnum#1#2#3#4{ +\cleardoublepage +\twocolumn[{ + \begin{@twocolumnfalse} + \includegraphics[width=#3\textwidth]{#2} + \chapter{#1}\label{#4} + \end{@twocolumnfalse} +}] +\squeezeup +} + +\def\chapterpictopnonum#1#2#3#4{ +\cleardoublepage +\twocolumn[{ + \begin{@twocolumnfalse} + \includegraphics[width=#3\textwidth]{#2} + \chapter*{#1}\label{#4} + \end{@twocolumnfalse} +}] +\squeezeup +} + +\def\chapterpicrightnum#1#2#3#4{ +\cleardoublepage +\twocolumn[{ + \moveup + \begin{@twocolumnfalse} + \begin{minipage}[t]{#3\textwidth} + \vspace{0pt}\raggedright + \chapter{#1}\label{#4} + \end{minipage} + \hspace{0.04\textwidth} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgap + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \end{@twocolumnfalse} + \movedown +}] +} + +\def\chapterpicrightnonum#1#2#3#4{ +\cleardoublepage +\twocolumn[{ + \moveup + \begin{@twocolumnfalse} + \begin{minipage}[t]{#3\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1}\label{#4} + \end{minipage} + \hspace{0.04\textwidth} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgap + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \end{@twocolumnfalse} + \movedown +}] +} + + +\def\chapterpicteasernum#1#2#3#4#5{ +\cleardoublepage +\twocolumn[{ + \moveup + \begin{@twocolumnfalse} + \begin{minipage}[t]{\textwidth} + \vspace{0pt}\raggedright + \chapter{#1}\label{#4} + \end{minipage} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgapsmall + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \hspace{0.04\textwidth} + \begin{minipage}[t]{#3\textwidth} + \picgapsmall + \LARGE + \raggedright + \textcolor{grey1}{\textbf{#5}} + \end{minipage} + \end{@twocolumnfalse} + \movedown +}] +} + +\def\chapterpicteasernonum#1#2#3#4#5{ +\cleardoublepage +\twocolumn[{ + \moveup + \begin{@twocolumnfalse} + \begin{minipage}[t]{\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1}\label{#4} + \end{minipage} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgapsmall + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \hspace{0.04\textwidth} + \begin{minipage}[t]{#3\textwidth} + \picgapsmall + \LARGE + \raggedright + \textcolor{grey1}{\textbf{#5}} + \end{minipage} + \end{@twocolumnfalse} + \movedown +}] +} + +\def\gentitlepagenopic#1#2{ +\fboxrule 0.0pt +\begin{titlepage} +\phantom{Die Titelseite} +\lefoot{\copyright \; Siemens AG \the\year. All rights reserved.} + +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageUpperLeft{% + \put(1.3cm,0cm){% + \makebox(0,0)[lt]{\includegraphics[width=4cm]{pics/cover/sie_logo_layer_petrol_rgb2.png}}% + }% + }% +} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageLowerLeft{% + \put(1.3cm,3cm){% + \makebox(0,0)[lt]{\includegraphics[width=3cm]{pics/cover/logo_multicore_expert-center_siemens.png}}% + }% + }% +} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-1.0cm} + \fcolorbox{black}{grey2}{ + \begin{minipage}[c][1cm]{\paperwidth} + \hspace{0.8cm} + \fontsize{16}{20}\selectfont + \textcolor{white}{#1} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} +% \vspace{-0.2cm} + \fcolorbox{black}{grey1}{ + \begin{minipage}[c][4cm]{\paperwidth} + \textcolor{white}{} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} +% \vspace{-0.2cm} + \hspace{0.8cm} + \begin{minipage}[c][4cm]{0.8\paperwidth} + \fontsize{36}{44}\selectfont + \textcolor{white}{#2} + \end{minipage} +\end{textblock} +\end{titlepage} +\ClearShipoutPicture +} + +\def\gentitlepage#1#2#3{ +\fboxrule 0.0pt +\begin{titlepage} +\phantom{Die Titelseite} +\lefoot{\copyright \; Siemens AG \the\year. All rights reserved.} +\AddToShipoutPictureBG{ + \AtPageUpperLeft{\raisebox{-\height} + {\includegraphics[width=\paperwidth]{#1}}} +} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageUpperLeft{% + \put(1.3cm,0cm){% + \makebox(0,0)[lt]{\includegraphics[width=4cm]{pics/cover/sie_logo_layer_petrol_rgb.png}}% + }% + }% +} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageLowerLeft{% + \put(1.3cm,3cm){% + \makebox(0,0)[lt]{\includegraphics[width=3cm]{pics/cover/logo_multicore_expert-center_siemens.png}}% + }% + }% +} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-1.0cm} + \fcolorbox{black}{grey2}{ + \begin{minipage}[c][1cm]{\paperwidth} + \hspace{0.8cm} + \fontsize{16}{20}\selectfont + \textcolor{white}{#2} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} +% \vspace{-0.2cm} + \fcolorbox{black}{grey1}{ + \begin{minipage}[c][4cm]{\paperwidth} + \textcolor{white}{} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} +% \vspace{-0.2cm} + \hspace{0.8cm} + \begin{minipage}[c][4cm]{0.8\paperwidth} + \fontsize{36}{44}\selectfont + \textcolor{white}{#3} + \end{minipage} +\end{textblock} +\end{titlepage} +\ClearShipoutPicture +} + +\def\genbackpage#1{ +\pagebreak +\cleardoubleevenemptypage +\thispagestyle{empty} +\renewcommand{\baselinestretch}{1.1}\normalsize +\begin{textblock}{4}[0.0,0.0](0,0) + \fcolorbox{black}{grey1}{ + \begin{minipage}[c][0.9\paperwidth]{\paperwidth} + \textcolor{white}{} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](0,0) + \vspace{0.44\paperheight} + \hspace{1.8cm} + \begin{minipage}[t][\paperwidth]{\paperwidth} + \textcolor{white}{#1} + \end{minipage} +\end{textblock} +\begin{textblock}{4}[0.0,0.0](0,0) + \vspace{\paperwidth} + \vspace{-3.6cm} + \begin{minipage}[c][1cm]{\paperwidth} + \hspace{1.8cm} + \textcolor{black}{Printed in Germany\;|\;\copyright\;\copyrightdate \;Siemens AG} + \end{minipage} + +\end{textblock} +} + +\def\tableofcontentspic#1#2#3{ +\cleardoublepage +\twocolumn[{ + \moveup + \begin{@twocolumnfalse} + \begin{minipage}[t]{#3\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1} + \end{minipage} + \hspace{0.04\textwidth} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgap + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \begin{minipage}[t]{0.2\textwidth} + \vspace{0pt}\raggedright + \hspace{15mm} + \end{minipage} + \begin{minipage}[t]{0.8\textwidth} + \phantomsection + \tableofcontents + \end{minipage} + \end{@twocolumnfalse} + \movedown +}] +} + +\def\genlistoffigures#1{ + \cleardoublepage + \onecolumn{ + \chapter*{#1} + \vspace{1.5cm} + \begin{addmargin}[0.3\textwidth]{0cm} + \listoffigures + \end{addmargin} + } +} + +\makeatother diff --git a/doc/tutorial/sty/siemens_single.sty b/doc/tutorial/sty/siemens_single.sty new file mode 100644 index 0000000..90f820a --- /dev/null +++ b/doc/tutorial/sty/siemens_single.sty @@ -0,0 +1,327 @@ +% Copyright (C) nemadesign GbR +% $Rev: 56 $: +% $Date: 2014-09-02 21:55:06 +0200 (Di, 02. Sep 2014) $: + +% definitions for single column style sheet + +\usepackage{sty/siemens_core} + +\usepackage{geometry} +\geometry +{ + left=2.3cm, %linker Seitenrand + right=2.3cm, %rechter Seitenrand + top=25mm, %oben der Abstand + bottom=32mm %Unten der Abstand +} +% gap between columns +\setlength{\columnsep}{12mm} + +% gap between paragraphs +\setlength{\parskip}{1ex} + +\renewcommand\section{\@startsection + {section}{1}{0mm} % name, ebene, einzug + {1.3\baselineskip} % vor-abstand + {0.1\baselineskip} % nach-abstand + {\bfseries\sffamily\fontsize{13pt}{1.0}\color{orange}} % layout +} + +\renewcommand\subsection{\@startsection + {subsection}{2}{0mm} + {0.8\baselineskip} + {0.1\baselineskip} + {\bfseries\sffamily\fontsize{13pt}{1.0}\color{grey1}} +} + +\newcommand\sectionnonum{\@startsection + {section}{1}{0mm} + {1.3\baselineskip} + {0.1\baselineskip} + {\normalfont\fontsize{13pt}{1.0}\bfseries\color{orange}}*} + +\newcommand\subsectionnonum{\@startsection + {subsection}{2}{0mm} + {0.8\baselineskip} + {0.01\baselineskip} + {\bfseries\sffamily\fontsize{13pt}{1.0}\color{grey1}}*} + +% TOC without indents +% add space before TOC +\addtocontents{toc}{\protect\vspace{100pt}} +\renewcommand*{\@dotsep}{0.5} +\renewcommand*\l@chapter{\bfseries\bprot@dottedtocline{1}{0em}{3.0em}} +\renewcommand*\l@section{\mdseries\bprot@dottedtocline{2}{0em}{3.0em}} +\renewcommand*\l@subsection{\mdseries\bprot@dottedtocline{3}{0em}{3.0em}} +\renewcommand*\l@subsubsection{\mdseries\bprot@dottedtocline{4}{0em}{3.0em}} + +\def\chapterpictopnum#1#2#3#4{ + \cleardoublepage + \begin{minipage}[t]{\textwidth} + \picgap + \includegraphics[width=#3\textwidth]{#2} + \end{minipage} + \begin{minipage}[t]{\textwidth} + \vspace{0pt}\raggedright + \chapter{#1}\label{#4} + \end{minipage} +} + +\def\chapterpictopnonum#1#2#3#4{ + \cleardoublepage + \begin{minipage}[t]{\textwidth} + \picgap + \includegraphics[width=#3\textwidth]{#2} + \end{minipage} + \begin{minipage}[t]{\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1}\label{#4} + \end{minipage} +} + +\def\chapterpicrightnum#1#2#3#4{ + \cleardoublepage + \begin{minipage}[t]{#3\textwidth} + \vspace{0pt}\raggedright + \chapter{#1}\label{#4} + \end{minipage} + \hspace{0.04\textwidth} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgap + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \vspace{2em} + +} + +\def\chapterpicrightnonum#1#2#3#4{ + \cleardoublepage + \begin{minipage}[t]{#3\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1}\label{#4} + \end{minipage} + \hspace{0.04\textwidth} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgap + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \vspace{2em} + +} + +\newcolumntype{a}[1]{>{\LARGE\raggedright\color{white}\columncolor{grey1}}p{#1}} +\newcolumntype{b}[1]{>{\columncolor{white}}p{#1}} +%\renewcommand*{\arraystretch}{0.0} +%\setlength{\arrayrulewidth}{0pt} +\arrayrulecolor{grey1} +\def\chapterpicteasernum#1#2#3#4#5{ + \cleardoublepage + \setlength{\tabcolsep}{0mm} + + \begin{minipage}[t]{\textwidth} + \vspace{0pt}\raggedright + \chapter{#1}\label{#4} + \end{minipage} + \begin{table}[ht] + \FPsub{\blah}{0.96}{#3} + \FPsub{\blub}{#3}{0.1} + \begin{tabular}[m]{b{\blah\textwidth} b{.05\textwidth} a{20pt} a{\blub\textwidth} a{19pt} } + \raisebox{-.92\totalheight}{\includegraphics[width=\blah\textwidth]{#2}} & & & #5 & \\ + \end{tabular} +\end{table} +\setlength{\tabcolsep}{6pt} + +} + +\def\chapterpicteasernonum#1#2#3#4#5{ + \cleardoublepage + \setlength{\tabcolsep}{0mm} + + \begin{minipage}[t]{\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1}\label{#4} + \end{minipage} + \begin{table}[ht] + \FPsub{\blah}{0.96}{#3} + \FPsub{\blub}{#3}{0.1} + \begin{tabular}[m]{b{\blah\textwidth} b{.05\textwidth} a{20pt} a{\blub\textwidth} a{19pt} } + \raisebox{-.92\totalheight}{\includegraphics[width=\blah\textwidth]{#2}} & & & #5 & \\ + \end{tabular} +\end{table} +\setlength{\tabcolsep}{6pt} + +} + +\def\gentitlepagenopic#1#2{ +\fboxrule 0.0pt +\begin{titlepage} +\phantom{Die Titelseite} +\lefoot{\copyright \; Siemens AG \the\year. All rights reserved.} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageUpperLeft{% + \put(1.3cm,0cm){% + \makebox(0,0)[lt]{\includegraphics[width=4cm]{pics/cover/sie_logo_layer_petrol_rgb2.png}}% + }% + }% +} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageLowerLeft{% + \put(1.3cm,3cm){% + \makebox(0,0)[lt]{\includegraphics[width=3cm]{pics/cover/logo_multicore_expert-center_siemens.png}}% + }% + }% +} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-1.2cm} + \fcolorbox{black}{grey2}{ + \begin{minipage}[c][1cm]{\paperwidth} + \hspace{0.8cm} + \fontsize{16}{20}\selectfont + \textcolor{white}{#1} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-0.2cm} + \fcolorbox{black}{grey1}{ + \begin{minipage}[c][4cm]{\paperwidth} + \textcolor{white}{} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-0.2cm} + \hspace{0.8cm} + \begin{minipage}[c][4cm]{0.8\paperwidth} + \fontsize{36}{44}\selectfont + \textcolor{white}{#2} + \end{minipage} +\end{textblock} +\end{titlepage} +\ClearShipoutPicture +} + +\def\gentitlepage#1#2#3{ +\fboxrule 0.0pt +\begin{titlepage} +\phantom{Die Titelseite} +\lefoot{\copyright \; Siemens AG \the\year. All rights reserved.} +\AddToShipoutPictureBG{ + \AtPageUpperLeft{\raisebox{-\height} + {\includegraphics[width=\paperwidth]{#1}}} +} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageUpperLeft{% + \put(1.3cm,0cm){% + \makebox(0,0)[lt]{\includegraphics[width=4cm]{pics/cover/sie_logo_layer_petrol_rgb.png}}% + }% + }% +} +% Siemens Logo 4cm width, 1.5cm from left corner +\AddToShipoutPictureFG*{ + \AtPageLowerLeft{% + \put(1.3cm,3cm){% + \makebox(0,0)[lt]{\includegraphics[width=3cm]{pics/cover/logo_multicore_expert-center_siemens.png}}% + }% + }% +} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-1.2cm} + \fcolorbox{black}{grey2}{ + \begin{minipage}[c][1cm]{\paperwidth} + \hspace{0.8cm} + \fontsize{16}{20}\selectfont + \textcolor{white}{#2} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-0.2cm} + \fcolorbox{black}{grey1}{ + \begin{minipage}[c][4cm]{\paperwidth} + \textcolor{white}{} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](1,0) + \vspace{\paperwidth} + \vspace{-0.2cm} + \hspace{0.8cm} + \begin{minipage}[c][4cm]{0.8\paperwidth} + \fontsize{36}{44}\selectfont + \textcolor{white}{#3} + \end{minipage} +\end{textblock} +\end{titlepage} +\ClearShipoutPicture +} + +\def\genbackpage#1{ +\pagebreak +\cleardoubleevenemptypage +\thispagestyle{empty} +\renewcommand{\baselinestretch}{1.1}\normalsize +\begin{textblock}{4}[0.0,0.0](0,0) + \fcolorbox{black}{grey1}{ + \begin{minipage}[c][0.9\paperwidth]{\paperwidth} + \textcolor{white}{} + \end{minipage} + } +\end{textblock} +\begin{textblock}{4}[0.0,0.0](0,0) + \vspace{0.38\paperheight} + \hspace{1.8cm} + \begin{minipage}[t][\paperwidth]{\paperwidth} + \textcolor{white}{#1} + \end{minipage} +\end{textblock} +\begin{textblock}{4}[0.0,0.0](0,0) + \vspace{\paperwidth} + \vspace{-4.0cm} + \begin{minipage}[c][1cm]{\paperwidth} + \hspace{1.8cm} + \textcolor{black}{Printed in Germany\;|\;\copyright\;\copyrightdate \;Siemens AG} + \end{minipage} +\end{textblock} +} + +\def\tableofcontentspic#1#2#3{ + \cleardoublepage + \onecolumn + \begin{minipage}[t]{\textwidth} + \begin{minipage}[t]{#3\textwidth} + \vspace{0pt}\raggedright + \chapter*{#1} + \end{minipage} + \hspace{0.04\textwidth} + \FPsub{\bla}{0.95}{#3} + \begin{minipage}[t]{\bla\textwidth} + \picgap + \includegraphics[width=\textwidth]{#2} + \end{minipage} + \end{minipage} + + +\tableofcontents +} + +\def\genlistoffigures#1{ + \cleardoublepage + \onecolumn{ + \chapter*{#1} + \vspace{1.5cm} + \begin{addmargin}[0cm]{0cm} + \listoffigures + \end{addmargin} + } +} diff --git a/doc/tutorial/tutorial.tex b/doc/tutorial/tutorial.tex new file mode 100644 index 0000000..7fbda7a --- /dev/null +++ b/doc/tutorial/tutorial.tex @@ -0,0 +1,140 @@ +% ---------------------------------------------------------------------- +% Document class +% ---------------------------------------------------------------------- + +\documentclass[a4paper, 11pt, twoside, openright, footinclude, headinclude]{scrbook} + +% ---------------------------------------------------------------------- +% Packages +% ---------------------------------------------------------------------- + +\usepackage{sty/siemens_single} +\usepackage{amsmath} +\usepackage{listing} +\usepackage{array} % for tabular +\usepackage{pifont} % for \myCheck, \myCross +\usepackage[hyphens]{url} +\usepackage[hidelinks]{hyperref} % hyperref without colored links +%\usepackage{draftwatermark} +\usepackage{xspace} +\usepackage{pgf} +\usepackage{tikz} + +% ---------------------------------------------------------------------- +% TikZ +% ---------------------------------------------------------------------- + +\usetikzlibrary{arrows} +\usetikzlibrary{shapes.geometric} +\usetikzlibrary{matrix} +\usetikzlibrary{patterns} +\usetikzlibrary{positioning} + +% ---------------------------------------------------------------------- +% Document specific macros and settings +% ---------------------------------------------------------------------- + +\def\lstwidth{0.8\columnwidth} +\def\embb{EMB$^\mathsf{2}$\xspace} +%\SetWatermarkColor[gray]{0.9} +\newcommand{\leadingzero}[1]{\ifnum #1<10 0\the#1\else\the#1\fi} +\newcommand{\monthword}[1]{\ifcase#1\or January\or February\or March\or April\or May\or June\or July\or August\or September\or October\or November\or December\fi} + +% ---------------------------------------------------------------------- +% Dataflow process networks +% ---------------------------------------------------------------------- + +\newlength{\dpnunit} +\setlength{\dpnunit}{5mm} + +\tikzstyle{nodestyle}=[draw, semithick, minimum size=\dpnunit, anchor=center, minimum height=2\dpnunit] +\tikzstyle{edgestyle}=[draw, thick, ->, >=stealth'] + +\tikzstyle{function}=[rectangle, nodestyle, inner sep=2pt, minimum width=2\dpnunit, minimum height=1.5\dpnunit] +\tikzstyle{invisible}=[anchor=center] +\tikzstyle{selector}=[trapezium, nodestyle, trapezium angle=-70, trapezium stretches=true, minimum width=2\dpnunit, minimum height=\dpnunit] +\tikzstyle{switch}=[trapezium, nodestyle, trapezium angle=70, trapezium stretches=true, minimum width=2\dpnunit, minimum height=\dpnunit] +\tikzstyle{junction}=[circle, draw, fill, inner sep=0pt, minimum size=1mm, anchor=center] + +\newcommand{\dpninv}[1]{\node [invisible, inner sep=2pt, minimum size=0pt] (#1) {};} +\newcommand{\dpnsrc}[2]{\node [function] (#1) {#2};} +\newcommand{\dpnsnk}[2]{\node [function] (#1) {#2};} +\newcommand{\dpnser}[2]{\node [function] (#1) {#2};} +\newcommand{\dpnpar}[2]{\node [function, double] (#1) {#2};} +\newcommand{\dpnsel}[1]{\node [selector] (#1) {}; \draw (#1.north west) [yshift=-4.5pt] node {\ttrue}; \draw (#1.north east) [yshift=-4.5pt] node {\tfalse};} +\newcommand{\dpnswi}[1]{\node [switch] (#1) {}; \draw (#1.south west) [yshift=4.5pt] node {\ttrue}; \draw (#1.south east) [yshift=4.5pt] node {\tfalse};} +\newcommand{\dpnjun}[1]{\node [junction] (#1) {};} + +% ---------------------------------------------------------------------- +\begin{document} +% ---------------------------------------------------------------------- + +\input{customize} % To get template specific stuff + +% Set title and author +% short title is used on the front cover and on each page header +\renewcommand{\shorttitle}{\embb Tutorial} +% short date is printed on each page header, right next to short title +\renewcommand{\shortdate}{\monthword{\month} \the\year} +% copyright date is printed on the back cover +\renewcommand{\copyrightdate}{\leadingzero{\month}.\the\year} +% 1st paramter: a square(!) picture +% 2nd parameter: short tile (one line) +% 3rd parameter: long title (up to two lines) +\gentitlepage{pics/chicago-square.jpg}{\LARGE Siemens Corporate Technology | \monthword{\month} 2014}{\scalebox{0.9}{Embedded Multicore Building Blocks}\\\scalebox{0.9}{Introduction and Tutorial}} + +% List the authors and contributors on the second page, right after the cover page +% 1st parameter: contributors (optional) +% 2nd parameter: author(s) +\printAuthors{ + Bernhard Gatzhammer, bernhard.gatzhammer@siemens.com \newline + Christian Kern, christian.kern@siemens.com \newline + Tobias Sch\"ule, tobias.schuele@siemens.com \newline + Marcus Winter, marcus.winter.ext@siemens.com \newline + \newline + Siemens AG \newline + Corporate Technology \newline + Research \& Technology Center \newline + CT RTC ITP SES-DE \newline + Otto-Hahn-Ring 6 \newline + 81739 M\"unchen \newline + Germany +} + +% This is from the template: +% \tableofcontentspic{Contents}{pics/chapter/Fotolia_43587256_X.jpg}{\chapterpicwidth} +% And this is our custom toc +\chapter*{Contents} +\tableofcontents + +% ---------------------------------------------------------------------- +% Content +% ---------------------------------------------------------------------- + +% \input{content/preface} +\input{content/introduction} +\input{content/mtapi} +\input{content/algorithms} +\input{content/dataflow} +\input{content/containers} + +% ---------------------------------------------------------------------- +% References +% ---------------------------------------------------------------------- + +% the following two macros fix the problem of the incorrect ToC pointer to the bibliography. +\cleardoublepage +\phantomsection +\addcontentsline{toc}{chapter}{Bibliography} +\bibliographystyle{IEEEtran} +% balance the columns at the last page of the bibliography +\balance +\bibliography{references} + +% ---------------------------------------------------------------------- +% Back Cover +% ---------------------------------------------------------------------- + +\backcover + +\end{document} diff --git a/mtapi_c/CMakeLists.txt b/mtapi_c/CMakeLists.txt new file mode 100644 index 0000000..ad4f535 --- /dev/null +++ b/mtapi_c/CMakeLists.txt @@ -0,0 +1,41 @@ +project (project_embb_mtapi_c) + +file(GLOB_RECURSE EMBB_MTAPI_C_SOURCES "src/*.c" "src/*.h") +file(GLOB_RECURSE EMBB_MTAPI_C_HEADERS "include/*.h") + +file(GLOB_RECURSE EMBB_MTAPI_TEST_SOURCES "test/*.cc" "test/*.h") + +IF(MSVC8 OR MSVC9 OR MSVC10 OR MSVC11) +FOREACH(src_tmp ${EMBB_MTAPI_TEST_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +FOREACH(src_tmp ${EMBB_MTAPI_C_SOURCES}) + SET_PROPERTY(SOURCE ${src_tmp} PROPERTY LANGUAGE CXX) +ENDFOREACH(src_tmp) +ENDIF() + +# Execute the GroupSources macro +include(../CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_MTAPI_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_MTAPI_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ) + +add_library(embb_mtapi_c ${EMBB_MTAPI_C_SOURCES} ${EMBB_MTAPI_C_HEADERS}) +target_link_libraries(embb_mtapi_c embb_base_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_mtapi_c_test ${EMBB_MTAPI_TEST_SOURCES}) + target_link_libraries(embb_mtapi_c_test embb_mtapi_c partest embb_base_c ${compiler_libs}) + CopyBin(BIN embb_mtapi_c_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_mtapi_c DESTINATION lib) diff --git a/mtapi_c/include/embb/mtapi/c/mtapi.h b/mtapi_c/include/embb/mtapi/c/mtapi.h new file mode 100644 index 0000000..38e9341 --- /dev/null +++ b/mtapi_c/include/embb/mtapi/c/mtapi.h @@ -0,0 +1,3478 @@ +/* + * MTAPI header contains the public MTAPI API and data type definitions. + * + * This file defines the MTAPI API. it has to be included by any application + * using MTAPI. + * + * \copyright + * Copyright (c) 2012, The Multicore Association. + * 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. + * (3) Neither the name of the Multicore Association nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * \note + * 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 OWNER 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 EMBB_MTAPI_C_MTAPI_H_ +#define EMBB_MTAPI_C_MTAPI_H_ + +/** + * \defgroup C_MTAPI MTAPI + * \ingroup C + * + * Multicore Task Management API (MTAPI®). + * + * MTAPI is an API standardized by the + * Multicore Association + * for leveraging task parallelism on a wide range of embedded devices + * containing symmetric or asymmetric multicore processors. + * A description of the basic terms and concepts is given below. More + * information can be found on the website of the + * Multicore + Task Management Working Group. + * + * ## Definitions + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ActionAn action is the hardware or software implementation of a job. An + * action implemented in software consists of the implementation of an + * action function with a predefined signature. Software actions are + * registered with the MTAPI runtime and associated with a job. While + * executing, an action is also associated with a task and task context. + * Hardware implementations of actions must be known a priori in the + * MTAPI runtime implementation. There is no standardized way of + * registering hardware actions because they are highly + * hardware-dependent. Hardware and software actions are referenced by + * handles or indirectly through job IDs and job handles.
Action FunctionThe callable, an executable function of an action, invoked by the + * MTAPI runtime when a task is started.
AffinityDefines which cores can execute a given action function.
BlockingA blocking function does not return until the function completes + * successfully or returns with an error.
CoreA core is an undividable processing element. Two cores can share + * resources such as memory or ALUs for hyperthreaded cores. The core + * notion is necessary for core affinity, but is + * implementation-specific.
DomainAn implementation of MTAPI includes one or more domains, each with + * one or more nodes. The concept of domains is consistent in all + * Multicore Association APIs. A domain is comparable to a subnet in a + * network or a namespace for unique names and IDs. Domains are + * supported by a runtime.
HandleAn abstract reference to an object on the same node or to an object + * managed by another node. A handle is valid only on the node on which + * it was requested and generated. A handle is opaque, that is, its + * underlying representation is implementation-defined. Handles can be + * copied, assigned, and passed as arguments, but the application + * should make no other assumptions about the type, representation, or + * contents of a handle.
JobA job provides a way to reference one or more actions. Jobs are + * abstractions of the processing implemented in hardware or software + * by actions. Multiple actions can implement the same job based on + * different hardware resources (for instance a job can be implemented + * by one action on a DSP and by another action on a general purpose + * core, or a job can be implemented by both hardware and software + * actions). Each job is represented by a domain-wide job ID, or by a + * job handle local to a node.
MCAThe Multicore Association.
MTAPIMulticore Task Management API, defined by The Multicore Association. + *
NodeA node represents an independent unit of execution that maps to a + * process, thread, thread pool, instance of an operating system, + * hardware accelerator, processor core, a cluster of processor cores, + * or other abstract processing entity with an independent program + * counter. Each node can belong to only one domain. The concept of + * nodes is consistent in all Multicore Associations APIs. Code + * executed on an MTAPI node shares memory (data) with any other code + * executed on the same node.
QueueA software or hardware entity in which tasks are enqueued in a + * given order. The queue can ensure in-order execution of tasks. + * Furthermore, queues might implement other scheduling policies that + * can be configured by setting queue attributes.
ReferenceA reference exists when an object or abstract entity has knowledge + * or access to another object, without regard to the specific means of + * the implementation.
ResourceA processing core or chip, hardware accelerator, memory region, or + * I/O.
Remote MemoryMemory that cannot be accessed using standard load and store + * operations. For example, host memory is remote to a GPU core.
Runtime SystemAn MTAPI runtime system (or "runtime") is the underlying + * implementation of MTAPI. The core of the runtime system supports + * task scheduling and communication with other nodes. Each MTAPI has + * an MTAPI runtime system.
SMPSMP is short for symmetric multiprocessing, in which two or more + * identical processing cores are connected to a shared main memory + * and are controlled by a single OS instance.
TaskA task is the invocation of an action. A task is associated with a + * job object, which is associated with one or more actions. A task + * may optionally be associated with a task group. A task has + * attributes and an internal state. A task begins its lifetime with a + * call to mtapi_task_start() or mtapi_task_enqueue(). A task is + * referenced by a handle of type mtapi_task_hndl_t. After a task has + * started, it is possible to wait for task completion from other + * parts of the program. Every task can run exactly once, i.e., the + * task cannot be started a second time. (Note that in other contexts, + * the term "task" has a different meaning. Some real-time operating + * systems use "task" for operating system threads, for example.)
Task ContextInformation about the task, accessible by the corresponding action + * function; useful for action code reflection.
+ * + * ## The MTAPI Feature Set + * + * MTAPI supports two programming modes derived from use cases of the working + * group members: + * - __Tasks__
+ * MTAPI allows a programmer to start tasks and to synchronize on task + * completion. Tasks are executed by the runtime system, concurrently to + * other tasks that have been started and have not been completed at that + * point in time. A task can be implemented by software or by hardware. + * Tasks can be started from remote nodes, i.e., the implementation can be + * done on one node, but the starting and synchronization of corresponding + * tasks can be done on other nodes. The developer decides where to deploy + * a task implementation. On the executing node, the runtime system selects + * the cores that execute a particular task. This mapping can be influenced + * by application-specific attributes. Tasks can start sub-tasks. MTAPI + * provides a basic mechanism to pass data to the node that executes a + * task, and back to the calling node. + * - __Queues__
+ * Explicit queues can be used to control the task scheduling policies for + * related tasks. Order-preserving queues ensure that tasks are executed + * sequentially in queue order with no subsequent task starting until the + * previous one is complete. MTAPI also supports non-order-preserving + * queues, allowing control of the scheduling policies of tasks started via + * the same queue (queues may offer implementation specific scheduling + * policies controlled by implementation specific queue attributes). Even + * hardware queues can be associated with queue objects. + * + * MTAPI also supports the following types of tasks: + * - __Single tasks__
+ * Single tasks are the standard case: After a task is started, the + * application may wait for completion of the task at a later point in + * time. In some cases the application waits for completion of a group of + * tasks. In other cases waiting is not required at all. When a + * software-implemented task is started, the corresponding code (action + * function) is executed once by the MTAPI runtime environment. When a + * hardware-implemented task is started, the task execution is triggered + * once by the MTAPI runtime system. + * - __Multi-instance tasks__
+ * Multi-instance tasks execute the same action multiple times in parallel + * (similar to parallel regions in OpenMP or parallel MPI processes). + * - __Multiple-implementation tasks / load balancing__
+ * In heterogeneous systems, there could be implementations of the same job + * for different types of processor cores, e.g., one general purpose + * implementation and a second one for a hardware accelerator. MTAPI allows + * attaching multiple actions to a job. The runtime system shall decide + * dynamically during runtime, depending on the system load, which action to + * utilize. Only one of the alternative actions will be executed. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- BASIC DEFINITIONS -------------------------------------------------- */ + +/** marks input parameters */ +#define MTAPI_IN const +/** marks output parameters */ +#define MTAPI_OUT +/** marks in/out parameters */ +#define MTAPI_INOUT + + +/* ---- BASIC DATA TYPES --------------------------------------------------- */ + +/* MCA type definitions */ +typedef int mca_int_t; +typedef int8_t mca_int8_t; +typedef int16_t mca_int16_t; +typedef int32_t mca_int32_t; +typedef int64_t mca_int64_t; + +typedef unsigned int mca_uint_t; +typedef uint8_t mca_uint8_t; +typedef uint16_t mca_uint16_t; +typedef uint32_t mca_uint32_t; +typedef uint64_t mca_uint64_t; + +typedef unsigned char mca_boolean_t; + +typedef unsigned int mca_domain_t; +typedef unsigned int mca_node_t; +typedef unsigned int mca_status_t; +typedef int mca_timeout_t; + +/* the MTAPI data types */ +typedef mca_int_t mtapi_int_t; +typedef mca_int8_t mtapi_int8_t; +typedef mca_int16_t mtapi_int16_t; +typedef mca_int32_t mtapi_int32_t; +typedef mca_int64_t mtapi_int64_t; + +typedef mca_uint_t mtapi_uint_t; +typedef mca_uint8_t mtapi_uint8_t; +typedef mca_uint16_t mtapi_uint16_t; +typedef mca_uint32_t mtapi_uint32_t; +typedef mca_uint64_t mtapi_uint64_t; + +typedef mca_domain_t mtapi_domain_t; +typedef mca_node_t mtapi_node_t; +typedef mca_timeout_t mtapi_timeout_t; + +typedef mca_boolean_t mtapi_boolean_t; +typedef mtapi_uint_t mtapi_size_t; + +/** + * Info structure. + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +struct mtapi_info_struct { + unsigned int hardware_concurrency; /**< number of CPU cores */ + unsigned int used_memory; /**< bytes of memory used by MTAPI */ +}; + +/** + * Info type. + * \memberof mtapi_info_struct + */ +typedef struct mtapi_info_struct mtapi_info_t; + +/** + * Core affinity type. + * \ingroup CORE_AFFINITY_MASKS + */ +typedef mtapi_uint64_t mtapi_affinity_t; + + +/* ---- BASIC enumerations ------------------------------------------------- */ + +/** + * Status codes returned in the status parameter of the MTAPI interface + * functions. + */ +enum mtapi_status_enum { + /* generic */ + MTAPI_SUCCESS, /**< success, no error */ + MTAPI_TIMEOUT, /**< timeout was reached */ + MTAPI_ERR_PARAMETER, /**< invalid parameter */ + MTAPI_ERR_ATTR_READONLY, /**< tried to write a read-only + attribute */ + MTAPI_ERR_ATTR_NUM, /**< invalid attribute number */ + MTAPI_ERR_ATTR_SIZE, /**< invalid attribute size */ + + /* node specific */ + MTAPI_ERR_NODE_INITFAILED, /**< general error in node + initialization */ + MTAPI_ERR_NODE_INITIALIZED, /**< \a mtapi_initialize called for a + node that already had been + initialized */ + MTAPI_ERR_NODE_INVALID, /**< The node id is not valid */ + MTAPI_ERR_DOMAIN_INVALID, /**< the domain id is not valid */ + MTAPI_ERR_NODE_NOTINIT, /**< the node is not initialized */ + + /* action specific */ + MTAPI_ERR_ACTION_INVALID, /**< The action id is not a valid action + id, i.e., no action was crated for + that ID or the action has been + deleted. */ + MTAPI_ERR_ACTION_EXISTS, /**< mtapi_action_create called with an + ID of an action that already had + been created */ + MTAPI_ERR_ACTION_LIMIT, /**< exceeded maximum number of actions + allowed */ + MTAPI_ERR_ACTION_NUM_INVALID, /**< The number of actions passed to + mtapi_task_start or + mtapi_queue_create is lower than + 1. */ + MTAPI_ERR_ACTION_FAILED, /**< status that can be passed to the + runtime by + \a mtapi_context_status_set if the + task could not be completed as + intended */ + MTAPI_ERR_ACTION_CANCELLED, /**< status that can be passed to the + runtime by + \a mtapi_context_status_set if the + task execution is canceled */ + MTAPI_ERR_ACTION_DELETED, /**< All actions associated with the + task have been deleted before the + execution of the task was started + or the error code has been set in + the action code to + MTAPI_ERR_ACTION_DELETED by + \a mtapi_action_result_set. */ + MTAPI_ERR_ACTION_DISABLED, /**< All actions associated with the + task have been disabled before the + execution of the task was started + or the error code has been + set in the action code to + MTAPI_ERR_ACTION_DISABLED by + \a mtapi_action_result_set. */ + + /* context specific */ + MTAPI_ERR_CONTEXT_INVALID, + MTAPI_ERR_CONTEXT_OUTOFCONTEXT, /**< returned if action code is not + called in the context of a task + execution. This function must be + used in an action function only. + The action function must be called + from the MTAPI runtime system */ + + /* task specific */ + MTAPI_ERR_TASK_INVALID, + MTAPI_ERR_TASK_LIMIT, /**< exceeded maximum number of tasks + allowed */ + + /* job specific */ + MTAPI_ERR_JOB_INVALID, /**< invalid job handle or job ID */ + + /* queue specific */ + MTAPI_ERR_QUEUE_INVALID, /**< argument is not a valid queue + handle or ID */ + MTAPI_ERR_QUEUE_DELETED, /**< a deleted queue is passed as an + argument */ + MTAPI_ERR_QUEUE_DISABLED, /**< a disable queue is passed as an + argument */ + MTAPI_ERR_QUEUE_LIMIT, /**< exceeded maximum number of queues + allowed */ + + /* group specific */ + MTAPI_ERR_GROUP_INVALID, + MTAPI_ERR_GROUP_LIMIT, /**< exceeded maximum number of groups + allowed */ + MTAPI_GROUP_COMPLETED, /**< group completed, i.e., there are no + more tasks to wait for in the group + when waiting with + \a mtapi_group_wait_any */ + + /* others */ + MTAPI_ERR_UNKNOWN, /**< unknown error */ + MTAPI_ERR_BUFFER_SIZE, /**< buffer size mismatch */ + MTAPI_ERR_RESULT_SIZE, /**< result buffer size mismatch + (e.g., in \a mtapi_task_wait) */ + MTAPI_ERR_ARG_SIZE, /**< invalid argument size */ + MTAPI_ERR_WAIT_PENDING, /**< mtapi_*_wait called twice on a + group or task which has not + finished */ + + /* unsupported functions */ + MTAPI_ERR_FUNC_NOT_IMPLEMENTED, /**< The MTAPI function called is not + implemented by the runtime + system. */ + MTAPI_ERR_ARG_NOT_IMPLEMENTED, /**< The MTAPI function called is + implemented by the runtime, but it + does not support the arguments + passed. */ + + /* features that may be not supported by some implementations */ + MTAPI_ERR_RUNTIME_REMOTETASKS_NOTSUPPORTED, + /**< The Runtime system does not support + remote tasks. This allows lighter + implementations for shared memory + environments. */ + MTAPI_ERR_RUNTIME_LOADBALANCING_NOTSUPPORTED, + /**< This error is returned when more + than one action is passed to + mtapi_task_start or + mtapi_queue_create and if the + runtime system does not implement + load balancing between nodes. This + allows light MTAPI implementation + for systems not having the + requirement for inter-node + load-balancing */ + + /* core affinity specific */ + MTAPI_ERR_CORE_NUM, /**< This error occurs when trying to + set an affinity to a non-existing + core */ +}; +typedef enum mtapi_status_enum mtapi_status_t; + /**< defines the MTAPI state codes */ + + +/** + * Task states. + */ +enum mtapi_task_state_enum { + MTAPI_TASK_INTENTIONALLY_UNUSED, /**< never used */ + MTAPI_TASK_ERROR, /**< indicates internal error */ + MTAPI_TASK_PRENATAL, /**< initialization value for newly + allocated task descriptor */ + MTAPI_TASK_CREATED, + MTAPI_TASK_SCHEDULED, + MTAPI_TASK_RUNNING, + MTAPI_TASK_WAITING, + MTAPI_TASK_RETAINED, + MTAPI_TASK_DELETED, + MTAPI_TASK_CANCELLED, /**< \a MTAPI_TASK_CANCELLED is the only + value specified by the MTAPI + specification, the others are + implementation specific and can be + used for debugging purposes. */ + MTAPI_TASK_COMPLETED +}; +typedef enum mtapi_task_state_enum mtapi_task_state_t; + /**< internal task state */ + +/** + * Notification types for the runtime. + */ +enum mtapi_notification_enum { + MTAPI_NOTIF_PREFETCH, /**< implementation specific example */ + MTAPI_NOTIF_EXECUTE_NEXT /**< implementation specific example */ +}; +typedef enum mtapi_notification_enum mtapi_notification_t; + /**< runtime notification */ + + +/** + * Node attributes, to be extended for implementation specific attributes + */ +enum mtapi_node_attributes_enum { + MTAPI_NODE_CORE_AFFINITY, /**< use the given cores only */ + MTAPI_NODE_NUMCORES, /**< number of cores provided by the + node */ + MTAPI_NODE_TYPE, /**< the nodes type, SMP or DSP */ + MTAPI_NODE_MAX_TASKS, /**< maximum number of tasks allowed by + the node */ + MTAPI_NODE_MAX_ACTIONS, /**< maximum number of actions allowed + by the node */ + MTAPI_NODE_MAX_GROUPS, /**< maximum number of groups allowed + by the node */ + MTAPI_NODE_MAX_QUEUES, /**< maximum number of queues allowed + by the node */ + MTAPI_NODE_QUEUE_LIMIT, /**< maximum number of elements that fit + into a queue */ + MTAPI_NODE_MAX_JOBS, /**< maximum number of jobs allowed by + the node */ + MTAPI_NODE_MAX_ACTIONS_PER_JOB, /**< maximum number of actions in a job + allowed by the node */ + MTAPI_NODE_MAX_PRIORITIES /**< maximum number of priorities + allowed by the node */ +}; +/** size of the \a MTAPI_NODE_CORE_AFFINITY attribute */ +#define MTAPI_NODE_CORE_AFFINITY_SIZE sizeof(embb_core_set_t) +/** size of the \a MTAPI_NODES_NUMCORES attribute */ +#define MTAPI_NODE_NUMCORES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_TYPE attribute */ +#define MTAPI_NODE_TYPE_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_TASKS attribute */ +#define MTAPI_NODE_MAX_TASKS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_ACTIONS attribute */ +#define MTAPI_NODE_MAX_ACTIONS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_GROUPS attribute */ +#define MTAPI_NODE_MAX_GROUPS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_QUEUES attribute */ +#define MTAPI_NODE_MAX_QUEUES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_QUEUE_LIMIT attribute */ +#define MTAPI_NODE_QUEUE_LIMIT_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_JOBS attribute */ +#define MTAPI_NODE_MAX_JOBS_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_ACTIONS_PER_JOB attribute */ +#define MTAPI_NODE_MAX_ACTIONS_PER_JOB_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_NODE_MAX_PRIORITIES attribute */ +#define MTAPI_NODE_MAX_PRIORITIES_SIZE sizeof(mtapi_uint_t) + +/* example attribute value */ +#define MTAPI_NODE_TYPE_SMP 1 +#define MTAPI_NODE_TYPE_DSP 2 + +/** task attributes */ +enum mtapi_task_attributes_enum { + MTAPI_TASK_DETACHED, /**< task is detached, i.e., the runtime + system cared about deleting + internal data structures + representing the task; detached + tasks cannot be accessed via task + handles */ + MTAPI_TASK_INSTANCES, /**< indicates how many parallel + instances of task shall be started + by MTAPI; the default case is that + each task is executed exactly once, + setting this value to \a n, the + corresponding action code will be + executed n times, if possible in + parallel */ + MTAPI_TASK_PRIORITY, + MTAPI_TASK_AFFINITY +}; +/** size of the \a MTAPI_TASK_DETACHED attribute */ +#define MTAPI_TASK_DETACHED_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_TASK_INSTANCES attribute */ +#define MTAPI_TASK_INSTANCES_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_TASK_PRIORITY attribute */ +#define MTAPI_TASK_PRIORITY_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_TASK_AFFINITY attribute */ +#define MTAPI_TASK_AFFINITY_SIZE sizeof(mtapi_affinity_t) + + +/** + * action attributes + */ +enum mtapi_action_attributes_enum { + MTAPI_ACTION_GLOBAL, + MTAPI_ACTION_AFFINITY, + MTAPI_ACTION_DOMAIN_SHARED +}; +/** size of the \a MTAPI_ACTION_GLOBAL attribute */ +#define MTAPI_ACTION_GLOBAL_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_ACTION_AFFINITY attribute */ +#define MTAPI_ACTION_AFFINITY_SIZE sizeof(mtapi_affinity_t) +/** size of the \a MTAPI_ACTION_DOMAIN_SHARED attribute */ +#define MTAPI_ACTION_DOMAIN_SHARED_SIZE sizeof(mtapi_boolean_t) + + +/** + * queue attributes + */ +enum mtapi_queue_attributes_enum { + MTAPI_QUEUE_GLOBAL, + MTAPI_QUEUE_PRIORITY, + MTAPI_QUEUE_LIMIT, + MTAPI_QUEUE_ORDERED, + MTAPI_QUEUE_RETAIN, + MTAPI_QUEUE_DOMAIN_SHARED +}; +/** size of the \a MTAPI_QUEUE_GLOBAL attribute */ +#define MTAPI_QUEUE_GLOBAL_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_QUEUE_PRIORITY attribute */ +#define MTAPI_QUEUE_PRIORITY_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_QUEUE_LIMIT attribute */ +#define MTAPI_QUEUE_LIMIT_SIZE sizeof(mtapi_uint_t) +/** size of the \a MTAPI_QUEUE_ORDERED attribute */ +#define MTAPI_QUEUE_ORDERED_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_QUEUE_RETAIN attribute */ +#define MTAPI_QUEUE_RETAIN_SIZE sizeof(mtapi_boolean_t) +/** size of the \a MTAPI_QUEUE_DOMAIN_SHARED attribute */ +#define MTAPI_QUEUE_DOMAIN_SHARED_SIZE sizeof(mtapi_boolean_t) + + +#define MTAPI_ATTRIBUTE_VALUE(value) ((void*)(value)) +#define MTAPI_ATTRIBUTE_POINTER_AS_VALUE 0 + + +/* ---- ATTRIBUTES --------------------------------------------------------- */ + +/** + * Node attributes. + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +struct mtapi_node_attributes_struct { + embb_core_set_t core_affinity; /**< stores MTAPI_NODE_CORE_AFFINITY */ + mtapi_uint_t num_cores; /**< stores MTAPI_NODE_NUMCORES */ + mtapi_uint_t type; /**< stores MTAPI_NODE_TYPE */ + mtapi_uint_t max_tasks; /**< stores MTAPI_NODE_MAX_TASKS */ + mtapi_uint_t max_actions; /**< stores MTAPI_NODE_MAX_ACTIONS */ + mtapi_uint_t max_groups; /**< stores MTAPI_NODE_MAX_GROUPS */ + mtapi_uint_t max_queues; /**< stores MTAPI_NODE_MAX_QUEUES */ + mtapi_uint_t queue_limit; /**< stores MTAPI_NODE_QUEUE_LIMIT */ + mtapi_uint_t max_jobs; /**< stores MTAPI_NODE_MAX_JOBS */ + mtapi_uint_t max_actions_per_job; /**< stores + MTAPI_NODE_MAX_ACTIONS_PER_JOB */ + mtapi_uint_t max_priorities; /**< stores MTAPI_NODE_MAX_PRIORITIES */ +}; + +/** + * Action attributes. + * \ingroup ACTIONS + */ +struct mtapi_action_attributes_struct { + mtapi_boolean_t global; /**< stores MTAPI_ACTION_GLOBAL */ + mtapi_affinity_t affinity; /**< stores MTAPI_ACTION_AFFINITY */ + mtapi_boolean_t domain_shared; /**< stores MTAPI_ACTION_DOMAIN_SHARED*/ +}; + +/** + * Task attributes. + * \ingroup TASKS + */ +struct mtapi_task_attributes_struct { + mtapi_boolean_t is_detached; /**< stores MTAPI_TASK_DETACHED */ + mtapi_uint_t num_instances; /**< stores MTAPI_TASK_INSTANCES */ + mtapi_uint_t priority; /**< stores MTAPI_TASK_PRIORITY */ + mtapi_affinity_t affinity; /**< stores MTAPI_TASK_AFFINITY */ +}; + +/** + * Queue attributes. + * \ingroup QUEUES + */ +struct mtapi_queue_attributes_struct { + mtapi_boolean_t global; /**< stores MTAPI_QUEUE_GLOBAL */ + mtapi_uint_t priority; /**< stores MTAPI_QUEUE_PRIORITY */ + mtapi_uint_t limit; /**< stores MTAPI_QUEUE_LIMIT */ + mtapi_boolean_t ordered; /**< stores MTAPI_QUEUE_ORDERED */ + mtapi_boolean_t retain; /**< stores MTAPI_QUEUE_RETAIN */ + mtapi_boolean_t domain_shared; /**< stores MTAPI_QUEUE_DOMAIN_SHARED */ +}; + +/** + * Group attributes. + * \ingroup TASK_GROUPS + */ +struct mtapi_group_attributes_struct { + mtapi_int_t some_value; /**< just a placeholder */ +}; + +/** + * Node attributes type. + * \memberof mtapi_node_attributes_struct + */ +typedef struct mtapi_node_attributes_struct mtapi_node_attributes_t; + +/** + * Action attributes type. + * \memberof mtapi_action_attributes_struct + */ +typedef struct mtapi_action_attributes_struct mtapi_action_attributes_t; + +/** + * Task attributes type. + * \memberof mtapi_task_attributes_struct + */ +typedef struct mtapi_task_attributes_struct mtapi_task_attributes_t; + +/** + * Queue attributes type. + * \memberof mtapi_queue_attributes_struct + */ +typedef struct mtapi_queue_attributes_struct mtapi_queue_attributes_t; + +/** + * Group attributes type. + * \memberof mtapi_group_attributes_struct + */ +typedef struct mtapi_group_attributes_struct mtapi_group_attributes_t; + +/** short form for using the default node attributes */ +#define MTAPI_DEFAULT_NODE_ATTRIBUTES MTAPI_NULL + +/** short form for using the default action attributes */ +#define MTAPI_DEFAULT_ACTION_ATTRIBUTES MTAPI_NULL + +/** short form for using the default task attributes */ +#define MTAPI_DEFAULT_TASK_ATTRIBUTES MTAPI_NULL + +/** short form for using the default queue attributes */ +#define MTAPI_DEFAULT_QUEUE_ATTRIBUTES MTAPI_NULL + +/** short form for using the default group attributes */ +#define MTAPI_DEFAULT_GROUP_ATTRIBUTES MTAPI_NULL + + +/* ---- FUNCTION TYPES ----------------------------------------------------- */ + +/** + * Task context type. + * \memberof embb_mtapi_task_context_struct + */ +typedef struct embb_mtapi_task_context_struct mtapi_task_context_t; + +/** + * An action function is the executable software function that implements an + * action. + * + * The runtime passes arguments to the action function when a task is started. + * Passing arguments from one node to another node should be implemented as a + * copy operation. Just as the arguments are passed before start of execution, + * the result buffer is copied back to the calling node after the action + * function terminates. In shared memory environments, the copying of data in + * both cases is not necessary. The node-local data is data used by several + * action functions being executed on the same node (or at least in the same + * address space). The shared data is specified when the action is created. + * + * An action function can interact with the runtime environment through a task + * context object of type mtapi_task_context_t . A task context object is + * allocated and managed by the runtime. The runtime passes a pointer to the + * context object when the action function is invoked. The action may then + * query information about the execution context (e.g., its core number, the + * number of tasks and task number in a multi-instance task, polling the task + * state) by calling the mtapi_context_* functions. Furthermore it is possible + * to pass information from the action function to the runtime system which + * is executing the action function (setting the status manually, for example). + * All of these mtapi_context_* functions are called in the context of task + * execution. + * + * \ingroup ACTION_FUNCTIONS + */ +typedef void(*mtapi_action_function_t)( + const void* args, /**< arguments */ + mtapi_size_t args_size, /**< length of arguments */ + void* result_buffer, /**< buffer for storing results */ + mtapi_size_t result_buffer_size, /**< length of result_buffer */ + const void* node_local_data, /**< node-local data, shared data by + several tasks executed on the same + node */ + mtapi_size_t node_local_data_size, /**< length of shared data */ + mtapi_task_context_t * context /**< MTAPI task context provided by the + runtime systems identifying the + current task for calling back the + runtime system from the action + function */ +); + + +/* ---- HANDLES and IDs ---------------------------------------------------- */ + +typedef mtapi_uint_t mtapi_action_id_t; +typedef mtapi_uint_t mtapi_job_id_t; +typedef mtapi_uint_t mtapi_queue_id_t; +typedef mtapi_uint_t mtapi_group_id_t; +typedef mtapi_uint_t mtapi_task_id_t; + +/** + * Action handle. + * \ingroup ACTIONS + */ +struct mtapi_action_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_action_id_t id; /**< pool index of this handle */ +}; + +/** + * Action handle type. + * \memberof mtapi_action_hndl_struct + */ +typedef struct mtapi_action_hndl_struct mtapi_action_hndl_t; + +/** + * Job handle. + * \ingroup JOBS + */ +struct mtapi_job_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_job_id_t id; /**< pool index of this handle */ +}; + +/** + * Job handle type. + * \memberof mtapi_job_hndl_struct + */ +typedef struct mtapi_job_hndl_struct mtapi_job_hndl_t; + +/** + * Queue handle. + * \ingroup QUEUES + */ +struct mtapi_queue_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_queue_id_t id; /**< pool index of this handle */ +}; + +/** + * Queue handle type. + * \memberof mtapi_queue_hndl_struct + */ +typedef struct mtapi_queue_hndl_struct mtapi_queue_hndl_t; + +/** + * Group handle. + * \ingroup TASK_GROUPS + */ +struct mtapi_group_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_group_id_t id; /**< pool index of this handle */ +}; + +/** + * Group handle type. + * \memberof mtapi_group_hndl_struct + */ +typedef struct mtapi_group_hndl_struct mtapi_group_hndl_t; + +/** + * Task handle. + * \ingroup TASKS + */ +struct mtapi_task_hndl_struct { + mtapi_uint_t tag; /**< version of this handle */ + mtapi_task_id_t id; /**< pool index of this handle */ +}; + +/** + * Task handle type. + * \memberof mtapi_task_hndl_struct + */ +typedef struct mtapi_task_hndl_struct mtapi_task_hndl_t; + + +/* ---- BASIC CONSTANTS ---------------------------------------------------- */ + +#define MTAPI_TRUE ((mtapi_boolean_t)1) +#define MTAPI_FALSE ((mtapi_boolean_t)0) + +extern mtapi_group_hndl_t MTAPI_GROUP_NONE; + +#define MTAPI_NULL 0 + +#define TEN_SECONDS 10000 +#define MTAPI_INFINITE -1 +#define MTAPI_NOWAIT 0 + +#define MTAPI_NODE_MAX_TASKS_DEFAULT 1024 +#define MTAPI_NODE_MAX_ACTIONS_DEFAULT 1024 +#define MTAPI_NODE_MAX_GROUPS_DEFAULT 128 +#define MTAPI_NODE_MAX_QUEUES_DEFAULT 16 +/** default size for MTAPI queues */ +#define MTAPI_NODE_QUEUE_LIMIT_DEFAULT 1024 +#define MTAPI_NODE_MAX_JOBS_DEFAULT 256 +#define MTAPI_NODE_MAX_ACTIONS_PER_JOB_DEFAULT 4 +#define MTAPI_NODE_MAX_PRIORITIES_DEFAULT 4 + +#define MTAPI_JOB_ID_INVALID 0 +#define MTAPI_DOMAIN_ID_INVALID 0 +#define MTAPI_NODE_ID_INVALID 0 + +#define MTAPI_TASK_ID_NONE 0 +#define MTAPI_GROUP_ID_NONE 0 +#define MTAPI_QUEUE_ID_NONE 0 +#define MTAPI_ACTION_ID_NONE 0 + + +/* ---- RUNTIME INIT & SHUTDOWN -------------------------------------------- */ + +/** + * \defgroup RUNTIME_INIT_SHUTDOWN General + * + * \ingroup C_MTAPI + * + * Initialization, introspection, and finalization functions. + * + * All applications wishing to use MTAPI functionality must use the + * initialization and finalization routines. After initialization, the + * introspection functions can provide important information to MTAPI-based + * applications. + */ + +/** + * This function initializes a node attributes object. + * + * A node attributes object is a container of node attributes. It is an + * optional argument passed to mtapi_initialize() to specify non-default node + * attributes when creating a node. + * + * To set node attributes to non-default values, the application must allocate + * a node attributes object of type mtapi_node_attributes_t and initialize it + * with a call to mtapi_nodeattr_init(). The application may call + * mtapi_nodeattr_set() to specify attribute values. Calls to + * mtapi_nodeattr_init() have no effect on node attributes after the node has + * been created and initialized with mtapi_initialize(). The + * mtapi_node_attributes_t object may safely be deleted by the application + * after the call to mtapi_nodeattr_init(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ---------------------- | --------------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * + * \see mtapi_initialize(), mtapi_nodeattr_set() + * + * \notthreadsafe + * \memberof mtapi_node_attributes_struct + */ +void mtapi_nodeattr_init( + MTAPI_OUT mtapi_node_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets node attribute values in a node attributes object. + * + * A node attributes object is a container of node attributes, optionally + * passed to mtapi_initialize() to specify non-default node attributes when + * creating a node. + * + * \c attributes is a pointer to a node attributes object that was previously + * initialized with a call to mtapi_nodeattr_init(). Calls to + * mtapi_nodeattr_set() have no effect on node attributes after the node has + * been created and initialized with mtapi_initialize(). The node attributes + * object may safely be deleted by the application after the call to + * mtapi_initialize(). + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. + * Additional attributes may be defined by the implementation. + * + * MTAPI-defined node attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
\c MTAPI_NODES_NUMCORES(Read-only) number of processor cores of the node.\c mtapi_uint_t(none)
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * + * \see mtapi_nodeattr_init(), mtapi_initialize() + * + * \notthreadsafe + * \memberof mtapi_node_attributes_struct + */ +void mtapi_nodeattr_set( + MTAPI_INOUT mtapi_node_attributes_t* attributes, + /**< [in, out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Initializes the MTAPI environment on a given MTAPI node in a given MTAPI + * domain. + * + * It must be called on each node using MTAPI. A node maps to a process, + * thread, thread pool, instance of an operating system, hardware accelerator, + * processor core, a cluster of processor cores, or another abstract processing + * entity with an independent program counter. In other words, an MTAPI node + * is an independent thread of control. + * + * Application software running on an MTAPI node must call mtapi_initialize() + * once per node. It is an error to call mtapi_initialize() multiple times + * from a given node, unless mtapi_finalize() is called in between. + * + * The values for \c domain_id and \c node_id must be known a priori by the + * application and MTAPI. + * + * \c mtapi_info is used to obtain information from the MTAPI implementation, + * including MTAPI and the underlying implementation version numbers, + * implementation vendor identification, the number of cores of a node, and + * vendor-specific implementation information. See the header files for + * additional information. + * + * A given MTAPI implementation will specify what is a node, i.e., how the + * concrete system is partitioned into nodes and what are the underlying units + * of execution executing tasks, e.g., threads, a thread pool, processes, or + * hardware units. + * + * \c attributes is a pointer to a node attributes object that was previously + * prepared with mtapi_nodeattr_init() and mtapi_nodeattr_set(). If + * \c attributes is \c MTAPI_NULL, then implementation-defined default + * attributes will be used. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_NODE_INITFAILED | MTAPI environment could not be initialized. + * \c MTAPI_ERR_NODE_INITIALIZED | MTAPI environment was already initialized. + * \c MTAPI_ERR_NODE_INVALID | The \c node_id parameter is not valid. + * \c MTAPI_ERR_DOMAIN_INVALID | The \c domain_id parameter is not valid. + * \c MTAPI_ERR_PARAMETER | Invalid mtapi_node_attributes or mtapi_info. + * + * \see mtapi_nodeattr_init(), mtapi_nodeattr_set() + * + * \notthreadsafe + * \memory Allocates some memory depending on the node attributes. The amount + * allocated is returned in the mtapi_info structure. + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +void mtapi_initialize( + MTAPI_IN mtapi_domain_t domain_id, /**< [in] Domain id */ + MTAPI_IN mtapi_node_t node_id, /**< [in] Node id */ + MTAPI_IN mtapi_node_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_info_t* mtapi_info, /**< [out] Pointer to info struct */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Given a node and attribute number, returns a copy of the corresponding + * attribute value in \c *attribute. + * + * See mtapi_nodeattr_set() for a list of predefined attribute numbers and the + * sizes of the attribute values. The application is responsible for allocating + * sufficient space for the returned attribute value and for setting + * \c attribute_size to the exact size in bytes of the attribute value. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value + * will be written to \c *attribute. On error, \c *status is set to the + * appropriate error defined below and \c *attribute is undefined. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_nodeattr_set() + * + * \waitfree + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +void mtapi_node_get_attribute( + MTAPI_IN mtapi_node_t node, /**< [in] Node handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Finalizes the MTAPI environment on a given MTAPI node and domain. + * + * It has to be called by each node using MTAPI. It is an error to call + * mtapi_finalize() without first calling mtapi_initialize(). An MTAPI node + * can call mtapi_finalize() once for each call to mtapi_initialize(), but it + * is an error to call mtapi_finalize() multiple times from a given node + * unless mtapi_initialize() has been called prior to each mtapi_finalize() + * call. + * + * All tasks that have not completed and that have been started on the node + * where mtapi_finalize() is called will be canceled + * (see mtapi_task_cancel()). mtapi_finalize() blocks until all tasks that + * have been started on the same node return (long-running tasks already + * executing must actively poll the task state and return if canceled). Tasks + * that execute actions on the node where mtapi_finalize() is called, also + * block finalization of the MTAPI runtime system on that node. They are + * canceled as well and return with an \c MTAPI_ERR_NODE_NOTINIT status. Other + * functions that have a dependency to the node and that are called after + * mtapi_finalize() also return \c MTAPI_ERR_NODE_NOTINIT (e.g., + * mtapi_task_get() starting a task associated with an action implemented on + * the already-finalized node). + * + * mtapi_finalize() may not be called from an action function. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_NODE_FINALFAILED | The MTAPI environment couldn't be finalized. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_initialize(), mtapi_task_cancel(), mtapi_task_get() + * + * \notthreadsafe + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +void mtapi_finalize( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the domain id associated with the local node. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \returns Domain id of local node + * \waitfree + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +mtapi_domain_t mtapi_domain_id_get( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the node id associated with the local node and domain. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \returns Node id of local node + * \waitfree + * \ingroup RUNTIME_INIT_SHUTDOWN + */ +mtapi_node_t mtapi_node_id_get( + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- ACTIONS ------------------------------------------------------------ */ + +/** + * \defgroup ACTIONS Actions + * + * \ingroup C_MTAPI + * + * Hardware or software implementations of jobs. + * + * An action is referenced by an opaque handle of type \c mtapi_action_hndl_t, + * or indirectly through a handle to a job of type \c mtapi_job_hndl_t. A job + * refers to all actions implementing the same job, regardless of the node(s) + * where they are implemented. + * + * An action's lifetime begins when the application successfully calls + * mtapi_action_create() and obtains a handle to the action. Its lifetime ends + * upon successful completion of mtapi_action_delete() or mtapi_finalize(). + * + * While an opaque handle to an action may be used in the scope of one node + * only, a job can be used to refer to all its associated actions implementing + * the same job, regardless of the node where they are implemented. Tasks may + * be invoked in this way from nodes that do not share memory or even the same + * ISA with the node where the action resides. + */ + +/** + * This function initializes an action attributes object. + * + * A action attributes object is a container of action attributes, optionally + * passed to mtapi_action_create() to create an action with non-default + * attributes. + * + * The application is responsible for allocating the + * \c mtapi_action_attributes_t + * object and initializing it with a call to mtapi_actionattr_init(). The + * application may then call mtapi_actionattr_set() to specify action + * attribute values. Calls to mtapi_actionattr_init() have no effect on action + * attributes after the action has been created with mtapi_action_create(). The + * \c mtapi_action_attributes_t object may safely be deleted by the application + * after the call to mtapi_action_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set(), mtapi_action_create() + * + * \notthreadsafe + * \memberof mtapi_action_attributes_struct + */ +void mtapi_actionattr_init( + MTAPI_OUT mtapi_action_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets action attribute values in an action attributes object. + * + * An action attributes object is a container of action attributes, optionally + * passed to mtapi_action_create() to create an action with non-default + * attributes. + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. Additional attributes may + * be defined by the implementation. + * + * Calls to mtapi_actionattr_set() have no effect on action attributes after + * the action has been created. The \c mtapi_action_attributes_t object may + * safely be deleted by the application after the call to + * mtapi_action_create(). + * + * MTAPI-defined action attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
MTAPI_ACTION_GLOBALIndicates whether or not this is a globally visible action. Local + * actions are not shared with other nodes.mtapi_boolean_tMTAPI_TRUE
MTAPI_ACTION_AFFINITYCore affinity of action code.mtapi_affinity_tall cores set
MTAPI_DOMAIN_SHAREDIndicates whether or not the action is shareable across + * domains.mtapi_boolean_tMTAPI_TRUE
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_action_create() + * + * \notthreadsafe + * \memberof mtapi_action_attributes_struct + */ +void mtapi_actionattr_set( + MTAPI_INOUT mtapi_action_attributes_t* attributes, + /**< [in,out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function creates a software action (hardware actions are considered to + * be pre-existent and do not need to be created). + * + * It is called on the node where the action function is implemented. An + * action is an abstract encapsulation of everything needed to implement a + * job. An action contains attributes, a reference to a job, a reference to an + * action function, and a reference to node-local data. After an action is + * created, it is referenced by the application using a node-local handle of + * type \c mtapi_action_hndl_t, or indirectly through a node-local job handle + * of type \c mtapi_job_hndl_t. An action's life-cycle begins with + * mtapi_action_create(), and ends when mtapi_action_delete() or + * mtapi_finalize() is called. + * + * To create an action, the application must supply the domain-wide job ID of + * the job associated with the action. Job IDs must be predefined in the + * application and runtime, of type \c mtapi_job_id_t, which is an + * implementation-defined type. The job ID is unique in the sense that it is + * unique for the job implemented by the action. However several actions may + * implement the same job for load balancing purposes. + * + * For non-default behavior, \c *attributes must be prepared with + * mtapi_actionattr_init() and mtapi_actionattr_set() prior to calling + * mtapi_action_create(). If attributes is \c MTAPI_NULL, then default + * attributes will be used. + * + * If \c node_local_data_size is not zero, \c node_local_data specifies the + * start of node local data shared by action functions executed on the same + * node. \c node_local_data_size can be used by the runtime for cache coherency + * operations. + * + * On success, an action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the action already exists, \c status will + * be set to \c MTAPI_ERR_ACTION_EXISTS and the handle returned will not be a + * valid handle. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_JOB_INVALIDThe \c job_id is not a valid job ID, i.e., no action was created for + * that ID or the action has been deleted.
\c MTAPI_ERR_ACTION_EXISTSThis action is already created.
\c MTAPI_ERR_ACTION_LIMITExceeded maximum number of actions allowed.
\c MTAPI_ERR_ACTION_NOAFFINITYThe action was created with an \c MTAPI_ACTION_AFFINITY attribute + * that has set the affinity to all cores of the node to + * \c MTAPI_FALSE.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_ERR_PARAMETERInvalid attributes parameter.
+ * + * \see mtapi_actionattr_init(), mtapi_actionattr_set(), mtapi_action_delete(), + * mtapi_finalize() + * + * \returns Handle to newly created action, invalid handle on error + * \threadsafe + * \ingroup ACTIONS + */ +mtapi_action_hndl_t mtapi_action_create( + MTAPI_IN mtapi_job_id_t job_id, /**< [in] Job id */ + MTAPI_IN mtapi_action_function_t function, + /**< [in] Action function pointer */ + MTAPI_IN void* node_local_data, /**< [in] Data shared across tasks */ + MTAPI_IN mtapi_size_t node_local_data_size, + /**< [in] Size of shared data */ + MTAPI_IN mtapi_action_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function changes the value of the attribute that corresponds to the + * given \c attribute_num for this action. + * + * \c attribute must point to the attribute value, and \c attribute_size must + * be set to the exact size of the attribute value. See mtapi_actionattr_set() + * for a list of predefined attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set() + * + * \notthreadsafe + * \ingroup ACTIONS + */ +void mtapi_action_set_attribute( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the attribute value that corresponds to the given \c attribute_num + * for this action. + * + * \c attribute must point to the location where the attribute value is to be + * returned, and \c attribute_size must be set to the exact size of the + * attribute value. See mtapi_actionattr_set() for a list of predefined + * attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and \c *attribute is undefined. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set() + * + * \waitfree + * \ingroup ACTIONS + */ +void mtapi_action_get_attribute( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function deletes a software action (Hardware actions exist perpetually + * and cannot be deleted). + * + * mtapi_action_delete() may be called by any node that has a valid action + * handle. Tasks associated with an action that has been deleted may still be + * executed depending on their internal state: + * - If mtapi_action_delete() is called on an action that is currently + * executing, the associated task's state will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * action functions must poll the task state with + * mtapi_context_taskstate_get(). A call to mtapi_task_wait() on the task + * executing this code will return the status set by + * mtapi_context_status_set(), or \c MTAPI_SUCCESS if not explicitly set. + * - Tasks that are started or enqueued but waiting for execution by the + * MTAPI runtime when mtapi_action_delete() is called will not be executed + * anymore if the deleted action is the only action associated with that + * task. A call to mtapi_task_wait() will return the status + * \c MTAPI_ERR_ACTION_DELETED. + * - Tasks that are started or enqueued after deletion of the action will + * return \c MTAPI_ERR_ACTION_INVALID if the deleted action is the only + * action associated with that task. + * + * Calling mtapi_action_get_attribute() on a deleted action will return + * \c MTAPI_ERR_ACTION_INVALID if all actions implementing the job had been + * deleted. + * + * The function mtapi_action_delete() blocks until the corresponding action + * code is left by all tasks that are executing the code or until the timeout + * is reached. If \c timeout is a constant 0 or the symbolic constant + * \c MTAPI_NOWAIT, this function only returns \c MTAPI_SUCCESS if no tasks are + * executing the action when it is called. If it is set to \c MTAPI_INFINITE, + * the function may block infinitely. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_TIMEOUT | Timeout was reached. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait() + * + * \threadsafe + * \ingroup ACTIONS + */ +void mtapi_action_delete( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function disables an action. + * + * Tasks associated with an action that has been disabled may still be + * executed depending on their internal state: + * - If mtapi_action_disable() is called on an action that is currently + * executing, the associated task's state will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * action functions must poll the task with mtapi_context_taskstate_get(). + * A call to mtapi_task_wait() on the task executing this code will return + * the status set by mtapi_context_status_set(), or \c MTAPI_SUCCESS if not + * explicitly set. + * - Tasks that are started or enqueued but waiting for execution by the + * MTAPI runtime when mtapi_action_disable() is called will not be executed + * anymore if the disabled action is the only action associated with that + * task. A call to mtapi_task_wait() will return the status + * \c MTAPI_ERR_ACTION_DISABLED. + * - Tasks that are started or enqueued after the action has been disabled + * will return \c MTAPI_ERR_ACTION_DISABLED if either the disabled action is + * the only action associated with a task or all actions associated with a + * task are disabled. + * mtapi_action_disable() blocks until all running tasks exit the code, or + * until the timeout is reached. If timeout is the constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function only returns \c MTAPI_SUCCESS if no + * tasks are executing the action when it is called. If it is set to + * \c MTAPI_INFINITE the function may block infinitely. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_TIMEOUT | Timeout was reached. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait() + * + * \waitfree + * \ingroup ACTIONS + */ +void mtapi_action_disable( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function enables a previously disabled action. + * + * If this function is called on an action that no longer exists, an + * \c MTAPI_ERR_ACTION_INVALID error will be returned. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * --------------------------- | ---------------------------------------------- + * \c MTAPI_ERR_ACTION_INVALID | Argument is not a valid action handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \waitfree + * \ingroup ACTIONS + */ +void mtapi_action_enable( + MTAPI_IN mtapi_action_hndl_t action, /**< [in] Action handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * \defgroup ACTION_FUNCTIONS Action Functions + * + * \ingroup C_MTAPI + * + * Executable software functions that implement actions. + * + * The runtime passes arguments to the action function when a task is started. + * Passing arguments from one node to another node should be implemented as a + * copy operation. Just as the arguments are passed before start of execution, + * the result buffer is copied back to the calling node after the action + * function terminates. In shared memory environments, the copying of data in + * both cases is not necessary. The node-local data is data used by several + * action functions being executed on the same node (or at least in the same + * address space). The shared data is specified when the action is created. + * + * An action function can interact with the runtime environment through a task + * context object of type \c mtapi_task_context_t. A task context object is + * allocated and managed by the runtime. The runtime passes a pointer to the + * context object when the action function is invoked.The action may then + * query information about the execution context(e.g., its core number, the + * number of tasks and task number in a multi - instance task, polling the + * task state) by calling the \c mtapi_context_* functions. Furthermore it is + * possible to pass information from the action function to the runtime system + * which is executing the action function(setting the status manually, for + * example). All of these \c mtapi_context_* functions are called in the + * context of task execution. + */ + +/** + * This function can be called from an action function to set the status that + * can be obtained by a subsequent call to mtapi_task_wait() or + * mtapi_group_wait_any(). + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * The status can be passed from the action function to the runtime system by + * setting error_code to one of the following values: + * - \c MTAPI_SUCCESS for successful completion + * - \c MTAPI_ERR_ACTION_CANCELLED if the action execution is canceled + * - \c MTAPI_ERR_ACTION_FAILED if the task could not be completed as intended + * The error code will be especially important in future versions of MTAPI + * where tasks shall be chained (flow graphs). The chain execution can then be + * aborted if the error code is not \c MTAPI_SUCCESS. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \see mtapi_task_wait(), mtapi_group_wait_any() + * + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +void mtapi_context_status_set( + MTAPI_INOUT mtapi_task_context_t* task_context, + /**< [in,out] Pointer to task context */ + MTAPI_IN mtapi_status_t error_code, /**< [in] Task return value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to notify the runtime + * system. + * + * This is used to communicate certain states to the runtime implementation to + * allow it to optimize task execution. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * The underlying type \c mtapi_notification_t and the valid values for + * notification are implementation-defined. The notification system is meant + * to be flexible, and can be used in many ways, for example: + * - To trigger prefetching of data for further processing + * - To order execution via queues there might be point in the action code + * where the next task in the queue may be started, even if the current + * code, started from the same queue, is still executing + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +void mtapi_context_runtime_notify( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_IN mtapi_notification_t notification, + /**< [in] Notification id */ + MTAPI_IN void* data, /**< [in] Pointer to associated data */ + MTAPI_IN mtapi_size_t data_size, /**< [in] Size of data */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * An action function may call this function to obtain the state of the task + * that is associated with the action function. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * The underlying representation of type \c mtapi_task_state_t is + * implementation-defined. Values of type \c mtapi_task_state_t may be copied, + * assigned, and compared with other values of type \c mtapi_task_state_t, but + * the caller should make no other assumptions about its type or contents. A + * minimal implementation must return a status of \c MTAPI_TASK_CANCELLED if + * the task is canceled, and \c MTAPI_TASK_RUNNING otherwise. Other values of + * the task state are implementation-defined. This task state can be used to + * abort a long running computation inside an action function. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Task state of current context + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_task_state_t mtapi_context_taskstate_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to query the instance + * number of the associated task. + * + * A task can have multiple instances (multi-instance tasks), in which case + * the same job is executed multiple times in parallel. Each instance has a + * number, and this function gives the instance number. Task instances are + * numbered sequentially, starting at zero. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it is invoked. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the task instance + * number is returned. On error, \c *status is set to the appropriate error + * defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Instance number of current task + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_uint_t mtapi_context_instnum_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to query the total + * number of parallel task instances. + * + * This value is greater than one for multi-instance tasks. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Total number of parallel task instances + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_uint_t mtapi_context_numinst_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function can be called from an action function to query the current + * core number for debugging purposes. + * + * The core numbering is implementation-defined. + * + * \c task_context must be the same value as the context parameter that the + * runtime passes to the action function when it was invoked. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_CONTEXT_OUTOFCONTEXTNot called in the context of a task execution. This function must + * be used in an action function only. The action function must be + * called from the MTAPI runtime system.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \returns Worker thread index the current task is running on + * \notthreadsafe + * \ingroup ACTION_FUNCTIONS + */ +mtapi_uint_t mtapi_context_corenum_get( + MTAPI_IN mtapi_task_context_t* task_context, + /**< [in] Pointer to task context */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- CORE AFFINITY MASKS ------------------------------------------------ */ + +/** + * \defgroup CORE_AFFINITY_MASKS Core Affinities + * + * \ingroup C_MTAPI + * + * Affinities for executing action functions on subsets of cores. + * + * To set core affinities, the application must allocate an affinity mask + * object of type \c mtapi_affinity_t and initialize it with a call to + * mtapi_affinity_init(). Affinities are specified by calling + * mtapi_affinity_set(). The application must also allocate and initialize an + * action attributes object of type \c mtapi_action_attributes_t. The affinity + * mask object is then passed to mtapi_actionattr_set() to set the prescribed + * affinities in the action attributes object. The action attributes object is + * then passed to mtapi_action_create() to create a new action with those + * attributes. + * + * It is in the nature of core affinities to be highly hardware dependent. The + * least common denominator for different architectures is enabling and + * disabling core numbers in the affinity mask. Action-to-core affinities can + * be set via the action attribute \c MTAPI_ACTION_AFFINITY during the creation + * of an action. + */ + +/** + * This function initializes an affinity mask object. + * + * The affinity to all cores will be initialized to the value of affinity. + * This function should be called prior to calling mtapi_affinity_set() to + * specify non-default affinity settings. The affinity mask object may then + * be used to set the \c MTAPI_ACTION_AFFINITY attribute when creating an + * action with mtapi_action_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_AFFINITY_MASK | Invalid mask parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_affinity_set(), mtapi_action_create() + * + * \notthreadsafe + * \ingroup CORE_AFFINITY_MASKS + */ +void mtapi_affinity_init( + MTAPI_OUT mtapi_affinity_t* mask, /**< [out] Pointer to affinity mask */ + MTAPI_IN mtapi_boolean_t affinity, /**< [in] Initial affinity */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function is used to change the default values of an affinity mask + * object. + * + * The affinity mask object can then be passed to mtapi_actionattr_set() to + * set the \c MTAPI_ACTION_AFFINITY action attribute. An action function will + * be executed on a core only if the core's affinity is set to \c MTAPI_TRUE. + * Calls to mtapi_affinity_set() have no effect on action attributes after the + * action has been created. + * + * \c mask must be a pointer to an affinity mask object previously initialized + * with mtapi_affinity_init(). + * + * The \c core_num is a hardware- and implementation-specific numeric + * identifier for a single core of the current node. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_AFFINITY_MASK | Invalid mask parameter. + * \c MTAPI_ERR_CORE_NUM | Unknown core number. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_actionattr_set(), mtapi_affinity_init() + * + * \notthreadsafe + * \ingroup CORE_AFFINITY_MASKS + */ +void mtapi_affinity_set( + MTAPI_INOUT mtapi_affinity_t* mask, /**< [in, out] Pointer to affinity + mask */ + MTAPI_IN mtapi_uint_t core_num, /**< [in] Core number */ + MTAPI_IN mtapi_boolean_t affinity, /**< [in] Affinity to given core */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the affinity that corresponds to the given \c core_num for this + * affinity mask. + * + * \c mask is a pointer to an affinity mask object previously initialized with + * mtapi_affinity_init(). + * + * Note that affinities may be queried but may not be changed for an action + * after it has been created. If affinities need to be modified at runtime, + * new actions must be created. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_AFFINITY_MASK | Invalid mask parameter. + * \c MTAPI_ERR_CORE_NUM | Unknown core number. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_affinity_init() + * + * \returns \c MTAPI_TRUE if affinity to \c core_num is set, \c MTAPI_FALSE + * otherwise + * \waitfree + * \ingroup CORE_AFFINITY_MASKS + */ +mtapi_boolean_t mtapi_affinity_get( + MTAPI_OUT mtapi_affinity_t* mask, /**< [out] Pointer to affinity mask */ + MTAPI_IN mtapi_uint_t core_num, /**< [in] Core number */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- QUEUES ------------------------------------------------------------- */ + +/** + * \defgroup QUEUES Queues + * + * \ingroup C_MTAPI + * + * Queues for controlling the scheduling policy of tasks. + * + * The default scheduling policy for queues is ordered task execution. Tasks + * that have to be executed sequentially are enqueued into the same queue. In + * this case every queue is associated with exactly one action. Tasks started + * via different queues can be executed in parallel. This is needed for packet + * processing applications, for example: each stream is processed by one + * queue. This ensures sequential processing of packets belonging to the same + * stream. Different streams are processed in parallel. + * + * Queues were made explicit in MTAPI. This allows mapping of queues onto + * hardware queues, if available. One MTAPI queue is associated with one + * action, or for purposes of load balancing, with actions implementing the + * same job on different nodes. + */ + +/** + * This function initializes a queue attributes object. + * + * A queue attributes object is a container of queue attributes, optionally + * passed to mtapi_queue_create() to create a queue with non-default + * attributes. + * + * The application is responsible for allocating the + * \c mtapi_queue_attributes_t + * object and initializing it with a call to mtapi_queueattr_init(). The + * application may then call mtapi_queueattr_set() to specify queue attribute + * values. Calls to mtapi_queueattr_init() have no effect on queue attributes + * after the queue has been created. To change an attribute of an existing + * queue, see mtapi_queue_set_attribute(). The \c mtapi_queue_attributes_t + * object may safely be deleted by the application after the call to + * mtapi_queue_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queue_create(), mtapi_queueattr_set(), + * mtapi_queue_set_attribute() + * + * \notthreadsafe + * \memberof mtapi_queue_attributes_struct + */ +void mtapi_queueattr_init( + MTAPI_OUT mtapi_queue_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets queue attribute values in a queue attributes object. + * + * A queue attributes object is a container of queue attributes, optionally + * passed to mtapi_queue_create() to create a queue with non-default + * attributes. + * + * \c attributes must be a pointer to a queue attributes object previously + * initialized by mtapi_queueattr_init(). + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. Additional attributes may + * be defined by the implementation. + * + * Calls to mtapi_queueattr_set() have no effect on queue attributes once the + * queue has been created. The \c mtapi_queue_attributes_t object may safely be + * deleted by the application after the call to mtapi_queue_create(). + * + * MTAPI-defined queue attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
\c MTAPI_QUEUE_GLOBALIndicates if this is a globally visible queue. Only global queues + * are shared with other nodes.\c mtapi_boolean_t\c MTAPI_TRUE
\c MTAPI_QUEUE_PRIORITYPriority of the queue.\c mtapi_uint_t0(default priority)
MTAPI_QUEUE_LIMITMax. number of elements in the queue; the queue blocks on queuing + * more items.\c mtapi_uint_t0(0 stands for 'unlimited')
\c MTAPI_QUEUE_ORDEREDSpecify if the queue is order-preserving.\c mtapi_boolean_t\c MTAPI_TRUE
\c MTAPI_QUEUE_RETAINAllow enqueuing of jobs when queue is disabled.\c mtapi_boolean_t\c MTAPI_FALSE
\c MTAPI_DOMAIN_SHAREDIndicates if the queue is shareable across domains.\c mtapi_boolean_t\c MTAPI_TRUE
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queue_create(), mtapi_queueattr_init() + * + * \notthreadsafe + * \memberof mtapi_queue_attributes_struct + */ +void mtapi_queueattr_set( + MTAPI_INOUT mtapi_queue_attributes_t* attributes, + /**< [in,out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function creates a software queue object and associates it with the + * specified job. + * + * A job is associated with one or more actions that provide the executable + * implementation of the job. Hardware queues are considered to be pre-existent + * and do not need to be created. + * + * \c queue_id is an identifier of implementation-defined type that must be + * supplied by the application. If \c queue_id is set to + * \c MTAPI_QUEUE_ID_NONE, the queue will be accessible only on the node on + * which it was created by using the returned queue handle. Otherwise the + * application may supply a \c queue_id by which the queue can be referenced + * domain-wide using mtapi_queue_get() to convert the id into a handle. The + * minimum and maximum values for \c queue_id may be derived from + * \c MTAPI_MIN_USER_QUEUE_ID and \c MTAPI_MAX_USER_QUEUE_ID. + * + * job is a handle to a job obtained by a previous call to mtapi_job_get(). If + * \c attributes is \c MTAPI_NULL, the queue will be created with default + * attribute values. Otherwise \c attributes must point to a queue attributes + * object previously prepared using mtapi_queueattr_init() and + * mtapi_queueattr_set(). + * + * There is an implementation-defined maximum number of queues permitted. + * + * If more than one action is associated with the job, the runtime system + * chooses dynamically which action is used for execution (for load balancing + * purposes). + * + * On success, a queue handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. In the case where the queue already exists, \c *status will + * be set to \c MTAPI_QUEUE_EXISTS and the handle returned will not be a + * valid handle. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | The \c queue_id is not a valid queue id. + * \c MTAPI_ERR_QUEUE_EXISTS | This queue is already created. + * \c MTAPI_ERR_QUEUE_LIMIT | Exceeded maximum number of queues allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_JOB_INVALID | The associated job is not valid. + * + * \see mtapi_queue_get(), mtapi_job_get(), mtapi_queueattr_init(), + * mtapi_queueattr_set() + * + * \returns Handle to newly created queue, invalid handle on error + * \threadsafe + * \ingroup QUEUES + */ +mtapi_queue_hndl_t mtapi_queue_create( + MTAPI_IN mtapi_queue_id_t queue_id, /**< [in] Queue id */ + MTAPI_IN mtapi_job_hndl_t job, /**< [in] Job handle */ + MTAPI_IN mtapi_queue_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Changes the attribute value that corresponds to the given \c attribute_num + * for the specified queue. + * + * See mtapi_queueattr_set() for a list of predefined attribute numbers and + * the sizes of the attribute values. The application must set + * \c attribute_size to the exact size in bytes of the attribute value. + * Additional attributes may be defined by the implementation. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below and the attribute value is + * undefined. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queueattr_set() + * + * \notthreadsafe + * \ingroup QUEUES + */ +void mtapi_queue_set_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the attribute value that corresponds to the given \c attribute_num + * for the specified queue. + * + * \c attribute must point to a location in memory sufficiently large to hold + * the returned attribute value. See mtapi_queueattr_set() for a list of + * predefined attribute numbers and the sizes of the attribute values. The + * application must set \c attribute_size to the exact size in bytes of the + * attribute value. Additional attributes may be defined by the implementation. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and the \c *attribute value is undefined. If this + * function is called on a queue that no longer exists, an + * \c MTAPI_ERR_QUEUE_INVALID error will be returned. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_queueattr_set() + * + * \waitfree + * \ingroup QUEUES + */ +void mtapi_queue_get_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function converts a domain-wide \c queue_id into a node-local queue + * handle. + * + * \c queue_id must match the \c queue_id that was associated with a software + * queue that was created with mtapi_queue_create(), or it must be a valid + * predefined queue identifier known a priori to the runtime and application + * (e.g., to reference a hardware queue. The minimum and maximum values for + * \c queue_id may be derived from \c MTAPI_MIN_USER_QUEUE_ID and + * \c MTAPI_MAX_USER_QUEUE_ID. + * + * On success, the queue handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. If this function is called on a queue that no longer exists, + * an \c MTAPI_ERR_QUEUE_INVALID error will be returned. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_QUEUE_INVALIDThe \c queue_id parameter does not refer to a valid queue or it is set + * to \c MTAPI_QUEUE_ID_ANY.
\c MTAPI_ERR_NODE_NOTINITThe node/domain is not initialized.
\c MTAPI_ERR_DOMAIN_NOTSHAREDThis resource cannot be shared by this domain.
+ * + * \see mtapi_queue_create() + * + * \returns Handle to preexisting queue with given \c queue_id, + * invalid handle on error + * \threadsafe + * \ingroup QUEUES + */ +mtapi_queue_hndl_t mtapi_queue_get( + MTAPI_IN mtapi_queue_id_t queue_id, /**< [in] Queue id */ + MTAPI_IN mtapi_domain_t domain_id, /**< [in] Domain id */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function deletes the specified software queue. + * + * Hardware queues are perpetual and cannot be deleted. + * + * \c queue must be a valid handle to an existing queue. + * + * \c timeout determines how long the function should wait for tasks already + * started via that queue to finish. The underlying type of \c mtapi_timeout_t + * is implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function deletes the queue and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * This function can be called from any node that has a valid queue handle. + * Tasks previously enqueued in a queue that has been deleted may still be + * executed depending on their internal state: + * - If mtapi_queue_delete() is called on a queue that is currently executing + * an action, the task state of the corresponding task will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * the action function must poll the task state with + * mtapi_context_taskstate_get(). A call to mtapi_task_wait() on the task + * executing this code will return the status set by + * mtapi_context_status_set(), or \c MTAPI_SUCCESS if not explicitly set. + * - Tasks that are enqueued and waiting for execution by the MTAPI runtime + * environment when mtapi_queue_delete() is called will not be executed any + * more. A call to mtapi_task_wait() will return the status + * \c MTAPI_ERR_QUEUE_DELETED. + * - Tasks that are enqueued after deletion of the queue will return a status + * of \c MTAPI_ERR_QUEUE_INVALID. + * + * If this function is called on a queue that no longer exists, an + * \c MTAPI_ERR_QUEUE_INVALID status will be returned. A call to + * mtapi_queue_get() on a deleted queue will return \c MTAPI_ERR_QUEUE_INVALID + * as well, as long as no new queue has been created for the same queue ID. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_TIMEOUT | Timeout was reached. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait(), mtapi_queue_get() + * + * \threadsafe + * \ingroup QUEUES + */ +void mtapi_queue_delete( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function disables the specified queue in such a way that it can be + * resumed later. + * + * This is needed to perform certain maintenance tasks. It can be called by + * any node that has a valid queue handle. + * + * \c timeout determines how long the function should wait for tasks already + * started via that queue to finish. The underlying type of \c mtapi_timeout_t + * is implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function deletes the queue and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * Tasks previously enqueued in a queue that has been disabled may still be + * executed depending on their internal state: + * - If mtapi_queue_disable() is called on a queue that is currently executing + * an action, the task state of the corresponding task will be set to + * \c MTAPI_TASK_CANCELLED and execution will continue. To accomplish this, + * the action function must poll the task state by calling + * mtapi_context_taskstate_get(). A call to mtapi_task_wait() on the task + * executing this code will return the status set by + * mtapi_context_status_set(), or \c MTAPI_SUCCESS if not explicitly set. + * - Tasks that are enqueued and waiting for execution by the MTAPI runtime + * environment when mtapi_queue_disable() is called will not be executed + * anymore. They will be held in anticipation the queue is enabled again + * if the \c MTAPI_QUEUE_RETAIN attribute is set to \c MTAPI_TRUE. A call to + * mtapi_task_wait() will return the status \c MTAPI_ERR_QUEUE_DISABLED. + * - Tasks that are enqueued after the queue had been disabled will return + * \c MTAPI_ERR_QUEUE_DISABLED if the \c MTAPI_QUEUE_RETAIN attribute is set + * to \c MTAPI_FALSE. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_TIMEOUT | Timeout was reached. + * + * \see mtapi_context_taskstate_get(), mtapi_context_status_set(), + * mtapi_task_wait() + * + * \threadsafe + * \ingroup QUEUES + */ +void mtapi_queue_disable( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function may be called from any node with a valid queue handle to + * re-enable a queue previously disabled with mtapi_queue_disable(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \threadsafe + * \ingroup QUEUES + */ +void mtapi_queue_enable( + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- JOBS --------------------------------------------------------------- */ + +/** + * \defgroup JOBS Jobs + * + * \ingroup C_MTAPI + * + * Jobs implementing one or more actions. + * + * An action is a hardware or software implementation of a job. In some cases, + * an action is referenced by an action handle, while in other cases, an action + * is referenced indirectly through a job handle. Each job is represented by a + * domain-wide job ID, or by a job handle which is local to one node. + * + * Several actions can implement the same job based on different hardware + * resources (for instance a job can be implemented by one action on a DSP and + * by another action on a general purpose core, or a job can be implemented by + * both hardware and software actions). + */ + +/** + * Given a \c job_id, this function returns the MTAPI handle for referencing + * the actions implementing the job. + * + * This function converts a domain-wide job ID into a node-local job handle. + * + * On success, the action handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * ----------------------------- | -------------------------------------------- + * \c MTAPI_ERR_JOB_INVALID | The job ID does not refer to a valid action. + * \c MTAPI_ERR_DOMAIN_NOTSHARED | The resource can't be shared by this domain. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \returns Handle to job with given \c job_id, invalid handle on error + * \threadsafe + * \ingroup JOBS + */ +mtapi_job_hndl_t mtapi_job_get( + MTAPI_IN mtapi_job_id_t job_id, /**< [in] Job id */ + MTAPI_IN mtapi_domain_t domain_id, /**< [in] Domain id */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- TASKS -------------------------------------------------------------- */ + +/** + * \defgroup TASKS Tasks + * + * \ingroup C_MTAPI + * + * Tasks representing pieces of work "in flight" (similar to a thread handles). + * + * A task is associated with a job object, which is associated with one or + * more actions implementing the same job for load balancing purposes. + * A task may optionally be associated with a task group. A task has + * attributes, and an internal state. A task begins its lifetime with a call + * to mtapi_task_start() or mtapi_task_enqueue(). A + * task is referenced by a handle of type \c mtapi_task_hndl_t. The underlying + * type of \c mtapi_task_hndl_t is implementation defined. Task handles may be + * copied, assigned, and passed as arguments, but otherwise the application + * should make no assumptions about the internal representation of a task + * handle. + * + * Once a task is started, it is possible to wait for task completion from + * other parts of the program. + */ + +/** + * This function initializes a task attributes object. + * + * A task attributes object is a container of task attributes. It is an + * optional argument passed to mtapi_task_start() or mtapi_task_enqueue() to + * specify non-default task attributes when starting a task. + * + * To set task attributes to non-default values, the application must allocate + * a task attributes object of type \c mtapi_task_attributes_t and initialize + * it with a call to mtapi_taskattr_init(). The application may call + * mtapi_taskattr_set() to specify attribute values. Calls to + * mtapi_taskattr_init() have no effect on task attributes after the task has + * started. The \c mtapi_task_attributes_t object may safely be deleted by the + * application after the task has started. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_taskattr_set() + * + * \notthreadsafe + * \memberof mtapi_task_attributes_struct + */ +void mtapi_taskattr_init( + MTAPI_OUT mtapi_task_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets task attribute values in a task attributes object. + * + * A task attributes object is a container of task attributes, optionally + * passed to mtapi_task_start() or mtapi_task_enqueue() to specify non-default + * task attributes when starting a task. + * + * attributes is a pointer to a task attributes object that was previously + * initialized with a call to mtapi_taskattr_init(). Calls to + * mtapi_taskattr_set() have no effect on task attributes after the task has + * been created. The task attributes object may safely be deleted by the + * application after the task has started. + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set attribute_size to + * the exact size in bytes of the attribute value. Additional attributes may + * be defined by the implementation. + * + * MTAPI-defined task attributes: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Attribute numDescriptionData TypeDefault
\c MTAPI_TASK_DETACHEDIndicates if this is a detached task. A detached task is deleted by + * MTAPI runtime after execution. The task handle of detached tasks + * must not be used, i.e., it is not possible to wait for completion + * of dedicated detached tasks. But it is possible to add detached + * tasks to a group and wait for completion of the group.\c mtapi_boolean_t\c MTAPI_FALSE
\c MTAPI_TASK_INSTANCESIndicates how many parallel instances of task shall be started by + * MTAPI. The default case is that each task is executed exactly once. + * Setting this value to n, the corresponding action code will be + * executed n times, in parallel, if the underlying hardware allows it. + * (see chapter 4.1.7 Multi-Instance Tasks, page 107)\c mtapi_uint_t1
+ * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_taskattr_init() + * + * \notthreadsafe + * \memberof mtapi_task_attributes_struct + */ +void mtapi_taskattr_set( + MTAPI_INOUT mtapi_task_attributes_t* attributes, + /**< [in, out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function schedules a task for execution. + * + * A task is associated with a job. A job is associated with one or more + * actions. An action provides an action function, which is the executable + * implementation of a job. If more than one action is associated with the job, + * the runtime system chooses dynamically which action is used for execution + * for load balancing purposes. + * + * \c task_id is an optional ID provided by the application for debugging + * purposes. If not needed, it can be set to \c MTAPI_TASK_ID_NONE. The minimum + * and maximum values for \c task_id may be derived from + * \c MTAPI_MIN_USER_TASK_ID and \c MTAPI_MAX_USER_TASK_ID. + * + * \c job must be a handle to a job obtained by a previous call to + * mtapi_job_get(). + * + * If \c arguments_size is not zero, then arguments must point to data of + * \c arguments_size bytes. The arguments will be transferred by the runtime + * from the node where the action was created to the executing node if + * necessary. Marshalling of arguments is not part of the MTAPI specification + * and is implementation-defined. + * + * If \c attributes is \c MTAPI_NULL, the task will be started with default + * attribute values. Otherwise \c attributes must point to a task attributes + * object previously prepared using mtapi_taskattr_init() and + * mtapi_taskattr_set(). The attributes of a task cannot be changed after the + * task is created. + * + * \c group must be set to \c MTAPI_GROUP_NONE if the task is not part of a + * task group. Otherwise \c group must be a group handle obtained by a previous + * call to mtapi_group_create(). + * + * On success, a task handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_TASK_LIMIT | Exceeded maximum number of tasks allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_JOB_INVALID | The associated job is not valid. + * + * \see mtapi_job_get(), mtapi_taskattr_init(), mtapi_taskattr_set(), + * mtapi_group_create() + * + * \returns Handle to newly started task, invalid handle on error + * \threadsafe + * \ingroup TASKS + */ +mtapi_task_hndl_t mtapi_task_start( + MTAPI_IN mtapi_task_id_t task_id, /**< [in] Task id */ + MTAPI_IN mtapi_job_hndl_t job, /**< [in] Job handle */ + MTAPI_IN void* arguments, /**< [in] Pointer to arguments */ + MTAPI_IN mtapi_size_t arguments_size,/**< [in] Size of arguments */ + MTAPI_OUT void* result_buffer, /**< [out] Pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /**< [in] Size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle, may be + \c MTAPI_GROUP_NONE */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function schedules a task for execution using a queue. + * + * A queue is a task associated with a job. A job is associated with one or + * more actions. An action provides an action function, which is the executable + * implementation of a job. + * + * \c task_id is an optional ID provided by the application for debugging + * purposes. If not needed, it can be set to \c MTAPI_TASK_ID_NONE. The + * underlying type of \c mtapi_task_id_t is implementation-defined. The + * minimum and maximum values for \c task_id may be derived from + * \c MTAPI_MIN_USER_TASK_ID and \c MTAPI_MAX_USER_TASK_ID. + * + * \c queue must be a handle to a queue obtained by a previous call to + * mtapi_queue_create(). + * + * If \c arguments_size is not zero, then arguments must point to data of + * \c arguments_size bytes. The arguments will be transferred by the runtime + * from the node where the action was created to the executing node. + * Marshalling of arguments is not part of the MTAPI specification and is + * implementation-defined. + * + * If \c attributes is \c MTAPI_NULL, the task will be started with default + * attribute values. Otherwise \c attributes must point to a task attributes + * object previously prepared using mtapi_taskattr_init() and + * mtapi_taskattr_set(). Once a task has been enqueued, its attributes may not + * be changed. + * + * \c group must be set to \c MTAPI_GROUP_NONE if the task is not part of a + * task group. Otherwise \c group must be a group handle obtained by a + * previous call to mtapi_group_create(). + * + * On success, a task handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_TASK_LIMIT | Exceeded maximum number of tasks allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_QUEUE_INVALID | Argument is not a valid queue handle. + * + * \see mtapi_queue_create(), mtapi_taskattr_init(), mtapi_taskattr_set(), + * mtapi_group_create() + * + * \returns Handle to newly enqueued task, invalid handle on error + * \threadsafe + * \ingroup TASKS + */ +mtapi_task_hndl_t mtapi_task_enqueue( + MTAPI_IN mtapi_task_id_t task_id, /**< [in] Task id */ + MTAPI_IN mtapi_queue_hndl_t queue, /**< [in] Queue handle */ + MTAPI_IN void* arguments, /**< [in] Pointer to arguments */ + MTAPI_IN mtapi_size_t arguments_size,/**< [in] Size of arguments */ + MTAPI_OUT void* result_buffer, /**< [out] Pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /**< [in] Size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + /**< [in] Pointer to task attributes */ + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle, may be + \c MTAPI_GROUP_NONE */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns a copy of the attribute value that corresponds to the given + * \c attribute_num for the specified task. + * + * The attribute value will be returned in \c *attribute. Note that task + * attributes may be queried but may not be changed after a task has been + * created. + * + * \c task must be a valid handle to a task that was obtained by a previous + * call to mtapi_task_start() or mtapi_task_enqueue(). + * + * See mtapi_taskattr_set() for a list of predefined attribute numbers + * and the sizes of the attribute values. The application is responsible for + * allocating sufficient space for the returned attribute value and for + * setting \c attribute_size to the exact size in bytes of the attribute value. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and the attribute value is undefined. If this function + * is called on a task that no longer exists, an \c MTAPI_ERR_TASK_INVALID + * error code will be returned. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_TASK_INVALID | Argument is not a valid task handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_taskattr_set() + * + * \waitfree + * \ingroup TASKS + */ +void mtapi_task_get_attribute( + MTAPI_IN mtapi_task_hndl_t task, /**< [in] Task handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function cancels a task and sets the task status to + * \c MTAPI_TASK_CANCELLED. + * + * \c task must be a valid handle to a task that was obtained by a previous + * call to mtapi_task_start() or mtapi_task_enqueue(). + * + * If the execution of a task has not been started, the runtime system might + * remove the task from the runtime-internal task queues. If task execution is + * already running, an action function implemented in software can poll the + * task status and react accordingly. + * + * Since the task is referenced by a task handle which can only be used + * node-locally, a task can be canceled only on the node where the task was + * created. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_TASK_INVALID | Argument is not a valid task handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_task_start(), mtapi_task_enqueue() + * + * \waitfree + * \ingroup TASKS + */ +void mtapi_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, /**< [in] Task handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function waits for the completion of the specified task. + * + * \c task must be a valid handle to a task that was obtained by a previous + * call to mtapi_task_start() or mtapi_task_enqueue(). The task handle becomes + * invalid on a successful wait, i.e., after the task had run to completion + * and mtapi_task_wait() returns \c MTAPI_SUCCESS. + * + * \c timeout determines how long the function should wait for tasks already + * started via that queue to finish. The underlying type of \c mtapi_timeout_t + * is implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function does not block and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation-defined. + * + * Results of completed tasks can be obtained via \c result_buffer associated + * with the task. The size of the buffer has to be equal to the result size + * written in the action code. If the result is not needed by the calling + * code, \c result_buffer may be set to \c MTAPI_NULL. For multi-instance + * tasks, the result buffer is filled by an array of all the task instances' + * results. I.e., the result buffer has to be allocated big enough (number of + * instances times size of result). + * + * Calling mtapi_task_wait() more than once for the same task results in + * undefined behavior. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. If this function is called on + * a task that no longer exists, an \c MTAPI_ERR_TASK_INVALID error code will + * be returned. \c status will be \c MTAPI_ERR_ARG_SIZE or + * \c MTAPI_ERR_RESULT_SIZE if the sizes of arguments or result buffer do + * not match. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_ERR_TASK_INVALIDArgument is not a valid task handle.
\c MTAPI_TIMEOUTTimeout was reached.
\c MTAPI_ERR_PARAMETERInvalid timeout parameter.
\c MTAPI_ERR_TASK_CANCELLEDThe task has been canceled because of mtapi_task_cancel() was + * called before the task was executed or the error code was set to + * \c MTAPI_ERR_TASK_CANCELLED by mtapi_context_status_set() in the + * action function.
\c MTAPI_ERR_WAIT_PENDINGmtapi_task_wait() had already been called for the same task and the + * first wait call is still pending.
\c MTAPI_ERR_ACTION_CANCELLEDAction execution was canceled by the action function + * (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_FAILEDError set by action function (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_DELETEDAll actions associated with the task have been deleted before the + * execution of the task was started or the error code has been set in + * the action function to \c MTAPI_ERR_ACTION_DELETED by + * mtapi_context_status_set().
\c MTAPI_ERR_ARG_SIZEThe size of the arguments expected by action differs from arguments + * size of the caller.
\c MTAPI_ERR_RESULT_SIZEThe size of the result buffer expected by action differs from + * result buffer size of the caller.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
+ * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_task_wait(), + * mtapi_task_cancel(), mtapi_context_status_set() + * + * \threadsafe + * \ingroup TASKS + */ +void mtapi_task_wait( + MTAPI_IN mtapi_task_hndl_t task, /**< [in] Task handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/* ---- TASK GROUPS -------------------------------------------------------- */ + +/** + * \defgroup TASK_GROUPS Task Groups + * + * \ingroup C_MTAPI + * + * Facilities for synchronizing on groups of tasks. + * + * This concept is similar to barrier synchronization of threads. MTAPI + * specifies a minimal task group feature set in order to allow small and + * efficient implementations. + */ + +/** + * This function initializes a group attributes object. + * + * A group attributes object is a container of group attributes. It is an + * optional argument passed to mtapi_group_create() to specify non-default + * group attributes when creating a task group. + * + * To set group attributes to non-default values, the application must + * allocate a group attributes object of type \c mtapi_group_attributes_t and + * initialize it with a call to mtapi_groupattr_init(). The application may + * call mtapi_groupattr_set() to specify attribute values. Calls to + * mtapi_groupattr_init() have no effect on group attributes after the group + * has been created. The \c mtapi_group_attributes_t object may safely be + * deleted by the application after the call to mtapi_group_create(). + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_group_create(), mtapi_groupattr_set() + * + * \notthreadsafe + * \memberof mtapi_group_attributes_struct + */ +void mtapi_groupattr_init( + MTAPI_OUT mtapi_group_attributes_t* attributes, + /**< [out] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function sets group attribute values in a group attributes object. + * + * A group attributes object is a container of group attributes, optionally + * passed to mtapi_group_create() to specify non-default group attributes when + * creating a task group. + * + * \c attributes is a pointer to a group attributes object that was previously + * initialized with a call to mtapi_groupattr_init(). Calls to + * mtapi_groupattr_set() have no effect on group attributes after the group + * has been created. The group attributes object may safely be deleted by the + * application after the call to mtapi_group_create(). + * + * See the table below for a list of predefined attribute numbers and the + * sizes of the attribute values. The application must set \c attribute_size to + * the exact size in bytes of the attribute value. + * Additional attributes may be defined by the implementation. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_ATTR_READONLY | Attribute cannot be modified. + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_group_create(), mtapi_groupattr_init() + * + * \notthreadsafe + * \memberof mtapi_group_attributes_struct + */ +void mtapi_groupattr_set( + MTAPI_INOUT mtapi_group_attributes_t* attributes, + /**< [in,out] Pointer to attributes */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_IN void* attribute, /**< [in] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function creates a task group and returns a handle to the group. + * + * After a group is created, a task may be associated with a group when the + * task is started with mtapi_task_start() or mtapi_task_enqueue(). + * + * \c group_id is an optional ID provided by the application for debugging + * purposes. If not needed, it can be set to \c MTAPI_GROUP_ID_NONE. The + * underlying type of \c mtapi_group_id_t is implementation-defined. The + * minimum and maximum values for \c group_id may be derived from + * \c MTAPI_MIN_USER_GROUP_ID and \c MTAPI_MAX_USER_GROUP_ID. + * + * If \c attributes is \c MTAPI_NULL, the group will be created with default + * attribute values. Otherwise \c attributes must point to a group attributes + * object previously prepared using mtapi_groupattr_init() and + * mtapi_groupattr_set(). + * + * On success, a group handle is returned and \c *status is set to + * \c MTAPI_SUCCESS. On error, \c *status is set to the appropriate error + * defined below. + * Error code | Description + * ------------------------- | ------------------------------------------------ + * \c MTAPI_ERR_GROUP_LIMIT | Exceeded maximum number of groups allowed. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * \c MTAPI_ERR_PARAMETER | Invalid attributes parameter. + * + * \see mtapi_task_start(), mtapi_task_enqueue(), mtapi_groupattr_init(), + * mtapi_groupattr_set() + * + * \returns Handle to newly created group, invalid handle on error + * \threadsafe + * \memory This function allocates a new queue for tracking completion of the + * tasks belonging to the group. + * \ingroup TASK_GROUPS + */ +mtapi_group_hndl_t mtapi_group_create( + MTAPI_IN mtapi_group_id_t group_id, /**< [in] Group id */ + MTAPI_IN mtapi_group_attributes_t* attributes, + /**< [in] Pointer to attributes */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Changes the value of the attribute that corresponds to the given + * \c attribute_num for the specified task group. + * + * \c attribute must point to the attribute value, and \c attribute_size must + * be set to the exact size of the attribute value. See mtapi_groupattr_set() + * for a list of predefined attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_groupattr_set() + * + * \notthreadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_set_attribute( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size, + /**< [in] Size of attribute value. may + be 0, attribute is interpreted as + value in that case */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * Returns the attribute value that corresponds to the given \c attribute_num + * for this task group. + * + * \c attribute must point to the location where the attribute value is to be + * returned, and \c attribute_size must be set to the exact size of the + * attribute value. See mtapi_groupattr_set() for a list of predefined + * attribute numbers and the sizes of their values. + * + * On success, \c *status is set to \c MTAPI_SUCCESS and the attribute value is + * returned in \c *attribute. On error, \c *status is set to the appropriate + * error defined below and \c *attribute is undefined. If this function is + * called on a group that no longer exists, an \c MTAPI_ERR_GROUP_INVALID + * error code will be returned. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_PARAMETER | Invalid attribute parameter. + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_ATTR_NUM | Unknown attribute number. + * \c MTAPI_ERR_ATTR_SIZE | Incorrect attribute size. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \see mtapi_groupattr_set() + * + * \waitfree + * \ingroup TASK_GROUPS + */ +void mtapi_group_get_attribute( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_IN mtapi_uint_t attribute_num, /**< [in] Attribute id */ + MTAPI_OUT void* attribute, /**< [out] Pointer to attribute value */ + MTAPI_IN mtapi_size_t attribute_size,/**< [in] Size of attribute value */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function waits for the completion of a task group. + * + * Tasks may be associated with groups when the tasks are started. Each task + * is associated with one or more actions. This function returns when all the + * associated action functions have completed or canceled. The group handle + * becomes invalid if this function returns \c MTAPI_SUCCESS. + * + * \c timeout determines how long the function should wait for tasks already + * started in the group to finish. The underlying type of \c mtapi_timeout_t is + * implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function does not block and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * To obtain results from a task, the application should call + * mtapi_group_wait_any() instead. + * + * During execution, an action function may optionally call + * mtapi_context_status_set() to set a task status that will be returned in + * this function in \c *status. If multiple action functions set different task + * status values, it is implementation-defined which of those is returned in + * mtapi_group_wait_all(). The following task status values may be set by an + * action function: \c MTAPI_ERR_TASK_CANCELLED, \c MTAPI_ERR_ACTION_CANCELLED, + * \c MTAPI_ERR_ACTION_FAILED, and \c MTAPI_ERR_ACTION_DELETED. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_TIMEOUTTimeout was reached.
\c MTAPI_ERR_GROUP_INVALIDArgument is not a valid task handle.
\c MTAPI_ERR_WAIT_PENDINGmtapi_group_wait_all() had already been called for the same group + * and the first wait call is still pending.
\c MTAPI_ERR_PARAMETERInvalid timeout parameter.
\c MTAPI_ERR_ARG_SIZEThe size of the arguments expected by action differs from arguments + * size of the caller.
\c MTAPI_ERR_RESULT_SIZEThe size of the result buffer expected by action differs from + * result buffer size of the caller.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_GROUP_COMPLETEDGroup completed, i.e., there are no more task to wait for in + * the group.
\c MTAPI_ERR_TASK_CANCELLEDAt least one task has been canceled because of mtapi_task_cancel() + * was called before the task was executed or the error code was set + * to \c MTAPI_ERR_TASK_CANCELLED by mtapi_context_status_set() in the + * action function.
\c MTAPI_ERR_ACTION_CANCELLEDThe action execution of at least one task was canceled by the + * action function (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_FAILEDError set by at least one action function + * (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_DELETEDAll actions associated with the task have been deleted before the + * execution of the task was started or the error code has been set in + * the action function to \c MTAPI_ERR_ACTION_DELETED by + * mtapi_context_status_set().
+ * + * \see mtapi_group_wait_any(), mtapi_context_status_set(), mtapi_task_cancel() + * + * \threadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_wait_all( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function waits for the completion of any task in a task group. + * + * Tasks may be associated with groups when the tasks are started. Each task + * is associated with one or more actions. This function returns when any of + * the associated action functions have completed or have been canceled. + * + * The group handle does not become invalid if this function returns + * \c MTAPI_SUCCESS. The group handle becomes invalid if this function returns + * \c MTAPI_GROUP_COMPLETED. + * + * \c group must be a valid group handle obtained by a previous call to + * mtapi_group_create(). + * + * Action functions may pass results that will be available in \c *result after + * mtapi_group_wait_any() returns. If the results are not needed, \c result may + * be set to \c MTAPI_NULL. Otherwise, \c result must point to an area in + * memory of sufficient size to hold the array of results from the completed + * task(s). The size of the result buffer is given in the argument + * \c result_buffer_size that the runtime passes to an action function upon + * invocation. + * + * \c timeout determines how long the function should wait for a task in the + * group to finish. The underlying type of \c mtapi_timeout_t is + * implementation-defined. If \c timeout is a constant 0 or the symbolic + * constant \c MTAPI_NOWAIT, this function does not block and returns + * immediately. If \c timeout is set to \c MTAPI_INFINITE the function may + * block infinitely. Other values for \c timeout and the units of measure are + * implementation defined. + * + * During execution, an action function may optionally call + * mtapi_context_status_set() to set a task status that will be returned in + * this function in \c *status. The following task status values may be set by + * an action function: \c MTAPI_ERR_TASK_CANCELLED, + * \c MTAPI_ERR_ACTION_CANCELLED, \c MTAPI_ERR_ACTION_FAILED, and + * \c MTAPI_ERR_ACTION_DELETED. + * + * On success, \c *status is either set to \c MTAPI_SUCCESS if one of the + * tasks in the group completed or to \c MTAPI_GROUP_COMPLETED if all tasks of + * the group have completed and successfully waited for. On error, \c *status + * is set to the appropriate error defined below. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Error codeDescription
\c MTAPI_TIMEOUTTimeout was reached.
\c MTAPI_ERR_GROUP_INVALIDArgument is not a valid task handle.
\c MTAPI_ERR_PARAMETERInvalid timeout parameter.
\c MTAPI_ERR_ARG_SIZEThe size of the arguments expected by action differs from arguments + * size of the caller.
\c MTAPI_ERR_RESULT_SIZEThe size of the result buffer expected by action differs from + * result buffer size of the caller.
\c MTAPI_ERR_NODE_NOTINITThe calling node is not initialized.
\c MTAPI_GROUP_COMPLETEDGroup completed, i.e., there are no more tasks to wait for in + * the group.
\c MTAPI_ERR_TASK_CANCELLEDThe task has been canceled because mtapi_task_cancel() was called + * before the task was executed, or the error code was set to + * \c MTAPI_ERR_TASK_CANCELLED by mtapi_context_status_set() in the + * action code.
\c MTAPI_ERR_ACTION_CANCELLEDAction execution was canceled by the action function + * (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_FAILEDError set by action function (mtapi_context_status_set()).
\c MTAPI_ERR_ACTION_DELETEDAll actions associated with the task have been deleted before the + * execution of the task was started or the error code has been set in + * the action code to \c MTAPI_ERR_ACTION_DELETED + * by mtapi_context_status_set().
+ * + * \see mtapi_group_create(), mtapi_context_status_set(), mtapi_task_cancel() + * + * \threadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_wait_any( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_OUT void** result, /**< [out] Pointer to result buffer + supplied at task start */ + MTAPI_IN mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + +/** + * This function deletes a task group. + * + * Deleting a group does not have any influence on tasks belonging to the + * group. Adding tasks to a group that is already deleted will result in an + * \c MTAPI_ERR_GROUP_INVALID error. + * + * On success, \c *status is set to \c MTAPI_SUCCESS. On error, \c *status is + * set to the appropriate error defined below. + * Error code | Description + * -------------------------- | ----------------------------------------------- + * \c MTAPI_ERR_GROUP_INVALID | Argument is not a valid group handle. + * \c MTAPI_ERR_NODE_NOTINIT | The calling node is not initialized. + * + * \threadsafe + * \ingroup TASK_GROUPS + */ +void mtapi_group_delete( + MTAPI_IN mtapi_group_hndl_t group, /**< [in] Group handle */ + MTAPI_OUT mtapi_status_t* status /**< [out] Pointer to error code, + may be \c MTAPI_NULL */ + ); + + +/** + * \internal + * + * \defgroup INTERNAL Internal Implementation + * + * \ingroup C_MTAPI + * + * This section describes the internal implementation of the MTAPI interface. + */ + + +#ifdef __cplusplus +} +#endif + +#endif // EMBB_MTAPI_C_MTAPI_H_ diff --git a/mtapi_c/include/mtapi.h b/mtapi_c/include/mtapi.h new file mode 100644 index 0000000..86ca961 --- /dev/null +++ b/mtapi_c/include/mtapi.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, 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_H_ +#define MTAPI_H_ + +// Just includes the actual MTAPI header for standard conformity + +#include + +#endif // MTAPI_H_ diff --git a/mtapi_c/src/embb_mtapi_action_t.c b/mtapi_c/src/embb_mtapi_action_t.c new file mode 100644 index 0000000..ba2e6ac --- /dev/null +++ b/mtapi_c/src/embb_mtapi_action_t.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +embb_mtapi_pool_implementation(action) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_action_initialize(embb_mtapi_action_t* that) { + that->action_function = NULL; + that->job_id = MTAPI_JOB_ID_INVALID; + that->domain_id = MTAPI_DOMAIN_ID_INVALID; + that->node_id = MTAPI_NODE_ID_INVALID; + that->enabled = MTAPI_FALSE; + that->node_local_data = NULL; + that->node_local_data_size = 0; + embb_atomic_store_int(&that->num_tasks, 0); +} + +void embb_mtapi_action_finalize(embb_mtapi_action_t* that) { + embb_mtapi_action_initialize(that); +} + +static mtapi_boolean_t embb_mtapi_action_delete_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_action_t * action = (embb_mtapi_action_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != action); + assert(MTAPI_NULL != task); + + if ( + task->action.id == action->handle.id && + task->action.tag == action->handle.tag) { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_ACTION_DELETED; + result = MTAPI_TRUE; + } + + return result; +} + +static mtapi_boolean_t embb_mtapi_action_disable_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_action_t * action = (embb_mtapi_action_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != action); + assert(MTAPI_NULL != task); + + if ( + task->action.id == action->handle.id && + task->action.tag == action->handle.tag) { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_ACTION_DISABLED; + result = MTAPI_TRUE; + } + + return result; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_action_hndl_t mtapi_action_create( + MTAPI_IN mtapi_job_id_t job_id, + MTAPI_IN mtapi_action_function_t action_function, + MTAPI_IN void* node_local_data, + MTAPI_IN mtapi_size_t node_local_data_size, + MTAPI_IN mtapi_action_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_action_hndl_t action_handle = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_action_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + /* check if job is valid */ + if (embb_mtapi_job_is_id_valid(node, job_id)) { + embb_mtapi_job_t* job = embb_mtapi_job_get_storage_for_id(node, job_id); + embb_mtapi_action_t* new_action = + embb_mtapi_action_pool_allocate(node->action_pool); + if (MTAPI_NULL != new_action) { + new_action->domain_id = node->domain_id; + new_action->node_id = node->node_id; + new_action->job_id = job_id; + new_action->action_function = action_function; + new_action->node_local_data = node_local_data; + new_action->node_local_data_size = node_local_data_size; + new_action->enabled = MTAPI_TRUE; + embb_atomic_store_int(&new_action->num_tasks, 0); + + /* set defaults if no attributes were given */ + if (MTAPI_NULL != attributes) { + new_action->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + /* use the default */ + mtapi_actionattr_init(&new_action->attributes, &local_status); + } + + /* check if affinity is sane */ + if (0 == new_action->attributes.affinity) { + local_status = MTAPI_ERR_PARAMETER; + } + + if (MTAPI_SUCCESS == local_status) { + action_handle = new_action->handle; + embb_mtapi_job_add_action(job, new_action); + } else { + embb_mtapi_action_pool_deallocate(node->action_pool, new_action); + } + } else { + /* no more space left in action pool */ + local_status = MTAPI_ERR_ACTION_LIMIT; + } + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return action_handle; +} + + +void mtapi_action_set_attribute( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_set_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + mtapi_actionattr_set( + &local_action->attributes, + attribute_num, + attribute, + attribute_size, + &local_status); + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + + +void mtapi_action_get_attribute( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_ACTION_GLOBAL: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_action->attributes.global, attribute, attribute_size); + break; + + case MTAPI_ACTION_AFFINITY: + local_status = embb_mtapi_attr_get_mtapi_affinity_t( + &local_action->attributes.affinity, attribute, attribute_size); + break; + + case MTAPI_ACTION_DOMAIN_SHARED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_action->attributes.domain_shared, + attribute, + attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + + +void mtapi_action_delete( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_delete() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* cancel all tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_action_delete_visitor, local_action); + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + local_status = MTAPI_SUCCESS; + while (embb_atomic_load_int(&local_action->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + + /* delete action */ + if (embb_mtapi_job_is_id_valid(node, local_action->job_id)) { + embb_mtapi_job_t* local_job = embb_mtapi_job_get_storage_for_id( + node, local_action->job_id); + embb_mtapi_job_remove_action(local_job, local_action); + } + embb_mtapi_action_finalize(local_action); + embb_mtapi_action_pool_deallocate(node->action_pool, local_action); + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_action_disable( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_disable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + local_action->enabled = MTAPI_FALSE; + + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* cancel all tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_action_disable_visitor, local_action); + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + local_status = MTAPI_SUCCESS; + while (embb_atomic_load_int(&local_action->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_action_enable( + MTAPI_IN mtapi_action_hndl_t action, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_enable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_action_pool_is_handle_valid(node->action_pool, action)) { + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, action); + local_action->enabled = MTAPI_TRUE; + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/embb_mtapi_action_t.h b/mtapi_c/src/embb_mtapi_action_t.h new file mode 100644 index 0000000..b86231a --- /dev/null +++ b/mtapi_c/src/embb_mtapi_action_t.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_ACTION_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Action class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_action_struct { + mtapi_action_hndl_t handle; + + mtapi_domain_t domain_id; + mtapi_node_t node_id; + mtapi_job_id_t job_id; + mtapi_action_function_t action_function; + const void* node_local_data; + mtapi_size_t node_local_data_size; + mtapi_action_attributes_t attributes; + mtapi_boolean_t enabled; + + embb_atomic_int num_tasks; +}; + +/** + * Action type + * \memberof embb_mtapi_action_struct + */ +typedef struct embb_mtapi_action_struct embb_mtapi_action_t; + +/** + * Default constructor. + * \memberof embb_mtapi_action_struct + */ +void embb_mtapi_action_initialize(embb_mtapi_action_t* that); + +/** + * Destructor. + * \memberof embb_mtapi_action_struct + */ +void embb_mtapi_action_finalize(embb_mtapi_action_t* that); + + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(action) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ACTION_T_H_ diff --git a/mtapi_c/src/embb_mtapi_alloc.c b/mtapi_c/src/embb_mtapi_alloc.c new file mode 100644 index 0000000..ac5c7a5 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_alloc.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +#include + +static embb_atomic_unsigned_int embb_mtapi_alloc_bytes_allocated = { 0 }; + +void * embb_mtapi_alloc_allocate(unsigned int bytes) { + void * ptr = embb_alloc(bytes); + if (ptr != NULL) { + embb_atomic_fetch_and_add_unsigned_int( + &embb_mtapi_alloc_bytes_allocated, sizeof(unsigned int)+bytes); + } + return ptr; +} + +void embb_mtapi_alloc_deallocate(void * ptr) { + if (ptr != NULL) { + embb_free(ptr); + } +} + +void embb_mtapi_alloc_reset_bytes_allocated() { + embb_atomic_store_unsigned_int(&embb_mtapi_alloc_bytes_allocated, 0); +} + +unsigned int embb_mtapi_alloc_get_bytes_allocated() { + return embb_atomic_load_unsigned_int(&embb_mtapi_alloc_bytes_allocated); +} diff --git a/mtapi_c/src/embb_mtapi_alloc.h b/mtapi_c/src/embb_mtapi_alloc.h new file mode 100644 index 0000000..41d69fd --- /dev/null +++ b/mtapi_c/src/embb_mtapi_alloc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_ALLOC_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ALLOC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +void * embb_mtapi_alloc_allocate(unsigned int bytes); +void embb_mtapi_alloc_deallocate(void * ptr); +void embb_mtapi_alloc_reset_bytes_allocated(); +unsigned int embb_mtapi_alloc_get_bytes_allocated(); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ALLOC_H_ diff --git a/mtapi_c/src/embb_mtapi_attr.c b/mtapi_c/src/embb_mtapi_attr.c new file mode 100644 index 0000000..ea14cb5 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_attr.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include + +#define embb_mtapi_attr_implementation(TYPE) \ +mtapi_status_t embb_mtapi_attr_set_##TYPE(TYPE* target, \ + const void* attribute, mtapi_size_t attribute_size) { \ + assert(MTAPI_NULL != target); \ +\ + if (sizeof(TYPE) == attribute_size) { \ + assert(MTAPI_NULL != attribute); \ + memcpy(target, attribute, sizeof(TYPE)); \ + return MTAPI_SUCCESS; \ + } else if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE == attribute_size) { \ + memcpy(target, &attribute, sizeof(TYPE)); \ + return MTAPI_SUCCESS; \ + } else { \ + return MTAPI_ERR_ATTR_SIZE; \ + } \ +} \ +\ +mtapi_status_t embb_mtapi_attr_get_##TYPE(const TYPE* source, \ + void* attribute, mtapi_size_t attribute_size) { \ + assert(MTAPI_NULL != source); \ + assert(MTAPI_NULL != attribute); \ +\ + if (sizeof(TYPE) == attribute_size) { \ + memcpy(attribute, source, sizeof(TYPE)); \ + return MTAPI_SUCCESS; \ + } else { \ + return MTAPI_ERR_ATTR_SIZE; \ + } \ +} + +embb_mtapi_attr_implementation(mtapi_uint_t); +embb_mtapi_attr_implementation(mtapi_affinity_t); +embb_mtapi_attr_implementation(mtapi_boolean_t); diff --git a/mtapi_c/src/embb_mtapi_attr.h b/mtapi_c/src/embb_mtapi_attr.h new file mode 100644 index 0000000..d8023d5 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_attr.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_ATTR_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ATTR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define embb_mtapi_attr(TYPE) \ +mtapi_status_t embb_mtapi_attr_set_##TYPE(TYPE* target, \ + const void* attribute, mtapi_size_t attribute_size); \ +mtapi_status_t embb_mtapi_attr_get_##TYPE(const TYPE* source, \ + void* attribute, mtapi_size_t attribute_size); + +embb_mtapi_attr(mtapi_uint_t) +embb_mtapi_attr(mtapi_affinity_t) +embb_mtapi_attr(mtapi_boolean_t) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ATTR_H_ diff --git a/mtapi_c/src/embb_mtapi_group_t.c b/mtapi_c/src/embb_mtapi_group_t.c new file mode 100644 index 0000000..13a099c --- /dev/null +++ b/mtapi_c/src/embb_mtapi_group_t.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +mtapi_group_hndl_t MTAPI_GROUP_NONE = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +embb_mtapi_pool_implementation(group) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_group_initialize(embb_mtapi_group_t * that) { + assert(MTAPI_NULL != that); + + that->group_id = MTAPI_GROUP_ID_NONE; + that->deleted = MTAPI_FALSE; + that->num_tasks.internal_variable = 0; + embb_mtapi_task_queue_initialize(&that->queue); +} + +void embb_mtapi_group_initialize_with_node( + embb_mtapi_group_t * that, + embb_mtapi_node_t * node) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + that->group_id = MTAPI_GROUP_ID_NONE; + that->deleted = MTAPI_FALSE; + that->num_tasks.internal_variable = 0; + embb_mtapi_task_queue_initialize_with_capacity( + &that->queue, node->attributes.queue_limit); +} + +void embb_mtapi_group_finalize(embb_mtapi_group_t * that) { + assert(MTAPI_NULL != that); + + that->deleted = MTAPI_TRUE; + that->num_tasks.internal_variable = 0; + embb_mtapi_task_queue_finalize(&that->queue); +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_group_hndl_t mtapi_group_create( + MTAPI_IN mtapi_group_id_t group_id, + MTAPI_IN mtapi_group_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_group_hndl_t group_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + embb_mtapi_group_t* group = NULL; + + embb_mtapi_log_trace("mtapi_group_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + group = embb_mtapi_group_pool_allocate(node->group_pool); + if (MTAPI_NULL != group) { + embb_mtapi_group_initialize_with_node(group, node); + group->group_id = group_id; + if (MTAPI_NULL != attributes) { + group->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + mtapi_groupattr_init(&group->attributes, &local_status); + } + if (MTAPI_SUCCESS == local_status) { + group_hndl = group->handle; + } else { + embb_mtapi_group_finalize(group); + embb_mtapi_group_pool_deallocate(node->group_pool, group); + } + } else { + local_status = MTAPI_ERR_GROUP_LIMIT; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return group_hndl; +} + +void mtapi_group_set_attribute( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_group_t* local_group; + + embb_mtapi_log_trace("mtapi_group_set_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + local_group = embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + mtapi_groupattr_set(&local_group->attributes, attribute_num, + attribute, attribute_size, &local_status); + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_group_get_attribute( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(attribute_num); + EMBB_UNUSED(attribute_size); + + embb_mtapi_log_trace("mtapi_group_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + /* the following is not needed for now, since there are no attributes + + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); */ + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + /* switch is not needed for now, since there are no attributes + switch (attribute_num) { + default: */ + local_status = MTAPI_ERR_ATTR_NUM; + /* break; + }*/ + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_group_wait_all( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_group_wait_all() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_thread_context_t * context = NULL; + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* wait for all tasks to arrive in the queue */ + local_status = MTAPI_SUCCESS; + while (embb_atomic_load_int(&local_group->num_tasks)) { + embb_mtapi_task_t* local_task; + + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* fetch and delete all available tasks */ + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + while (MTAPI_NULL != local_task) { + if (MTAPI_SUCCESS != local_task->error_code) { + local_status = local_task->error_code; + } + embb_mtapi_task_delete(local_task, node->task_pool); + embb_atomic_fetch_and_add_int(&local_group->num_tasks, -1); + + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + if (MTAPI_TIMEOUT != local_status) { + /* group becomes invalid, so delete it */ + mtapi_group_delete(group, MTAPI_NULL); + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + embb_mtapi_log_trace("mtapi_group_wait_all() returns\n"); +} + +void mtapi_group_wait_any( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT void** result, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_group_wait_any() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + + embb_mtapi_task_t* local_task; + /* are there any tasks left? */ + if (0 == embb_atomic_load_int(&local_group->num_tasks)) { + /* group becomes invalid, so delete it */ + mtapi_group_delete(group, &local_status); + local_status = MTAPI_GROUP_COMPLETED; + } else { + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* wait for any task to arrive */ + local_status = MTAPI_SUCCESS; + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + while (MTAPI_NULL == local_task) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + + /* try to pop a task from the group queue */ + local_task = embb_mtapi_task_queue_pop(&local_group->queue); + } + /* was there a timeout, or is there a result? */ + if (MTAPI_NULL != local_task) { + /* store result */ + if (MTAPI_NULL != result) { + *result = local_task->result_buffer; + } + + /* return error code set by the task */ + local_status = local_task->error_code; + + /* delete task */ + embb_mtapi_task_delete(local_task, node->task_pool); + embb_atomic_fetch_and_add_int(&local_group->num_tasks, -1); + } + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + embb_mtapi_log_trace("mtapi_group_wait_any() returns\n"); +} + +void mtapi_group_delete( + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + + if (local_group->deleted) { + local_status = MTAPI_ERR_GROUP_INVALID; + } else { + embb_mtapi_group_finalize(local_group); + embb_mtapi_group_pool_deallocate(node->group_pool, local_group); + local_status = MTAPI_SUCCESS; + } + } else { + local_status = MTAPI_ERR_GROUP_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/embb_mtapi_group_t.h b/mtapi_c/src/embb_mtapi_group_t.h new file mode 100644 index 0000000..799bcf1 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_group_t.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_GROUP_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_node_struct embb_mtapi_node_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Group class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_group_struct { + mtapi_group_hndl_t handle; + + mtapi_group_id_t group_id; + volatile mtapi_boolean_t deleted; + embb_atomic_int num_tasks; + mtapi_group_attributes_t attributes; + embb_mtapi_task_queue_t queue; +}; + +/** + * Group type + * \memberof embb_mtapi_group_struct + */ +typedef struct embb_mtapi_group_struct embb_mtapi_group_t; + +/** + * Default constructor. + * \memberof embb_mtapi_group_struct + */ +void embb_mtapi_group_initialize(embb_mtapi_group_t * that); + +/** + * Constructor using parameters from embb_mtapi_node_struct. + * \memberof embb_mtapi_group_struct + */ +void embb_mtapi_group_initialize_with_node( + embb_mtapi_group_t * that, + embb_mtapi_node_t * node); + +/** + * Destructor. + * \memberof embb_mtapi_group_struct + */ +void embb_mtapi_group_finalize(embb_mtapi_group_t * that); + + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(group) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_GROUP_T_H_ diff --git a/mtapi_c/src/embb_mtapi_id_pool_t.c b/mtapi_c/src/embb_mtapi_id_pool_t.c new file mode 100644 index 0000000..9ee047d --- /dev/null +++ b/mtapi_c/src/embb_mtapi_id_pool_t.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include +#include + +void embb_mtapi_id_pool_initialize( + embb_mtapi_id_pool_t * that, + mtapi_uint_t capacity) { + mtapi_uint_t ii; + + that->capacity = capacity; + that->id_buffer = (mtapi_uint_t*) + embb_mtapi_alloc_allocate(sizeof(mtapi_uint_t)*(capacity)); + that->id_buffer[0] = EMBB_MTAPI_IDPOOL_INVALID_ID; + for (ii = 1; ii < capacity; ii++) { + that->id_buffer[ii] = ii; + } + that->ids_availabe = capacity - 1; + that->put_id_position = 0; + that->get_id_position = 1; + embb_mtapi_spinlock_initialize(&that->lock); +} + +void embb_mtapi_id_pool_finalize(embb_mtapi_id_pool_t * that) { + that->capacity = 0; + that->ids_availabe = 0; + that->get_id_position = 0; + that->put_id_position = 0; + embb_mtapi_alloc_deallocate(that->id_buffer); + that->id_buffer = NULL; + embb_mtapi_spinlock_finalize(&that->lock); +} + +mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that) { + mtapi_uint_t id = EMBB_MTAPI_IDPOOL_INVALID_ID; + + assert(MTAPI_NULL != that); + + if (embb_mtapi_spinlock_acquire(&that->lock)) { + if (0 < that->ids_availabe) { + /* take away one id */ + that->ids_availabe--; + + /* 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) { + that->get_id_position = 0; + } + + /* fetch id */ + id = that->id_buffer[id_position]; + + /* make id entry invalid just in case */ + that->id_buffer[id_position] = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + embb_mtapi_spinlock_release(&that->lock); + } + + return id; +} + +void embb_mtapi_id_pool_deallocate( + embb_mtapi_id_pool_t * that, + mtapi_uint_t id) { + assert(MTAPI_NULL != that); + + if (embb_mtapi_spinlock_acquire(&that->lock)) { + if (that->capacity > that->ids_availabe) { + /* 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) { + that->put_id_position = 0; + } + + /* put id back into buffer */ + that->id_buffer[id_position] = id; + + /* make it available */ + that->ids_availabe++; + } + embb_mtapi_spinlock_release(&that->lock); + } else { + embb_mtapi_log_error( + "could not acquire lock in embb_mtapi_IdPool_deallocate\n"); + } +} diff --git a/mtapi_c/src/embb_mtapi_id_pool_t.h b/mtapi_c/src/embb_mtapi_id_pool_t.h new file mode 100644 index 0000000..71a5e87 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_id_pool_t.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_ID_POOL_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_ID_POOL_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * IdPool class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_id_pool_struct { + mtapi_uint_t capacity; + mtapi_uint_t *id_buffer; + mtapi_uint_t ids_availabe; + mtapi_uint_t get_id_position; + mtapi_uint_t put_id_position; + embb_mtapi_spinlock_t lock; +}; + +/** + * IdPool type. + * \memberof embb_mtapi_id_pool_struct + */ +typedef struct embb_mtapi_id_pool_struct embb_mtapi_id_pool_t; + +#define EMBB_MTAPI_IDPOOL_INVALID_ID 0 + +/** + * Constructor with configurable capacity. + * \memberof embb_mtapi_id_pool_struct + */ +void embb_mtapi_id_pool_initialize( + embb_mtapi_id_pool_t * that, + mtapi_uint_t capacity); + +/** + * Destructor. + * \memberof embb_mtapi_id_pool_struct + */ +void embb_mtapi_id_pool_finalize(embb_mtapi_id_pool_t * that); + +/** + * Allocates a single item and removes its id from the pool. + * \memberof embb_mtapi_id_pool_struct + */ +mtapi_uint_t embb_mtapi_id_pool_allocate(embb_mtapi_id_pool_t * that); + +/** + * Dellocates a single item and puts its id back into the pool. + * \memberof embb_mtapi_id_pool_struct + */ +void embb_mtapi_id_pool_deallocate( + embb_mtapi_id_pool_t * that, + mtapi_uint_t id); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_ID_POOL_T_H_ diff --git a/mtapi_c/src/embb_mtapi_job_t.c b/mtapi_c/src/embb_mtapi_job_t.c new file mode 100644 index 0000000..6523f9c --- /dev/null +++ b/mtapi_c/src/embb_mtapi_job_t.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +mtapi_boolean_t embb_mtapi_job_initialize_list(embb_mtapi_node_t * node) { + node->job_list = (embb_mtapi_job_t*)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_job_t)*node->attributes.max_jobs); + mtapi_uint_t ii; + for (ii = 0; ii < node->attributes.max_jobs; ii++) { + embb_mtapi_job_initialize( + &node->job_list[ii], node->attributes.max_actions_per_job); + node->job_list[ii].handle.id = ii; + node->job_list[ii].handle.tag = 0; + } + return MTAPI_TRUE; +} + +void embb_mtapi_job_finalize_list(embb_mtapi_node_t * node) { + mtapi_uint_t ii; + for (ii = 0; ii < node->attributes.max_jobs; ii++) { + embb_mtapi_job_finalize(&node->job_list[ii]); + node->job_list[ii].handle.id = 0; + } + embb_mtapi_alloc_deallocate(node->job_list); + node->job_list = MTAPI_NULL; +} + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +mtapi_boolean_t embb_mtapi_job_is_handle_valid( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle) { + assert(MTAPI_NULL != node); + return ((0 < handle.id) && + (handle.id < node->attributes.max_jobs) && + (node->job_list[handle.id].handle.tag == handle.tag)) ? + MTAPI_TRUE : MTAPI_FALSE; +} + +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_handle( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle) { + assert(MTAPI_NULL != node); + assert(MTAPI_NULL != node->job_list); + assert(embb_mtapi_job_is_handle_valid(node, handle)); + return &node->job_list[handle.id]; +} + +mtapi_boolean_t embb_mtapi_job_is_id_valid( + embb_mtapi_node_t * node, + mtapi_job_id_t id) { + assert(MTAPI_NULL != node); + return ((0 < id) && (id < node->attributes.max_jobs)) ? + MTAPI_TRUE : MTAPI_FALSE; +} + +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_id( + embb_mtapi_node_t * node, + mtapi_job_id_t id) { + assert(MTAPI_NULL != node); + assert(MTAPI_NULL != node->job_list); + assert(embb_mtapi_job_is_id_valid(node, id)); + return &node->job_list[id]; +} + +void embb_mtapi_job_initialize( + embb_mtapi_job_t * that, + mtapi_uint_t max_actions) { + mtapi_uint_t ii; + + assert(MTAPI_NULL != that); + + that->handle.tag = 0; + + that->domain_id = 0; + that->node_id = 0; + that->num_actions = 0; + that->max_actions = max_actions; + that->actions = (mtapi_action_hndl_t*) + embb_mtapi_alloc_allocate(sizeof(mtapi_action_hndl_t)*max_actions); + for (ii = 0; ii < max_actions; ii++) { + that->actions[ii].id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } +} + +void embb_mtapi_job_finalize(embb_mtapi_job_t * that) { + assert(MTAPI_NULL != that); + + that->handle.tag++; + + that->domain_id = 0; + that->node_id = 0; + that->num_actions = 0; + that->max_actions = 0; + embb_mtapi_alloc_deallocate(that->actions); + that->actions = NULL; +} + +mtapi_boolean_t embb_mtapi_job_add_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action) { + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != action); + + /* check if job has enough room for another action */ + if (that->max_actions > that->num_actions) { + that->domain_id = action->domain_id; + that->node_id = action->node_id; + that->actions[that->num_actions] = action->handle; + that->num_actions++; + } else { + result = MTAPI_FALSE; + } + + return result; +} + +void embb_mtapi_job_remove_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != action); + mtapi_uint_t ii = 0; + + for (ii = 0; ii + 1 < that->num_actions; ii++) { + if (that->actions[ii].id == action->handle.id && + that->actions[ii].tag == action->handle.tag) { + that->actions[ii] = that->actions[that->num_actions - 1]; + } + } + + that->num_actions--; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_job_hndl_t mtapi_job_get( + MTAPI_IN mtapi_job_id_t job_id, + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + embb_mtapi_job_t* job = MTAPI_NULL; + mtapi_job_hndl_t job_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + EMBB_UNUSED(domain_id); + + embb_mtapi_log_trace("mtapi_job_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_job_is_id_valid(node, job_id)) { + job = embb_mtapi_job_get_storage_for_id(node, job_id); + job_hndl = job->handle; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_log_error("mtapi not initialized\n"); + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return job_hndl; +} diff --git a/mtapi_c/src/embb_mtapi_job_t.h b/mtapi_c/src/embb_mtapi_job_t.h new file mode 100644 index 0000000..f389c16 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_job_t.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_JOB_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_JOB_T_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_node_struct embb_mtapi_node_t; +typedef struct embb_mtapi_action_struct embb_mtapi_action_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Job class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_job_struct { + mtapi_job_hndl_t handle; + + mtapi_domain_t domain_id; + mtapi_node_t node_id; + mtapi_uint_t num_actions; + mtapi_uint_t max_actions; + mtapi_action_hndl_t* actions; +}; + +/** + * Job type. + * \memberof embb_mtapi_job_struct + */ +typedef struct embb_mtapi_job_struct embb_mtapi_job_t; + +/** + * Constructs the global job list. + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_initialize_list(embb_mtapi_node_t * node); + +/** + * Destroys the global job list. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_finalize_list(embb_mtapi_node_t * node); + +/** + * Checks if a given job handle is valid. + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_is_handle_valid( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle); + +/** + * Gets the pointer associated with the given handle. + * + * The handle is expected to be valid, so check it beforehand using + * embb_mtapi_job_is_handle_valid(). + * \memberof embb_mtapi_job_struct + */ +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_handle( + embb_mtapi_node_t * node, + mtapi_job_hndl_t handle); + +/** + * Checks if a given job id is valid. + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_is_id_valid( + embb_mtapi_node_t * node, + mtapi_job_id_t id); + +/** + * Gets the pointer associated with the given id. + * + * The id is expected to be valid, so check it beforehand using + * embb_mtapi_job_is_id_valid(). + * + * \memberof embb_mtapi_job_struct + */ +embb_mtapi_job_t * embb_mtapi_job_get_storage_for_id( + embb_mtapi_node_t * node, + mtapi_job_id_t id); + +/** + * Constructor with configurable maximum action count. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_initialize( + embb_mtapi_job_t * that, + mtapi_uint_t max_actions); + +/** + * Destructor. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_finalize(embb_mtapi_job_t * that); + +/** + * Add an action to the job. + * + * Returns MTAPI_TRUE if there are less actions than the configured maximum + * action count in the job already. Otherwise returns MTAPI_FALSE: + * + * \memberof embb_mtapi_job_struct + */ +mtapi_boolean_t embb_mtapi_job_add_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action); + +/** + * Remove an action from the job. + * \memberof embb_mtapi_job_struct + */ +void embb_mtapi_job_remove_action( + embb_mtapi_job_t * that, + embb_mtapi_action_t * action); + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_JOB_T_H_ diff --git a/mtapi_c/src/embb_mtapi_log.h b/mtapi_c/src/embb_mtapi_log.h new file mode 100644 index 0000000..da9ca59 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_log.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_LOG_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define embb_mtapi_log_trace(...) \ + embb_log_trace("mtapi_c", __VA_ARGS__) +#define embb_mtapi_log_info(...) \ + embb_log_info("mtapi_c", __VA_ARGS__) +#define embb_mtapi_log_warning(...) \ + embb_log_warning("mtapi_c", __VA_ARGS__) +#define embb_mtapi_log_error(...) \ + embb_log_error("mtapi_c", __VA_ARGS__) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_LOG_H_ diff --git a/mtapi_c/src/embb_mtapi_node_t.c b/mtapi_c/src/embb_mtapi_node_t.c new file mode 100644 index 0000000..7f8f33c --- /dev/null +++ b/mtapi_c/src/embb_mtapi_node_t.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static embb_mtapi_node_t* embb_mtapi_node_instance = NULL; +extern embb_atomic_int embb_mtapi_spinlock_spins; + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +mtapi_boolean_t embb_mtapi_node_is_initialized() { + return (mtapi_boolean_t)(embb_mtapi_node_instance != NULL); +} + +embb_mtapi_node_t* embb_mtapi_node_get_instance() { + return embb_mtapi_node_instance; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_initialize( + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_IN mtapi_node_t node_id, + MTAPI_IN mtapi_node_attributes_t* attributes, + MTAPI_OUT mtapi_info_t* mtapi_info, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node; + + embb_mtapi_log_trace( + "mtapi_initialize() called (domain: %i, node: %i)\n", domain_id, node_id); + + /* check if node was already initialized */ + if (embb_mtapi_node_is_initialized()) { + local_status = MTAPI_ERR_NODE_INITIALIZED; + + node = embb_mtapi_node_instance; + + /* return previously set information structure */ + if (MTAPI_NULL != mtapi_info) { + *mtapi_info = node->info; + } + } else { + embb_mtapi_alloc_reset_bytes_allocated(); + /* create node instance */ + embb_mtapi_node_instance = (embb_mtapi_node_t*) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_node_t)); + if (NULL == embb_mtapi_node_instance) { + /* out of memory! */ + local_status = MTAPI_ERR_UNKNOWN; + } else { + embb_atomic_store_int(&embb_mtapi_spinlock_spins, 0); + + node = embb_mtapi_node_instance; + + node->domain_id = domain_id; + node->node_id = node_id; + + if (MTAPI_NULL != attributes) { + node->attributes = *attributes; + local_status = MTAPI_SUCCESS; + } else { + mtapi_nodeattr_init(&node->attributes, &local_status); + } + + if (MTAPI_SUCCESS == local_status) { + mtapi_affinity_init(&node->affinity_all, MTAPI_TRUE, &local_status); + } + + if (MTAPI_SUCCESS == local_status) { + embb_atomic_store_int(&node->is_scheduler_running, MTAPI_FALSE); + + /* initialize storage */ + embb_mtapi_job_initialize_list(node); + node->action_pool = embb_mtapi_action_pool_new( + node->attributes.max_actions); + node->group_pool = embb_mtapi_group_pool_new( + node->attributes.max_groups); + node->task_pool = embb_mtapi_task_pool_new( + node->attributes.max_tasks); + node->queue_pool = embb_mtapi_queue_pool_new( + node->attributes.max_queues); + + /* initialize scheduler for local node */ + node->scheduler = embb_mtapi_scheduler_new(); + if (MTAPI_NULL != node->scheduler) { + /* fill information structure */ + node->info.hardware_concurrency = embb_core_count_available(); + node->info.used_memory = embb_mtapi_alloc_get_bytes_allocated(); + if (MTAPI_NULL != mtapi_info) { + *mtapi_info = node->info; + } + + /* initialization succeeded, tell workers to start working */ + embb_atomic_store_int(&node->is_scheduler_running, MTAPI_TRUE); + } else { + mtapi_finalize(MTAPI_NULL); + local_status = MTAPI_ERR_NODE_INITFAILED; + } + + } else { + embb_mtapi_alloc_deallocate(node); + local_status = MTAPI_ERR_PARAMETER; + } + } + } + + mtapi_status_set(status, local_status); +} + +void mtapi_finalize(MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_finalize() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + + /* finalize scheduler */ + if (MTAPI_NULL != node->scheduler) { + embb_mtapi_scheduler_delete(node->scheduler); + node->scheduler = MTAPI_NULL; + } + + /* finalize storage in reverse order */ + embb_mtapi_queue_pool_delete(node->queue_pool); + node->queue_pool = MTAPI_NULL; + + embb_mtapi_task_pool_delete(node->task_pool); + node->task_pool = MTAPI_NULL; + + embb_mtapi_group_pool_delete(node->group_pool); + node->group_pool = MTAPI_NULL; + + embb_mtapi_action_pool_delete(node->action_pool); + node->action_pool = MTAPI_NULL; + + embb_mtapi_job_finalize_list(node); + + /* free system instance */ + embb_mtapi_alloc_deallocate(node); + embb_mtapi_node_instance = MTAPI_NULL; + + embb_mtapi_log_info("mtapi spinlock spun %d times.\n", + embb_atomic_load_int(&embb_mtapi_spinlock_spins)); + + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_node_get_attribute( + MTAPI_IN mtapi_node_t node, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* local_node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_node_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (local_node->node_id == node) { + if (MTAPI_NULL != attribute) { + switch (attribute_num) { + case MTAPI_NODE_CORE_AFFINITY: + if (MTAPI_NODE_CORE_AFFINITY_SIZE == attribute_size) { + *(embb_core_set_t*)attribute = + local_node->attributes.core_affinity; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ATTR_SIZE; + } + break; + + case MTAPI_NODE_NUMCORES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.num_cores, attribute, attribute_size); + break; + + case MTAPI_NODE_TYPE: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.type, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_TASKS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_tasks, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_actions, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_GROUPS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_groups, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_QUEUES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_queues, attribute, attribute_size); + break; + + case MTAPI_NODE_QUEUE_LIMIT: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.queue_limit, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_JOBS: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_jobs, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS_PER_JOB: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_actions_per_job, attribute, + attribute_size); + break; + + case MTAPI_NODE_MAX_PRIORITIES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_node->attributes.max_priorities, attribute, attribute_size); + break; + + default: + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +mtapi_domain_t mtapi_domain_id_get( + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_domain_t domain_id = MTAPI_DOMAIN_ID_INVALID; + + embb_mtapi_log_trace("mtapi_domain_id_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + domain_id = node->domain_id; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return domain_id; +} + +mtapi_node_t mtapi_node_id_get( + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_node_t node_id = MTAPI_NODE_ID_INVALID; + + embb_mtapi_log_trace("mtapi_domain_id_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + node_id = node->node_id; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return node_id; +} diff --git a/mtapi_c/src/embb_mtapi_node_t.h b/mtapi_c/src/embb_mtapi_node_t.h new file mode 100644 index 0000000..3218857 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_node_t.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_NODE_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_NODE_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_job_struct embb_mtapi_job_t; +typedef struct embb_mtapi_scheduler_struct embb_mtapi_scheduler_t; +typedef struct embb_mtapi_action_pool_struct embb_mtapi_action_pool_t; +typedef struct embb_mtapi_group_pool_struct embb_mtapi_group_pool_t; +typedef struct embb_mtapi_task_pool_struct embb_mtapi_task_pool_t; +typedef struct embb_mtapi_queue_pool_struct embb_mtapi_queue_pool_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Node class. + * + * The node class is used as a singleton and is the root of this + * MTAPI implementation. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_node_struct { + mtapi_domain_t domain_id; + mtapi_node_t node_id; + mtapi_node_attributes_t attributes; + mtapi_info_t info; + embb_mtapi_scheduler_t * scheduler; + embb_mtapi_job_t * job_list; + embb_mtapi_action_pool_t * action_pool; + embb_mtapi_group_pool_t * group_pool; + embb_mtapi_task_pool_t * task_pool; + embb_mtapi_queue_pool_t * queue_pool; + embb_atomic_int is_scheduler_running; + mtapi_affinity_t affinity_all; +}; + +/** + * Node type. + * \memberof embb_mtapi_node_struct + */ +typedef struct embb_mtapi_node_struct embb_mtapi_node_t; + +/** + * Checks if the node singleton was initialized already. + * \memberof embb_mtapi_node_struct + */ +mtapi_boolean_t embb_mtapi_node_is_initialized(); + +/** + * Retrieves the previously initialized node instance, but returns MTAPI_NULL + * if the node singleton is not yet created. + * \memberof embb_mtapi_node_struct + */ +embb_mtapi_node_t* embb_mtapi_node_get_instance(); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_NODE_T_H_ diff --git a/mtapi_c/src/embb_mtapi_pool_template-inl.h b/mtapi_c/src/embb_mtapi_pool_template-inl.h new file mode 100644 index 0000000..9d633f6 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_pool_template-inl.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_POOL_TEMPLATE_INL_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_INL_H_ + +#include +#include + +#include +#include + +#define embb_mtapi_pool_implementation(TYPE) \ +\ +/* ---- POOL STORAGE FUNCTIONS ------------------------------------------- */ \ +\ +embb_mtapi_##TYPE##_pool_t * embb_mtapi_##TYPE##_pool_new( \ + mtapi_uint_t capacity) { \ + embb_mtapi_##TYPE##_pool_t * that = (embb_mtapi_##TYPE##_pool_t*) \ + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_##TYPE##_pool_t)); \ + if (MTAPI_NULL != that) { \ + embb_mtapi_##TYPE##_pool_initialize(that, capacity); \ + } \ + return that; \ +} \ +\ +void embb_mtapi_##TYPE##_pool_delete(embb_mtapi_##TYPE##_pool_t * that) { \ + assert(MTAPI_NULL != that); \ + embb_mtapi_##TYPE##_pool_finalize(that); \ + embb_mtapi_alloc_deallocate(that); \ +} \ +\ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_initialize( \ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_uint_t capacity) { \ + mtapi_uint_t ii; \ + assert(MTAPI_NULL != that); \ + embb_mtapi_id_pool_initialize(&that->id_pool, capacity); \ + that->storage = (embb_mtapi_##TYPE##_t*)embb_mtapi_alloc_allocate( \ + sizeof(embb_mtapi_##TYPE##_t)*capacity); \ + for (ii = 0; ii < capacity; ii++) { \ + that->storage[ii].handle.id = EMBB_MTAPI_IDPOOL_INVALID_ID; \ + that->storage[ii].handle.tag = 0; \ + } \ + /* use entry 0 as invalid */ \ + embb_mtapi_##TYPE##_initialize(that->storage); \ + return MTAPI_TRUE; \ +} \ +\ +void embb_mtapi_##TYPE##_pool_finalize(embb_mtapi_##TYPE##_pool_t * that) { \ + embb_mtapi_id_pool_finalize(&that->id_pool); \ + embb_mtapi_alloc_deallocate(that->storage); \ + that->storage = MTAPI_NULL; \ +} \ +\ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_allocate( \ + embb_mtapi_##TYPE##_pool_t * that) { \ + mtapi_uint_t pool_id = embb_mtapi_id_pool_allocate(&that->id_pool); \ + if (EMBB_MTAPI_IDPOOL_INVALID_ID != pool_id) { \ + that->storage[pool_id].handle.id = pool_id; \ + return &that->storage[pool_id]; \ + } else { \ + return MTAPI_NULL; \ + } \ +} \ +\ +void embb_mtapi_##TYPE##_pool_deallocate( \ + embb_mtapi_##TYPE##_pool_t * that, \ + embb_mtapi_##TYPE##_t * object) { \ + mtapi_uint_t pool_id = object->handle.id; \ + embb_mtapi_##TYPE##_finalize(object); \ + object->handle.id = EMBB_MTAPI_IDPOOL_INVALID_ID; \ + object->handle.tag++; \ + embb_mtapi_id_pool_deallocate(&that->id_pool, pool_id); \ +} \ +\ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_is_handle_valid( \ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle) { \ + assert(MTAPI_NULL != that); \ + return ((0 < handle.id) && \ + (handle.id < that->id_pool.capacity) && \ + (that->storage[handle.id].handle.tag == handle.tag)) ? \ + MTAPI_TRUE : MTAPI_FALSE; \ +} \ +\ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_get_storage_for_handle( \ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle) { \ + assert(MTAPI_NULL != that); \ + assert(embb_mtapi_##TYPE##_pool_is_handle_valid(that, handle)); \ + return &that->storage[handle.id]; \ +} + +#endif // MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_INL_H_ diff --git a/mtapi_c/src/embb_mtapi_pool_template.h b/mtapi_c/src/embb_mtapi_pool_template.h new file mode 100644 index 0000000..b7738ec --- /dev/null +++ b/mtapi_c/src/embb_mtapi_pool_template.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_POOL_TEMPLATE_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_H_ + +#include + +#include + +#define embb_mtapi_pool(TYPE) \ +\ +/** \internal +TYPE pool class providing a fixed number of TYPE elements. Used to allocate +TYPE elements without using dynamic memory. + +\ingroup INTERNAL +*/ \ +struct embb_mtapi_##TYPE##_pool_struct \ +{ \ + embb_mtapi_id_pool_t id_pool; \ + embb_mtapi_##TYPE##_t * storage; \ +}; \ +\ +/** TYPE pool type. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +typedef struct embb_mtapi_##TYPE##_pool_struct embb_mtapi_##TYPE##_pool_t; \ +\ +/** operator new with configurable capacity. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +embb_mtapi_##TYPE##_pool_t * embb_mtapi_##TYPE##_pool_new(\ + mtapi_uint_t capacity); \ +\ +/** operator delete. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +void embb_mtapi_##TYPE##_pool_delete(embb_mtapi_##TYPE##_pool_t * that); \ +\ +/** Constructor with configurable capacity. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_initialize(\ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_uint_t capacity); \ +\ +/** Destructor. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +void embb_mtapi_##TYPE##_pool_finalize(\ + embb_mtapi_##TYPE##_pool_t * that); \ +\ +/** Allocate a single TYPE element in the pool. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_allocate(\ + embb_mtapi_##TYPE##_pool_t * that); \ +\ +/** Deallocate given TYPE element in the pool. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +void embb_mtapi_##TYPE##_pool_deallocate(\ + embb_mtapi_##TYPE##_pool_t * that, \ + embb_mtapi_##TYPE##_t * object); \ +\ +/** Check if given pool handle is valid. +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +mtapi_boolean_t embb_mtapi_##TYPE##_pool_is_handle_valid(\ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle); \ +\ +/** Return pointer to storage for given handle. Handle is expected to be valid, +so check it beforehand using embb_mtapi_##TYPE##_pool_is_handle_valid(). +\memberof embb_mtapi_##TYPE##_pool_struct +*/ \ +embb_mtapi_##TYPE##_t * embb_mtapi_##TYPE##_pool_get_storage_for_handle(\ + embb_mtapi_##TYPE##_pool_t * that, \ + mtapi_##TYPE##_hndl_t handle); + +#endif // MTAPI_C_SRC_EMBB_MTAPI_POOL_TEMPLATE_H_ diff --git a/mtapi_c/src/embb_mtapi_queue_t.c b/mtapi_c/src/embb_mtapi_queue_t.c new file mode 100644 index 0000000..62d9fce --- /dev/null +++ b/mtapi_c/src/embb_mtapi_queue_t.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +embb_mtapi_pool_implementation(queue) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_queue_initialize(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + + mtapi_queueattr_init(&that->attributes, MTAPI_NULL); + that->queue_id = MTAPI_QUEUE_ID_NONE; + embb_atomic_store_char(&that->enabled, MTAPI_FALSE); + embb_atomic_store_int(&that->num_tasks, 0); + that->job_handle.id = 0; + that->job_handle.tag = 0; +} + +void embb_mtapi_queue_initialize_with_attributes_and_job( + embb_mtapi_queue_t* that, + mtapi_queue_attributes_t* attributes, + mtapi_job_hndl_t job) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != attributes); + + that->attributes = *attributes; + that->queue_id = MTAPI_QUEUE_ID_NONE; + embb_atomic_store_char(&that->enabled, MTAPI_TRUE); + embb_atomic_store_int(&that->num_tasks, 0); + that->job_handle = job; +} + +void embb_mtapi_queue_finalize(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + + that->job_handle.id = 0; + that->job_handle.tag = 0; + embb_mtapi_queue_initialize(that); +} + +void embb_mtapi_queue_task_started(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + embb_atomic_fetch_and_add_int(&that->num_tasks, 1); +} + +void embb_mtapi_queue_task_finished(embb_mtapi_queue_t* that) { + assert(MTAPI_NULL != that); + embb_atomic_fetch_and_add_int(&that->num_tasks, -1); +} + +static mtapi_boolean_t embb_mtapi_queue_delete_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_queue_t * queue = (embb_mtapi_queue_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != queue); + assert(MTAPI_NULL != task); + + if (task->queue.id == queue->handle.id && + task->queue.tag == queue->handle.tag) { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_QUEUE_DELETED; + result = MTAPI_TRUE; + } + + return result; +} + +static mtapi_boolean_t embb_mtapi_queue_disable_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_queue_t * queue = (embb_mtapi_queue_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != queue); + assert(MTAPI_NULL != task); + + if (task->queue.id == queue->handle.id && + task->queue.tag == queue->handle.tag) { + if (queue->attributes.retain) { + /* task is scheduled and needs to be retained */ + embb_mtapi_task_set_state(task, MTAPI_TASK_RETAINED); + } else { + /* task is scheduled and needs to be cancelled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_CANCELLED); + task->error_code = MTAPI_ERR_QUEUE_DISABLED; + } + result = MTAPI_TRUE; + } + + return result; +} + +static mtapi_boolean_t embb_mtapi_queue_enable_visitor( + embb_mtapi_task_t * task, + void * user_data) { + embb_mtapi_queue_t * queue = (embb_mtapi_queue_t*)user_data; + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != queue); + assert(MTAPI_NULL != task); + + if (task->queue.id == queue->handle.id && + task->queue.tag == queue->handle.tag) { + /* task is retained and should be scheduled */ + embb_mtapi_task_set_state(task, MTAPI_TASK_SCHEDULED); + result = MTAPI_TRUE; + } + + return result; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_queue_hndl_t mtapi_queue_create( + MTAPI_IN mtapi_queue_id_t queue_id, + MTAPI_IN mtapi_job_hndl_t job, + MTAPI_IN mtapi_queue_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_queue_t* queue = MTAPI_NULL; + mtapi_queue_hndl_t queue_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + mtapi_queue_attributes_t attr; + + embb_mtapi_log_trace("mtapi_queue_create() called\n"); + + if (embb_mtapi_node_is_initialized()) { + queue = embb_mtapi_queue_pool_allocate(node->queue_pool); + if (MTAPI_NULL != queue) { + if (MTAPI_NULL != attributes) { + attr = *attributes; + local_status = MTAPI_SUCCESS; + } else { + mtapi_queueattr_init(&attr, &local_status); + } + if (MTAPI_SUCCESS == local_status) { + if (embb_mtapi_job_is_handle_valid(node, job)) { + embb_mtapi_queue_initialize_with_attributes_and_job( + queue, &attr, job); + /* for an ordered queue, initialize affinity */ + if (queue->attributes.ordered) { + mtapi_affinity_init( + &queue->ordered_affinity, + MTAPI_FALSE, MTAPI_NULL); + mtapi_affinity_set( + &queue->ordered_affinity, + queue->handle.id % node->scheduler->worker_count, + MTAPI_TRUE, MTAPI_NULL); + } + queue->queue_id = queue_id; + queue_hndl = queue->handle; + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + embb_mtapi_queue_pool_deallocate(node->queue_pool, queue); + } + } else { + local_status = MTAPI_ERR_QUEUE_LIMIT; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return queue_hndl; +} + +void mtapi_queue_set_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_queue_t* local_queue; + + embb_mtapi_log_trace("mtapi_queue_set_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + local_queue = embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + mtapi_queueattr_set(&local_queue->attributes, attribute_num, + attribute, attribute_size, &local_status); + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queue_get_attribute( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status + ) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_queue_t* local_queue; + + embb_mtapi_log_trace("mtapi_queue_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + local_queue = embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_QUEUE_GLOBAL: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.global, attribute, attribute_size); + break; + + case MTAPI_QUEUE_PRIORITY: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_queue->attributes.priority, attribute, attribute_size); + break; + + case MTAPI_QUEUE_LIMIT: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_queue->attributes.limit, attribute, attribute_size); + break; + + case MTAPI_QUEUE_ORDERED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.ordered, attribute, attribute_size); + break; + + case MTAPI_QUEUE_RETAIN: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.retain, attribute, attribute_size); + break; + + case MTAPI_QUEUE_DOMAIN_SHARED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_queue->attributes.domain_shared, attribute, attribute_size); + break; + + default: + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +mtapi_queue_hndl_t mtapi_queue_get( + MTAPI_IN mtapi_queue_id_t queue_id, + MTAPI_IN mtapi_domain_t domain_id, + MTAPI_OUT mtapi_status_t* status) { + mtapi_queue_hndl_t queue_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(domain_id); + + embb_mtapi_log_trace("mtapi_queue_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_uint_t ii = 0; + + local_status = MTAPI_ERR_QUEUE_INVALID; + for (ii = 0; ii < node->attributes.max_queues; ii++) { + if (queue_id == node->queue_pool->storage[ii].queue_id) { + queue_hndl = node->queue_pool->storage[ii].handle; + local_status = MTAPI_SUCCESS; + break; + } + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return queue_hndl; +} + +void mtapi_queue_delete( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queue_delete() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + embb_mtapi_thread_context_t * context = NULL; + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* cancel all tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_queue_delete_visitor, local_queue); + + /* wait for tasks in queue to finish */ + local_status = MTAPI_SUCCESS; + while (0 != embb_atomic_load_int(&local_queue->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + + /* delete queue */ + embb_mtapi_queue_finalize(local_queue); + embb_mtapi_queue_pool_deallocate(node->queue_pool, local_queue); + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queue_disable( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queue_disable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + embb_atomic_store_char(&local_queue->enabled, MTAPI_FALSE); + + /* cancel or retain all tasks scheduled via queue */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_queue_disable_visitor, local_queue); + + /* if queue is not retaining, wait for all tasks to finish */ + if (MTAPI_FALSE == local_queue->attributes.retain) { + /* find out on which thread we are */ + embb_mtapi_thread_context_t * context = + embb_mtapi_scheduler_get_current_thread_context(node->scheduler); + + embb_duration_t wait_duration; + embb_time_t end_time; + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds( + &wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* wait for tasks in queue to finish */ + local_status = MTAPI_SUCCESS; + while (0 != embb_atomic_load_int(&local_queue->num_tasks)) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + local_status = MTAPI_TIMEOUT; + break; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + } else { + local_status = MTAPI_SUCCESS; + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queue_enable( + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queue_enable() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + embb_atomic_store_char(&local_queue->enabled, MTAPI_TRUE); + local_status = MTAPI_SUCCESS; + if (local_queue->attributes.retain) { + /* reschedule retained tasks */ + embb_mtapi_scheduler_process_tasks( + node->scheduler, embb_mtapi_queue_enable_visitor, local_queue); + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/embb_mtapi_queue_t.h b/mtapi_c/src/embb_mtapi_queue_t.h new file mode 100644 index 0000000..2ccde19 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_queue_t.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_QUEUE_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_task_queue_struct embb_mtapi_task_queue_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Queue class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_queue_struct { + mtapi_queue_hndl_t handle; + + mtapi_queue_id_t queue_id; + embb_atomic_char enabled; + mtapi_job_hndl_t job_handle; + mtapi_queue_attributes_t attributes; + + embb_atomic_int num_tasks; + mtapi_affinity_t ordered_affinity; +}; + +/** + * Queue type. + * \memberof embb_mtapi_queue_struct + */ +typedef struct embb_mtapi_queue_struct embb_mtapi_queue_t; + +/** + * Default constructor. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_initialize(embb_mtapi_queue_t* that); + +/** + * Constructor with attributes and job. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_initialize_with_attributes_and_job( + embb_mtapi_queue_t* that, + mtapi_queue_attributes_t* attributes, + mtapi_job_hndl_t job); + +/** + * Destructor. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_finalize(embb_mtapi_queue_t* that); + +/** + * Notify queue that an associated Task has started. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_task_started(embb_mtapi_queue_t* that); + +/** + * Notify queue that an associated Task has finished. + * \memberof embb_mtapi_queue_struct + */ +void embb_mtapi_queue_task_finished(embb_mtapi_queue_t* that); + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(queue) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_QUEUE_T_H_ diff --git a/mtapi_c/src/embb_mtapi_scheduler_t.c b/mtapi_c/src/embb_mtapi_scheduler_t.c new file mode 100644 index 0000000..a684ac9 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_scheduler_t.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +embb_mtapi_task_t * embb_mtapi_scheduler_get_private_task_from_context( + embb_mtapi_scheduler_t * that, + embb_mtapi_thread_context_t * thread_context, + mtapi_uint_t priority) { + EMBB_UNUSED(that); + + assert(MTAPI_NULL != that); + assert(NULL != thread_context); + + embb_mtapi_task_t * task = + embb_mtapi_task_queue_pop(thread_context->private_queue[priority]); + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_public_task_from_context( + embb_mtapi_scheduler_t * that, + embb_mtapi_thread_context_t * thread_context, + mtapi_uint_t priority) { + EMBB_UNUSED(that); + + embb_mtapi_task_t * task; + + assert(MTAPI_NULL != that); + assert(NULL != thread_context); + + task = embb_mtapi_task_queue_pop(thread_context->queue[priority]); + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task_vhpf( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + embb_mtapi_task_t * task = MTAPI_NULL; + mtapi_uint_t ii = 0; + mtapi_uint_t kk = 0; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + assert(NULL != thread_context); + + for (ii = 0; + ii < node->attributes.max_priorities && MTAPI_NULL == task; + ii++) { + /* try local queues, first private. */ + task = embb_mtapi_scheduler_get_private_task_from_context( + that, thread_context, ii); + if (MTAPI_NULL == task) { + /* found nothing, so local public next. */ + task = embb_mtapi_scheduler_get_public_task_from_context( + that, thread_context, ii); + if (MTAPI_NULL == task) { + /* still nothing, steal from public queues of other workers. + the process starts at the worker "after" the current worker, + it might be better to start at a random worker + */ + mtapi_uint_t context_index = + (thread_context->worker_index + 1) % that->worker_count; + for (kk = 0; + kk < that->worker_count - 1 && MTAPI_NULL == task; + kk++) { + task = embb_mtapi_task_queue_pop( + that->worker_contexts[context_index].queue[ii]); + context_index = + (context_index + 1) % that->worker_count; + } + } + } + } + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task_lf( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + embb_mtapi_task_t * task = MTAPI_NULL; + mtapi_uint_t prio = 0; + mtapi_uint_t kk = 0; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + assert(NULL != thread_context); + + /* Try local queues on all priorities, first private. */ + for (prio = 0; + MTAPI_NULL == task && prio < node->attributes.max_priorities; + prio++) { + task = embb_mtapi_scheduler_get_private_task_from_context( + that, thread_context, prio); + } + + /* found nothing, so local public next. */ + for (prio = 0; + MTAPI_NULL == task && prio < node->attributes.max_priorities; + prio++) { + task = embb_mtapi_scheduler_get_public_task_from_context( + that, thread_context, prio); + } + + /* still nothing, steal from public queues of other workers. + the process starts at the worker "after" the current worker, + it might be better to start at a random worker + */ + for (prio = 0; + MTAPI_NULL == task && prio < node->attributes.max_priorities; + prio++) { + mtapi_uint_t context_index = + (thread_context->worker_index + 1) % that->worker_count; + for (kk = 0; + kk < that->worker_count - 1 && MTAPI_NULL == task; + kk++) { + task = embb_mtapi_task_queue_pop( + that->worker_contexts[context_index].queue[prio]); + context_index = + (context_index + 1) % that->worker_count; + } + } + return task; +} + +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + embb_mtapi_task_t * task = MTAPI_NULL; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + assert(NULL != thread_context); + + switch (that->mode) { + case WORK_STEAL_LF: + task = embb_mtapi_scheduler_get_next_task_lf( + that, node, thread_context); + break; + case WORK_STEAL_VHPF: + task = embb_mtapi_scheduler_get_next_task_vhpf( + that, node, thread_context); + break; + case NUM_SCHEDULER_MODES: + default: + embb_mtapi_log_error( + "embb_mtapi_Scheduler_getNextTask() unknown scheduler mode: %d\n", + that->mode); + } + return task; +} + +embb_mtapi_thread_context_t * embb_mtapi_scheduler_get_current_thread_context( + embb_mtapi_scheduler_t * that) { + mtapi_uint_t ii = 0; + embb_mtapi_thread_context_t * context = NULL; + + assert(MTAPI_NULL != that); + + /* find out on which thread we are */ + for (ii = 0; ii < that->worker_count; ii++) { + context = (embb_mtapi_thread_context_t*)embb_tss_get( + &(that->worker_contexts[ii].tss_id)); + if (NULL != context) { + break; + } + } + + return context; +} + +void embb_mtapi_scheduler_execute_task_or_yield( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + if (NULL != thread_context) { + embb_mtapi_task_t* new_task = embb_mtapi_scheduler_get_next_task( + that, node, thread_context); + /* if there was work, execute it */ + if (MTAPI_NULL != new_task) { + embb_mtapi_task_context_t task_context; + embb_mtapi_task_context_initialize_with_thread_context_and_task( + &task_context, thread_context, new_task); + embb_mtapi_task_execute(new_task, &task_context); + } else { + embb_thread_yield(); + } + } else { + embb_thread_yield(); + } +} + +embb_mtapi_scheduler_worker_func_t * +embb_mtapi_scheduler_worker_func(embb_mtapi_scheduler_t * that) { + EMBB_UNUSED(that); + + assert(MTAPI_NULL != that); + + /* Currently just returns embb_mtapi_scheduler_worker, + but could return any custom worker function, e.g. depending + on scheduler->mode. + */ + return &embb_mtapi_scheduler_worker; +} + +int embb_mtapi_scheduler_worker(void * arg) { + embb_mtapi_thread_context_t * thread_context = + (embb_mtapi_thread_context_t*)arg; + embb_mtapi_task_context_t task_context; + embb_mtapi_node_t * node; + embb_duration_t sleep_duration; + int err; + int counter = 0; + + embb_mtapi_log_trace( + "embb_mtapi_scheduler_worker() called for thread %d on core %d\n", + thread_context->worker_index, thread_context->core_num); + + assert(MTAPI_NULL != thread_context); + + err = embb_tss_create(&thread_context->tss_id); + if (EMBB_SUCCESS != err) { + /* report error to scheduler */ + embb_atomic_store_int(&thread_context->run, -1); + return MTAPI_FALSE; + } + + /* node is initialized here, otherwise the worker would not run */ + node = thread_context->node; + + embb_tss_set(&(thread_context->tss_id), thread_context); + + embb_duration_set_milliseconds(&sleep_duration, 10); + + /* signal that we're up & running */ + embb_atomic_store_int(&thread_context->run, 1); + /* potentially wait for node to come up completely */ + while (MTAPI_FALSE == embb_atomic_load_int(&node->is_scheduler_running)) { + embb_thread_yield(); + } + + /* do work while not requested to stop */ + while (embb_atomic_load_int(&thread_context->run)) { + /* try to get work */ + embb_mtapi_task_t * task = embb_mtapi_scheduler_get_next_task( + node->scheduler, node, thread_context); + /* check if there was work */ + if (MTAPI_NULL != task) { + embb_mtapi_queue_t * local_queue = MTAPI_NULL; + + /* is task associated with a queue? */ + if (embb_mtapi_queue_pool_is_handle_valid( + node->queue_pool, task->queue)) { + local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, task->queue); + } + + switch (task->state) { + case MTAPI_TASK_SCHEDULED: + /* there was work, execute it */ + embb_mtapi_task_context_initialize_with_thread_context_and_task( + &task_context, thread_context, task); + embb_mtapi_task_execute(task, &task_context); + /* tell queue that a task is done */ + if (MTAPI_NULL != local_queue) { + embb_mtapi_queue_task_finished(local_queue); + } + counter = 0; + break; + + case MTAPI_TASK_RETAINED: + /* put task into queue again for later execution */ + embb_mtapi_scheduler_schedule_task( + node->scheduler, task); + /* yield, as there may be only retained tasks in the queue */ + embb_thread_yield(); + /* task is not done, so do not notify queue */ + break; + + case MTAPI_TASK_CANCELLED: + /* set return value to cancelled */ + task->error_code = MTAPI_ERR_ACTION_CANCELLED; + /* tell queue that a task is done */ + if (MTAPI_NULL != local_queue) { + embb_mtapi_queue_task_finished(local_queue); + } + break; + + case MTAPI_TASK_COMPLETED: + case MTAPI_TASK_DELETED: + case MTAPI_TASK_WAITING: + case MTAPI_TASK_RUNNING: + case MTAPI_TASK_CREATED: + case MTAPI_TASK_PRENATAL: + case MTAPI_TASK_ERROR: + case MTAPI_TASK_INTENTIONALLY_UNUSED: + default: + /* do nothing, although this is an error */ + break; + } + } else if (counter < 1024) { + /* spin and yield for a while before going to sleep */ + embb_thread_yield(); + counter++; + } else { + /* no work, go to sleep */ + embb_mutex_lock(&thread_context->work_available_mutex); + embb_condition_wait_for( + &thread_context->work_available, + &thread_context->work_available_mutex, + &sleep_duration); + embb_mutex_unlock(&thread_context->work_available_mutex); + } + } + + embb_tss_delete(&(thread_context->tss_id)); + + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_scheduler_wait_for_task( + embb_mtapi_task_t * task, + mtapi_timeout_t timeout) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + embb_mtapi_thread_context_t * context = NULL; + embb_duration_t wait_duration; + embb_time_t end_time; + + assert(MTAPI_NULL != node); + assert(MTAPI_NULL != task); + + if (MTAPI_INFINITE < timeout) { + embb_duration_set_milliseconds(&wait_duration, (unsigned long long)timeout); + embb_time_in(&end_time, &wait_duration); + } + + /* find out on which thread we are */ + context = embb_mtapi_scheduler_get_current_thread_context( + node->scheduler); + + /* now wait and schedule new tasks if we are on a worker */ + while ( + (MTAPI_TASK_SCHEDULED == task->state) || + (MTAPI_TASK_RUNNING == task->state) || + (MTAPI_TASK_RETAINED == task->state) ) { + if (MTAPI_INFINITE < timeout) { + embb_time_t current_time; + embb_time_now(¤t_time); + if (embb_time_compare(¤t_time, &end_time) > 0) { + /* timeout! */ + return MTAPI_FALSE; + } + } + + /* do other work if applicable */ + embb_mtapi_scheduler_execute_task_or_yield( + node->scheduler, + node, + context); + } + + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_scheduler_initialize( + embb_mtapi_scheduler_t * that) { + return embb_mtapi_scheduler_initialize_with_mode(that, WORK_STEAL_VHPF); +} + +mtapi_boolean_t embb_mtapi_scheduler_initialize_with_mode( + embb_mtapi_scheduler_t * that, + embb_mtapi_scheduler_mode_t mode) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + mtapi_uint_t ii = 0; + + embb_mtapi_log_trace("embb_mtapi_scheduler_initialize() called\n"); + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + embb_atomic_store_int(&that->affine_task_counter, 0); + + /* Paranoia sanitizing of scheduler mode */ + if (mode < 0 || mode >= NUM_SCHEDULER_MODES) { + mode = WORK_STEAL_VHPF; + } + that->mode = mode; + + assert(node->attributes.num_cores == + embb_core_set_count(&node->attributes.core_affinity)); + that->worker_count = node->attributes.num_cores; + + that->worker_contexts = (embb_mtapi_thread_context_t*) + embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_thread_context_t)*that->worker_count); + for (ii = 0; ii < that->worker_count; ii++) { + unsigned int core_num = 0; + mtapi_uint_t ll = 0; + mtapi_boolean_t run = MTAPI_TRUE; + while (run) { + if (embb_core_set_contains(&node->attributes.core_affinity, core_num)) { + if (ll == ii) break; + ll++; + } + core_num++; + } + embb_mtapi_thread_context_initialize_with_node_worker_and_core( + &that->worker_contexts[ii], node, ii, core_num); + } + for (ii = 0; ii < that->worker_count; ii++) { + if (MTAPI_FALSE == embb_mtapi_thread_context_start( + &that->worker_contexts[ii], that)) { + /* on error return false, finalize will shut everything down */ + return MTAPI_FALSE; + } + } + return MTAPI_TRUE; +} + +void embb_mtapi_scheduler_finalize(embb_mtapi_scheduler_t * that) { + mtapi_uint_t ii = 0; + embb_mtapi_log_trace("embb_mtapi_scheduler_finalize() called\n"); + + assert(MTAPI_NULL != that); + + /* finalize all workers */ + for (ii = 0; ii < that->worker_count; ii++) { + embb_mtapi_thread_context_stop(&that->worker_contexts[ii]); + } + for (ii = 0; ii < that->worker_count; ii++) { + embb_mtapi_thread_context_finalize(&that->worker_contexts[ii]); + } + + that->worker_count = 0; + embb_mtapi_alloc_deallocate(that->worker_contexts); + that->worker_contexts = MTAPI_NULL; +} + +embb_mtapi_scheduler_t * embb_mtapi_scheduler_new() { + embb_mtapi_scheduler_t * that = + (embb_mtapi_scheduler_t*)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_scheduler_t)); + if (MTAPI_NULL != that) { + if (MTAPI_FALSE == embb_mtapi_scheduler_initialize(that)) { + /* on error delete and return MTAPI_NULL */ + embb_mtapi_scheduler_delete(that); + return MTAPI_NULL; + } + } + return that; +} + +void embb_mtapi_scheduler_delete(embb_mtapi_scheduler_t * that) { + assert(MTAPI_NULL != that); + + embb_mtapi_scheduler_finalize(that); + embb_mtapi_alloc_deallocate(that); +} + +mtapi_boolean_t embb_mtapi_scheduler_process_tasks( + embb_mtapi_scheduler_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data) { + mtapi_uint_t ii; + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + + for (ii = 0; ii < that->worker_count; ii++) { + result = embb_mtapi_thread_context_process_tasks( + &that->worker_contexts[ii], process, user_data); + if (MTAPI_FALSE == result) { + break; + } + } + + return result; +} + +mtapi_boolean_t embb_mtapi_scheduler_schedule_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_task_t * task) { + embb_mtapi_scheduler_t * scheduler = that; + /* distribute round robin */ + mtapi_uint_t ii = task->handle.id % scheduler->worker_count; + mtapi_boolean_t pushed = MTAPI_FALSE; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + + assert(MTAPI_NULL != node); + + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, task->action)) { + embb_mtapi_queue_t* local_queue = MTAPI_NULL; + /* fetch action and schedule */ + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + node->action_pool, task->action); + + mtapi_affinity_t affinity = + local_action->attributes.affinity & task->attributes.affinity; + + /* check if task is running from an ordered queue */ + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, task->queue)) { + local_queue = embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, task->queue); + if (local_queue->attributes.ordered) { + /* yes, modify affinity accordingly */ + affinity = local_queue->ordered_affinity; + } + } + + /* check affinity */ + if (affinity == 0) { + affinity = node->affinity_all; + } + + /* one more task in flight for this action */ + embb_atomic_fetch_and_add_int(&local_action->num_tasks, 1); + + if (affinity == node->affinity_all) { + /* no affinity restrictions, schedule for stealing */ + pushed = embb_mtapi_task_queue_push( + scheduler->worker_contexts[ii].queue[task->attributes.priority], + task); + } else { + mtapi_status_t affinity_status; + + /* affinity is restricted, check and adapt scheduling target */ + ii = (mtapi_uint_t)embb_atomic_fetch_and_add_int( + &scheduler->affine_task_counter, 1); + while (MTAPI_FALSE == mtapi_affinity_get( + &affinity, ii, &affinity_status)) { + ii = (ii + 1) % scheduler->worker_count; + } + /* schedule into private queue to disable stealing */ + pushed = embb_mtapi_task_queue_push( + scheduler->worker_contexts[ii].private_queue[task->attributes.priority], + task); + } + + if (pushed) { + /* signal all threads */ + for (ii = 0; ii < scheduler->worker_count; ii++) { + embb_condition_notify_one( + &scheduler->worker_contexts[ii].work_available); + } + } else { + /* task could not be launched */ + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + } + } + + return pushed; +} diff --git a/mtapi_c/src/embb_mtapi_scheduler_t.h b/mtapi_c/src/embb_mtapi_scheduler_t.h new file mode 100644 index 0000000..cd0b00c --- /dev/null +++ b/mtapi_c/src/embb_mtapi_scheduler_t.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_SCHEDULER_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_queue_struct embb_mtapi_queue_t; +typedef struct embb_mtapi_thread_context_struct embb_mtapi_thread_context_t; +typedef struct embb_mtapi_task_struct embb_mtapi_task_t; +typedef struct embb_mtapi_node_struct embb_mtapi_node_t; +typedef int (embb_mtapi_scheduler_worker_func_t)(void * args); + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Scheduler mode type + * + * \ingroup INTERNAL + */ +enum embb_mtapi_scheduler_mode_enum { + // Victim Higher Priority First. Steal if at least one local queue is empty. + WORK_STEAL_VHPF = 0, + // Local First. Steal if all local queues are empty. + WORK_STEAL_LF = 1, + + NUM_SCHEDULER_MODES +}; + +/** + * Scheduler mode type. + * \memberof embb_mtapi_scheduler_struct + */ +typedef enum embb_mtapi_scheduler_mode_enum embb_mtapi_scheduler_mode_t; + +/** + * \internal + * Scheduler class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_scheduler_struct { + mtapi_uint_t worker_count; + embb_mtapi_thread_context_t * worker_contexts; + mtapi_action_attributes_t attributes; + + // using enum value instead of function pointer to simplify testing + // for modes, like + // if (scheduler->mode == WORK_STEAL_VHPF) + embb_mtapi_scheduler_mode_t mode; + + embb_atomic_int affine_task_counter; +}; + +/** + * Scheduler type. + * \memberof embb_mtapi_scheduler_struct + */ +typedef struct embb_mtapi_scheduler_struct embb_mtapi_scheduler_t; + +/** + * The default worker thread function used by the scheduler. Implements + * scheduling strategy as defined by scheduler mode. + * \memberof embb_mtapi_scheduler_struct + */ +int embb_mtapi_scheduler_worker(void * arg); + +/** + * \internal + * Resolve the worker function for a scheduler. + * \memberof embb_mtapi_scheduler_struct + * \ingroup INTERNAL + */ +embb_mtapi_scheduler_worker_func_t * +embb_mtapi_scheduler_worker_func(embb_mtapi_scheduler_t * that); + +/** + * Wait for a given task and schedule new ones while waiting. + * \memberof embb_mtapi_scheduler_struct + */ +mtapi_boolean_t embb_mtapi_scheduler_wait_for_task( + embb_mtapi_task_t * task, + mtapi_timeout_t timeout); + +/** + * Get a task from any of the available queues. + * \memberof embb_mtapi_scheduler_struct + */ +embb_mtapi_task_t * embb_mtapi_scheduler_get_next_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context); + +/** + * Set the scheduling strategy. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_set_mode( + embb_mtapi_scheduler_t * that, + embb_mtapi_scheduler_mode_t mode); + +/** + * Determines the thread context associated with the current thread. + * \memberof embb_mtapi_scheduler_struct + */ +embb_mtapi_thread_context_t * embb_mtapi_scheduler_get_current_thread_context( + embb_mtapi_scheduler_t * that); + +/** + * Fetches and executes a single task if the thread context is valid, + * yields otherwise. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_execute_task_or_yield( + embb_mtapi_scheduler_t * that, + embb_mtapi_node_t * node, + embb_mtapi_thread_context_t * thread_context); + +/** + * operator new. + * \memberof embb_mtapi_scheduler_struct + * \returns pointer to the scheduler oder MTAPI_NULL on error + */ +embb_mtapi_scheduler_t * embb_mtapi_scheduler_new(); + +/** + * operator delete. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_delete(embb_mtapi_scheduler_t * that); + +/** + * Default constructor. Using default scheduling strategy. + * \memberof embb_mtapi_scheduler_struct + * \returns MTAPI_TRUE on success, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_scheduler_initialize(embb_mtapi_scheduler_t * that); + +/** + * Constructor allowing manual setting of scheduling strategy. + * \memberof embb_mtapi_scheduler_struct + * \returns MTAPI_TRUE on success, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_scheduler_initialize_with_mode( + embb_mtapi_scheduler_t * that, + embb_mtapi_scheduler_mode_t mode); + +/** + * Destructor. + * \memberof embb_mtapi_scheduler_struct + */ +void embb_mtapi_scheduler_finalize(embb_mtapi_scheduler_t * that); + +/** + * Apply visitor to all Tasks in the queues of the scheduler. + * \memberof embb_mtapi_scheduler_struct + */ +mtapi_boolean_t embb_mtapi_scheduler_process_tasks( + embb_mtapi_scheduler_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data); + +/** + * Put a Task into one of the queues of the scheduler, the tasks state needs + * to be either MTAPI_TASK_SCHEDULED or MTAPI_TASK_RETAINED. + * \memberof embb_mtapi_scheduler_struct + */ +mtapi_boolean_t embb_mtapi_scheduler_schedule_task( + embb_mtapi_scheduler_t * that, + embb_mtapi_task_t * task); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_SCHEDULER_T_H_ diff --git a/mtapi_c/src/embb_mtapi_spinlock_t.c b/mtapi_c/src/embb_mtapi_spinlock_t.c new file mode 100644 index 0000000..c61502f --- /dev/null +++ b/mtapi_c/src/embb_mtapi_spinlock_t.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, 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 + +void embb_mtapi_spinlock_initialize(embb_mtapi_spinlock_t * that) { + embb_atomic_store_int(that, 0); +} + +void embb_mtapi_spinlock_finalize(embb_mtapi_spinlock_t * that) { + embb_atomic_store_int(that, 0); +} + +embb_atomic_int embb_mtapi_spinlock_spins = { 0 }; + +mtapi_boolean_t embb_mtapi_spinlock_acquire(embb_mtapi_spinlock_t * that) { + int expected = 0; + while (0 == embb_atomic_compare_and_swap_int(that, &expected, 1)) { + /* empty */ + embb_atomic_fetch_and_add_int(&embb_mtapi_spinlock_spins, 1); + expected = 0; + } + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_spinlock_acquire_with_spincount( + embb_mtapi_spinlock_t * that, + mtapi_uint_t max_spin_count) { + int expected = 0; + mtapi_uint_t spin_count = max_spin_count; + while (0 == embb_atomic_compare_and_swap_int(that, &expected, 1)) { + embb_atomic_fetch_and_add_int(&embb_mtapi_spinlock_spins, 1); + spin_count--; + if (0 == spin_count) { + return MTAPI_FALSE; + } + expected = 0; + } + + return MTAPI_TRUE; +} + +mtapi_boolean_t embb_mtapi_spinlock_release(embb_mtapi_spinlock_t * that) { + int expected = 1; + return embb_atomic_compare_and_swap_int(that, &expected, 0) ? + MTAPI_TRUE : MTAPI_FALSE; +} diff --git a/mtapi_c/src/embb_mtapi_spinlock_t.h b/mtapi_c/src/embb_mtapi_spinlock_t.h new file mode 100644 index 0000000..d24521b --- /dev/null +++ b/mtapi_c/src/embb_mtapi_spinlock_t.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_SPINLOCK_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_SPINLOCK_T_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +typedef embb_atomic_int embb_mtapi_spinlock_t; + +void embb_mtapi_spinlock_initialize(embb_mtapi_spinlock_t * that); +void embb_mtapi_spinlock_finalize(embb_mtapi_spinlock_t * that); +mtapi_boolean_t embb_mtapi_spinlock_acquire(embb_mtapi_spinlock_t * that); +mtapi_boolean_t embb_mtapi_spinlock_acquire_with_spincount( + embb_mtapi_spinlock_t * that, + mtapi_uint_t max_spin_count); +mtapi_boolean_t embb_mtapi_spinlock_release(embb_mtapi_spinlock_t * that); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_SPINLOCK_T_H_ diff --git a/mtapi_c/src/embb_mtapi_task_context_t.c b/mtapi_c/src/embb_mtapi_task_context_t.c new file mode 100644 index 0000000..5e4b558 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_context_t.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_task_context_initialize_with_thread_context_and_task( + embb_mtapi_task_context_t* that, + embb_mtapi_thread_context_t* thread_context, + embb_mtapi_task_t* task) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != thread_context); + assert(MTAPI_NULL != task); + + that->task = task; + that->thread_context = thread_context; + that->num_instances = task->attributes.num_instances; + that->instance_num = embb_atomic_fetch_and_add_unsigned_int( + &task->current_instance, 1); +} + +void embb_mtapi_task_context_finalize(embb_mtapi_task_context_t* that) { + assert(MTAPI_NULL != that); + + that->instance_num = 0; + that->num_instances = 0; + that->task = MTAPI_NULL; + that->thread_context = MTAPI_NULL; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_context_status_set( + MTAPI_INOUT mtapi_task_context_t* task_context, + MTAPI_IN mtapi_status_t error_code, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_action_status_set() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + /* for remote actions the result shall be transferred to the + waiting node at the end of the task */ + switch (error_code) { + case MTAPI_SUCCESS: + case MTAPI_ERR_ARG_SIZE: + case MTAPI_ERR_RESULT_SIZE: + case MTAPI_ERR_ACTION_CANCELLED: + case MTAPI_ERR_ACTION_FAILED: + case MTAPI_ERR_ACTION_DELETED: + task_context->task->error_code = error_code; + local_status = MTAPI_SUCCESS; + break; + case MTAPI_ERR_CORE_NUM: + case MTAPI_ERR_RUNTIME_LOADBALANCING_NOTSUPPORTED: + case MTAPI_ERR_RUNTIME_REMOTETASKS_NOTSUPPORTED: + case MTAPI_ERR_ARG_NOT_IMPLEMENTED: + case MTAPI_ERR_FUNC_NOT_IMPLEMENTED: + case MTAPI_ERR_WAIT_PENDING: + case MTAPI_ERR_BUFFER_SIZE: + case MTAPI_ERR_UNKNOWN: + case MTAPI_GROUP_COMPLETED: + case MTAPI_ERR_GROUP_LIMIT: + case MTAPI_ERR_GROUP_INVALID: + case MTAPI_ERR_QUEUE_LIMIT: + case MTAPI_ERR_QUEUE_DISABLED: + case MTAPI_ERR_QUEUE_DELETED: + case MTAPI_ERR_QUEUE_INVALID: + case MTAPI_ERR_JOB_INVALID: + case MTAPI_ERR_TASK_LIMIT: + case MTAPI_ERR_TASK_INVALID: + case MTAPI_ERR_CONTEXT_OUTOFCONTEXT: + case MTAPI_ERR_CONTEXT_INVALID: + case MTAPI_ERR_ACTION_DISABLED: + case MTAPI_ERR_ACTION_NUM_INVALID: + case MTAPI_ERR_ACTION_LIMIT: + case MTAPI_ERR_ACTION_EXISTS: + case MTAPI_ERR_ACTION_INVALID: + case MTAPI_ERR_NODE_NOTINIT: + case MTAPI_ERR_DOMAIN_INVALID: + case MTAPI_ERR_NODE_INVALID: + case MTAPI_ERR_NODE_INITIALIZED: + case MTAPI_ERR_NODE_INITFAILED: + case MTAPI_ERR_ATTR_SIZE: + case MTAPI_ERR_ATTR_READONLY: + case MTAPI_ERR_PARAMETER: + case MTAPI_TIMEOUT: + case MTAPI_ERR_ATTR_NUM: + default: + /* trying to set invalid error code */ + local_status = MTAPI_ERR_PARAMETER; + break; + } + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_context_runtime_notify( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_IN mtapi_notification_t notification, + MTAPI_IN void* data, + MTAPI_IN mtapi_size_t data_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(notification); + EMBB_UNUSED(data); + EMBB_UNUSED(data_size); + + embb_mtapi_log_trace("mtapi_context_runtime_notify() called\n"); + + if (MTAPI_NULL != task_context) { + mtapi_task_context_t* local_context = (mtapi_task_context_t*) embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context) { + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); +} + +mtapi_task_state_t mtapi_context_taskstate_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_task_state_t task_state = MTAPI_TASK_ERROR; + + embb_mtapi_log_trace("mtapi_context_taskstate_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + task_state = task_context->task->state; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return task_state; +} + +/* derived from OpenMP's omp_get_thread_num(); */ +mtapi_uint_t mtapi_context_instnum_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t instnum = 0; + + embb_mtapi_log_trace("mtapi_context_instnum_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + instnum = task_context->instance_num; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return instnum; +} + +/* derived from OpenMP's omp_get_num_threads(); */ +mtapi_uint_t mtapi_context_numinst_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t numinst = 0; + + embb_mtapi_log_trace("mtapi_context_numinst_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + numinst = task_context->num_instances; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return numinst; +} + +mtapi_uint_t mtapi_context_corenum_get( + MTAPI_IN mtapi_task_context_t* task_context, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_uint_t corenum = 0; + + embb_mtapi_log_trace("mtapi_context_corenum_get() called\n"); + + if (MTAPI_NULL != task_context) { + embb_mtapi_thread_context_t* local_context = + (embb_mtapi_thread_context_t*)embb_tss_get( + &(task_context->thread_context->tss_id)); + + if (local_context == task_context->thread_context) { + corenum = task_context->thread_context->core_num; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CONTEXT_OUTOFCONTEXT; + } + } else { + local_status = MTAPI_ERR_CONTEXT_INVALID; + } + + mtapi_status_set(status, local_status); + return corenum; +} diff --git a/mtapi_c/src/embb_mtapi_task_context_t.h b/mtapi_c/src/embb_mtapi_task_context_t.h new file mode 100644 index 0000000..860b075 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_context_t.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_TASK_CONTEXT_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_thread_context_struct embb_mtapi_thread_context_t; +typedef struct embb_mtapi_task_struct embb_mtapi_task_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Task context class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_task_context_struct { + mtapi_uint_t instance_num; + mtapi_uint_t num_instances; + embb_mtapi_task_t* task; + embb_mtapi_thread_context_t* thread_context; +}; + +/** + * Task context type. + * \memberof embb_mtapi_task_context_struct + */ +typedef struct embb_mtapi_task_context_struct embb_mtapi_task_context_t; + +/** + * Constructor from a thread_context and a task. + * \memberof embb_mtapi_task_context_struct + */ +void embb_mtapi_task_context_initialize_with_thread_context_and_task( + embb_mtapi_task_context_t* that, + embb_mtapi_thread_context_t* thread_context, + embb_mtapi_task_t* task); + +/** + * Destructor. + * \memberof embb_mtapi_task_context_struct + */ +void embb_mtapi_task_context_finalize(embb_mtapi_task_context_t* that); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_CONTEXT_T_H_ diff --git a/mtapi_c/src/embb_mtapi_task_queue_t.c b/mtapi_c/src/embb_mtapi_task_queue_t.c new file mode 100644 index 0000000..6e1b0de --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_queue_t.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_task_queue_initialize(embb_mtapi_task_queue_t* that) { + assert(MTAPI_NULL != that); + + that->task_buffer = MTAPI_NULL; + that->tasks_available = 0; + that->get_task_position = 0; + that->put_task_position = 0; + mtapi_queueattr_init(&that->attributes, MTAPI_NULL); + embb_mtapi_spinlock_initialize(&that->lock); +} + +void embb_mtapi_task_queue_initialize_with_capacity( + embb_mtapi_task_queue_t* that, + mtapi_uint_t capacity) { + assert(MTAPI_NULL != that); + + that->task_buffer = (embb_mtapi_task_t **) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_task_t *)*capacity); + that->tasks_available = 0; + that->get_task_position = 0; + that->put_task_position = 0; + mtapi_queueattr_init(&that->attributes, MTAPI_NULL); + that->attributes.limit = capacity; + embb_mtapi_spinlock_initialize(&that->lock); +} + +void embb_mtapi_task_queue_finalize(embb_mtapi_task_queue_t* that) { + embb_mtapi_alloc_deallocate(that->task_buffer); + that->task_buffer = MTAPI_NULL; + + embb_mtapi_task_queue_initialize(that); + + embb_mtapi_spinlock_finalize(&that->lock); +} + +embb_mtapi_task_t * embb_mtapi_task_queue_pop(embb_mtapi_task_queue_t* that) { + embb_mtapi_task_t * task = MTAPI_NULL; + + assert(MTAPI_NULL != that); + + if (embb_mtapi_spinlock_acquire_with_spincount(&that->lock, 128)) { + if (0 < that->tasks_available) { + /* take away one task */ + that->tasks_available--; + + /* acquire position to fetch task from */ + mtapi_uint_t task_position = that->get_task_position; + that->get_task_position++; + if (that->attributes.limit <= that->get_task_position) { + that->get_task_position = 0; + } + + /* fetch task */ + task = that->task_buffer[task_position]; + + /* make task entry invalid just in case */ + that->task_buffer[task_position] = MTAPI_NULL; + } + embb_mtapi_spinlock_release(&that->lock); + } + + return task; +} + +mtapi_boolean_t embb_mtapi_task_queue_push( + embb_mtapi_task_queue_t* that, + embb_mtapi_task_t * task) { + mtapi_boolean_t result = MTAPI_FALSE; + + assert(MTAPI_NULL != that); + + if (embb_mtapi_spinlock_acquire(&that->lock)) { + if (that->attributes.limit > that->tasks_available) { + /* acquire position to put task into */ + mtapi_uint_t task_position = that->put_task_position; + that->put_task_position++; + if (that->attributes.limit <= that->put_task_position) { + that->put_task_position = 0; + } + + /* put task into buffer */ + that->task_buffer[task_position] = task; + + /* make task available */ + that->tasks_available++; + + result = MTAPI_TRUE; + } + embb_mtapi_spinlock_release(&that->lock); + } + + return result; +} + +mtapi_boolean_t embb_mtapi_task_queue_process( + embb_mtapi_task_queue_t * that, + embb_mtapi_task_visitor_function_t process, + void * user_data) { + mtapi_boolean_t result = MTAPI_TRUE; + mtapi_uint_t ii; + mtapi_uint_t idx; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != process); + + if (embb_mtapi_spinlock_acquire(&that->lock)) { + idx = that->get_task_position; + for (ii = 0; ii < that->tasks_available; ii++) { + result = process(that->task_buffer[ii], user_data); + if (MTAPI_FALSE == result) { + break; + } + idx = (idx + 1) % that->attributes.limit; + } + embb_mtapi_spinlock_release(&that->lock); + } + + return result; +} diff --git a/mtapi_c/src/embb_mtapi_task_queue_t.h b/mtapi_c/src/embb_mtapi_task_queue_t.h new file mode 100644 index 0000000..1a4b149 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_queue_t.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_TASK_QUEUE_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_task_struct embb_mtapi_task_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Task queue class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_task_queue_struct { + embb_mtapi_task_t ** task_buffer; + mtapi_uint_t tasks_available; + mtapi_uint_t get_task_position; + mtapi_uint_t put_task_position; + mtapi_queue_attributes_t attributes; + embb_mtapi_spinlock_t lock; +}; + +/** + * Task queue type. + * \memberof embb_mtapi_task_queue_struct + */ +typedef struct embb_mtapi_task_queue_struct embb_mtapi_task_queue_t; + +/** + * Default constructor. + * \memberof embb_mtapi_task_queue_struct + */ +void embb_mtapi_task_queue_initialize(embb_mtapi_task_queue_t* that); + +/** + * Constructor with configurable capacity. + * \memberof embb_mtapi_task_queue_struct + */ +void embb_mtapi_task_queue_initialize_with_capacity( + embb_mtapi_task_queue_t* that, + mtapi_uint_t capacity); + +/** + * Destructor. + * \memberof embb_mtapi_task_queue_struct + */ +void embb_mtapi_task_queue_finalize(embb_mtapi_task_queue_t* that); + +/** + * Pop a task from the queue. Returns MTAPI_NULL if the queue is empty. + * \memberof embb_mtapi_task_queue_struct + */ +embb_mtapi_task_t * embb_mtapi_task_queue_pop(embb_mtapi_task_queue_t* that); + +/** + * Push a task into the queue. Returns MTAPI_TRUE if successfull and + * MTAPI_FALSE if the queue is full or cannot be locked in time. + * \memberof embb_mtapi_task_queue_struct + */ +mtapi_boolean_t embb_mtapi_task_queue_push( + embb_mtapi_task_queue_t* that, + embb_mtapi_task_t * task); + + +/** + * Process all elements of the task queue using the given functor. + * \memberof embb_mtapi_task_queue_struct + */ +mtapi_boolean_t embb_mtapi_task_queue_process( + embb_mtapi_task_queue_t * that, + embb_mtapi_task_visitor_function_t process, + void * user_data); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_QUEUE_T_H_ diff --git a/mtapi_c/src/embb_mtapi_task_t.c b/mtapi_c/src/embb_mtapi_task_t.c new file mode 100644 index 0000000..ee82e53 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_t.c @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ---- POOL STORAGE FUNCTIONS --------------------------------------------- */ + +#include +embb_mtapi_pool_implementation(task) + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +embb_mtapi_task_t* embb_mtapi_task_new(embb_mtapi_task_pool_t* pool) { + embb_mtapi_task_t* that; + + assert(MTAPI_NULL != pool); + + that = embb_mtapi_task_pool_allocate(pool); + if (MTAPI_NULL != that) { + embb_mtapi_task_initialize(that); + } + + return that; +} + +void embb_mtapi_task_delete( + embb_mtapi_task_t* that, + embb_mtapi_task_pool_t* pool) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != pool); + + embb_mtapi_task_finalize(that); + embb_mtapi_task_pool_deallocate(pool, that); +} + +void embb_mtapi_task_initialize(embb_mtapi_task_t* that) { + assert(MTAPI_NULL != that); + + that->action.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->job.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->state = MTAPI_TASK_ERROR; + that->task_id = MTAPI_TASK_ID_NONE; + that->group.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->queue.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + that->error_code = MTAPI_SUCCESS; + embb_atomic_store_unsigned_int(&that->current_instance, 0); + embb_mtapi_spinlock_initialize(&that->state_lock); +} + +void embb_mtapi_task_finalize(embb_mtapi_task_t* that) { + assert(MTAPI_NULL != that); + + embb_mtapi_task_initialize(that); + embb_mtapi_spinlock_finalize(&that->state_lock); +} + +void embb_mtapi_task_execute( + embb_mtapi_task_t* that, + embb_mtapi_task_context_t * context) { + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != context); + + embb_mtapi_task_set_state(that, MTAPI_TASK_RUNNING); + + /* is the associated action valid? */ + if (embb_mtapi_action_pool_is_handle_valid( + context->thread_context->node->action_pool, that->action)) { + /* fetch action and execute */ + embb_mtapi_action_t* local_action = + embb_mtapi_action_pool_get_storage_for_handle( + context->thread_context->node->action_pool, that->action); + local_action->action_function( + that->arguments, + that->arguments_size, + that->result_buffer, + that->result_size, + local_action->node_local_data, + local_action->node_local_data_size, + context); + embb_atomic_memory_barrier(); + /* task has completed successfully */ + embb_mtapi_task_set_state(that, MTAPI_TASK_COMPLETED); + embb_atomic_fetch_and_add_int(&local_action->num_tasks, -1); + } else { + /* action was deleted, task did not complete */ + that->error_code = MTAPI_ERR_ACTION_DELETED; + embb_mtapi_task_set_state(that, MTAPI_TASK_ERROR); + } + + /* is task associated with a group? */ + if (embb_mtapi_group_pool_is_handle_valid( + context->thread_context->node->group_pool, that->group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + context->thread_context->node->group_pool, that->group); + embb_mtapi_task_queue_push(&local_group->queue, that); + } +} + +void embb_mtapi_task_set_state( + embb_mtapi_task_t* that, + mtapi_task_state_t state) { + assert(MTAPI_NULL != that); + + embb_mtapi_spinlock_acquire(&that->state_lock); + that->state = state; + embb_atomic_memory_barrier(); + embb_mtapi_spinlock_release(&that->state_lock); +} + +static mtapi_task_hndl_t embb_mtapi_task_start( + MTAPI_IN mtapi_task_id_t task_id, + MTAPI_IN mtapi_job_hndl_t job, + MTAPI_IN void* arguments, + MTAPI_IN mtapi_size_t arguments_size, + MTAPI_OUT void* result_buffer, + MTAPI_IN mtapi_size_t result_size, + MTAPI_IN mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_task_hndl_t task_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("embb_mtapi_task_start() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_job_is_handle_valid(node, job)) { + embb_mtapi_job_t* local_job = + embb_mtapi_job_get_storage_for_id(node, job.id); + embb_mtapi_task_t* task = embb_mtapi_task_pool_allocate(node->task_pool); + if (MTAPI_NULL != task) { + mtapi_uint_t action_index; + + embb_mtapi_task_initialize(task); + embb_mtapi_task_set_state(task, MTAPI_TASK_PRENATAL); + task->task_id = task_id; + task->job = job; + task->arguments = arguments; + task->arguments_size = arguments_size; + task->result_buffer = result_buffer; + task->result_size = result_size; + + if (MTAPI_NULL != attributes) { + task->attributes = *attributes; + } else { + mtapi_taskattr_init(&task->attributes, &local_status); + } + + if (embb_mtapi_group_pool_is_handle_valid(node->group_pool, group)) { + embb_mtapi_group_t* local_group = + embb_mtapi_group_pool_get_storage_for_handle( + node->group_pool, group); + task->group = group; + embb_atomic_fetch_and_add_int(&local_group->num_tasks, 1); + } else { + task->group.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle( + node->queue_pool, queue); + task->queue = queue; + embb_mtapi_queue_task_started(local_queue); + } else { + task->queue.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + + /* load balancing is unsupported right now, + so always choose action 0 */ + action_index = 0; + if (embb_mtapi_action_pool_is_handle_valid( + node->action_pool, local_job->actions[action_index])) { + task->action = local_job->actions[action_index]; + embb_mtapi_task_set_state(task, MTAPI_TASK_CREATED); + task_hndl = task->handle; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ACTION_INVALID; + } + + /* check priority for validity */ + if (node->attributes.max_priorities <= task->attributes.priority) { + local_status = MTAPI_ERR_PARAMETER; + } + + if (MTAPI_SUCCESS == local_status) { + embb_mtapi_scheduler_t * scheduler = node->scheduler; + mtapi_boolean_t was_scheduled; + + embb_mtapi_task_set_state(task, MTAPI_TASK_SCHEDULED); + + was_scheduled = + embb_mtapi_scheduler_schedule_task(scheduler, task); + + if (was_scheduled) { + /* if task is detached, do not return a handle, it will be deleted + on completion */ + if (task->attributes.is_detached) { + task_hndl.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + + local_status = MTAPI_SUCCESS; + } else { + /* task could not be pushed */ + local_status = MTAPI_ERR_TASK_LIMIT; + embb_mtapi_task_set_state(task, MTAPI_TASK_ERROR); + } + } + + if (MTAPI_SUCCESS != local_status) { + embb_mtapi_task_delete(task, node->task_pool); + task_hndl.id = EMBB_MTAPI_IDPOOL_INVALID_ID; + } + } else { + local_status = MTAPI_ERR_TASK_LIMIT; + } + } else { + local_status = MTAPI_ERR_JOB_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return task_hndl; +} + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +mtapi_task_hndl_t mtapi_task_start( + MTAPI_IN mtapi_task_id_t task_id, + MTAPI_IN mtapi_job_hndl_t job, + MTAPI_IN void* arguments, + MTAPI_IN mtapi_size_t arguments_size, + MTAPI_OUT void* result_buffer, /* pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /* size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT mtapi_status_t* status) { + mtapi_queue_hndl_t queue_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_task_start() called\n"); + + return embb_mtapi_task_start( + task_id, + job, + arguments, + arguments_size, + result_buffer, + result_size, + attributes, + group, + queue_hndl, + status); +} + +mtapi_task_hndl_t mtapi_task_enqueue( + MTAPI_IN mtapi_task_id_t task_id, + MTAPI_IN mtapi_queue_hndl_t queue, + MTAPI_IN void* arguments, + MTAPI_IN mtapi_size_t arguments_size, + MTAPI_OUT void* result_buffer, /* pointer to result buffer */ + MTAPI_IN mtapi_size_t result_size, /* size of one result */ + MTAPI_IN mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_group_hndl_t group, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_task_hndl_t task_hndl = { 0, EMBB_MTAPI_IDPOOL_INVALID_ID }; + + embb_mtapi_log_trace("mtapi_task_enqueue() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_queue_pool_is_handle_valid(node->queue_pool, queue)) { + embb_mtapi_queue_t* local_queue = + embb_mtapi_queue_pool_get_storage_for_handle(node->queue_pool, queue); + if ((MTAPI_TRUE == embb_atomic_load_char(&local_queue->enabled)) || + local_queue->attributes.retain) { + mtapi_task_attributes_t local_attributes; + if (MTAPI_NULL != attributes) { + local_attributes = *attributes; + } else { + mtapi_taskattr_init(&local_attributes, MTAPI_NULL); + } + local_attributes.priority = local_queue->attributes.priority; + + task_hndl = embb_mtapi_task_start( + task_id, + local_queue->job_handle, + arguments, + arguments_size, + result_buffer, + result_size, + &local_attributes, + group, + queue, + &local_status); + } else { + local_status = MTAPI_ERR_QUEUE_DISABLED; + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return task_hndl; +} + +void mtapi_task_get_attribute( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_OUT void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_task_get_attribute() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t* local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + + if (MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_TASK_DETACHED: + local_status = embb_mtapi_attr_get_mtapi_boolean_t( + &local_task->attributes.is_detached, attribute, attribute_size); + break; + + case MTAPI_TASK_INSTANCES: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_task->attributes.num_instances, attribute, attribute_size); + break; + + case MTAPI_TASK_PRIORITY: + local_status = embb_mtapi_attr_get_mtapi_uint_t( + &local_task->attributes.priority, attribute, attribute_size); + break; + + default: + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_QUEUE_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_task_wait( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_IN mtapi_timeout_t timeout, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_task_wait() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t* local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + if (embb_mtapi_scheduler_wait_for_task(local_task, timeout)) { + local_status = local_task->error_code; + + /* delete task if it is not in a group, otherwise the group will take + care of deletion */ + if (MTAPI_FALSE == embb_mtapi_group_pool_is_handle_valid( + node->group_pool, local_task->group)) { + embb_mtapi_task_delete(local_task, node->task_pool); + } + } else { + local_status = MTAPI_TIMEOUT; + } + } else { + local_status = MTAPI_ERR_TASK_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_task_cancel( + MTAPI_IN mtapi_task_hndl_t task, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_task_cancel() called\n"); + + if (embb_mtapi_node_is_initialized()) { + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + if (embb_mtapi_task_pool_is_handle_valid(node->task_pool, task)) { + embb_mtapi_task_t* local_task = + embb_mtapi_task_pool_get_storage_for_handle(node->task_pool, task); + embb_mtapi_task_set_state(local_task, MTAPI_TASK_CANCELLED); + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_TASK_INVALID; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/embb_mtapi_task_t.h b/mtapi_c/src/embb_mtapi_task_t.h new file mode 100644 index 0000000..29ac5f3 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_t.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_TASK_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_T_H_ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_task_context_struct embb_mtapi_task_context_t; +typedef struct embb_mtapi_task_pool_struct embb_mtapi_task_pool_t; + + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Task class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_task_struct { + mtapi_task_hndl_t handle; + + mtapi_task_id_t task_id; + mtapi_job_hndl_t job; + const void * arguments; + mtapi_size_t arguments_size; + void * result_buffer; + mtapi_size_t result_size; + mtapi_task_attributes_t attributes; + mtapi_group_hndl_t group; + mtapi_queue_hndl_t queue; + + mtapi_action_hndl_t action; + embb_mtapi_spinlock_t state_lock; + volatile mtapi_task_state_t state; + embb_atomic_unsigned_int current_instance; + + mtapi_status_t error_code; +}; + +/** + * Task type. + * \memberof embb_mtapi_task_struct + */ +typedef struct embb_mtapi_task_struct embb_mtapi_task_t; + +/** + * Pooled operator new. + * \memberof embb_mtapi_task_struct + */ +embb_mtapi_task_t* embb_mtapi_task_new(embb_mtapi_task_pool_t* pool); + +/** + * Pooled operator delete. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_delete( + embb_mtapi_task_t* that, + embb_mtapi_task_pool_t* pool); + +/** + * Default constructor. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_initialize(embb_mtapi_task_t* that); + +/** + * Destructor. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_finalize(embb_mtapi_task_t* that); + +/** + * Execute the action function of a task within the given context. Notfies + * the associated task group or queue if set. Deletes the task if it is + * detached. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_execute( + embb_mtapi_task_t* that, + embb_mtapi_task_context_t * context); + +/** + * Set the current task state. + * \memberof embb_mtapi_task_struct + */ +void embb_mtapi_task_set_state( + embb_mtapi_task_t* that, + mtapi_task_state_t state); + + +/* ---- POOL DECLARATION --------------------------------------------------- */ + +embb_mtapi_pool(task) + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_T_H_ diff --git a/mtapi_c/src/embb_mtapi_task_visitor_function_t.h b/mtapi_c/src/embb_mtapi_task_visitor_function_t.h new file mode 100644 index 0000000..45e15f1 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_task_visitor_function_t.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_TASK_VISITOR_FUNCTION_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_TASK_VISITOR_FUNCTION_T_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_task_struct embb_mtapi_task_t; + + +/* ---- TYPE DEFINITIONS --------------------------------------------------- */ + +typedef mtapi_boolean_t(*embb_mtapi_task_visitor_function_t) + (embb_mtapi_task_t * task, void * user_data); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_TASK_VISITOR_FUNCTION_T_H_ diff --git a/mtapi_c/src/embb_mtapi_thread_context_t.c b/mtapi_c/src/embb_mtapi_thread_context_t.c new file mode 100644 index 0000000..e0e8cef --- /dev/null +++ b/mtapi_c/src/embb_mtapi_thread_context_t.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include +#include +#include +#include +#include + + +/* ---- CLASS MEMBERS ------------------------------------------------------ */ + +void embb_mtapi_thread_context_initialize_with_node_worker_and_core( + embb_mtapi_thread_context_t* that, + embb_mtapi_node_t* node, + mtapi_uint_t worker_index, + mtapi_uint_t core_num) { + mtapi_uint_t ii; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != node); + + that->node = node; + that->worker_index = worker_index; + that->core_num = core_num; + that->priorities = node->attributes.max_priorities; + embb_atomic_store_int(&that->run, 0); + that->queue = (embb_mtapi_task_queue_t**)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_task_queue_t)*that->priorities); + that->private_queue = (embb_mtapi_task_queue_t**)embb_mtapi_alloc_allocate( + sizeof(embb_mtapi_task_queue_t)*that->priorities); + for (ii = 0; ii < that->priorities; ii++) { + that->queue[ii] = (embb_mtapi_task_queue_t*) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_task_queue_t)); + embb_mtapi_task_queue_initialize_with_capacity( + that->queue[ii], node->attributes.queue_limit); + that->private_queue[ii] = (embb_mtapi_task_queue_t*) + embb_mtapi_alloc_allocate(sizeof(embb_mtapi_task_queue_t)); + embb_mtapi_task_queue_initialize_with_capacity( + that->private_queue[ii], node->attributes.queue_limit); + } + + embb_mutex_init(&that->work_available_mutex, EMBB_MUTEX_PLAIN); + embb_condition_init(&that->work_available); +} + +mtapi_boolean_t embb_mtapi_thread_context_start( + embb_mtapi_thread_context_t* that, + embb_mtapi_scheduler_t * scheduler) { + int err; + embb_mtapi_scheduler_worker_func_t * worker_func; + embb_core_set_t core_set; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != scheduler); + + worker_func = embb_mtapi_scheduler_worker_func(scheduler); + + /* pin thread to core */ + embb_core_set_init(&core_set, 0); + embb_core_set_add(&core_set, that->core_num); + + /* create thread */ + err = embb_thread_create(&that->thread, &core_set, worker_func, that); + if (EMBB_SUCCESS != err) { + embb_mtapi_log_error( + "embb_mtapi_ThreadContext_initializeWithNodeAndCoreNumber() could not " + "create thread %d on core %d\n", that->worker_index, that->core_num); + return MTAPI_FALSE; + } + + /* wait for worker to come up */ + while (0 == embb_atomic_load_int(&that->run)) { + embb_thread_yield(); + } + + if (0 < embb_atomic_load_int(&that->run)) { + return MTAPI_TRUE; + } else { + return MTAPI_FALSE; + } +} + +void embb_mtapi_thread_context_stop(embb_mtapi_thread_context_t* that) { + int result; + if (0 < embb_atomic_load_int(&that->run)) { + embb_atomic_store_int(&that->run, 0); + embb_condition_notify_one(&that->work_available); + embb_thread_join(&(that->thread), &result); + } +} + +void embb_mtapi_thread_context_finalize(embb_mtapi_thread_context_t* that) { + mtapi_uint_t ii; + + assert(MTAPI_NULL != that); + + embb_mtapi_log_trace("embb_mtapi_thread_context_finalize() called\n"); + + embb_condition_destroy(&that->work_available); + embb_mutex_destroy(&that->work_available_mutex); + + for (ii = 0; ii < that->priorities; ii++) { + embb_mtapi_task_queue_finalize(that->queue[ii]); + embb_mtapi_alloc_deallocate(that->queue[ii]); + that->queue[ii] = MTAPI_NULL; + embb_mtapi_task_queue_finalize(that->private_queue[ii]); + embb_mtapi_alloc_deallocate(that->private_queue[ii]); + that->private_queue[ii] = MTAPI_NULL; + } + embb_mtapi_alloc_deallocate(that->queue); + that->queue = MTAPI_NULL; + embb_mtapi_alloc_deallocate(that->private_queue); + that->private_queue = MTAPI_NULL; + that->priorities = 0; + + that->node = MTAPI_NULL; +} + +mtapi_boolean_t embb_mtapi_thread_context_process_tasks( + embb_mtapi_thread_context_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data) { + mtapi_uint_t ii; + mtapi_boolean_t result = MTAPI_TRUE; + + assert(MTAPI_NULL != that); + assert(MTAPI_NULL != process); + + for (ii = 0; ii < that->priorities; ii++) { + result = embb_mtapi_task_queue_process( + that->private_queue[ii], process, user_data); + if (MTAPI_FALSE == result) { + break; + } + result = embb_mtapi_task_queue_process( + that->queue[ii], process, user_data); + if (MTAPI_FALSE == result) { + break; + } + } + + return result; +} diff --git a/mtapi_c/src/embb_mtapi_thread_context_t.h b/mtapi_c/src/embb_mtapi_thread_context_t.h new file mode 100644 index 0000000..bd78049 --- /dev/null +++ b/mtapi_c/src/embb_mtapi_thread_context_t.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, 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_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_H_ +#define MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ---- FORWARD DECLARATIONS ----------------------------------------------- */ + +typedef struct embb_mtapi_task_queue_struct embb_mtapi_task_queue_t; +typedef struct embb_mtapi_node_struct embb_mtapi_node_t; +typedef struct embb_mtapi_scheduler_struct embb_mtapi_scheduler_t; + +/* ---- CLASS DECLARATION -------------------------------------------------- */ + +/** + * \internal + * Thread context class. + * + * \ingroup INTERNAL + */ +struct embb_mtapi_thread_context_struct { + embb_mutex_t work_available_mutex; + embb_condition_t work_available; + embb_thread_t thread; + embb_tss_t tss_id; + + embb_mtapi_node_t* node; + embb_mtapi_task_queue_t** queue; + embb_mtapi_task_queue_t** private_queue; + + mtapi_uint_t priorities; + mtapi_uint_t worker_index; + mtapi_uint_t core_num; + embb_atomic_int run; + mtapi_status_t status; +}; + +/** + * Thread context type. + * \memberof embb_mtapi_thread_context_struct + */ +typedef struct embb_mtapi_thread_context_struct embb_mtapi_thread_context_t; + +/** + * Constructor using attributes from node and a given core number. + * \memberof embb_mtapi_thread_context_struct + */ +void embb_mtapi_thread_context_initialize_with_node_worker_and_core( + embb_mtapi_thread_context_t* that, + embb_mtapi_node_t* node, + mtapi_uint_t worker_index, + mtapi_uint_t core_num); + +/** + * Destructor. + * \memberof embb_mtapi_thread_context_struct + */ +void embb_mtapi_thread_context_finalize(embb_mtapi_thread_context_t* that); + +/** + * Start worker thread. + * \memberof embb_mtapi_thread_context_struct + * \returns MTAPI_TRUE if successful, MTAPI_FALSE on error + */ +mtapi_boolean_t embb_mtapi_thread_context_start( + embb_mtapi_thread_context_t* that, + embb_mtapi_scheduler_t * scheduler); + +/** + * Stop worker thread. + * \memberof embb_mtapi_thread_context_struct + */ +void embb_mtapi_thread_context_stop(embb_mtapi_thread_context_t* that); + +/** + * Apply visitor function to all tasks in the queues of the context. + * \memberof embb_mtapi_thread_context_struct + */ +mtapi_boolean_t embb_mtapi_thread_context_process_tasks( + embb_mtapi_thread_context_t* that, + embb_mtapi_task_visitor_function_t process, + void * user_data); + + +#ifdef __cplusplus +} +#endif + +#endif // MTAPI_C_SRC_EMBB_MTAPI_THREAD_CONTEXT_T_H_ diff --git a/mtapi_c/src/mtapi_action_attributes_t.c b/mtapi_c/src/mtapi_action_attributes_t.c new file mode 100644 index 0000000..308305f --- /dev/null +++ b/mtapi_c/src/mtapi_action_attributes_t.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_actionattr_init( + MTAPI_OUT mtapi_action_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_actionattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->domain_shared = MTAPI_TRUE; + attributes->global = MTAPI_TRUE; + mtapi_affinity_init(&attributes->affinity, MTAPI_TRUE, &local_status); + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_actionattr_set( + MTAPI_INOUT mtapi_action_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_actionattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_ACTION_GLOBAL: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->global, attribute, attribute_size); + break; + + case MTAPI_ACTION_AFFINITY: + local_status = embb_mtapi_attr_set_mtapi_affinity_t( + &attributes->affinity, attribute, attribute_size); + break; + + case MTAPI_ACTION_DOMAIN_SHARED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->domain_shared, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + /* this should not happen, if someone calls set, a valid action_attributes + pointer should be supplied */ + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/mtapi_affinity_t.c b/mtapi_c/src/mtapi_affinity_t.c new file mode 100644 index 0000000..baa5a99 --- /dev/null +++ b/mtapi_c/src/mtapi_affinity_t.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_affinity_init( + MTAPI_OUT mtapi_affinity_t* mask, + MTAPI_IN mtapi_boolean_t affinity, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_affinity_init() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (MTAPI_NULL != mask) { + embb_bitset_clear_all(mask); + if (affinity) { + embb_bitset_set_n(mask, node->attributes.num_cores); + } + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_affinity_set( + MTAPI_INOUT mtapi_affinity_t* mask, + MTAPI_IN mtapi_uint_t core_num, + MTAPI_IN mtapi_boolean_t affinity, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_affinity_set() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (MTAPI_NULL != mask) { + if (core_num < node->attributes.num_cores) { + if (affinity) { + embb_bitset_set(mask, core_num); + } else { + embb_bitset_clear(mask, core_num); + } + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CORE_NUM; + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +mtapi_boolean_t mtapi_affinity_get( + MTAPI_OUT mtapi_affinity_t* mask, + MTAPI_IN mtapi_uint_t core_num, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + mtapi_boolean_t affinity = MTAPI_FALSE; + embb_mtapi_node_t * node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_affinity_get() called\n"); + + if (embb_mtapi_node_is_initialized()) { + if (MTAPI_NULL != mask) { + if (core_num < node->attributes.num_cores) { + affinity = + embb_bitset_is_set(mask, core_num) ? MTAPI_TRUE : MTAPI_FALSE; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_CORE_NUM; + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); + return affinity; +} diff --git a/mtapi_c/src/mtapi_group_attributes_t.c b/mtapi_c/src/mtapi_group_attributes_t.c new file mode 100644 index 0000000..63cfacc --- /dev/null +++ b/mtapi_c/src/mtapi_group_attributes_t.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_groupattr_init( + MTAPI_OUT mtapi_group_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_groupattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->some_value = 0; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_groupattr_set( + MTAPI_INOUT mtapi_group_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + EMBB_UNUSED(attribute_num); + + embb_mtapi_log_trace("mtapi_groupattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + /* switch is not needed for now, since there are no attributes + switch (attribute_num) { + default:*/ + /* unknown attribute */ + local_status = MTAPI_ERR_ATTR_NUM; + /* break; + }*/ + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/mtapi_node_attributes_t.c b/mtapi_c/src/mtapi_node_attributes_t.c new file mode 100644 index 0000000..9343bd3 --- /dev/null +++ b/mtapi_c/src/mtapi_node_attributes_t.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_nodeattr_init( + MTAPI_OUT mtapi_node_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_nodeattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->max_tasks = MTAPI_NODE_MAX_TASKS_DEFAULT; + attributes->type = MTAPI_NODE_TYPE_SMP; + attributes->max_actions = MTAPI_NODE_MAX_ACTIONS_DEFAULT; + attributes->max_groups = MTAPI_NODE_MAX_GROUPS_DEFAULT; + attributes->max_queues = MTAPI_NODE_MAX_QUEUES_DEFAULT; + attributes->queue_limit = MTAPI_NODE_QUEUE_LIMIT_DEFAULT; + attributes->max_jobs = MTAPI_NODE_MAX_JOBS_DEFAULT; + attributes->max_actions_per_job = MTAPI_NODE_MAX_ACTIONS_PER_JOB_DEFAULT; + attributes->max_priorities = MTAPI_NODE_MAX_PRIORITIES_DEFAULT; + + embb_core_set_init(&attributes->core_affinity, 1); + attributes->num_cores = embb_core_set_count(&attributes->core_affinity); + + assert(embb_core_set_count(&attributes->core_affinity) == + embb_core_count_available()); + + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_nodeattr_set( + MTAPI_INOUT mtapi_node_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_nodeattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_NODE_CORE_AFFINITY: + if (MTAPI_NODE_CORE_AFFINITY_SIZE == attribute_size) { + attributes->core_affinity = *(embb_core_set_t*)attribute; + attributes->num_cores = + embb_core_set_count(&attributes->core_affinity); + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_ATTR_SIZE; + } + break; + + case MTAPI_NODE_NUMCORES: + local_status = MTAPI_ERR_ATTR_READONLY; + break; + + case MTAPI_NODE_TYPE: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->type, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_TASKS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_tasks, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_actions, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_GROUPS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_groups, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_QUEUES: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_queues, attribute, attribute_size); + break; + + case MTAPI_NODE_QUEUE_LIMIT: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->queue_limit, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_JOBS: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_jobs, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_ACTIONS_PER_JOB: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_actions_per_job, attribute, attribute_size); + break; + + case MTAPI_NODE_MAX_PRIORITIES: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->max_priorities, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + /* this should not happen, if someone calls set, a valid action_attributes + pointer should be supplied */ + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/mtapi_queue_attributes_t.c b/mtapi_c/src/mtapi_queue_attributes_t.c new file mode 100644 index 0000000..532a271 --- /dev/null +++ b/mtapi_c/src/mtapi_queue_attributes_t.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_queueattr_init( + MTAPI_OUT mtapi_queue_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + embb_mtapi_node_t* node = embb_mtapi_node_get_instance(); + + embb_mtapi_log_trace("mtapi_queueattr_init() called\n"); + + if (MTAPI_NULL != node) { + if (MTAPI_NULL != attributes) { + attributes->global = MTAPI_TRUE; + attributes->priority = 0; + attributes->limit = node->attributes.queue_limit; + attributes->ordered = MTAPI_TRUE; + attributes->retain = MTAPI_FALSE; + attributes->domain_shared = MTAPI_TRUE; + local_status = MTAPI_SUCCESS; + } else { + local_status = MTAPI_ERR_PARAMETER; + } + } else { + local_status = MTAPI_ERR_NODE_NOTINIT; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_queueattr_set( + MTAPI_INOUT mtapi_queue_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_queueattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_QUEUE_GLOBAL: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->global, attribute, attribute_size); + break; + + case MTAPI_QUEUE_PRIORITY: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->priority, attribute, attribute_size); + break; + + case MTAPI_QUEUE_LIMIT: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->limit, attribute, attribute_size); + break; + + case MTAPI_QUEUE_ORDERED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->ordered, attribute, attribute_size); + break; + + case MTAPI_QUEUE_RETAIN: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->retain, attribute, attribute_size); + break; + + case MTAPI_QUEUE_DOMAIN_SHARED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->domain_shared, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/src/mtapi_status_t.h b/mtapi_c/src/mtapi_status_t.h new file mode 100644 index 0000000..51af480 --- /dev/null +++ b/mtapi_c/src/mtapi_status_t.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, 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_SRC_MTAPI_STATUS_T_H_ +#define MTAPI_C_SRC_MTAPI_STATUS_T_H_ + +#include +#include + +EMBB_INLINE void mtapi_status_set( + mtapi_status_t* status, + mtapi_status_t value) { + if (MTAPI_NULL != status) { + *status = value; + } +} + +#endif // MTAPI_C_SRC_MTAPI_STATUS_T_H_ diff --git a/mtapi_c/src/mtapi_task_attributes_t.c b/mtapi_c/src/mtapi_task_attributes_t.c new file mode 100644 index 0000000..718181f --- /dev/null +++ b/mtapi_c/src/mtapi_task_attributes_t.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include +#include + + +/* ---- INTERFACE FUNCTIONS ------------------------------------------------ */ + +void mtapi_taskattr_init( + MTAPI_OUT mtapi_task_attributes_t* attributes, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_taskattr_init() called\n"); + + if (MTAPI_NULL != attributes) { + attributes->num_instances = 1; + attributes->is_detached = MTAPI_FALSE; + attributes->priority = 0; + mtapi_affinity_init(&attributes->affinity, MTAPI_TRUE, &local_status); + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} + +void mtapi_taskattr_set( + MTAPI_INOUT mtapi_task_attributes_t* attributes, + MTAPI_IN mtapi_uint_t attribute_num, + MTAPI_IN void* attribute, + MTAPI_IN mtapi_size_t attribute_size, + MTAPI_OUT mtapi_status_t* status) { + mtapi_status_t local_status = MTAPI_ERR_UNKNOWN; + + embb_mtapi_log_trace("mtapi_taskattr_set() called\n"); + + if (MTAPI_NULL != attributes) { + if (MTAPI_ATTRIBUTE_POINTER_AS_VALUE != attribute_size && + MTAPI_NULL == attribute) { + local_status = MTAPI_ERR_PARAMETER; + } else { + switch (attribute_num) { + case MTAPI_TASK_DETACHED: + local_status = embb_mtapi_attr_set_mtapi_boolean_t( + &attributes->is_detached, attribute, attribute_size); + break; + + case MTAPI_TASK_INSTANCES: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->num_instances, attribute, attribute_size); + break; + + case MTAPI_TASK_PRIORITY: + local_status = embb_mtapi_attr_set_mtapi_uint_t( + &attributes->priority, attribute, attribute_size); + break; + + case MTAPI_TASK_AFFINITY: + local_status = embb_mtapi_attr_set_mtapi_affinity_t( + &attributes->affinity, attribute, attribute_size); + break; + + default: + /* attribute unknown */ + local_status = MTAPI_ERR_ATTR_NUM; + break; + } + } + } else { + local_status = MTAPI_ERR_PARAMETER; + } + + mtapi_status_set(status, local_status); +} diff --git a/mtapi_c/test/embb_mtapi_test_config.h b/mtapi_c/test/embb_mtapi_test_config.h new file mode 100644 index 0000000..1b119ec --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_config.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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_CONFIG_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_CONFIG_H_ + +#include +#include + +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#define MTAPI_CHECK_STATUS(status) \ +if (MTAPI_SUCCESS != status) { \ + embb_mtapi_log_error("...error %d\n\n", status); \ +} \ +PT_ASSERT(MTAPI_SUCCESS == status) + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_CONFIG_H_ diff --git a/mtapi_c/test/embb_mtapi_test_group.cc b/mtapi_c/test/embb_mtapi_test_group.cc new file mode 100644 index 0000000..64d40de --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_group.cc @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 + +struct result_example_struct { + mtapi_uint_t value1; + mtapi_uint_t value2; +}; + +typedef struct result_example_struct result_example_t; + +static void testGroupAction( + const void* args, + mtapi_size_t /*arg_size*/, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context) { + result_example_t * result = + reinterpret_cast(result_buffer); + mtapi_uint_t workload_id = *reinterpret_cast(args); + int ii; + mtapi_uint_t core_num = mtapi_context_corenum_get(task_context, MTAPI_NULL); + srand(core_num); + for (ii = 10; ii < 10 + rand() % 100; ii++) { + embb_thread_yield(); + } + embb_mtapi_log_info( + "testGroupAction %d called from worker %d...\n", workload_id, core_num); + if (MTAPI_NULL != result_buffer && + sizeof(result_example_t) == result_buffer_size) { + result->value1 = workload_id; + result->value2 = core_num; + } +} + +static void testDoSomethingElse() { +} + +GroupTest::GroupTest() { + CreateUnit("mtapi group test").Add(&GroupTest::TestBasic, this, 1, 1000); +} + +void GroupTest::TestBasic() { + mtapi_info_t info; + mtapi_status_t status = MTAPI_ERR_UNKNOWN; + mtapi_action_hndl_t action; + mtapi_job_hndl_t job; + mtapi_task_attributes_t task_attributes; + mtapi_group_hndl_t group; +#define NUM_TASKS 10 + int argument[NUM_TASKS]; + const mtapi_size_t args_size = sizeof(int); + const mtapi_boolean_t att_val_true = MTAPI_TRUE; + const mtapi_size_t bool_size = sizeof(mtapi_boolean_t); + result_example_t results[NUM_TASKS]; + result_example_t* tmp_result; + int ii; + + embb_mtapi_log_info("running testGroup...\n"); + + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, &info, &status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_trace("mtapi successfully initialized...\n"); + embb_mtapi_log_trace( + "hardware concurrency : %d\n", info.hardware_concurrency); + embb_mtapi_log_trace("used memory : %d\n", info.used_memory); + + /* create action */ + action = mtapi_action_create(JOB_TEST_TASK, (testGroupAction), + MTAPI_NULL, 0, MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + MTAPI_CHECK_STATUS(status); + + /* get job */ + job = mtapi_job_get(JOB_TEST_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + /* set task attribute DETACHED because we are not interested in task + handles after the tasks have been started */ + mtapi_taskattr_init(&task_attributes, &status); + MTAPI_CHECK_STATUS(status); + mtapi_taskattr_set(&task_attributes, MTAPI_TASK_DETACHED, + reinterpret_cast(&att_val_true), bool_size, &status); + MTAPI_CHECK_STATUS(status); + + /* ---- mtapi_group_wait_all test ---- */ + + /* prepare group */ + group = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + + /* create several tasks using the same group (in this example we use the + same action for all of them, of course it is possible to use different + actions for the different tasks) */ + for (ii = 0; ii < NUM_TASKS; ii++) { + argument[ii] = ii; + /* start task */ + mtapi_task_start(MTAPI_TASK_ID_NONE, job, + reinterpret_cast(&argument[ii]), + args_size, MTAPI_NULL, 0, &task_attributes, group, &status); + MTAPI_CHECK_STATUS(status); + } + + /* do something else */ + testDoSomethingElse(); + + /* wait for completion of all tasks in the group */ + mtapi_group_wait_all(group, 10000, &status); + MTAPI_CHECK_STATUS(status); + + /* ---- mtapi_group_wait_any test ---- */ + + /* prepare group */ + group = mtapi_group_create(MTAPI_GROUP_ID_NONE, + MTAPI_DEFAULT_GROUP_ATTRIBUTES, &status); + + /* create several tasks using the same group (in this example we use the + same action for all of them, of course it is possible to use different + actions for the different tasks) */ + for (ii = 0; ii < NUM_TASKS; ii++) { + argument[ii] = ii; + /* start task */ + mtapi_task_start(MTAPI_TASK_ID_NONE, job, + reinterpret_cast(&argument[ii]), + args_size, &results[ii], sizeof(result_example_t), + &task_attributes, group, &status); + MTAPI_CHECK_STATUS(status); + } + + /* do something else */ + testDoSomethingElse(); + + /* wait for completion of tasks in the group and handle results */ + bool run = true; + while (run) { + mtapi_group_wait_any(group, reinterpret_cast(&tmp_result), + 10000, &status); + if (status == MTAPI_TIMEOUT) { + embb_mtapi_log_error("wait timed out\n"); + status = MTAPI_SUCCESS; + } + /* status will be MTAPI_ERR_RESULT_SIZE on result size mismatch */ + if (status != MTAPI_SUCCESS) { + /* MTAPI_GROUP_COMPLETED */ + run = false; + } else { + /* ... process 'tmp_result' here ... + temp_result contains the pointer that was passed at mtapi_task_start + to the task which just returned */ + embb_mtapi_log_trace( + "result.value1 = %i, result.value2 = %i\n", + tmp_result->value1, tmp_result->value2); + } + } + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_info("...done\n\n"); +} diff --git a/mtapi_c/test/embb_mtapi_test_group.h b/mtapi_c/test/embb_mtapi_test_group.h new file mode 100644 index 0000000..6116e3d --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_group.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_GROUP_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_GROUP_H_ + +#include + +class GroupTest : public partest::TestCase { + public: + GroupTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_GROUP_H_ diff --git a/mtapi_c/test/embb_mtapi_test_init_finalize.cc b/mtapi_c/test/embb_mtapi_test_init_finalize.cc new file mode 100644 index 0000000..4cf5a03 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_init_finalize.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, 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 +#include + +InitFinalizeTest::InitFinalizeTest() { + CreateUnit("mtapi init/finalize test"). + Add(&InitFinalizeTest::TestBasic, this); +} + +void InitFinalizeTest::TestBasic() { + embb_mtapi_log_info("running testInitFinalize...\n"); + + mtapi_node_attributes_t node_attr; + mtapi_info_t info; + mtapi_status_t status; + + for (int ii = 0; ii < 100; ii++) { + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_set(&node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + } + + embb_mtapi_log_info("...done\n\n"); +} diff --git a/mtapi_c/test/embb_mtapi_test_init_finalize.h b/mtapi_c/test/embb_mtapi_test_init_finalize.h new file mode 100644 index 0000000..fc66cb3 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_init_finalize.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_INIT_FINALIZE_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_INIT_FINALIZE_H_ + +#include + +class InitFinalizeTest : public partest::TestCase { + public: + InitFinalizeTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_INIT_FINALIZE_H_ diff --git a/mtapi_c/test/embb_mtapi_test_queue.cc b/mtapi_c/test/embb_mtapi_test_queue.cc new file mode 100644 index 0000000..acb5c99 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_queue.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 +#define QUEUE_TEST_ID 17 + +static void testQueueAction( + const void* args, + mtapi_size_t /*arg_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context) { + int workload_id = *reinterpret_cast(args); + int ii; + mtapi_uint_t core_num = mtapi_context_corenum_get(task_context, MTAPI_NULL); + srand(core_num); + for (ii = 1000; ii < rand()%1000000; ii ++) { + } + embb_mtapi_log_info("testQueueAction %d called from worker %d...\n", + workload_id, core_num); + EMBB_UNUSED_IN_RELEASE(workload_id); +} + +static void testDoSomethingElse() { +} + +QueueTest::QueueTest() { + CreateUnit("mtapi queue test").Add(&QueueTest::TestBasic, this); +} + +void QueueTest::TestBasic() { + mtapi_info_t info; + mtapi_status_t status; + mtapi_action_hndl_t action; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task; + mtapi_queue_hndl_t queue; + const mtapi_size_t args_size = sizeof(int); + const int args = 42; + + embb_mtapi_log_info("running testQueue...\n"); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize(THIS_DOMAIN_ID, THIS_NODE_ID, + MTAPI_DEFAULT_NODE_ATTRIBUTES, &info, &status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_trace("mtapi successfully initialized...\n"); + embb_mtapi_log_trace( + "hardware concurrency : %d\n", info.hardware_concurrency); + embb_mtapi_log_trace("used memory : %d\n", info.used_memory); + + /* create action */ + status = MTAPI_ERR_UNKNOWN; + action = mtapi_action_create(JOB_TEST_TASK, (testQueueAction), + MTAPI_NULL, 0, MTAPI_DEFAULT_ACTION_ATTRIBUTES, &status); + MTAPI_CHECK_STATUS(status); + + /* get job */ + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(JOB_TEST_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + /* create queue */ + status = MTAPI_ERR_UNKNOWN; + queue = mtapi_queue_create(QUEUE_TEST_ID, job, + MTAPI_DEFAULT_QUEUE_ATTRIBUTES, &status); + MTAPI_CHECK_STATUS(status); + + /* enqueue task in queue (repeat this for all tasks that should be + processed by that queue) */ + status = MTAPI_ERR_UNKNOWN; + task = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue, + reinterpret_cast(&args), + args_size, MTAPI_NULL, 0, MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, &status); + MTAPI_CHECK_STATUS(status); + + /* do something else */ + testDoSomethingElse(); + + /* wait for task completion */ + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task, MTAPI_INFINITE, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_queue_delete(queue, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_info("...done\n\n"); +} diff --git a/mtapi_c/test/embb_mtapi_test_queue.h b/mtapi_c/test/embb_mtapi_test_queue.h new file mode 100644 index 0000000..1b256b3 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_queue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_QUEUE_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_QUEUE_H_ + +#include + +class QueueTest : public partest::TestCase { + public: + QueueTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_QUEUE_H_ diff --git a/mtapi_c/test/embb_mtapi_test_task.cc b/mtapi_c/test/embb_mtapi_test_task.cc new file mode 100644 index 0000000..71e83b3 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_task.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 + +static void testTaskAction( + const void* args, + mtapi_size_t /*arg_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t* task_context) { + int ii; + mtapi_uint_t core_num = mtapi_context_corenum_get(task_context, MTAPI_NULL); + srand(core_num); + for (ii = 1000; ii < rand()%1000000; ii ++) { + } + embb_mtapi_log_info("testTaskAction %d called from worker %d...\n", + *reinterpret_cast(args), core_num); + EMBB_UNUSED_IN_RELEASE(args); +} + +static void testDoSomethingElse() { +} + +TaskTest::TaskTest() { + CreateUnit("mtapi task test").Add(&TaskTest::TestBasic, this); +} + +void TaskTest::TestBasic() { + mtapi_node_attributes_t node_attr; + mtapi_action_attributes_t action_attr; + mtapi_affinity_t affinity; + mtapi_info_t info; + mtapi_status_t status; + mtapi_action_hndl_t action; + mtapi_job_hndl_t job; + mtapi_task_hndl_t task[100]; + int ii; + + embb_mtapi_log_info("running testTask...\n"); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_init(&node_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_nodeattr_set( + &node_attr, + MTAPI_NODE_TYPE, + MTAPI_ATTRIBUTE_VALUE(MTAPI_NODE_TYPE_SMP), + MTAPI_ATTRIBUTE_POINTER_AS_VALUE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_initialize( + THIS_DOMAIN_ID, + THIS_NODE_ID, + &node_attr, + &info, + &status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_trace("mtapi successfully initialized...\n"); + embb_mtapi_log_trace( + "hardware concurrency : %d\n", info.hardware_concurrency); + embb_mtapi_log_trace("used memory : %d\n", info.used_memory); + + status = MTAPI_ERR_UNKNOWN; + mtapi_affinity_init(&affinity, MTAPI_TRUE, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_init(&action_attr, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_actionattr_set( + &action_attr, + MTAPI_ACTION_AFFINITY, + &affinity, + MTAPI_ACTION_AFFINITY_SIZE, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + action = mtapi_action_create( + JOB_TEST_TASK, + testTaskAction, + MTAPI_NULL, + 0, + &action_attr, + &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + job = mtapi_job_get(JOB_TEST_TASK, THIS_DOMAIN_ID, &status); + MTAPI_CHECK_STATUS(status); + + for (ii = 0; ii < 100; ii++) { + status = MTAPI_ERR_UNKNOWN; + int arg = ii; + task[ii] = mtapi_task_start( + TASK_TEST_ID, + job, + reinterpret_cast(&arg), + 0, + MTAPI_NULL, + 0, + MTAPI_DEFAULT_TASK_ATTRIBUTES, + MTAPI_GROUP_NONE, + &status); + MTAPI_CHECK_STATUS(status); + } + + testDoSomethingElse(); + + for (ii = 0; ii < 100; ii++) { + status = MTAPI_ERR_UNKNOWN; + mtapi_task_wait(task[ii], 100, &status); + MTAPI_CHECK_STATUS(status); + } + + status = MTAPI_ERR_UNKNOWN; + mtapi_action_delete(action, 10, &status); + MTAPI_CHECK_STATUS(status); + + status = MTAPI_ERR_UNKNOWN; + mtapi_finalize(&status); + MTAPI_CHECK_STATUS(status); + + embb_mtapi_log_info("...done\n\n"); +} diff --git a/mtapi_c/test/embb_mtapi_test_task.h b/mtapi_c/test/embb_mtapi_test_task.h new file mode 100644 index 0000000..3b3e839 --- /dev/null +++ b/mtapi_c/test/embb_mtapi_test_task.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_TASK_H_ +#define MTAPI_C_TEST_EMBB_MTAPI_TEST_TASK_H_ + +#include + +class TaskTest : public partest::TestCase { + public: + TaskTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_C_TEST_EMBB_MTAPI_TEST_TASK_H_ diff --git a/mtapi_c/test/main.cc b/mtapi_c/test/main.cc new file mode 100644 index 0000000..c2548ac --- /dev/null +++ b/mtapi_c/test/main.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include + +#include +#include +#include +#include + +PT_MAIN("MTAPI C") { + embb_log_set_log_level(EMBB_LOG_LEVEL_NONE); + + PT_RUN(InitFinalizeTest); + PT_RUN(TaskTest); + PT_RUN(GroupTest); + PT_RUN(QueueTest); +} diff --git a/mtapi_cpp/CMakeLists.txt b/mtapi_cpp/CMakeLists.txt new file mode 100644 index 0000000..1f2c7e4 --- /dev/null +++ b/mtapi_cpp/CMakeLists.txt @@ -0,0 +1,35 @@ +project (project_mtapi_cpp) + +file(GLOB_RECURSE EMBB_MTAPI_CPP_SOURCES "src/*.cc" "src/*.h") +file(GLOB_RECURSE EMBB_MTAPI_CPP_HEADERS "include/*.h") +file(GLOB_RECURSE EMBB_MTAPI_CPP_TEST_SOURCES "test/*.cc" "test/*.h") + +# Execute the GroupSources macro +include(../CMakeCommon/GroupSourcesMSVC.cmake) +GroupSourcesMSVC(include) +GroupSourcesMSVC(src) +GroupSourcesMSVC(test) + +set (EMBB_MTAPI_CPP_INCLUDE_DIRS "include" "src" "test") +include_directories(${EMBB_MTAPI_CPP_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../base_c/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_c/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base_cpp/include + ${CMAKE_CURRENT_BINARY_DIR}/../base_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/../mtapi_c/include + ) + +add_library (embb_mtapi_cpp ${EMBB_MTAPI_CPP_SOURCES} ${EMBB_MTAPI_CPP_HEADERS}) +target_link_libraries(embb_mtapi_cpp embb_mtapi_c) + +if (BUILD_TESTS STREQUAL ON) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/../partest/include) + add_executable (embb_mtapi_cpp_test ${EMBB_MTAPI_CPP_TEST_SOURCES}) + target_link_libraries(embb_mtapi_cpp_test embb_mtapi_cpp embb_mtapi_c partest + embb_base_cpp embb_base_c ${compiler_libs}) + CopyBin(BIN embb_mtapi_cpp_test DEST ${local_install_dir}) +endif() + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include FILES_MATCHING PATTERN "*.h") +install(TARGETS embb_mtapi_cpp DESTINATION lib) diff --git a/mtapi_cpp/include/embb/mtapi/action.h b/mtapi_cpp/include/embb/mtapi/action.h new file mode 100644 index 0000000..240d323 --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/action.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_ACTION_H_ +#define EMBB_MTAPI_ACTION_H_ + +#include +#include +#include + +namespace embb { +namespace mtapi { + +/** + * A function to be spawned as a Task. + * + * \ingroup CPP_MTAPI + */ +class Action { + public: + /** + * Constructs an empty Action. + */ + Action() + : function_() + , affinity_() { + // empty + } + + /** + * Constructs an Action from any entity that provides an + * operator() (TaskContext &). + */ + template + Action( + Function func /**< [in] Anything that provides an + operator() (TaskContext &). */ + ) + : function_(func) + , affinity_() { + // empty + } + + /** + * Constructs an Action from any entity that provides an + * operator() (TaskContext &) and an Affinity. + */ + template + Action( + Function func, /**< [in] Anything that provides an + operator() (TaskContext &). */ + Affinity affinity /**< [in] Core affinity */ + ) + : function_(func) + , affinity_(affinity) { + // empty + } + + /** + * Executes the Action in a given TaskContext. + */ + void operator() ( + TaskContext & context /**< [in, out] Context the operator + is executed in */ + ) { + function_(context); + } + + /** + * Returns the Affinity specified during creation. + * \return The Affinity of the Action + * \waitfree + */ + Affinity GetAffinity() const { + return affinity_; + } + + private: + embb::base::Function function_; + Affinity affinity_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_ACTION_H_ diff --git a/mtapi_cpp/include/embb/mtapi/affinity.h b/mtapi_cpp/include/embb/mtapi/affinity.h new file mode 100644 index 0000000..486d51d --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/affinity.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_AFFINITY_H_ +#define EMBB_MTAPI_AFFINITY_H_ + +#include + +namespace embb { +namespace mtapi { + +/** + * Describes the Affinity of a Task to worker threads. + * + * \ingroup CPP_MTAPI + */ +class Affinity { + public: + /** + * Constructs an Affinity including all worker threads. + * \memory Calls embb::mtapi::Node::Initialize() which potentially allocates + * \throws ErrorException if the Affinity object could not be constructed. + */ + Affinity(); + + /** + * Constructs an Affinity including all or no worker threads. + * \memory Calls embb::mtapi::Node::Initialize() which potentially allocates + * \throws ErrorException if the Affinity object could not be constructed. + */ + Affinity( + bool initial_affinity /**< [in] Initial affinity + (true = all worker threads, + false = no worker threads) */ + ); + + /** + * Sets Affinity to a specific worker thread. + * \threadsafe + */ + void Add( + mtapi_uint_t worker /**< [in] Worker thread index */ + ); + + /** + * Removes Affinity to a specific worker thread. + * \threadsafe + */ + void Remove( + mtapi_uint_t worker /**< [in] Worker thread index */ + ); + + /** + * Checks if Affinity to a specific worker thread is set. + * \return \c true if \c *this is affine to the given worker, otherwise + * \c false. + * \threadsafe + */ + bool IsSet( + mtapi_uint_t worker /**< [in] Worker thread index */ + ); + + friend class Task; + + private: + mtapi_affinity_t affinity_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_AFFINITY_H_ diff --git a/mtapi_cpp/include/embb/mtapi/continuation.h b/mtapi_cpp/include/embb/mtapi/continuation.h new file mode 100644 index 0000000..94b21f2 --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/continuation.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_CONTINUATION_H_ +#define EMBB_MTAPI_CONTINUATION_H_ + +#include +#include +#include +#include + +namespace embb { +namespace mtapi { + +/** + * Helper struct for Continuation. + * + * \ingroup CPP_MTAPI + */ +struct ContinuationStage; + +/** + * A Continuation encapsulates a chain of \link Action Actions \endlink to be + * executed consecutively. + * + * \ingroup CPP_MTAPI + */ +class Continuation { + public: + /** + * Copy constructor. + */ + Continuation( + Continuation const & cont /**< [in] The Continuation to copy. */ + ); + + /** + * Destructor. + */ + ~Continuation(); + + /** + * Appends an Action to the Continuation chain. + * \returns A reference to this Continuation chain. + * \notthreadsafe + */ + Continuation & Then( + Action action /**< [in] The Action to append to the + continuation */ + ); + + /** + * Runs the Continuation chain. + * \returns The Task representing the Continuation chain. + * \notthreadsafe + */ + Task Spawn(); + + /** + * Runs the Continuation chain with the specified priority. + * \returns The Task representing the Continuation chain. + * \notthreadsafe + */ + Task Spawn( + mtapi_uint_t priority /**< [in] The priority to use */ + ); + + friend class Node; + + private: + explicit Continuation(Action action); + + void ExecuteContinuation(TaskContext & context); + + ContinuationStage * first_; + ContinuationStage * last_; + + mtapi_uint_t priority_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_CONTINUATION_H_ diff --git a/mtapi_cpp/include/embb/mtapi/group.h b/mtapi_cpp/include/embb/mtapi/group.h new file mode 100644 index 0000000..c2baea8 --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/group.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_GROUP_H_ +#define EMBB_MTAPI_GROUP_H_ + +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace mtapi { + +/** + * Represents a facility to wait for multiple related + * \link Task Tasks\endlink. + * + * \ingroup CPP_MTAPI + */ +class Group { + public: + /** + * Runs an Action within the Group. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action within the Group with the specified priority. + * \return A Task identifying the Action to run + * \threadsafe + */ + Task Spawn( + Action action, /**< [in] The Action to run */ + mtapi_uint_t priority /**< [in] The priority to use */ + ); + + /** + * Runs an Action within the Group. The \c id is returned by WaitAny(). + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + mtapi_task_id_t id, /**< [in] The id to return by + WaitAny() */ + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action within the Group with the specified priority. The \c id is + * returned by WaitAny(). + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + mtapi_task_id_t id, /**< [in] The id to return by + WaitAny() */ + Action action, /**< [in] The Action to run */ + mtapi_uint_t priority /**< [in] The priority to use */ + ); + + /** + * Waits for any Task in the Group to finish for \c timeout milliseconds. + * \return The status of the Task that finished execution + * \threadsafe + */ + mtapi_status_t WaitAny( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ); + + /** + * Waits for any Task in the Group to finish for \c timeout milliseconds and + * retrieves the id given in Spawn(). + * \return The status of the Task that finished execution, \c MTAPI_TIMEOUT + * or \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t WaitAny( + mtapi_timeout_t timeout, /**< [in] Timeout duration in + milliseconds */ + mtapi_task_id_t & id /**< [out] The id given to Spawn() */ + ); + + /** + * Waits for all Task in the Group to finish for \c timeout milliseconds. + * \return \c MTAPI_SUCCESS, \c MTAPI_TIMEOUT, \c MTAPI_ERR_* or the status + * of any failed Task + * \threadsafe + */ + mtapi_status_t WaitAll( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ); + + friend class embb::base::Allocation; + friend class Node; + friend class Queue; + + private: + Group(Group const & group); + Group(); + ~Group(); + + void Create(); + + mtapi_group_hndl_t handle_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_GROUP_H_ diff --git a/mtapi_cpp/include/embb/mtapi/mtapi.h b/mtapi_cpp/include/embb/mtapi/mtapi.h new file mode 100644 index 0000000..68138dd --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/mtapi.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_MTAPI_H_ +#define EMBB_MTAPI_MTAPI_H_ + +/** + * \defgroup CPP_MTAPI MTAPI + * C++ wrapper around C implementation of MTAPI. + * For a description of the basic concepts, see the + * \ref C_MTAPI "C implementation of MTAPI". + * \ingroup CPP + */ + +#define MTAPI_CPP_TASK_JOB 1 +#define MTAPI_CPP_AUTOMATIC_INITIALIZE 1 +#if MTAPI_CPP_AUTOMATIC_INITIALIZE +#define MTAPI_CPP_AUTOMATIC_DOMAIN_ID 1 +#define MTAPI_CPP_AUTOMATIC_NODE_ID 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // EMBB_MTAPI_MTAPI_H_ diff --git a/mtapi_cpp/include/embb/mtapi/node.h b/mtapi_cpp/include/embb/mtapi/node.h new file mode 100644 index 0000000..8a5102c --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/node.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_NODE_H_ +#define EMBB_MTAPI_NODE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace mtapi { + +/** + * A singleton representing the MTAPI runtime. + * + * \ingroup CPP_MTAPI + */ +class Node { + public: + /** + * Initializes the runtime singleton using default values. + * \notthreadsafe + * \throws ErrorException if the singleton was already initialized or the + * Node could not be initialized. + * \memory Allocates about 200kb of memory. + */ + static void Initialize( + mtapi_domain_t domain_id, /**< [in] The domain id to use */ + mtapi_node_t node_id /**< [in] The node id to use */ + ); + + /** + * Initializes the runtime singleton. + * \notthreadsafe + * \throws ErrorException if the singleton was already initialized or the + * Node could not be initialized. + * \memory Allocates some memory depending on the values given. + */ + static void Initialize( + mtapi_domain_t domain_id, /**< [in] The domain id to use */ + mtapi_node_t node_id, /**< [in] The node id to use */ + embb::base::CoreSet const & core_set, + /**< [in] A set of cores MTAPI should + use for its worker threads */ + mtapi_uint_t max_tasks, /**< [in] Maximum number of concurrent + \link Task Tasks \endlink */ + mtapi_uint_t max_groups, /**< [in] Maximum number of concurrent + \link Group Groups \endlink */ + mtapi_uint_t max_queues, /**< [in] Maximum number of concurrent + \link Queue Queues \endlink */ + mtapi_uint_t queue_limit, /**< [in] Maximum Queue capacity */ + mtapi_uint_t max_priorities /**< [in] Maximum number of priorities, + priorities will be between 0 and + max_priorities-1 */ + ); + + /** + * Checks if runtime is initialized. + * \return \c true if the Node singleton is already initialized, false + * otherwise + * \waitfree + */ + static bool IsInitialized(); + + /** + * Gets the instance of the runtime system. + * \return Reference to the Node singleton + * \threadsafe + */ + static Node & GetInstance(); + + /** + * Shuts the runtime system down. + * \throws ErrorException if the singleton is not initialized. + * \notthreadsafe + */ + static void Finalize(); + + /** + * Returns the number of available cores. + * \return The number of available cores + * \waitfree + */ + mtapi_uint_t GetCoreCount() const { + return core_count_; + } + + /** + * Creates a Group to launch \link Task Tasks \endlink in. + * \return A reference to the created Group + * \throws ErrorException if the Group object could not be constructed. + * \threadsafe + * \memory Allocates some memory depending on the configuration of the + * runtime. + */ + Group & CreateGroup(); + + /** + * Destroys a Group. \link Task Tasks \endlink running in the Group will + * finish execution. + * \threadsafe + */ + void DestroyGroup( + Group & group /**< [in,out] The Group to destroy */ + ); + + /** + * Creates a Queue for stream processing. The queue might execute its + * \link Task Tasks \endlink either in order or unordered. + * \return A reference to the new Queue + * \throws ErrorException if the Queue object could not be constructed. + * \threadsafe + * \memory Allocates some memory depending on the configuration of the + * runtime. + */ + Queue & CreateQueue( + mtapi_uint_t priority, /**< [in] Priority of the Queue */ + bool ordered /**< [in] \c true if the Queue should be + ordered, otherwise \c false */ + ); + + /** + * Destroys a Queue. Running \link Task Tasks \endlink will be canceled. + * \threadsafe + */ + void DestroyQueue( + Queue & queue /**< [in,out] The Queue to destroy */ + ); + + /** + * Runs an Action. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action /**< [in] The Action to execute */ + ); + + /** + * Runs an Action with the specified priority. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action, /**< [in] The Action to execute */ + mtapi_uint_t priority /**< [in] The priority to use */ + ); + + /** + * Creates a Continuation. + * \return A Continuation chain + * \threadsafe + */ + Continuation First( + Action action /**< [in] The first Action of the + Continuation chain */ + ); + + friend class embb::base::Allocation; + + private: + Node(Node const & node); + Node( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + mtapi_node_attributes_t * attr); + ~Node(); + + static void action_func( + const void* args, + mtapi_size_t args_size, + void* result_buffer, + mtapi_size_t result_buffer_size, + const void* node_local_data, + mtapi_size_t node_local_data_size, + mtapi_task_context_t * context); + + mtapi_uint_t core_count_; + mtapi_action_hndl_t action_handle_; + std::list queues_; + std::list groups_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_NODE_H_ diff --git a/mtapi_cpp/include/embb/mtapi/queue.h b/mtapi_cpp/include/embb/mtapi/queue.h new file mode 100644 index 0000000..5ffd926 --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/queue.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_QUEUE_H_ +#define EMBB_MTAPI_QUEUE_H_ + +#include +#include +#include +#include + +namespace embb { + +namespace base { + +class Allocation; + +} // namespace base + +namespace mtapi { + +/** + * Allows for stream processing, either ordered or unordered. + * + * \ingroup CPP_MTAPI + */ +class Queue { + public: + /** + * Enables the Queue. \link Task Tasks \endlink enqueued while the Queue was + * disabled are executed. + * \waitfree + */ + void Enable(); + + /** + * Disables the Queue. Running \link Task Tasks \endlink are canceled. + * \waitfree + */ + void Disable(); + + /** + * Runs an Action. + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action in the specified Group + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + Group const * group, /**< [in] The Group to run the Action + in */ + Action action /**< [in] The Action to run */ + ); + + /** + * Runs an Action in the specified Group. The \c id is returned by + * Group::WaitAny(). + * \return A Task identifying the Action to run + * \throws ErrorException if the Task object could not be constructed. + * \threadsafe + */ + Task Spawn( + mtapi_task_id_t id, /**< [in] The id to return in + Group::WaitAny() */ + Group const * group, /**< [in] The Group to run the Action + in */ + Action action /**< [in] The Action to run */ + ); + + friend class embb::base::Allocation; + friend class Node; + + private: + Queue(Queue const & taskqueue); + Queue(mtapi_uint_t priority, bool ordered); + ~Queue(); + + mtapi_queue_hndl_t handle_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_QUEUE_H_ diff --git a/mtapi_cpp/include/embb/mtapi/task.h b/mtapi_cpp/include/embb/mtapi/task.h new file mode 100644 index 0000000..27badf5 --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/task.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_TASK_H_ +#define EMBB_MTAPI_TASK_H_ + +#include +#include + +namespace embb { +namespace mtapi { + +/** + * A Task represents a running Action. + * + * \ingroup CPP_MTAPI + */ +class Task { + public: + /** + * Constructs an empty Task + */ + Task(); + + /** + * Copies a Task + */ + Task( + Task const & task /**< The task to copy. */ + ); + + /** + * Destroys a Task + */ + ~Task(); + + /** + * Waits for Task to finish for \c timeout milliseconds. + * \return The status of the finished Task, \c MTAPI_TIMEOUT or + * \c MTAPI_ERR_* + * \threadsafe + */ + mtapi_status_t Wait( + mtapi_timeout_t timeout /**< [in] Timeout duration in + milliseconds */ + ); + + /** + * Signals the Task to cancel computation. + * \waitfree + */ + void Cancel(); + + friend class Group; + friend class Queue; + friend class Node; + + private: + Task( + Action action, + mtapi_uint_t priority + ); + + Task( + Action action, + mtapi_group_hndl_t group, + mtapi_uint_t priority); + + Task( + mtapi_task_id_t id, + Action action, + mtapi_group_hndl_t group, + mtapi_uint_t priority); + + Task( + Action action, + mtapi_queue_hndl_t queue, + mtapi_uint_t priority); + + Task( + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group, + mtapi_uint_t priority); + + Task( + mtapi_task_id_t id, + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group, + mtapi_uint_t priority); + + mtapi_task_hndl_t handle_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_TASK_H_ diff --git a/mtapi_cpp/include/embb/mtapi/taskcontext.h b/mtapi_cpp/include/embb/mtapi/taskcontext.h new file mode 100644 index 0000000..d1aeb0c --- /dev/null +++ b/mtapi_cpp/include/embb/mtapi/taskcontext.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, 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 EMBB_MTAPI_TASKCONTEXT_H_ +#define EMBB_MTAPI_TASKCONTEXT_H_ + +#include + +namespace embb { +namespace mtapi { + +/** + * Provides information about the status of the currently running Task. + * + * \ingroup CPP_MTAPI + */ +class TaskContext { + public: + /** + * Queries whether the Task running in the TaskContext should finish. + * \return \c true if the Task should finish, otherwise \c false + * \notthreadsafe + */ + bool ShouldCancel(); + + /** + * Queries the index of the worker thread the Task is running on. + * \return The worker thread index the Task is running on + * \notthreadsafe + */ + mtapi_uint_t GetCurrentCoreNumber(); + + /** + * Sets the return status of the running Task. This will be returned by + * Task::Wait() and is set to \c MTAPI_SUCCESS by default. + * \notthreadsafe + */ + void SetStatus( + mtapi_status_t error_code /**< [in] The status to return by + Task::Wait(), Group::WaitAny(), + Group::WaitAll() */ + ); + + friend class Node; + + private: + explicit TaskContext(mtapi_task_context_t * task_context); + + mtapi_task_context_t * context_; +}; + +} // namespace mtapi +} // namespace embb + +#endif // EMBB_MTAPI_TASKCONTEXT_H_ diff --git a/mtapi_cpp/src/affinity.cc b/mtapi_cpp/src/affinity.cc new file mode 100644 index 0000000..2dddbe4 --- /dev/null +++ b/mtapi_cpp/src/affinity.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +namespace embb { +namespace mtapi { + +Affinity::Affinity() { +#if MTAPI_CPP_AUTOMATIC_INITIALIZE + Node::GetInstance(); // MTAPI has to be initialized +#endif + mtapi_status_t status; + mtapi_affinity_init(&affinity_, MTAPI_TRUE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "Could not default construct Affinity."); + } +} + +Affinity::Affinity(bool initial_affinity) { +#if MTAPI_CPP_AUTOMATIC_INITIALIZE + Node::GetInstance(); // MTAPI has to be initialized +#endif + mtapi_status_t status; + mtapi_boolean_t aff = initial_affinity ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_affinity_init(&affinity_, aff, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "Could not default construct Affinity."); + } +} + +void Affinity::Add(mtapi_uint_t worker) { + mtapi_status_t status; + mtapi_affinity_set(&affinity_, worker, MTAPI_TRUE, &status); + assert(MTAPI_SUCCESS == status); +} + +void Affinity::Remove(mtapi_uint_t worker) { + mtapi_status_t status; + mtapi_affinity_set(&affinity_, worker, MTAPI_FALSE, &status); + assert(MTAPI_SUCCESS == status); +} + +bool Affinity::IsSet(mtapi_uint_t worker) { + mtapi_status_t status; + mtapi_boolean_t aff = mtapi_affinity_get(&affinity_, worker, &status); + assert(MTAPI_SUCCESS == status); + return MTAPI_TRUE == aff; +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/src/continuation.cc b/mtapi_cpp/src/continuation.cc new file mode 100644 index 0000000..031bd4b --- /dev/null +++ b/mtapi_cpp/src/continuation.cc @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include +#include + +#include + +namespace embb { +namespace mtapi { + +Continuation::Continuation(Action action) { + first_ = last_ = embb::base::Allocation::New(); + first_->action = action; + first_->next = NULL; +} + +Continuation::Continuation(Continuation const & cont) + : first_(cont.first_) + , last_(cont.last_) { +} + +Continuation::~Continuation() { +} + +void Continuation::ExecuteContinuation(TaskContext &) { + mtapi::ContinuationStage * stage = first_; + mtapi::Node & node = mtapi::Node::GetInstance(); + while (NULL != stage) { + mtapi::Task task = node.Spawn(stage->action, priority_); + task.Wait(MTAPI_INFINITE); + stage = stage->next; + } + + // delete stages + stage = first_; + while (NULL != stage) { + mtapi::ContinuationStage * next = stage->next; + embb::base::Allocation::Delete(stage); + stage = next; + } +} + +Continuation & Continuation::Then(Action action) { + ContinuationStage * cur = embb::base::Allocation::New(); + cur->action = action; + cur->next = NULL; + + last_->next = cur; + last_ = cur; + + return *this; +} + +Task Continuation::Spawn() { + return Spawn(0); +} + +Task Continuation::Spawn(mtapi_uint_t priority) { + priority_ = priority; + Node & node = Node::GetInstance(); + return node.Spawn( + embb::base::MakeFunction(*this, &Continuation::ExecuteContinuation), + priority); +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/src/continuationstage.h b/mtapi_cpp/src/continuationstage.h new file mode 100644 index 0000000..b649b01 --- /dev/null +++ b/mtapi_cpp/src/continuationstage.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, 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_CPP_SRC_CONTINUATIONSTAGE_H_ +#define MTAPI_CPP_SRC_CONTINUATIONSTAGE_H_ + +#include + +namespace embb { +namespace mtapi { + +struct ContinuationStage { + mtapi::Action action; + ContinuationStage * next; +}; + +} // namespace mtapi +} // namespace embb + +#endif // MTAPI_CPP_SRC_CONTINUATIONSTAGE_H_ diff --git a/mtapi_cpp/src/group.cc b/mtapi_cpp/src/group.cc new file mode 100644 index 0000000..adffa76 --- /dev/null +++ b/mtapi_cpp/src/group.cc @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include +#include + +namespace embb { +namespace mtapi { + +Group::Group() { + Create(); +} + +Group::~Group() { + mtapi_status_t status; + mtapi_group_delete(handle_, &status); + assert(MTAPI_SUCCESS == status); +} + +void Group::Create() { + mtapi_status_t status; + handle_ = mtapi_group_create(MTAPI_GROUP_ID_NONE, MTAPI_NULL, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Group could not be constructed"); + } +} + +Task Group::Spawn(Action action) { + return Spawn(action, 0); +} + +Task Group::Spawn(Action action, mtapi_uint_t priority) { + return Task(action, handle_, priority); +} + +Task Group::Spawn(mtapi_task_id_t id, Action action) { + return Spawn(id, action, 0); +} + +Task Group::Spawn(mtapi_task_id_t id, Action action, mtapi_uint_t priority) { + return Task(id, action, handle_, priority); +} + +mtapi_status_t Group::WaitAny(mtapi_timeout_t timeout) { + mtapi_status_t status; + mtapi_group_wait_any(handle_, MTAPI_NULL, timeout, &status); + if (MTAPI_GROUP_COMPLETED == status) { + // group has been deleted, so recreate it for simplicity + Create(); + } + return status; +} + +mtapi_status_t Group::WaitAny( + mtapi_timeout_t timeout, + mtapi_task_id_t & result) { + mtapi_status_t status; + void * res; + mtapi_group_wait_any(handle_, &res, timeout, &status); + memcpy(&result, &res, sizeof(result)); + if (MTAPI_GROUP_COMPLETED == status) { + // group has been deleted, so recreate it for simplicity + Create(); + } + return status; +} + +mtapi_status_t Group::WaitAll(mtapi_timeout_t timeout) { + mtapi_status_t status; + mtapi_group_wait_all(handle_, timeout, &status); + // group has been deleted, so recreate it for simplicity + Create(); + return status; +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/src/node.cc b/mtapi_cpp/src/node.cc new file mode 100644 index 0000000..10ae981 --- /dev/null +++ b/mtapi_cpp/src/node.cc @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2014, 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 +#include +#include +#include + +#include +#include +#include +#if MTAPI_CPP_AUTOMATIC_INITIALIZE +#include +#endif + +namespace { + +static embb::mtapi::Node * node_instance = NULL; +static embb::base::Mutex init_mutex; + +} + +namespace embb { +namespace mtapi { + +void Node::action_func( + const void* args, + mtapi_size_t /*args_size*/, + void* /*result_buffer*/, + mtapi_size_t /*result_buffer_size*/, + const void* /*node_local_data*/, + mtapi_size_t /*node_local_data_size*/, + mtapi_task_context_t * context) { + mtapi::Action * action = + reinterpret_cast(const_cast(args)); + mtapi::TaskContext task_context(context); + (*action)(task_context); + embb::base::Allocation::Delete(action); +} + +Node::Node( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + mtapi_node_attributes_t * attr) { + mtapi_status_t status; + mtapi_info_t info; + mtapi_initialize(domain_id, node_id, attr, &info, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node could not initialize mtapi"); + } + core_count_ = info.hardware_concurrency; + action_handle_ = mtapi_action_create(MTAPI_CPP_TASK_JOB, action_func, + MTAPI_NULL, 0, MTAPI_NULL, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node could not create an action"); + } +} + +Node::~Node() { + for (std::list::iterator ii = queues_.begin(); + ii != queues_.end(); + ++ii) { + embb::base::Allocation::Delete(*ii); + } + queues_.clear(); + + for (std::list::iterator ii = groups_.begin(); + ii != groups_.end(); + ++ii) { + embb::base::Allocation::Delete(*ii); + } + groups_.clear(); + + mtapi_status_t status; + mtapi_action_delete(action_handle_, MTAPI_INFINITE, &status); + assert(MTAPI_SUCCESS == status); + mtapi_finalize(&status); + assert(MTAPI_SUCCESS == status); +} + +void Node::Initialize( + mtapi_domain_t domain_id, + mtapi_node_t node_id) { + if (IsInitialized()) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node was already initialized"); + } else { + mtapi_status_t status; + mtapi_node_attributes_t attr; + mtapi_uint_t tmp; + mtapi_nodeattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_JOBS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + tmp = 1; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS_PER_JOB, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + node_instance = embb::base::Allocation::New( + domain_id, node_id, &attr); + } +} + +void Node::Initialize( + mtapi_domain_t domain_id, + mtapi_node_t node_id, + embb::base::CoreSet const & core_set, + mtapi_uint_t max_tasks, + mtapi_uint_t max_groups, + mtapi_uint_t max_queues, + mtapi_uint_t queue_limit, + mtapi_uint_t max_priorities) { + if (IsInitialized()) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node was already initialized"); + } else { + mtapi_status_t status; + mtapi_node_attributes_t attr; + mtapi_uint_t tmp; + mtapi_nodeattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + embb_core_set_t cs; + embb_core_set_init(&cs, 0); + for (unsigned int ii = 0; ii < core_set.Count(); ii++) { + if (core_set.IsContained(ii)) { + embb_core_set_add(&cs, ii); + } + } + mtapi_nodeattr_set(&attr, MTAPI_NODE_CORE_AFFINITY, + &cs, sizeof(cs), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_TASKS, + &max_tasks, sizeof(max_tasks), &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_GROUPS, + &max_groups, sizeof(max_groups), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_QUEUES, + &max_queues, sizeof(max_queues), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_QUEUE_LIMIT, + &queue_limit, sizeof(queue_limit), &status); + assert(MTAPI_SUCCESS == status); + tmp = 4; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_JOBS, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + tmp = 1; + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_ACTIONS_PER_JOB, + &tmp, sizeof(tmp), &status); + assert(MTAPI_SUCCESS == status); + mtapi_nodeattr_set(&attr, MTAPI_NODE_MAX_PRIORITIES, + &max_priorities, sizeof(max_priorities), &status); + assert(MTAPI_SUCCESS == status); + node_instance = embb::base::Allocation::New( + domain_id, node_id, &attr); + } +} + +bool Node::IsInitialized() { + return NULL != node_instance; +} + +Node & Node::GetInstance() { +#if MTAPI_CPP_AUTOMATIC_INITIALIZE + init_mutex.Lock(); + if (!IsInitialized()) { + Node::Initialize( + MTAPI_CPP_AUTOMATIC_DOMAIN_ID, MTAPI_CPP_AUTOMATIC_NODE_ID); + atexit(Node::Finalize); + } + init_mutex.Unlock(); + return *node_instance; +#else + if (IsInitialized()) { + return *node_instance; + } else { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node is not initialized"); + } +#endif +} + +void Node::Finalize() { + if (IsInitialized()) { + embb::base::Allocation::Delete(node_instance); + node_instance = NULL; + } else { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Node is not initialized"); + } +} + +Group & Node::CreateGroup() { + Group * group = embb::base::Allocation::New(); + groups_.push_back(group); + return *group; +} + +void Node::DestroyGroup(Group & group) { + std::list::iterator ii = + std::find(groups_.begin(), groups_.end(), &group); + if (ii != groups_.end()) { + embb::base::Allocation::Delete(*ii); + groups_.erase(ii); + } +} + +Queue & Node::CreateQueue(mtapi_uint_t priority, bool ordered) { + Queue * queue = embb::base::Allocation::New(priority, ordered); + queues_.push_back(queue); + return *queue; +} + +void Node::DestroyQueue(Queue & queue) { + std::list::iterator ii = + std::find(queues_.begin(), queues_.end(), &queue); + if (ii != queues_.end()) { + embb::base::Allocation::Delete(*ii); + queues_.erase(ii); + } +} + +Task Node::Spawn(Action action) { + return Spawn(action, 0); +} + +Task Node::Spawn(Action action, mtapi_uint_t priority) { + return Task(action, priority); +} + +Continuation Node::First(Action action) { + return Continuation(action); +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/src/queue.cc b/mtapi_cpp/src/queue.cc new file mode 100644 index 0000000..6012524 --- /dev/null +++ b/mtapi_cpp/src/queue.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +namespace embb { +namespace mtapi { + +Queue::Queue(mtapi_uint_t priority, bool ordered) { + mtapi_status_t status; + mtapi_queue_attributes_t attr; + mtapi_boolean_t bb; + mtapi_queueattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_queueattr_set(&attr, MTAPI_QUEUE_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + bb = ordered ? MTAPI_TRUE : MTAPI_FALSE; + mtapi_queueattr_set(&attr, MTAPI_QUEUE_ORDERED, + &bb, sizeof(bb), &status); + assert(MTAPI_SUCCESS == status); + bb = MTAPI_TRUE; + mtapi_queueattr_set(&attr, MTAPI_QUEUE_RETAIN, + &bb, sizeof(bb), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(MTAPI_CPP_TASK_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + handle_ = mtapi_queue_create(MTAPI_QUEUE_ID_NONE, job, &attr, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Queue could not be constructed"); + } +} + +Queue::~Queue() { + mtapi_status_t status; + mtapi_queue_delete(handle_, MTAPI_INFINITE, &status); + assert(MTAPI_SUCCESS == status); +} + +void Queue::Enable() { + mtapi_status_t status; + mtapi_queue_enable(handle_, &status); + assert(MTAPI_SUCCESS == status); +} + +void Queue::Disable() { + mtapi_status_t status; + mtapi_queue_disable(handle_, MTAPI_INFINITE, &status); + assert(MTAPI_SUCCESS == status); +} + +Task Queue::Spawn(Action action) { + return Task(action, handle_, 0); +} + +Task Queue::Spawn(Group const * group, Action action) { + return Task(action, handle_, group->handle_, 0); +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/src/task.cc b/mtapi_cpp/src/task.cc new file mode 100644 index 0000000..07321cd --- /dev/null +++ b/mtapi_cpp/src/task.cc @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2014, 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 +#include + +#include +#include +#include + +namespace embb { +namespace mtapi { + +Task::Task() { + handle_.id = 0; + handle_.tag = 0; +} + +Task::Task(Task const & task) + : handle_(task.handle_) { + // empty +} + +Task::Task( + Action action, + mtapi_uint_t priority) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + Affinity affinity = action.GetAffinity(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &affinity.affinity_, sizeof(affinity.affinity_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(MTAPI_CPP_TASK_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_start(MTAPI_TASK_ID_NONE, job, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, MTAPI_GROUP_NONE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + Action action, + mtapi_group_hndl_t group, + mtapi_uint_t priority) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + Affinity affinity = action.GetAffinity(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &affinity.affinity_, sizeof(affinity.affinity_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(MTAPI_CPP_TASK_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_start(MTAPI_TASK_ID_NONE, job, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + mtapi_task_id_t id, + Action action, + mtapi_group_hndl_t group, + mtapi_uint_t priority) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + Affinity affinity = action.GetAffinity(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &affinity.affinity_, sizeof(affinity.affinity_), &status); + assert(MTAPI_SUCCESS == status); + mtapi_domain_t domain_id = mtapi_domain_id_get(&status); + assert(MTAPI_SUCCESS == status); + mtapi_job_hndl_t job = mtapi_job_get(MTAPI_CPP_TASK_JOB, domain_id, &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + void * idptr = MTAPI_NULL; + memcpy(&idptr, &id, sizeof(id)); + handle_ = mtapi_task_start(id, job, + holder, sizeof(Action), idptr, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + Action action, + mtapi_queue_hndl_t queue, + mtapi_uint_t priority) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + Affinity affinity = action.GetAffinity(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &affinity.affinity_, sizeof(affinity.affinity_), &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, MTAPI_GROUP_NONE, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group, + mtapi_uint_t priority) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + Affinity affinity = action.GetAffinity(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &affinity.affinity_, sizeof(affinity.affinity_), &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + handle_ = mtapi_task_enqueue(MTAPI_TASK_ID_NONE, queue, + holder, sizeof(Action), MTAPI_NULL, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::Task( + mtapi_task_id_t id, + Action action, + mtapi_queue_hndl_t queue, + mtapi_group_hndl_t group, + mtapi_uint_t priority) { + mtapi_status_t status; + mtapi_task_attributes_t attr; + Affinity affinity = action.GetAffinity(); + mtapi_taskattr_init(&attr, &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_PRIORITY, + &priority, sizeof(priority), &status); + assert(MTAPI_SUCCESS == status); + mtapi_taskattr_set(&attr, MTAPI_TASK_AFFINITY, + &affinity.affinity_, sizeof(affinity.affinity_), &status); + assert(MTAPI_SUCCESS == status); + Action* holder = embb::base::Allocation::New(action); + void * idptr = MTAPI_NULL; + memcpy(&idptr, &id, sizeof(id)); + handle_ = mtapi_task_enqueue(id, queue, + holder, sizeof(Action), idptr, 0, &attr, group, &status); + if (MTAPI_SUCCESS != status) { + EMBB_THROW(embb::base::ErrorException, + "mtapi::Task could not be started"); + } +} + +Task::~Task() { +} + +mtapi_status_t Task::Wait(mtapi_timeout_t timeout) { + mtapi_status_t status; + mtapi_task_wait(handle_, timeout, &status); + return status; +} + +void Task::Cancel() { + mtapi_status_t status; + mtapi_task_cancel(handle_, &status); + assert(MTAPI_SUCCESS == status); +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/src/taskcontext.cc b/mtapi_cpp/src/taskcontext.cc new file mode 100644 index 0000000..e3ef4ef --- /dev/null +++ b/mtapi_cpp/src/taskcontext.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, 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 + +#include + +namespace embb { +namespace mtapi { + +TaskContext::TaskContext(mtapi_task_context_t * task_context) + : context_(task_context) { +} + +bool TaskContext::ShouldCancel() { + mtapi_status_t status; + bool result = + MTAPI_TASK_CANCELLED == mtapi_context_taskstate_get(context_, &status); + assert(MTAPI_SUCCESS == status); + return result; +} + +mtapi_uint_t TaskContext::GetCurrentCoreNumber() { + mtapi_status_t status; + mtapi_uint_t result = + mtapi_context_corenum_get(context_, &status); + assert(MTAPI_SUCCESS == status); + return result; +} + +void TaskContext::SetStatus(mtapi_status_t error_code) { + mtapi_status_t status; + mtapi_context_status_set(context_, error_code, &status); + assert(MTAPI_SUCCESS == status); +} + +} // namespace mtapi +} // namespace embb diff --git a/mtapi_cpp/test/main.cc b/mtapi_cpp/test/main.cc new file mode 100644 index 0000000..b58e922 --- /dev/null +++ b/mtapi_cpp/test/main.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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 + +#include + +#include +#include +#include + + +PT_MAIN("MTAPI C++") { + PT_RUN(TaskTest); + PT_RUN(GroupTest); + PT_RUN(QueueTest); +} diff --git a/mtapi_cpp/test/mtapi_cpp_test_config.h b/mtapi_cpp/test/mtapi_cpp_test_config.h new file mode 100644 index 0000000..58b2ad8 --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_config.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014, 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_CPP_TEST_MTAPI_CPP_TEST_CONFIG_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_CONFIG_H_ + +#include +#include + +#define THIS_DOMAIN_ID 1 +#define THIS_NODE_ID 1 + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_CONFIG_H_ diff --git a/mtapi_cpp/test/mtapi_cpp_test_group.cc b/mtapi_cpp/test/mtapi_cpp_test_group.cc new file mode 100644 index 0000000..3de33f8 --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_group.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +struct result_example_struct { + mtapi_uint_t value1; + mtapi_uint_t value2; +}; + +typedef struct result_example_struct result_example_t; + +static void testGroupAction(embb::mtapi::TaskContext & /*context*/) { + //std::cout << "testGroupAction on core " << + // context.GetCurrentCoreNumber() << std::endl; +} + +static void testDoSomethingElse() { +} + +GroupTest::GroupTest() { + CreateUnit("mtapi group test").Add(&GroupTest::TestBasic, this); +} + +void GroupTest::TestBasic() { + //std::cout << "running testGroup..." << std::endl; + + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + embb::mtapi::Group & group = node.CreateGroup(); + embb::mtapi::Task task; + + //std::cout << "wait all..." << std::endl; + + for (int ii = 0; ii < 4; ii++) { + task = group.Spawn(testGroupAction); + } + testDoSomethingElse(); + group.WaitAll(MTAPI_INFINITE); + + //std::cout << "wait any..." << std::endl; + + for (int ii = 0; ii < 4; ii++) { + task = group.Spawn(mtapi_task_id_t(ii + 1), testGroupAction); + } + testDoSomethingElse(); + mtapi_status_t status; + mtapi_task_id_t result; + while (MTAPI_SUCCESS == (status = group.WaitAny(MTAPI_INFINITE, result))) { + //std::cout << "got a result from task " << result << std::endl; + } + + node.DestroyGroup(group); + + embb::mtapi::Node::Finalize(); + + //std::cout << "...done" << std::endl << std::endl; +} diff --git a/mtapi_cpp/test/mtapi_cpp_test_group.h b/mtapi_cpp/test/mtapi_cpp_test_group.h new file mode 100644 index 0000000..7993751 --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_group.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_CPP_TEST_MTAPI_CPP_TEST_GROUP_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_GROUP_H_ + +#include + +class GroupTest : public partest::TestCase { + public: + GroupTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_GROUP_H_ diff --git a/mtapi_cpp/test/mtapi_cpp_test_queue.cc b/mtapi_cpp/test/mtapi_cpp_test_queue.cc new file mode 100644 index 0000000..1476068 --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_queue.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014, 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 + +#include +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 +#define QUEUE_TEST_ID 17 + +static void testQueueAction(embb::mtapi::TaskContext & /*context*/) { + //std::cout << "testQueueAction on core " << + // context.GetCurrentCoreNumber() << std::endl; +} + +static void testDoSomethingElse() { +} + +QueueTest::QueueTest() { + CreateUnit("mtapi queue test").Add(&QueueTest::TestBasic, this); +} + +void QueueTest::TestBasic() { + //std::cout << "running testQueue..." << std::endl; + + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + embb::mtapi::Queue & queue = node.CreateQueue(0, false); + + embb::mtapi::Task task = queue.Spawn(testQueueAction); + + testDoSomethingElse(); + + task.Wait(MTAPI_INFINITE); + + node.DestroyQueue(queue); + + embb::mtapi::Node::Finalize(); + + //std::cout << "...done" << std::endl << std::endl; +} diff --git a/mtapi_cpp/test/mtapi_cpp_test_queue.h b/mtapi_cpp/test/mtapi_cpp_test_queue.h new file mode 100644 index 0000000..7f98a1c --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_queue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_CPP_TEST_MTAPI_CPP_TEST_QUEUE_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_QUEUE_H_ + +#include + +class QueueTest : public partest::TestCase { + public: + QueueTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_QUEUE_H_ diff --git a/mtapi_cpp/test/mtapi_cpp_test_task.cc b/mtapi_cpp/test/mtapi_cpp_test_task.cc new file mode 100644 index 0000000..58f1722 --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_task.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014, 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 +#include +#include + +#include +#include + +#define JOB_TEST_TASK 42 +#define TASK_TEST_ID 23 + +static void testTaskAction( + char const * msg, + std::string * output, + embb::mtapi::TaskContext & /*context*/) { + //std::cout << "testTaskAction " << msg << " on core " << + // context.GetCurrentCoreNumber() << std::endl; + *output = msg; +} + +static void testRecursiveTaskAction( + int * value, + embb::mtapi::TaskContext & /*context*/) { + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + *value = *value + 1; + if (*value < 1000) { + embb::mtapi::Task task = node.Spawn( + embb::base::Bind( + testRecursiveTaskAction, value, embb::base::Placeholder::_1)); + task.Wait(MTAPI_INFINITE); + } + PT_EXPECT(*value == 1000); +} + +static void testErrorTaskAction(embb::mtapi::TaskContext & context) { + context.SetStatus(MTAPI_ERR_ACTION_FAILED); +} + +static void testDoSomethingElse() { +} + +TaskTest::TaskTest() { + CreateUnit("mtapi_cpp task test").Add(&TaskTest::TestBasic, this); +} + +void TaskTest::TestBasic() { + //std::cout << "running testTask..." << std::endl; + + embb::mtapi::Node::Initialize(THIS_DOMAIN_ID, THIS_NODE_ID); + + embb::mtapi::Node & node = embb::mtapi::Node::GetInstance(); + + std::string test; + embb::mtapi::Task task = node.Spawn( + embb::base::Bind( + testTaskAction, "simple", &test, embb::base::Placeholder::_1)); + testDoSomethingElse(); + task.Wait(MTAPI_INFINITE); + PT_EXPECT(test == "simple"); + //std::cout << "result: " << test.c_str() << std::endl; + + std::string test1, test2, test3; + task = node.First( + embb::base::Bind( + testTaskAction, "first", &test1, embb::base::Placeholder::_1)). + Then(embb::base::Bind( + testTaskAction, "second", &test2, embb::base::Placeholder::_1)). + Then(embb::base::Bind( + testTaskAction, "third", &test3, embb::base::Placeholder::_1)). + Spawn(); + testDoSomethingElse(); + task.Wait(MTAPI_INFINITE); + PT_EXPECT(test1 == "first"); + PT_EXPECT(test2 == "second"); + PT_EXPECT(test3 == "third"); + //std::cout << "result1: " << test1.c_str() << std::endl; + //std::cout << "result2: " << test2.c_str() << std::endl; + //std::cout << "result3: " << test3.c_str() << std::endl; + + int value = 0; + task = node.Spawn( + embb::base::Bind( + testRecursiveTaskAction, &value, embb::base::Placeholder::_1)); + task.Wait(MTAPI_INFINITE); + PT_EXPECT(value == 1000); + + mtapi_status_t status; + task = node.Spawn(testErrorTaskAction); + testDoSomethingElse(); + status = task.Wait(MTAPI_INFINITE); + PT_EXPECT(MTAPI_ERR_ACTION_FAILED == status); + + embb::mtapi::Node::Finalize(); + + //std::cout << "...done" << std::endl << std::endl; +} diff --git a/mtapi_cpp/test/mtapi_cpp_test_task.h b/mtapi_cpp/test/mtapi_cpp_test_task.h new file mode 100644 index 0000000..17e8d69 --- /dev/null +++ b/mtapi_cpp/test/mtapi_cpp_test_task.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, 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_CPP_TEST_MTAPI_CPP_TEST_TASK_H_ +#define MTAPI_CPP_TEST_MTAPI_CPP_TEST_TASK_H_ + +#include + +class TaskTest : public partest::TestCase { + public: + TaskTest(); + + private: + void TestBasic(); +}; + +#endif // MTAPI_CPP_TEST_MTAPI_CPP_TEST_TASK_H_ diff --git a/partest.tar b/partest.tar new file mode 100644 index 0000000..ff8ecdc Binary files /dev/null and b/partest.tar differ diff --git a/scripts/create_tarball.sh b/scripts/create_tarball.sh new file mode 100644 index 0000000..ceef8e5 --- /dev/null +++ b/scripts/create_tarball.sh @@ -0,0 +1,173 @@ +#!/bin/bash +# Copyright (c) 2014, 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. + +usage() { +echo "Create a tarball of the project. Specify the project root with"; +echo "-d parameter."; +echo ""; +echo "Version number and project name is automatically derived from CMakeLists.txt in the project root."; +echo "Tarball that will be created: [PROJECT_NAME]_[VERSION_NUMBER].tar.gz"; +echo "Example call (from the scripts directory as working directory):"; +echo "$0 -d ../"; +echo ""; +echo "Usage: $0 [-d ]" 1>&2; exit 1; +} + + +while getopts ":d:" o; do + case "${o}" in + d) + d=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${d}" ]; then + usage +fi + +if [ ! -d "$d" ]; then + echo "Error, directory $d does not exist or is not a directory!" + echo "" + usage +fi + +CMAKEFILE="$d/CMakeLists.txt" + +if [ ! -f "$CMAKEFILE" ]; then + echo "Error, could no locate CMakeLists.txt" + echo "" + usage +fi + +#derive version number from cmake script +VERSION_MAJOR=`cat $CMAKEFILE | grep EMBB_BASE_VERSION_MAJOR | sed "s/^[^0-9]*\([0-9]\+\)[^0-9]*$/\1/g"` +VERSION_MINOR=`cat $CMAKEFILE | grep EMBB_BASE_VERSION_MINOR | sed "s/^[^0-9]*\([0-9]\+\)[^0-9]*$/\1/g"` +VERSION_PATCH=`cat $CMAKEFILE | grep EMBB_BASE_VERSION_PATCH | sed "s/^[^0-9]*\([0-9]\+\)[^0-9]*$/\1/g"` +VERSION_NUMBER="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" +PROJECT_NAME=`cat $CMAKEFILE | grep project.*\(.*\) | sed "s/^[^(]*(\([^)]*\)).*$/\1/g" | tr '[:upper:]' '[:lower:]'` +n="${PROJECT_NAME}-${VERSION_NUMBER}" +TARBALL_NAME="${n}.tar.gz" + +if ! [[ $TARBALL_NAME =~ ^[a-zA-Z0-9|\.|\_|-]+$ ]]; then + echo "Want to create tarball with name $TARBALL_NAME." >&2 + echo 'Filename not valid, only a-z, A-Z, .,- and _ characters are allowed' >&2 # write to stderr + exit 1 +fi + +echo "Do you wish to create a tarball with the name $TARBALL_NAME in the current directory?" +select yn in "Yes" "No"; do + case $yn in + Yes ) break;; + No ) echo Leaving tarball creation; exit;; + esac +done + +if [ -f "$TARBALL_NAME" ]; then + echo "File $TARBALL_NAME exists. Delete file?" + select yn in "Yes" "No"; do + case $yn in + Yes ) break;; + No ) echo Leaving tarball creation; exit;; + esac + done + rm $TARBALL_NAME + if [ -f "$TARBALL_NAME" ]; then + echo "Could not delete $TARBALL_NAME" + exit + fi +fi + + + +MYTMPDIR=`mktemp -d` +echo "Using temporary directory $MYTMPDIR" +trap "rm -rf $MYTMPDIR" EXIT + +echo "Calling rsync to temporary folder" + +rsync \ + --exclude ".git" \ + --exclude ".gitignore" \ + --exclude "build*/" \ + --exclude "scripts/*.tar.gz" \ + --exclude "scripts/cpplint.py" \ + --exclude "scripts/create_tarball.sh" \ + --exclude "scripts/insert_license.sh" \ + --exclude "scripts/license.*" \ + --exclude "scripts/license_*" \ + --exclude "scripts/remove_license.sh" \ + --exclude "mtapi/MTAPI.mm" \ + --exclude ".cproject" \ + --exclude ".gitattributes" \ + --exclude ".project" \ + --exclude "*.blg" \ + --exclude "*.fls" \ + --exclude "*.bbl" \ + --exclude "*.fdb_latexmk" \ + --exclude "*.log" \ + --exclude "*.out" \ + --exclude "*.toc" \ + --exclude "*.aux" \ + --exclude "doc/tutorial/sty" \ + --exclude "doc/tutorial/pics" \ + --exclude "doc/tutorial/content" \ + --exclude "doc/tutorial/*.tex" \ + --exclude "doc/tutorial/*.bib" \ + --exclude "doc/examples/insert_snippets.py" \ + --archive --recursive ${d} $MYTMPDIR/${n} + +echo "Replace version number in README" + +README_FILE="$MYTMPDIR/${n}/README.txt" + +if [ -f $README_FILE ]; then + sed -i "s/\[VERSION_NUMBER_TEMPLATE\]/$VERSION_NUMBER/g" $README_FILE +fi + +echo "Checking line endings" + +WINLINES=`find $MYTMPDIR/${n} -not -type d -exec file "{}" ";" | grep CRLF` + +if [ -n "$WINLINES" ]; then + echo "Detected Dos line endings in following files:" + echo "$WINLINES" + echo "Warning: The project guidelines forbid Dos line endings. Continue creating tarball?" + select yn in "Yes" "No"; do + case $yn in + Yes ) break;; + No ) echo Leaving tarball creation; exit;; + esac + done +fi + +echo "Calling tar" +tar -czf $TARBALL_NAME -C $MYTMPDIR ${n} + +echo "Done. Created $TARBALL_NAME." diff --git a/scripts/insert_license.sh b/scripts/insert_license.sh new file mode 100644 index 0000000..ffceb73 --- /dev/null +++ b/scripts/insert_license.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Copyright (c) 2014, 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. + +usage() { +echo "Specify license file to add to each source file"; +echo "and directory where to start to find files."; +echo "Example call:"; +echo "./insert_license.sh -d ../ -l license.txt"; +echo ""; +echo "Usage: $0 [-d directory] [-l license_file]" 1>&2; exit 1; +} + +while getopts ":d:l:" o; do + case "${o}" in + d) + d=${OPTARG} + ;; + l) + l=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${d}" ] || [ -z "${l}" ]; then + usage +fi + +if [ ! -d "$d" ]; then + echo "Error, directory $d does not exist or is not a directory!" + echo "" + usage +fi + +if [ ! -f "$l" ]; then + echo "Error, license file $l does not exist or is not a file!" + echo "" + usage +fi + +for FILE in `find ${d} -name "*.h" -o -name "*.cc" -o -name "*.c" -o -name "*.h.in" -type f` +do +if ! grep -q COPYRIGHT $FILE + then + echo "Inserting license to: $FILE" + cat ${l} $FILE > $FILE.new && mv $FILE.new $FILE +fi +done diff --git a/scripts/license.txt b/scripts/license.txt new file mode 100644 index 0000000..eb34253 --- /dev/null +++ b/scripts/license.txt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 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. + */ + diff --git a/scripts/license_scripts.txt b/scripts/license_scripts.txt new file mode 100644 index 0000000..8180e7a --- /dev/null +++ b/scripts/license_scripts.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2014, 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. + diff --git a/scripts/remove_license.sh b/scripts/remove_license.sh new file mode 100644 index 0000000..5e700cf --- /dev/null +++ b/scripts/remove_license.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# Copyright (c) 2014, 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. + +usage() { +echo "Specify license file to remove from each source file"; +echo "and directory where to start to find files."; +echo "License is only removed if existing."; +echo ""; +echo "Example call:"; +echo "./remove_license.sh -d ../ -l license.txt"; +echo ""; +echo "Usage: $0 [-d directory] [-l license_file]" 1>&2; exit 1; +} + +while getopts ":d:l:" o; do + case "${o}" in + d) + d=${OPTARG} + ;; + l) + l=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${d}" ] || [ -z "${l}" ]; then + usage +fi + +if [ ! -d "$d" ]; then + echo "Error, directory $d does not exist or is not a directory!" + echo "" + usage +fi + +if [ ! -f "$l" ]; then + echo "Error, license file $l does not exist or is not a file!" + echo "" + usage +fi + +LINES=`cat ${l} | wc -l` +LINESP1=$(($LINES + 1)) + + +for FILE in `find ${d} -name "*.h" -o -name "*.cc" -o -name "*.c" -o -name "*.h.in" -type f` +do +if grep -q COPYRIGHT $FILE + then + echo "Processing: $FILE" + + TEMP=$(mktemp /tmp/temporary-file.XXXXXXXX) + head -n $LINES $FILE > ${TEMP} + + if [[ ! `diff ${TEMP} ${l}` ]]; then + echo "Strip license from file: $FILE" + tail -n +$LINESP1 $FILE > ${FILE}.new && mv ${FILE}.new ${FILE} + fi +fi +done diff --git a/scripts/run_cpplint.sh b/scripts/run_cpplint.sh new file mode 100644 index 0000000..0d83a68 --- /dev/null +++ b/scripts/run_cpplint.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# Copyright (c) 2014, 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. + +usage() { +echo "Run Google C++ Style Guide checking on project."; +echo "Specify EMBB root directory with -d and the"; +echo "cpplint executable with -c. If not specified"; +echo "it is assumed the cpplint binary is contained"; +echo "in the current working directory."; +echo ""; +echo "Usage: $0 [-d directory] [-c cpplint_executable] " 1>&2; exit 1; +} + +while getopts ":d:c:" o; do + case "${o}" in + d) + d=${OPTARG} + ;; + c) + c=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${d}" ]; then + usage +fi + +if [ ! -d "$d" ]; then + echo "Error: directory $d does not exist or is not a directory!" + echo "" + usage +fi + +if [ -z "${c}" ]; then + echo "cpplint binary not specified, assuming to be contained in the current working directory!" + c="./cpplint.py" +fi + +if [ ! -f "${c}" ]; then + echo "Error: cannot find cpplint script (Location: ${c})" + echo + usage +fi + +##Excluded Rules +EXCLUDED_RULES="-runtime/references,-runtime/int,-build/include_order,-readability/multiline_comment,-readability/streams,-readability/alt_tokens,-whitespace/comments" +PARENTHESIS_RULE=",-whitespace/parens" +C_CAST_RULE=",-readability/casting" +LONG_LINES_RULE=",-whitespace/line_length" + +##Excluded files +RAND_FILES=( embb_mtapi_test_group.cc embb_mtapi_test_queue.cc embb_mtapi_test_task.cc queue_test-inl.h ) +for project in base_c mtapi_c base_cpp mtapi_cpp algorithms_cpp containers_cpp dataflow_cpp +do + echo "-> Doing project: $project" + dir=$d/$project + if [ ! -d "$dir" ]; then + echo "Error: cannot find directory $dir for project $project" + echo + usage + fi + for file in `find $dir -name "*.cc" -o -name "*.h" -o -name "*inl.h" -o -name "*.c"` + do + echo "--> Run cpplint on file $file" + current_rules=$EXCLUDED_RULES + if [[ $file =~ \.h$ ]]; then + current_rules+=$PARENTHESIS_RULE + fi + if [[ $file =~ \.c$ ]] || [[ $file =~ \mtapi.h$ ]]; then + current_rules+=$C_CAST_RULE + fi + if [[ $file == *atomic* ]]; then + current_rules+=$LONG_LINES_RULE + fi + ############ + #Per file exclusion rules + if [[ $file == *generate_atomic_implementation_template.h ]]; then + current_rules+=",-build/header_guard" # This file needs to be included multiple times + fi + if [[ $file == *atomic.h ]]; then + current_rules+=",-whitespace/indent" # indention is misinterpreted for this file + fi + if [[ $file == *atomic_arithmetic.h ]]; then + current_rules+=",-readability/function" # All parameters should be named in a function + fi + for filename in "${RAND_FILES[@]}"; do + if [[ $file =~ $filename ]]; then + current_rules+=",-runtime/threadsafe_fn" # These tests are allowed to use the thread unsafe rand() + fi + done + python ${c} --filter=$current_rules --root="$project/include" $file + done +done diff --git a/scripts/run_tests_cygwin.sh b/scripts/run_tests_cygwin.sh new file mode 100644 index 0000000..bc4d035 --- /dev/null +++ b/scripts/run_tests_cygwin.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright (c) 2014, 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. +# + +# Needs to be located in the folder containing the tests!! +# Is copied automatically there when generating build files with cmake. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +"$DIR/embb_base_c_test.exe" +echo +"$DIR/embb_base_cpp_test.exe" +echo +"$DIR/embb_mtapi_c_test.exe" +echo +"$DIR/embb_mtapi_cpp_test.exe" +echo +"$DIR/embb_algorithms_cpp_test.exe" +echo +"$DIR/embb_containers_cpp_test.exe" +echo +"$DIR/embb_dataflow_cpp_test.exe" diff --git a/scripts/run_tests_linux.sh b/scripts/run_tests_linux.sh new file mode 100644 index 0000000..97a57f3 --- /dev/null +++ b/scripts/run_tests_linux.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright (c) 2014, 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. + + +# Needs to be located in the folder containing the tests!! +# Is copied automatically there when generating build files with cmake. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +"$DIR/embb_base_c_test" +echo +"$DIR/embb_base_cpp_test" +echo +"$DIR/embb_mtapi_c_test" +echo +"$DIR/embb_mtapi_cpp_test" +echo +"$DIR/embb_algorithms_cpp_test" +echo +"$DIR/embb_containers_cpp_test" +echo +"$DIR/embb_dataflow_cpp_test" diff --git a/scripts/run_tests_windows.bat b/scripts/run_tests_windows.bat new file mode 100644 index 0000000..76044f1 --- /dev/null +++ b/scripts/run_tests_windows.bat @@ -0,0 +1,56 @@ +:: Copyright (c) 2014, 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. + +:: Needs to be located in the folder containing the tests!! +:: Is copied automatically there when generating build files with cmake. +@echo off +setlocal EnableDelayedExpansion +SET NUM_ERRORS=0 +SET DIR=%~dp0 +"%DIR:~0,-1%\embb_base_c_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +echo. +"%DIR:~0,-1%\embb_base_cpp_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +echo. +"%DIR:~0,-1%\embb_mtapi_c_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +echo. +"%DIR:~0,-1%\embb_mtapi_cpp_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +echo. +"%DIR:~0,-1%\embb_algorithms_cpp_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +echo. +"%DIR:~0,-1%\embb_containers_cpp_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +echo. +"%DIR:~0,-1%\embb_dataflow_cpp_test.exe" +if not !ERRORLEVEL! ==0 set /a NUM_ERRORS=!NUM_ERRORS!+1 +if not !NUM_ERRORS! ==0 ( + echo. + SET ERRORLEVEL=1 + echo "Number of failed tests: !NUM_ERRORS!" + exit /b !NUM_ERRORS! +) -- libgit2 0.26.0