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 | // Declarations for rock-bottom simple test harness. |
18 | // Just include this file to use it. |
19 | // Every test is presumed to have a command line of the form "test [-v] [MinThreads[:MaxThreads]]" |
20 | // The default for MinThreads is 1, for MaxThreads 4. |
21 | // The defaults can be overridden by defining macros HARNESS_DEFAULT_MIN_THREADS |
22 | // and HARNESS_DEFAULT_MAX_THREADS before including harness.h |
23 | |
24 | #ifndef tbb_tests_harness_H |
25 | #define tbb_tests_harness_H |
26 | |
27 | #include "tbb/tbb_config.h" |
28 | #include "harness_defs.h" |
29 | |
30 | namespace Harness { |
31 | enum TestResult { |
32 | Done, |
33 | Skipped, |
34 | Unknown |
35 | }; |
36 | } |
37 | |
38 | //! Entry point to a TBB unit test application |
39 | /** It MUST be defined by the test application. |
40 | |
41 | If HARNESS_NO_PARSE_COMMAND_LINE macro was not explicitly set before including harness.h, |
42 | then global variables MinThread, and MaxThread will be available and |
43 | initialized when it is called. |
44 | |
45 | Returns Harness::Done when the tests passed successfully. When the test fail, it must |
46 | not return, calling exit(errcode) or abort() instead. When the test is not supported |
47 | for the given platform/compiler/etc, it should return Harness::Skipped. |
48 | |
49 | To provide non-standard variant of main() for the test, define HARNESS_CUSTOM_MAIN |
50 | before including harness.h **/ |
51 | int TestMain (); |
52 | |
53 | #if __SUNPRO_CC |
54 | #include <stdlib.h> |
55 | #include <string.h> |
56 | #include <ucontext.h> |
57 | #else /* !__SUNPRO_CC */ |
58 | #include <cstdlib> |
59 | #include <cstring> |
60 | #endif /* !__SUNPRO_CC */ |
61 | |
62 | #include <new> |
63 | |
64 | #if __TBB_MIC_NATIVE |
65 | #include "harness_mic.h" |
66 | #else |
67 | #define HARNESS_EXPORT |
68 | #define REPORT_FATAL_ERROR REPORT |
69 | #endif /* !__MIC__ */ |
70 | |
71 | #if _WIN32||_WIN64 |
72 | #include "tbb/machine/windows_api.h" |
73 | #if _WIN32_WINNT > 0x0501 && _MSC_VER && !_M_ARM |
74 | // Suppress "typedef ignored ... when no variable is declared" warning by vc14 |
75 | #pragma warning (push) |
76 | #pragma warning (disable: 4091) |
77 | #include <dbghelp.h> |
78 | #pragma warning (pop) |
79 | #pragma comment (lib, "dbghelp.lib") |
80 | #endif |
81 | #if __TBB_WIN8UI_SUPPORT |
82 | #include <thread> |
83 | #endif |
84 | #if _MSC_VER |
85 | #include <crtdbg.h> |
86 | #endif |
87 | #include <process.h> |
88 | #else |
89 | #include <pthread.h> |
90 | #endif |
91 | |
92 | #if __linux__ |
93 | #include <sys/utsname.h> /* for uname */ |
94 | #include <errno.h> /* for use in LinuxKernelVersion() */ |
95 | #include <features.h> |
96 | #endif |
97 | // at least GLIBC 2.1 or OSX 10.5 |
98 | #if __GLIBC__>2 || ( __GLIBC__==2 && __GLIBC_MINOR__ >= 1) || __APPLE__ |
99 | #include <execinfo.h> /*backtrace*/ |
100 | #define BACKTRACE_FUNCTION_AVAILABLE 1 |
101 | #endif |
102 | |
103 | namespace Harness { |
104 | class NativeMutex { |
105 | #if _WIN32||_WIN64 |
106 | CRITICAL_SECTION my_critical_section; |
107 | public: |
108 | NativeMutex() { |
109 | InitializeCriticalSectionEx(&my_critical_section, 4000, 0); |
110 | } |
111 | void lock() { |
112 | EnterCriticalSection(&my_critical_section); |
113 | } |
114 | void unlock() { |
115 | LeaveCriticalSection(&my_critical_section); |
116 | } |
117 | ~NativeMutex() { |
118 | DeleteCriticalSection(&my_critical_section); |
119 | } |
120 | #else |
121 | pthread_mutex_t m_mutex; |
122 | public: |
123 | NativeMutex() { |
124 | pthread_mutex_init(&m_mutex, NULL); |
125 | } |
126 | void lock() { |
127 | pthread_mutex_lock(&m_mutex); |
128 | } |
129 | void unlock() { |
130 | pthread_mutex_unlock(&m_mutex); |
131 | } |
132 | ~NativeMutex() { |
133 | pthread_mutex_destroy(&m_mutex); |
134 | } |
135 | #endif |
136 | }; |
137 | namespace internal { |
138 | static NativeMutex print_stack_mutex; |
139 | } |
140 | } |
141 | |
142 | #include "harness_runtime_loader.h" |
143 | #include "harness_report.h" |
144 | |
145 | //! Prints current call stack |
146 | void print_call_stack() { |
147 | Harness::internal::print_stack_mutex.lock(); |
148 | fflush(stdout); fflush(stderr); |
149 | #if BACKTRACE_FUNCTION_AVAILABLE |
150 | const int sz = 100; // max number of frames to capture |
151 | void *buff[sz]; |
152 | int n = backtrace(buff, sz); |
153 | REPORT("Call stack info (%d):\n" , n); |
154 | backtrace_symbols_fd(buff, n, fileno(stdout)); |
155 | #elif __SUNPRO_CC |
156 | REPORT("Call stack info:\n" ); |
157 | printstack(fileno(stdout)); |
158 | #elif _WIN32_WINNT > 0x0501 && _MSC_VER>=1500 && !__TBB_WIN8UI_SUPPORT |
159 | const int sz = 62; // XP limitation for number of frames |
160 | void *buff[sz]; |
161 | int n = CaptureStackBackTrace(0, sz, buff, NULL); |
162 | REPORT("Call stack info (%d):\n" , n); |
163 | static LONG once = 0; |
164 | if( !InterlockedExchange(&once, 1) ) |
165 | SymInitialize(GetCurrentProcess(), NULL, TRUE); |
166 | const int len = 255; // just some reasonable string buffer size |
167 | union { SYMBOL_INFO sym; char pad[sizeof(SYMBOL_INFO)+len]; }; |
168 | sym.MaxNameLen = len; |
169 | sym.SizeOfStruct = sizeof( SYMBOL_INFO ); |
170 | DWORD64 offset; |
171 | for(int i = 1; i < n; i++) { // skip current frame |
172 | if(!SymFromAddr( GetCurrentProcess(), DWORD64(buff[i]), &offset, &sym )) { |
173 | sym.Address = ULONG64(buff[i]); offset = 0; sym.Name[0] = 0; |
174 | } |
175 | REPORT("[%d] %016I64X+%04I64X: %s\n" , i, sym.Address, offset, sym.Name); //TODO: print module name |
176 | } |
177 | #endif /*BACKTRACE_FUNCTION_AVAILABLE*/ |
178 | Harness::internal::print_stack_mutex.unlock(); |
179 | } |
180 | |
181 | #if !HARNESS_NO_ASSERT |
182 | #include <exception> //for set_terminate |
183 | #include "harness_assert.h" |
184 | #if TEST_USES_TBB |
185 | #include <tbb/tbb_stddef.h> /*set_assertion_handler*/ |
186 | #endif |
187 | |
188 | struct InitReporter { |
189 | void (*default_terminate_handler)() ; |
190 | InitReporter(): default_terminate_handler(NULL) { |
191 | #if TEST_USES_TBB |
192 | #if TBB_USE_ASSERT |
193 | tbb::set_assertion_handler(ReportError); |
194 | #endif |
195 | ASSERT_WARNING(TBB_INTERFACE_VERSION <= tbb::TBB_runtime_interface_version(), "runtime version mismatch" ); |
196 | #endif |
197 | #if TBB_USE_EXCEPTIONS |
198 | default_terminate_handler = std::set_terminate(handle_terminate); |
199 | #endif |
200 | } |
201 | static void handle_terminate(); |
202 | }; |
203 | static InitReporter InitReportError; |
204 | |
205 | void InitReporter::handle_terminate(){ |
206 | REPORT("std::terminate called.\n" ); |
207 | print_call_stack(); |
208 | if (InitReportError.default_terminate_handler){ |
209 | InitReportError.default_terminate_handler(); |
210 | } |
211 | } |
212 | |
213 | typedef void (*)(void); |
214 | static test_error_extra_t ; |
215 | //! Set additional handler to process failed assertions |
216 | void SetHarnessErrorProcessing( test_error_extra_t ) { |
217 | ErrorExtraCall = extra_call; |
218 | } |
219 | |
220 | //! Reports errors issued by failed assertions |
221 | void ReportError( const char* filename, int line, const char* expression, const char * message ) { |
222 | print_call_stack(); |
223 | #if __TBB_ICL_11_1_CODE_GEN_BROKEN |
224 | printf("%s:%d, assertion %s: %s\n" , filename, line, expression, message ? message : "failed" ); |
225 | #else |
226 | REPORT_FATAL_ERROR("%s:%d, assertion %s: %s\n" , filename, line, expression, message ? message : "failed" ); |
227 | #endif |
228 | |
229 | if( ErrorExtraCall ) |
230 | (*ErrorExtraCall)(); |
231 | fflush(stdout); fflush(stderr); |
232 | #if HARNESS_TERMINATE_ON_ASSERT |
233 | TerminateProcess(GetCurrentProcess(), 1); |
234 | #elif HARNESS_EXIT_ON_ASSERT |
235 | exit(1); |
236 | #elif HARNESS_CONTINUE_ON_ASSERT |
237 | // continue testing |
238 | #elif _MSC_VER && _DEBUG |
239 | // aligned with tbb_assert_impl.h behavior |
240 | if(1 == _CrtDbgReport(_CRT_ASSERT, filename, line, NULL, "%s\r\n%s" , expression, message?message:"" )) |
241 | _CrtDbgBreak(); |
242 | #else |
243 | abort(); |
244 | #endif /* HARNESS_EXIT_ON_ASSERT */ |
245 | } |
246 | //! Reports warnings issued by failed warning assertions |
247 | void ReportWarning( const char* filename, int line, const char* expression, const char * message ) { |
248 | REPORT("Warning: %s:%d, assertion %s: %s\n" , filename, line, expression, message ? message : "failed" ); |
249 | } |
250 | |
251 | #else /* !HARNESS_NO_ASSERT */ |
252 | |
253 | #define ASSERT(p,msg) (Harness::suppress_unused_warning(p), (void)0) |
254 | #define ASSERT_WARNING(p,msg) (Harness::suppress_unused_warning(p), (void)0) |
255 | |
256 | #endif /* !HARNESS_NO_ASSERT */ |
257 | |
258 | namespace Harness { |
259 | //TODO: unify with utility::internal::array_length from examples common utilities |
260 | template<typename T, size_t N> |
261 | inline size_t array_length(const T(&)[N]) |
262 | { |
263 | return N; |
264 | } |
265 | |
266 | template<typename T, size_t N> |
267 | inline T* end( T(& array)[N]) |
268 | { |
269 | return array+ array_length(array) ; |
270 | } |
271 | |
272 | } //namespace Harness |
273 | |
274 | #if TEST_USES_TBB |
275 | #include "tbb/blocked_range.h" |
276 | |
277 | namespace Harness { |
278 | template<typename T, size_t N> |
279 | tbb::blocked_range<T*> make_blocked_range( T(& array)[N]){ return tbb::blocked_range<T*>(array, array + N);} |
280 | } |
281 | #endif |
282 | |
283 | #if !HARNESS_NO_PARSE_COMMAND_LINE |
284 | |
285 | //! Controls level of commentary printed via printf-like REMARK() macro. |
286 | /** If true, makes the test print commentary. If false, test should print "done" and nothing more. */ |
287 | static bool Verbose; |
288 | |
289 | #ifndef HARNESS_DEFAULT_MIN_THREADS |
290 | #define HARNESS_DEFAULT_MIN_THREADS 1 |
291 | #endif |
292 | |
293 | //! Minimum number of threads |
294 | static int MinThread = HARNESS_DEFAULT_MIN_THREADS; |
295 | |
296 | #ifndef HARNESS_DEFAULT_MAX_THREADS |
297 | #define HARNESS_DEFAULT_MAX_THREADS 4 |
298 | #endif |
299 | |
300 | //! Maximum number of threads |
301 | static int MaxThread = HARNESS_DEFAULT_MAX_THREADS; |
302 | |
303 | //! Parse command line of the form "name [-v] [MinThreads[:MaxThreads]]" |
304 | /** Sets Verbose, MinThread, and MaxThread accordingly. |
305 | The nthread argument can be a single number or a range of the form m:n. |
306 | A single number m is interpreted as if written m:m. |
307 | The numbers must be non-negative. |
308 | Clients often treat the value 0 as "run sequentially." */ |
309 | inline void ParseCommandLine( int argc, char* argv[] ) { |
310 | if( !argc ) REPORT("Command line with 0 arguments\n" ); |
311 | int i = 1; |
312 | if( i<argc ) { |
313 | if( strncmp( argv[i], "-v" , 2 )==0 ) { |
314 | Verbose = true; |
315 | ++i; |
316 | } |
317 | } |
318 | if( i<argc ) { |
319 | char* endptr; |
320 | MinThread = strtol( argv[i], &endptr, 0 ); |
321 | if( *endptr==':' ) |
322 | MaxThread = strtol( endptr+1, &endptr, 0 ); |
323 | else if( *endptr=='\0' ) |
324 | MaxThread = MinThread; |
325 | if( *endptr!='\0' ) { |
326 | REPORT_FATAL_ERROR("garbled nthread range\n" ); |
327 | exit(1); |
328 | } |
329 | if( MinThread<0 ) { |
330 | REPORT_FATAL_ERROR("nthread must be nonnegative\n" ); |
331 | exit(1); |
332 | } |
333 | if( MaxThread<MinThread ) { |
334 | REPORT_FATAL_ERROR("nthread range is backwards\n" ); |
335 | exit(1); |
336 | } |
337 | ++i; |
338 | } |
339 | #if __TBB_STDARGS_BROKEN |
340 | if ( !argc ) |
341 | argc = 1; |
342 | else { |
343 | while ( i < argc && argv[i][0] == 0 ) |
344 | ++i; |
345 | } |
346 | #endif /* __TBB_STDARGS_BROKEN */ |
347 | if( i!=argc ) { |
348 | REPORT_FATAL_ERROR("Usage: %s [-v] [nthread|minthread:maxthread]\n" , argv[0] ); |
349 | exit(1); |
350 | } |
351 | } |
352 | #endif /* HARNESS_NO_PARSE_COMMAND_LINE */ |
353 | |
354 | #if !HARNESS_CUSTOM_MAIN |
355 | |
356 | #if __TBB_MPI_INTEROP |
357 | #undef SEEK_SET |
358 | #undef SEEK_CUR |
359 | #undef SEEK_END |
360 | #include "mpi.h" |
361 | #endif |
362 | |
363 | #if __TBB_MIC_OFFLOAD && __MIC__ |
364 | extern "C" int COIProcessProxyFlush(); |
365 | #endif |
366 | |
367 | HARNESS_EXPORT |
368 | #if HARNESS_NO_PARSE_COMMAND_LINE |
369 | int main() { |
370 | #if __TBB_MPI_INTEROP |
371 | MPI_Init(NULL,NULL); |
372 | #endif |
373 | #else |
374 | int main(int argc, char* argv[]) { |
375 | ParseCommandLine( argc, argv ); |
376 | #if __TBB_MPI_INTEROP |
377 | MPI_Init(&argc,&argv); |
378 | #endif |
379 | #endif |
380 | #if HARNESS_SKIP_TEST |
381 | REPORT( "skip\n" ); |
382 | return 0; |
383 | #else |
384 | #if __TBB_MPI_INTEROP |
385 | // Simple TBB/MPI interoperability harness for most of tests |
386 | // Worker processes send blocking messages to the master process about their rank and group size |
387 | // Master process receives this info and print it in verbose mode |
388 | int rank, size, myrank; |
389 | MPI_Status status; |
390 | MPI_Comm_size(MPI_COMM_WORLD,&size); |
391 | MPI_Comm_rank(MPI_COMM_WORLD,&myrank); |
392 | if (myrank == 0) { |
393 | #if !HARNESS_NO_PARSE_COMMAND_LINE |
394 | REMARK("Hello mpi world. I am %d of %d\n" , myrank, size); |
395 | #endif |
396 | for ( int i = 1; i < size; i++ ) { |
397 | MPI_Recv (&rank, 1, MPI_INT, i, 1, MPI_COMM_WORLD, &status); |
398 | MPI_Recv (&size, 1, MPI_INT, i, 1, MPI_COMM_WORLD, &status); |
399 | #if !HARNESS_NO_PARSE_COMMAND_LINE |
400 | REMARK("Hello mpi world. I am %d of %d\n" , rank, size); |
401 | #endif |
402 | } |
403 | } else { |
404 | MPI_Send (&myrank, 1, MPI_INT, 0, 1, MPI_COMM_WORLD); |
405 | MPI_Send (&size, 1, MPI_INT, 0, 1, MPI_COMM_WORLD); |
406 | } |
407 | #endif |
408 | |
409 | int res = Harness::Unknown; |
410 | #if __TBB_MIC_OFFLOAD |
411 | // "mic:-1" or "mandatory" specifies execution on the target. The runtime |
412 | // system chooses the specific target. Execution on the CPU is not allowed. |
413 | #if __INTEL_COMPILER < 1400 |
414 | #pragma offload target(mic:-1) out(res) |
415 | #else |
416 | #pragma offload target(mic) out(res) mandatory |
417 | #endif |
418 | #endif |
419 | { |
420 | res = TestMain(); |
421 | #if __TBB_MIC_OFFLOAD && __MIC__ |
422 | // It is recommended not to use the __MIC__ macro directly in the offload block but it is Ok here |
423 | // since it does not lead to an unexpected difference between host and target compilation phases. |
424 | // We need to flush internal Intel(R) Coprocessor Offload Infrastructure (Intel(R) COI) buffers |
425 | // to order output from the offload part before the host part. |
426 | // Also it is work-around for the issue with missed output. |
427 | COIProcessProxyFlush(); |
428 | #endif |
429 | } |
430 | |
431 | ASSERT( res==Harness::Done || res==Harness::Skipped, "Wrong return code by TestMain" ); |
432 | #if __TBB_MPI_INTEROP |
433 | if (myrank == 0) { |
434 | REPORT( res==Harness::Done ? "done\n" : "skip\n" ); |
435 | } |
436 | MPI_Finalize(); |
437 | #else |
438 | REPORT( res==Harness::Done ? "done\n" : "skip\n" ); |
439 | #endif |
440 | return 0; |
441 | #endif /* HARNESS_SKIP_TEST */ |
442 | } |
443 | |
444 | #endif /* !HARNESS_CUSTOM_MAIN */ |
445 | |
446 | //! Base class for prohibiting compiler-generated operator= |
447 | class NoAssign { |
448 | //! Assignment not allowed |
449 | void operator=( const NoAssign& ); |
450 | public: |
451 | NoAssign() {} // explicitly defined to prevent gratuitous warnings |
452 | }; |
453 | |
454 | //! Base class for prohibiting compiler-generated copy constructor or operator= |
455 | class NoCopy: NoAssign { |
456 | //! Copy construction not allowed |
457 | NoCopy( const NoCopy& ); |
458 | public: |
459 | NoCopy() {} |
460 | }; |
461 | |
462 | #if __TBB_CPP11_RVALUE_REF_PRESENT |
463 | #include <utility> |
464 | |
465 | //! Base class for objects which support move ctors |
466 | class Movable { |
467 | public: |
468 | Movable() : alive(true) {} |
469 | void Reset() { alive = true; } |
470 | Movable(Movable&& other) { |
471 | ASSERT(other.alive, "Moving from a dead object" ); |
472 | alive = true; |
473 | other.alive = false; |
474 | } |
475 | Movable& operator=(Movable&& other) { |
476 | ASSERT(alive, "Assignment to a dead object" ); |
477 | ASSERT(other.alive, "Assignment of a dead object" ); |
478 | other.alive = false; |
479 | return *this; |
480 | } |
481 | Movable& operator=(const Movable& other) { |
482 | ASSERT(alive, "Assignment to a dead object" ); |
483 | ASSERT(other.alive, "Assignment of a dead object" ); |
484 | return *this; |
485 | } |
486 | Movable(const Movable& other) { |
487 | ASSERT(other.alive, "Const reference to a dead object" ); |
488 | alive = true; |
489 | } |
490 | ~Movable() { alive = false; } |
491 | volatile bool alive; |
492 | }; |
493 | |
494 | class MoveOnly : Movable, NoCopy { |
495 | public: |
496 | MoveOnly() : Movable() {} |
497 | MoveOnly(MoveOnly&& other) : Movable( std::move(other) ) {} |
498 | }; |
499 | #endif /* __TBB_CPP11_RVALUE_REF_PRESENT */ |
500 | |
501 | #if HARNESS_TBBMALLOC_THREAD_SHUTDOWN && __TBB_SOURCE_DIRECTLY_INCLUDED && (_WIN32||_WIN64) |
502 | #include "../tbbmalloc/tbbmalloc_internal_api.h" |
503 | #endif |
504 | |
505 | //! For internal use by template function NativeParallelFor |
506 | template<typename Index, typename Body> |
507 | class NativeParallelForTask: NoCopy { |
508 | public: |
509 | NativeParallelForTask( Index index_, const Body& body_ ) : |
510 | index(index_), |
511 | body(body_) |
512 | {} |
513 | |
514 | //! Start task |
515 | void start() { |
516 | #if _WIN32||_WIN64 |
517 | unsigned thread_id; |
518 | #if __TBB_WIN8UI_SUPPORT |
519 | std::thread* thread_tmp=new std::thread(thread_function, this); |
520 | thread_handle = thread_tmp->native_handle(); |
521 | thread_id = 0; |
522 | #else |
523 | unsigned stack_size = 0; |
524 | #if HARNESS_THREAD_STACK_SIZE |
525 | stack_size = HARNESS_THREAD_STACK_SIZE; |
526 | #endif |
527 | thread_handle = (HANDLE)_beginthreadex( NULL, stack_size, thread_function, this, 0, &thread_id ); |
528 | #endif |
529 | ASSERT( thread_handle!=0, "NativeParallelFor: _beginthreadex failed" ); |
530 | #else |
531 | #if __ICC==1100 |
532 | #pragma warning (push) |
533 | #pragma warning (disable: 2193) |
534 | #endif /* __ICC==1100 */ |
535 | // Some machines may have very large hard stack limit. When the test is |
536 | // launched by make, the default stack size is set to the hard limit, and |
537 | // calls to pthread_create fail with out-of-memory error. |
538 | // Therefore we set the stack size explicitly (as for TBB worker threads). |
539 | #if !defined(HARNESS_THREAD_STACK_SIZE) |
540 | #if __i386__||__i386||__arm__ |
541 | const size_t stack_size = 1*MByte; |
542 | #elif __x86_64__ |
543 | const size_t stack_size = 2*MByte; |
544 | #else |
545 | const size_t stack_size = 4*MByte; |
546 | #endif |
547 | #else |
548 | const size_t stack_size = HARNESS_THREAD_STACK_SIZE; |
549 | #endif /* HARNESS_THREAD_STACK_SIZE */ |
550 | pthread_attr_t attr_stack; |
551 | int status = pthread_attr_init(&attr_stack); |
552 | ASSERT(0==status, "NativeParallelFor: pthread_attr_init failed" ); |
553 | status = pthread_attr_setstacksize( &attr_stack, stack_size ); |
554 | ASSERT(0==status, "NativeParallelFor: pthread_attr_setstacksize failed" ); |
555 | status = pthread_create(&thread_id, &attr_stack, thread_function, this); |
556 | ASSERT(0==status, "NativeParallelFor: pthread_create failed" ); |
557 | pthread_attr_destroy(&attr_stack); |
558 | #if __ICC==1100 |
559 | #pragma warning (pop) |
560 | #endif |
561 | #endif /* _WIN32||_WIN64 */ |
562 | } |
563 | |
564 | //! Wait for task to finish |
565 | void wait_to_finish() { |
566 | #if _WIN32||_WIN64 |
567 | DWORD status = WaitForSingleObjectEx( thread_handle, INFINITE, FALSE ); |
568 | ASSERT( status!=WAIT_FAILED, "WaitForSingleObject failed" ); |
569 | CloseHandle( thread_handle ); |
570 | #else |
571 | int status = pthread_join( thread_id, NULL ); |
572 | ASSERT( !status, "pthread_join failed" ); |
573 | #endif |
574 | #if HARNESS_NO_ASSERT |
575 | (void)status; |
576 | #endif |
577 | } |
578 | |
579 | private: |
580 | #if _WIN32||_WIN64 |
581 | HANDLE thread_handle; |
582 | #else |
583 | pthread_t thread_id; |
584 | #endif |
585 | |
586 | //! Range over which task will invoke the body. |
587 | const Index index; |
588 | |
589 | //! Body to invoke over the range. |
590 | const Body body; |
591 | |
592 | #if _WIN32||_WIN64 |
593 | static unsigned __stdcall thread_function( void* object ) |
594 | #else |
595 | static void* thread_function(void* object) |
596 | #endif |
597 | { |
598 | NativeParallelForTask& self = *static_cast<NativeParallelForTask*>(object); |
599 | (self.body)(self.index); |
600 | #if HARNESS_TBBMALLOC_THREAD_SHUTDOWN && __TBB_SOURCE_DIRECTLY_INCLUDED && (_WIN32||_WIN64) |
601 | // in those cases can't release per-thread cache automatically, |
602 | // so do it manually |
603 | // TODO: investigate less-intrusive way to do it, for example via FLS keys |
604 | __TBB_mallocThreadShutdownNotification(); |
605 | #endif |
606 | return 0; |
607 | } |
608 | }; |
609 | |
610 | //! Execute body(i) in parallel for i in the interval [0,n). |
611 | /** Each iteration is performed by a separate thread. */ |
612 | template<typename Index, typename Body> |
613 | void NativeParallelFor( Index n, const Body& body ) { |
614 | typedef NativeParallelForTask<Index,Body> task; |
615 | |
616 | if( n>0 ) { |
617 | // Allocate array to hold the tasks |
618 | task* array = static_cast<task*>(operator new( n*sizeof(task) )); |
619 | |
620 | // Construct the tasks |
621 | for( Index i=0; i!=n; ++i ) |
622 | new( &array[i] ) task(i,body); |
623 | |
624 | // Start the tasks |
625 | for( Index i=0; i!=n; ++i ) |
626 | array[i].start(); |
627 | |
628 | // Wait for the tasks to finish and destroy each one. |
629 | for( Index i=n; i; --i ) { |
630 | array[i-1].wait_to_finish(); |
631 | array[i-1].~task(); |
632 | } |
633 | |
634 | // Deallocate the task array |
635 | operator delete(array); |
636 | } |
637 | } |
638 | |
639 | //! The function to zero-initialize arrays; useful to avoid warnings |
640 | template <typename T> |
641 | void zero_fill(void* array, size_t n) { |
642 | memset(array, 0, sizeof(T)*n); |
643 | } |
644 | |
645 | #if __SUNPRO_CC && defined(min) |
646 | #undef min |
647 | #undef max |
648 | #endif |
649 | |
650 | #ifndef min |
651 | //! Utility template function returning lesser of the two values. |
652 | /** Provided here to avoid including not strict safe <algorithm>.\n |
653 | In case operands cause signed/unsigned or size mismatch warnings it is caller's |
654 | responsibility to do the appropriate cast before calling the function. **/ |
655 | template<typename T1, typename T2> |
656 | T1 min ( const T1& val1, const T2& val2 ) { |
657 | return val1 < val2 ? val1 : val2; |
658 | } |
659 | #endif /* !min */ |
660 | |
661 | #ifndef max |
662 | //! Utility template function returning greater of the two values. |
663 | /** Provided here to avoid including not strict safe <algorithm>.\n |
664 | In case operands cause signed/unsigned or size mismatch warnings it is caller's |
665 | responsibility to do the appropriate cast before calling the function. **/ |
666 | template<typename T1, typename T2> |
667 | T1 max ( const T1& val1, const T2& val2 ) { |
668 | return val1 < val2 ? val2 : val1; |
669 | } |
670 | #endif /* !max */ |
671 | |
672 | template<typename T> |
673 | static inline bool is_aligned(T arg, size_t alignment) { |
674 | return 0==((size_t)arg & (alignment-1)); |
675 | } |
676 | |
677 | #if __linux__ |
678 | inline unsigned LinuxKernelVersion() |
679 | { |
680 | unsigned digit1, digit2, digit3; |
681 | struct utsname utsnameBuf; |
682 | |
683 | if (-1 == uname(&utsnameBuf)) { |
684 | REPORT_FATAL_ERROR("Can't call uname: errno %d\n" , errno); |
685 | exit(1); |
686 | } |
687 | if (3 != sscanf(utsnameBuf.release, "%u.%u.%u" , &digit1, &digit2, &digit3)) { |
688 | REPORT_FATAL_ERROR("Unable to parse OS release '%s'\n" , utsnameBuf.release); |
689 | exit(1); |
690 | } |
691 | return 1000000*digit1+1000*digit2+digit3; |
692 | } |
693 | #endif |
694 | |
695 | namespace Harness { |
696 | |
697 | #if !HARNESS_NO_ASSERT |
698 | //! Base class that asserts that no operations are made with the object after its destruction. |
699 | class NoAfterlife { |
700 | protected: |
701 | enum state_t { |
702 | LIVE=0x56781234, |
703 | DEAD=0xDEADBEEF |
704 | } m_state; |
705 | |
706 | public: |
707 | NoAfterlife() : m_state(LIVE) {} |
708 | NoAfterlife( const NoAfterlife& src ) : m_state(LIVE) { |
709 | ASSERT( src.IsLive(), "Constructing from the dead source" ); |
710 | } |
711 | ~NoAfterlife() { |
712 | ASSERT( IsLive(), "Repeated destructor call" ); |
713 | m_state = DEAD; |
714 | } |
715 | const NoAfterlife& operator=( const NoAfterlife& src ) { |
716 | ASSERT( IsLive(), NULL ); |
717 | ASSERT( src.IsLive(), NULL ); |
718 | return *this; |
719 | } |
720 | void AssertLive() const { |
721 | ASSERT( IsLive(), "Already dead" ); |
722 | } |
723 | bool IsLive() const { |
724 | return m_state == LIVE; |
725 | } |
726 | }; // NoAfterlife |
727 | #endif /* !HARNESS_NO_ASSERT */ |
728 | |
729 | #if _WIN32 || _WIN64 |
730 | void Sleep ( int ms ) { |
731 | #if !__TBB_WIN8UI_SUPPORT |
732 | ::Sleep(ms); |
733 | #else |
734 | std::chrono::milliseconds sleep_time( ms ); |
735 | std::this_thread::sleep_for( sleep_time ); |
736 | #endif |
737 | |
738 | } |
739 | |
740 | typedef DWORD tid_t; |
741 | tid_t CurrentTid () { return GetCurrentThreadId(); } |
742 | |
743 | #else /* !WIN */ |
744 | |
745 | void Sleep ( int ms ) { |
746 | timespec requested = { ms / 1000, (ms % 1000)*1000000 }; |
747 | timespec remaining = { 0, 0 }; |
748 | nanosleep(&requested, &remaining); |
749 | } |
750 | |
751 | typedef pthread_t tid_t; |
752 | tid_t CurrentTid () { return pthread_self(); } |
753 | #endif /* !WIN */ |
754 | |
755 | static const unsigned Primes[] = { |
756 | 0x9e3779b1, 0xffe6cc59, 0x2109f6dd, 0x43977ab5, 0xba5703f5, 0xb495a877, 0xe1626741, 0x79695e6b, |
757 | 0xbc98c09f, 0xd5bee2b3, 0x287488f9, 0x3af18231, 0x9677cd4d, 0xbe3a6929, 0xadc6a877, 0xdcf0674b, |
758 | 0xbe4d6fe9, 0x5f15e201, 0x99afc3fd, 0xf3f16801, 0xe222cfff, 0x24ba5fdb, 0x0620452d, 0x79f149e3, |
759 | 0xc8b93f49, 0x972702cd, 0xb07dd827, 0x6c97d5ed, 0x085a3d61, 0x46eb5ea7, 0x3d9910ed, 0x2e687b5b, |
760 | 0x29609227, 0x6eb081f1, 0x0954c4e1, 0x9d114db9, 0x542acfa9, 0xb3e6bd7b, 0x0742d917, 0xe9f3ffa7, |
761 | 0x54581edb, 0xf2480f45, 0x0bb9288f, 0xef1affc7, 0x85fa0ca7, 0x3ccc14db, 0xe6baf34b, 0x343377f7, |
762 | 0x5ca19031, 0xe6d9293b, 0xf0a9f391, 0x5d2e980b, 0xfc411073, 0xc3749363, 0xb892d829, 0x3549366b, |
763 | 0x629750ad, 0xb98294e5, 0x892d9483, 0xc235baf3, 0x3d2402a3, 0x6bdef3c9, 0xbec333cd, 0x40c9520f |
764 | }; |
765 | |
766 | class FastRandom { |
767 | unsigned x, a; |
768 | public: |
769 | unsigned short get() { |
770 | unsigned short r = (unsigned short)(x >> 16); |
771 | x = x*a + 1; |
772 | return r; |
773 | } |
774 | explicit FastRandom( unsigned seed ) { |
775 | x = seed; |
776 | a = Primes[seed % (sizeof(Primes) / sizeof(Primes[0]))]; |
777 | } |
778 | }; |
779 | template<typename T> |
780 | class FastRandomBody { |
781 | FastRandom r; |
782 | public: |
783 | explicit FastRandomBody( unsigned seed ) : r(seed) {} |
784 | // Depending on the input type T the result distribution formed from this operator() |
785 | // might possess different characteristics than the original one used in FastRandom instance. |
786 | T operator()() { return T(r.get()); } |
787 | }; |
788 | |
789 | int SetEnv( const char *envname, const char *envval ) { |
790 | ASSERT( envname && envval, "Harness::SetEnv() requires two valid C strings" ); |
791 | #if __TBB_WIN8UI_SUPPORT |
792 | ASSERT( false, "Harness::SetEnv() should not be called in code built for win8ui" ); |
793 | return -1; |
794 | #elif !(_MSC_VER || __MINGW32__ || __MINGW64__) |
795 | // On POSIX systems use setenv |
796 | return setenv(envname, envval, /*overwrite=*/1); |
797 | #elif __STDC_SECURE_LIB__>=200411 |
798 | // this macro is set in VC & MinGW if secure API functions are present |
799 | return _putenv_s(envname, envval); |
800 | #else |
801 | // If no secure API on Windows, use _putenv |
802 | size_t namelen = strlen(envname), valuelen = strlen(envval); |
803 | char* buf = new char[namelen+valuelen+2]; |
804 | strncpy(buf, envname, namelen); |
805 | buf[namelen] = '='; |
806 | strncpy(buf+namelen+1, envval, valuelen); |
807 | buf[namelen+1+valuelen] = char(0); |
808 | int status = _putenv(buf); |
809 | delete[] buf; |
810 | return status; |
811 | #endif |
812 | } |
813 | |
814 | char* GetEnv(const char *envname) { |
815 | ASSERT(envname, "Harness::GetEnv() requires a valid C string" ); |
816 | #if __TBB_WIN8UI_SUPPORT |
817 | return NULL; |
818 | #else |
819 | return std::getenv(envname); |
820 | #endif |
821 | } |
822 | |
823 | class DummyBody { |
824 | int m_numIters; |
825 | public: |
826 | explicit DummyBody( int iters ) : m_numIters( iters ) {} |
827 | void operator()( int ) const { |
828 | for ( volatile int i = 0; i < m_numIters; ++i ) {} |
829 | } |
830 | }; |
831 | } // namespace Harness |
832 | |
833 | #endif /* tbb_tests_harness_H */ |
834 | |