| 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 |  | 
|---|