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