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