| 1 | /* |
| 2 | Copyright (c) 2005-2019 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #if _MSC_VER && !defined(__INTEL_COMPILER) |
| 18 | #pragma warning(disable: 4180) // "qualifier applied to function type has no meaning; ignored" |
| 19 | #endif |
| 20 | |
| 21 | #ifndef TBB_PREVIEW_VARIADIC_PARALLEL_INVOKE |
| 22 | #define TBB_PREVIEW_VARIADIC_PARALLEL_INVOKE __TBB_CPF_BUILD |
| 23 | #endif |
| 24 | |
| 25 | #include "tbb/parallel_invoke.h" |
| 26 | #include "tbb/task_scheduler_init.h" |
| 27 | #include "tbb/atomic.h" |
| 28 | #include "tbb/tbb_exception.h" |
| 29 | #include "harness.h" |
| 30 | |
| 31 | #if !__INTEL_COMPILER && (_MSC_VER && _MSC_VER <= 1400 || __GNUC__==3 && __GNUC_MINOR__<=3 || __SUNPRO_CC) |
| 32 | #define __TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN 1 |
| 33 | #endif |
| 34 | |
| 35 | tbb::atomic<size_t> function_counter; |
| 36 | |
| 37 | // Some macros to make the test easier to read |
| 38 | |
| 39 | // 10 functions test0 ... test9 are defined |
| 40 | // pointer to each function is also defined |
| 41 | |
| 42 | #define TEST_FUNCTION(value) void test##value () \ |
| 43 | { \ |
| 44 | ASSERT(!(function_counter & (1 << value)), "Test function has already been called"); \ |
| 45 | function_counter += 1 << value; \ |
| 46 | } \ |
| 47 | void (*test_pointer##value)(void) = test##value; |
| 48 | |
| 49 | TEST_FUNCTION(0) |
| 50 | TEST_FUNCTION(1) |
| 51 | TEST_FUNCTION(2) |
| 52 | TEST_FUNCTION(3) |
| 53 | TEST_FUNCTION(4) |
| 54 | TEST_FUNCTION(5) |
| 55 | TEST_FUNCTION(6) |
| 56 | TEST_FUNCTION(7) |
| 57 | TEST_FUNCTION(8) |
| 58 | TEST_FUNCTION(9) |
| 59 | |
| 60 | // The same with functors |
| 61 | #define TEST_FUNCTOR(value) class test_functor##value \ |
| 62 | { \ |
| 63 | public: \ |
| 64 | void operator() () const { \ |
| 65 | function_counter += 1 << value; \ |
| 66 | } \ |
| 67 | } functor##value; |
| 68 | |
| 69 | TEST_FUNCTOR(0) |
| 70 | TEST_FUNCTOR(1) |
| 71 | TEST_FUNCTOR(2) |
| 72 | TEST_FUNCTOR(3) |
| 73 | TEST_FUNCTOR(4) |
| 74 | TEST_FUNCTOR(5) |
| 75 | TEST_FUNCTOR(6) |
| 76 | TEST_FUNCTOR(7) |
| 77 | TEST_FUNCTOR(8) |
| 78 | TEST_FUNCTOR(9) |
| 79 | |
| 80 | #define INIT_TEST function_counter = 0; |
| 81 | |
| 82 | #define VALIDATE_INVOKE_RUN(number_of_args, test_type) \ |
| 83 | ASSERT( size_t(function_counter) == (size_t(1) << number_of_args) - 1, "parallel_invoke called with " #number_of_args " arguments didn't process all " #test_type); |
| 84 | |
| 85 | // Calls parallel_invoke for different number of arguments |
| 86 | // It can be called with and without user context |
| 87 | template <typename F0, typename F1, typename F2, typename F3, typename F4, typename F5, |
| 88 | typename F6, typename F7, typename F8, typename F9> |
| 89 | void call_parallel_invoke( size_t n, F0& f0, F1& f1, F2& f2, F3& f3, F4 &f4, F5 &f5, |
| 90 | F6& f6, F7 &f7, F8 &f8, F9 &f9, tbb::task_group_context* context) { |
| 91 | switch(n) { |
| 92 | case 2: |
| 93 | if (context) |
| 94 | tbb::parallel_invoke (f0, f1, *context); |
| 95 | else |
| 96 | tbb::parallel_invoke (f0, f1); |
| 97 | break; |
| 98 | case 3: |
| 99 | if (context) |
| 100 | tbb::parallel_invoke (f0, f1, f2, *context); |
| 101 | else |
| 102 | tbb::parallel_invoke (f0, f1, f2); |
| 103 | break; |
| 104 | case 4: |
| 105 | if(context) |
| 106 | tbb::parallel_invoke (f0, f1, f2, f3, *context); |
| 107 | else |
| 108 | tbb::parallel_invoke (f0, f1, f2, f3); |
| 109 | break; |
| 110 | case 5: |
| 111 | if(context) |
| 112 | tbb::parallel_invoke (f0, f1, f2, f3, f4, *context); |
| 113 | else |
| 114 | tbb::parallel_invoke (f0, f1, f2, f3, f4); |
| 115 | break; |
| 116 | case 6: |
| 117 | if(context) |
| 118 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, *context); |
| 119 | else |
| 120 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5); |
| 121 | break; |
| 122 | case 7: |
| 123 | if(context) |
| 124 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, *context); |
| 125 | else |
| 126 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6); |
| 127 | break; |
| 128 | case 8: |
| 129 | if(context) |
| 130 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, *context); |
| 131 | else |
| 132 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7); |
| 133 | break; |
| 134 | case 9: |
| 135 | if(context) |
| 136 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8, *context); |
| 137 | else |
| 138 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8); |
| 139 | break; |
| 140 | case 10: |
| 141 | if(context) |
| 142 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, *context); |
| 143 | else |
| 144 | tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); |
| 145 | break; |
| 146 | default: |
| 147 | ASSERT(false, "number of arguments must be between 2 and 10" ); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | #if !__TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN |
| 152 | template<typename function> void aux_invoke(const function& f) { |
| 153 | f(); |
| 154 | } |
| 155 | |
| 156 | bool function_by_constref_in_template_codegen_broken() { |
| 157 | function_counter = 0; |
| 158 | aux_invoke(test1); |
| 159 | return function_counter==0; |
| 160 | } |
| 161 | #endif /* !__TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN */ |
| 162 | |
| 163 | void test_parallel_invoke() |
| 164 | { |
| 165 | REMARK (__FUNCTION__); |
| 166 | // Testing with pointers to functions |
| 167 | for (int n = 2; n <=10; n++) |
| 168 | { |
| 169 | INIT_TEST; |
| 170 | call_parallel_invoke(n, test_pointer0, test_pointer1, test_pointer2, test_pointer3, test_pointer4, |
| 171 | test_pointer5, test_pointer6, test_pointer7, test_pointer8, test_pointer9, NULL); |
| 172 | VALIDATE_INVOKE_RUN(n, "pointers to function" ); |
| 173 | } |
| 174 | |
| 175 | // Testing parallel_invoke with functors |
| 176 | for (int n = 2; n <=10; n++) |
| 177 | { |
| 178 | INIT_TEST; |
| 179 | call_parallel_invoke(n, functor0, functor1, functor2, functor3, functor4, |
| 180 | functor5, functor6, functor7, functor8, functor9, NULL); |
| 181 | VALIDATE_INVOKE_RUN(n, "functors" ); |
| 182 | } |
| 183 | |
| 184 | #if __TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN |
| 185 | // some old compilers can't cope with passing function name into parallel_invoke |
| 186 | #else |
| 187 | // and some compile but generate broken code that does not call the function |
| 188 | if (function_by_constref_in_template_codegen_broken()) |
| 189 | return; |
| 190 | |
| 191 | // Testing parallel_invoke with functions |
| 192 | for (int n = 2; n <=10; n++) |
| 193 | { |
| 194 | INIT_TEST; |
| 195 | call_parallel_invoke(n, test0, test1, test2, test3, test4, test5, test6, test7, test8, test9, NULL); |
| 196 | VALIDATE_INVOKE_RUN(n, "functions" ); |
| 197 | } |
| 198 | #endif |
| 199 | } |
| 200 | |
| 201 | // Exception handling support test |
| 202 | |
| 203 | #if __TBB_TASK_GROUP_CONTEXT |
| 204 | #define HARNESS_EH_SIMPLE_MODE 1 |
| 205 | #include "harness_eh.h" |
| 206 | |
| 207 | #if TBB_USE_EXCEPTIONS |
| 208 | volatile size_t exception_mask; // each bit represents whether the function should throw exception or not |
| 209 | |
| 210 | // throws exception if corresponding exception_mask bit is set |
| 211 | #define TEST_FUNCTOR_WITH_THROW(value) \ |
| 212 | struct throwing_functor##value { \ |
| 213 | void operator() () const { \ |
| 214 | if (exception_mask & (1 << value)) \ |
| 215 | ThrowTestException(); \ |
| 216 | } \ |
| 217 | } test_with_throw##value; |
| 218 | |
| 219 | TEST_FUNCTOR_WITH_THROW(0) |
| 220 | TEST_FUNCTOR_WITH_THROW(1) |
| 221 | TEST_FUNCTOR_WITH_THROW(2) |
| 222 | TEST_FUNCTOR_WITH_THROW(3) |
| 223 | TEST_FUNCTOR_WITH_THROW(4) |
| 224 | TEST_FUNCTOR_WITH_THROW(5) |
| 225 | TEST_FUNCTOR_WITH_THROW(6) |
| 226 | TEST_FUNCTOR_WITH_THROW(7) |
| 227 | TEST_FUNCTOR_WITH_THROW(8) |
| 228 | TEST_FUNCTOR_WITH_THROW(9) |
| 229 | |
| 230 | void TestExceptionHandling() |
| 231 | { |
| 232 | REMARK (__FUNCTION__); |
| 233 | for( size_t n = 2; n <= 10; ++n ) { |
| 234 | for( exception_mask = 1; exception_mask < (size_t(1) << n); ++exception_mask ) { |
| 235 | ResetEhGlobals(); |
| 236 | TRY(); |
| 237 | REMARK("Calling parallel_invoke, number of functions = %d, exception_mask = %d\n" , n, exception_mask); |
| 238 | call_parallel_invoke(n, test_with_throw0, test_with_throw1, test_with_throw2, test_with_throw3, |
| 239 | test_with_throw4, test_with_throw5, test_with_throw6, test_with_throw7, test_with_throw8, test_with_throw9, NULL); |
| 240 | CATCH_AND_ASSERT(); |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | #endif /* TBB_USE_EXCEPTIONS */ |
| 245 | |
| 246 | // Cancellation support test |
| 247 | void function_to_cancel() { |
| 248 | ++g_CurExecuted; |
| 249 | CancellatorTask::WaitUntilReady(); |
| 250 | } |
| 251 | |
| 252 | // The function is used to test cancellation |
| 253 | void simple_test_nothrow (){ |
| 254 | ++g_CurExecuted; |
| 255 | } |
| 256 | |
| 257 | size_t g_numFunctions, |
| 258 | g_functionToCancel; |
| 259 | |
| 260 | class ParInvokeLauncherTask : public tbb::task |
| 261 | { |
| 262 | tbb::task_group_context &my_ctx; |
| 263 | void(*func_array[10])(void); |
| 264 | |
| 265 | tbb::task* execute () __TBB_override { |
| 266 | func_array[g_functionToCancel] = &function_to_cancel; |
| 267 | call_parallel_invoke(g_numFunctions, func_array[0], func_array[1], func_array[2], func_array[3], |
| 268 | func_array[4], func_array[5], func_array[6], func_array[7], func_array[8], func_array[9], &my_ctx); |
| 269 | return NULL; |
| 270 | } |
| 271 | public: |
| 272 | ParInvokeLauncherTask ( tbb::task_group_context& ctx ) : my_ctx(ctx) { |
| 273 | for (int i = 0; i <=9; ++i) |
| 274 | func_array[i] = &simple_test_nothrow; |
| 275 | } |
| 276 | }; |
| 277 | |
| 278 | void TestCancellation () |
| 279 | { |
| 280 | REMARK (__FUNCTION__); |
| 281 | for ( int n = 2; n <= 10; ++n ) { |
| 282 | for ( int m = 0; m <= n - 1; ++m ) { |
| 283 | g_numFunctions = n; |
| 284 | g_functionToCancel = m; |
| 285 | ResetEhGlobals(); |
| 286 | RunCancellationTest<ParInvokeLauncherTask, CancellatorTask>(); |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | #endif /* __TBB_TASK_GROUP_CONTEXT */ |
| 291 | |
| 292 | //------------------------------------------------------------------------ |
| 293 | // Entry point |
| 294 | //------------------------------------------------------------------------ |
| 295 | |
| 296 | #include "harness_cpu.h" |
| 297 | |
| 298 | int TestMain () { |
| 299 | MinThread = min(MinThread, MaxThread); |
| 300 | ASSERT (MinThread>=1, "Minimal number of threads must be 1 or more" ); |
| 301 | for ( int p = MinThread; p <= MaxThread; ++p ) { |
| 302 | tbb::task_scheduler_init init(p); |
| 303 | test_parallel_invoke(); |
| 304 | if (p > 1) { |
| 305 | #if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN |
| 306 | REPORT("Known issue: exception handling tests are skipped.\n" ); |
| 307 | #elif TBB_USE_EXCEPTIONS |
| 308 | TestExceptionHandling(); |
| 309 | #endif /* TBB_USE_EXCEPTIONS */ |
| 310 | #if __TBB_TASK_GROUP_CONTEXT |
| 311 | TestCancellation(); |
| 312 | #endif /* __TBB_TASK_GROUP_CONTEXT */ |
| 313 | } |
| 314 | TestCPUUserTime(p); |
| 315 | } |
| 316 | return Harness::Done; |
| 317 | } |
| 318 | |