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
35tbb::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} \
47void (*test_pointer##value)(void) = test##value;
48
49TEST_FUNCTION(0)
50TEST_FUNCTION(1)
51TEST_FUNCTION(2)
52TEST_FUNCTION(3)
53TEST_FUNCTION(4)
54TEST_FUNCTION(5)
55TEST_FUNCTION(6)
56TEST_FUNCTION(7)
57TEST_FUNCTION(8)
58TEST_FUNCTION(9)
59
60// The same with functors
61#define TEST_FUNCTOR(value) class test_functor##value \
62{ \
63public: \
64 void operator() () const { \
65 function_counter += 1 << value; \
66 } \
67} functor##value;
68
69TEST_FUNCTOR(0)
70TEST_FUNCTOR(1)
71TEST_FUNCTOR(2)
72TEST_FUNCTOR(3)
73TEST_FUNCTOR(4)
74TEST_FUNCTOR(5)
75TEST_FUNCTOR(6)
76TEST_FUNCTOR(7)
77TEST_FUNCTOR(8)
78TEST_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
87template <typename F0, typename F1, typename F2, typename F3, typename F4, typename F5,
88 typename F6, typename F7, typename F8, typename F9>
89void 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
152template<typename function> void aux_invoke(const function& f) {
153 f();
154}
155
156bool 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
163void 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
208volatile 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) \
212struct throwing_functor##value { \
213 void operator() () const { \
214 if (exception_mask & (1 << value)) \
215 ThrowTestException(); \
216 } \
217} test_with_throw##value;
218
219TEST_FUNCTOR_WITH_THROW(0)
220TEST_FUNCTOR_WITH_THROW(1)
221TEST_FUNCTOR_WITH_THROW(2)
222TEST_FUNCTOR_WITH_THROW(3)
223TEST_FUNCTOR_WITH_THROW(4)
224TEST_FUNCTOR_WITH_THROW(5)
225TEST_FUNCTOR_WITH_THROW(6)
226TEST_FUNCTOR_WITH_THROW(7)
227TEST_FUNCTOR_WITH_THROW(8)
228TEST_FUNCTOR_WITH_THROW(9)
229
230void 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
247void function_to_cancel() {
248 ++g_CurExecuted;
249 CancellatorTask::WaitUntilReady();
250}
251
252// The function is used to test cancellation
253void simple_test_nothrow (){
254 ++g_CurExecuted;
255}
256
257size_t g_numFunctions,
258 g_functionToCancel;
259
260class 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 }
271public:
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
278void 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
298int 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