You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
575 lines
17 KiB
575 lines
17 KiB
/*
|
|
Copyright 2005-2013 Intel Corporation. All Rights Reserved.
|
|
|
|
This file is part of Threading Building Blocks.
|
|
|
|
Threading Building Blocks is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License
|
|
version 2 as published by the Free Software Foundation.
|
|
|
|
Threading Building Blocks is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Threading Building Blocks; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
As a special exception, you may use this file as part of a free software
|
|
library without restriction. Specifically, if other files instantiate
|
|
templates or use macros or inline functions from this file, or you compile
|
|
this file and link it with other files to produce an executable, this
|
|
file does not by itself cause the resulting executable to be covered by
|
|
the GNU General Public License. This exception does not however
|
|
invalidate any other reasons why the executable file might be covered by
|
|
the GNU General Public License.
|
|
*/
|
|
|
|
// Declarations for rock-bottom simple test harness.
|
|
// Just include this file to use it.
|
|
// Every test is presumed to have a command line of the form "test [-v] [MinThreads[:MaxThreads]]"
|
|
// The default for MinThreads is 1, for MaxThreads 4.
|
|
// The defaults can be overridden by defining macros HARNESS_DEFAULT_MIN_THREADS
|
|
// and HARNESS_DEFAULT_MAX_THREADS before including harness.h
|
|
|
|
#ifndef tbb_tests_harness_H
|
|
#define tbb_tests_harness_H
|
|
|
|
#include "tbb/tbb_config.h"
|
|
#include "harness_defs.h"
|
|
|
|
namespace Harness {
|
|
enum TestResult {
|
|
Done,
|
|
Skipped,
|
|
Unknown
|
|
};
|
|
}
|
|
|
|
//! Entry point to a TBB unit test application
|
|
/** It MUST be defined by the test application.
|
|
|
|
If HARNESS_NO_PARSE_COMMAND_LINE macro was not explicitly set before including harness.h,
|
|
then global variables MinThread, and MaxThread will be available and
|
|
initialized when it is called.
|
|
|
|
Returns Harness::Done when the tests passed successfully. When the test fail, it must
|
|
not return, calling exit(errcode) or abort() instead. When the test is not supported
|
|
for the given platform/compiler/etc, it should return Harness::Skipped.
|
|
|
|
To provide non-standard variant of main() for the test, define HARNESS_CUSTOM_MAIN
|
|
before including harness.h **/
|
|
int TestMain ();
|
|
|
|
#if __SUNPRO_CC
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#else /* !__SUNPRO_CC */
|
|
#include <cstdlib>
|
|
#if !TBB_USE_EXCEPTIONS && _MSC_VER
|
|
// Suppress "C++ exception handler used, but unwind semantics are not enabled" warning in STL headers
|
|
#pragma warning (push)
|
|
#pragma warning (disable: 4530)
|
|
#endif
|
|
#include <cstring>
|
|
#if !TBB_USE_EXCEPTIONS && _MSC_VER
|
|
#pragma warning (pop)
|
|
#endif
|
|
#endif /* !__SUNPRO_CC */
|
|
|
|
#include <new>
|
|
|
|
#if __TBB_MIC_NATIVE
|
|
#include "harness_mic.h"
|
|
#else
|
|
#define HARNESS_EXPORT
|
|
#define REPORT_FATAL_ERROR REPORT
|
|
#endif /* !__MIC__ */
|
|
|
|
#if _WIN32||_WIN64
|
|
#include "tbb/machine/windows_api.h"
|
|
#if _XBOX
|
|
#undef HARNESS_NO_PARSE_COMMAND_LINE
|
|
#define HARNESS_NO_PARSE_COMMAND_LINE 1
|
|
#endif
|
|
#if __TBB_WIN8UI_SUPPORT
|
|
#include <thread>
|
|
#endif
|
|
#include <process.h>
|
|
#else
|
|
#include <pthread.h>
|
|
#endif
|
|
#if __linux__
|
|
#include <sys/utsname.h> /* for uname */
|
|
#include <errno.h> /* for use in LinuxKernelVersion() */
|
|
#endif
|
|
|
|
#include "harness_report.h"
|
|
|
|
#if !HARNESS_NO_ASSERT
|
|
#include "harness_assert.h"
|
|
|
|
typedef void (*test_error_extra_t)(void);
|
|
static test_error_extra_t ErrorExtraCall;
|
|
//! Set additional handler to process failed assertions
|
|
void SetHarnessErrorProcessing( test_error_extra_t extra_call ) {
|
|
ErrorExtraCall = extra_call;
|
|
// TODO: add tbb::set_assertion_handler(ReportError);
|
|
}
|
|
|
|
//! Reports errors issued by failed assertions
|
|
void ReportError( const char* filename, int line, const char* expression, const char * message ) {
|
|
#if __TBB_ICL_11_1_CODE_GEN_BROKEN
|
|
printf("%s:%d, assertion %s: %s\n", filename, line, expression, message ? message : "failed" );
|
|
#else
|
|
REPORT_FATAL_ERROR("%s:%d, assertion %s: %s\n", filename, line, expression, message ? message : "failed" );
|
|
#endif
|
|
if( ErrorExtraCall )
|
|
(*ErrorExtraCall)();
|
|
#if HARNESS_TERMINATE_ON_ASSERT
|
|
TerminateProcess(GetCurrentProcess(), 1);
|
|
#elif HARNESS_EXIT_ON_ASSERT
|
|
exit(1);
|
|
#elif HARNESS_CONTINUE_ON_ASSERT
|
|
// continue testing
|
|
#else
|
|
abort();
|
|
#endif /* HARNESS_EXIT_ON_ASSERT */
|
|
}
|
|
//! Reports warnings issued by failed warning assertions
|
|
void ReportWarning( const char* filename, int line, const char* expression, const char * message ) {
|
|
REPORT("Warning: %s:%d, assertion %s: %s\n", filename, line, expression, message ? message : "failed" );
|
|
}
|
|
#else /* !HARNESS_NO_ASSERT */
|
|
//! Utility template function to prevent "unused" warnings by various compilers.
|
|
template<typename T> void suppress_unused_warning( const T& ) {}
|
|
|
|
#define ASSERT(p,msg) (suppress_unused_warning(p), (void)0)
|
|
#define ASSERT_WARNING(p,msg) (suppress_unused_warning(p), (void)0)
|
|
#endif /* !HARNESS_NO_ASSERT */
|
|
|
|
template<typename T, size_t N>
|
|
inline size_t array_length(const T(&)[N])
|
|
{
|
|
return N;
|
|
}
|
|
|
|
//TODO: remove this #if __TBB_INITIALIZER_LISTS_PRESENT
|
|
//it looks like all other compilers except gcc issue warnings/errors then they see
|
|
//declaration of zero sized array
|
|
#if __TBB_INITIALIZER_LISTS_PRESENT
|
|
template<typename T>
|
|
inline size_t array_length(const T[0])
|
|
{
|
|
return 0;
|
|
}
|
|
#endif //__TBB_INITIALIZER_LISTS_PRESENT
|
|
|
|
#if !HARNESS_NO_PARSE_COMMAND_LINE
|
|
|
|
//! Controls level of commentary printed via printf-like REMARK() macro.
|
|
/** If true, makes the test print commentary. If false, test should print "done" and nothing more. */
|
|
static bool Verbose;
|
|
|
|
#ifndef HARNESS_DEFAULT_MIN_THREADS
|
|
#define HARNESS_DEFAULT_MIN_THREADS 1
|
|
#endif
|
|
|
|
//! Minimum number of threads
|
|
static int MinThread = HARNESS_DEFAULT_MIN_THREADS;
|
|
|
|
#ifndef HARNESS_DEFAULT_MAX_THREADS
|
|
#define HARNESS_DEFAULT_MAX_THREADS 4
|
|
#endif
|
|
|
|
//! Maximum number of threads
|
|
static int MaxThread = HARNESS_DEFAULT_MAX_THREADS;
|
|
|
|
//! Parse command line of the form "name [-v] [MinThreads[:MaxThreads]]"
|
|
/** Sets Verbose, MinThread, and MaxThread accordingly.
|
|
The nthread argument can be a single number or a range of the form m:n.
|
|
A single number m is interpreted as if written m:m.
|
|
The numbers must be non-negative.
|
|
Clients often treat the value 0 as "run sequentially." */
|
|
static void ParseCommandLine( int argc, char* argv[] ) {
|
|
if( !argc ) REPORT("Command line with 0 arguments\n");
|
|
int i = 1;
|
|
if( i<argc ) {
|
|
if( strncmp( argv[i], "-v", 2 )==0 ) {
|
|
Verbose = true;
|
|
++i;
|
|
}
|
|
}
|
|
if( i<argc ) {
|
|
char* endptr;
|
|
MinThread = strtol( argv[i], &endptr, 0 );
|
|
if( *endptr==':' )
|
|
MaxThread = strtol( endptr+1, &endptr, 0 );
|
|
else if( *endptr=='\0' )
|
|
MaxThread = MinThread;
|
|
if( *endptr!='\0' ) {
|
|
REPORT_FATAL_ERROR("garbled nthread range\n");
|
|
exit(1);
|
|
}
|
|
if( MinThread<0 ) {
|
|
REPORT_FATAL_ERROR("nthread must be nonnegative\n");
|
|
exit(1);
|
|
}
|
|
if( MaxThread<MinThread ) {
|
|
REPORT_FATAL_ERROR("nthread range is backwards\n");
|
|
exit(1);
|
|
}
|
|
++i;
|
|
}
|
|
#if __TBB_STDARGS_BROKEN
|
|
if ( !argc )
|
|
argc = 1;
|
|
else {
|
|
while ( i < argc && argv[i][0] == 0 )
|
|
++i;
|
|
}
|
|
#endif /* __TBB_STDARGS_BROKEN */
|
|
if( i!=argc ) {
|
|
REPORT_FATAL_ERROR("Usage: %s [-v] [nthread|minthread:maxthread]\n", argv[0] );
|
|
exit(1);
|
|
}
|
|
}
|
|
#endif /* HARNESS_NO_PARSE_COMMAND_LINE */
|
|
|
|
#if HARNESS_USE_PROXY
|
|
#define TBB_PREVIEW_RUNTIME_LOADER 1
|
|
#include "tbb/runtime_loader.h"
|
|
static char const * _path[] = { ".", NULL };
|
|
static tbb::runtime_loader _runtime_loader( _path );
|
|
#endif // HARNESS_USE_PROXY
|
|
|
|
#if !HARNESS_CUSTOM_MAIN
|
|
|
|
#if __TBB_MPI_INTEROP
|
|
#undef SEEK_SET
|
|
#undef SEEK_CUR
|
|
#undef SEEK_END
|
|
#include "mpi.h"
|
|
#endif
|
|
|
|
HARNESS_EXPORT
|
|
#if HARNESS_NO_PARSE_COMMAND_LINE
|
|
int main() {
|
|
#if __TBB_MPI_INTEROP
|
|
MPI_Init(NULL,NULL);
|
|
#endif
|
|
#else
|
|
int main(int argc, char* argv[]) {
|
|
ParseCommandLine( argc, argv );
|
|
#if __TBB_MPI_INTEROP
|
|
MPI_Init(&argc,&argv);
|
|
#endif
|
|
#endif
|
|
#if __TBB_MPI_INTEROP
|
|
// Simple TBB/MPI interoperability harness for most of tests
|
|
// Worker processes send blocking messages to the master process about their rank and group size
|
|
// Master process receives this info and print it in verbose mode
|
|
int rank, size, myrank;
|
|
MPI_Status status;
|
|
MPI_Comm_size(MPI_COMM_WORLD,&size);
|
|
MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
|
|
if (myrank == 0) {
|
|
#if !HARNESS_NO_PARSE_COMMAND_LINE
|
|
REMARK("Hello mpi world. I am %d of %d\n", myrank, size);
|
|
#endif
|
|
for ( int i = 1; i < size; i++ ) {
|
|
MPI_Recv (&rank, 1, MPI_INT, i, 1, MPI_COMM_WORLD, &status);
|
|
MPI_Recv (&size, 1, MPI_INT, i, 1, MPI_COMM_WORLD, &status);
|
|
#if !HARNESS_NO_PARSE_COMMAND_LINE
|
|
REMARK("Hello mpi world. I am %d of %d\n", rank, size);
|
|
#endif
|
|
}
|
|
} else {
|
|
MPI_Send (&myrank, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
|
|
MPI_Send (&size, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
|
|
}
|
|
#endif
|
|
#if __TBB_MIC_OFFLOAD
|
|
int res = Harness::Unknown;
|
|
#pragma offload target(mic:-1) out(res)
|
|
{
|
|
#if __MIC__
|
|
res = TestMain ();
|
|
#else
|
|
ASSERT( 0, "Host execution in offload mode!" );
|
|
exit(1);
|
|
#endif
|
|
}
|
|
#else
|
|
int res = TestMain ();
|
|
#endif
|
|
ASSERT( res==Harness::Done || res==Harness::Skipped, "Wrong return code by TestMain");
|
|
#if __TBB_MPI_INTEROP
|
|
if (myrank == 0) {
|
|
REPORT( res==Harness::Done ? "done\n" : "skip\n" );
|
|
}
|
|
MPI_Finalize();
|
|
#else
|
|
REPORT( res==Harness::Done ? "done\n" : "skip\n" );
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#endif /* !HARNESS_CUSTOM_MAIN */
|
|
|
|
//! Base class for prohibiting compiler-generated operator=
|
|
class NoAssign {
|
|
//! Assignment not allowed
|
|
void operator=( const NoAssign& );
|
|
public:
|
|
#if __GNUC__
|
|
//! Explicitly define default construction, because otherwise gcc issues gratuitous warning.
|
|
NoAssign() {}
|
|
#endif /* __GNUC__ */
|
|
};
|
|
|
|
//! Base class for prohibiting compiler-generated copy constructor or operator=
|
|
class NoCopy: NoAssign {
|
|
//! Copy construction not allowed
|
|
NoCopy( const NoCopy& );
|
|
public:
|
|
NoCopy() {}
|
|
};
|
|
|
|
//! For internal use by template function NativeParallelFor
|
|
template<typename Index, typename Body>
|
|
class NativeParallelForTask: NoCopy {
|
|
public:
|
|
NativeParallelForTask( Index index_, const Body& body_ ) :
|
|
index(index_),
|
|
body(body_)
|
|
{}
|
|
|
|
//! Start task
|
|
void start() {
|
|
#if _WIN32||_WIN64
|
|
unsigned thread_id;
|
|
#if __TBB_WIN8UI_SUPPORT
|
|
std::thread* thread_tmp=new std::thread(thread_function, this);
|
|
thread_handle = thread_tmp->native_handle();
|
|
thread_id = 0;
|
|
#else
|
|
thread_handle = (HANDLE)_beginthreadex( NULL, 0, thread_function, this, 0, &thread_id );
|
|
#endif
|
|
ASSERT( thread_handle!=0, "NativeParallelFor: _beginthreadex failed" );
|
|
#else
|
|
#if __ICC==1100
|
|
#pragma warning (push)
|
|
#pragma warning (disable: 2193)
|
|
#endif /* __ICC==1100 */
|
|
// Some machines may have very large hard stack limit. When the test is
|
|
// launched by make, the default stack size is set to the hard limit, and
|
|
// calls to pthread_create fail with out-of-memory error.
|
|
// Therefore we set the stack size explicitly (as for TBB worker threads).
|
|
// TODO: make a single definition of MByte used by all tests.
|
|
const size_t MByte = 1024*1024;
|
|
#if __i386__||__i386||__arm__
|
|
const size_t stack_size = 1*MByte;
|
|
#elif __x86_64__
|
|
const size_t stack_size = 2*MByte;
|
|
#else
|
|
const size_t stack_size = 4*MByte;
|
|
#endif
|
|
pthread_attr_t attr_stack;
|
|
int status = pthread_attr_init(&attr_stack);
|
|
ASSERT(0==status, "NativeParallelFor: pthread_attr_init failed");
|
|
status = pthread_attr_setstacksize( &attr_stack, stack_size );
|
|
ASSERT(0==status, "NativeParallelFor: pthread_attr_setstacksize failed");
|
|
status = pthread_create(&thread_id, &attr_stack, thread_function, this);
|
|
ASSERT(0==status, "NativeParallelFor: pthread_create failed");
|
|
pthread_attr_destroy(&attr_stack);
|
|
#if __ICC==1100
|
|
#pragma warning (pop)
|
|
#endif
|
|
#endif /* _WIN32||_WIN64 */
|
|
}
|
|
|
|
//! Wait for task to finish
|
|
void wait_to_finish() {
|
|
#if _WIN32||_WIN64
|
|
DWORD status = WaitForSingleObjectEx( thread_handle, INFINITE, FALSE );
|
|
ASSERT( status!=WAIT_FAILED, "WaitForSingleObject failed" );
|
|
CloseHandle( thread_handle );
|
|
#else
|
|
int status = pthread_join( thread_id, NULL );
|
|
ASSERT( !status, "pthread_join failed" );
|
|
#endif
|
|
#if HARNESS_NO_ASSERT
|
|
(void)status;
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
#if _WIN32||_WIN64
|
|
HANDLE thread_handle;
|
|
#else
|
|
pthread_t thread_id;
|
|
#endif
|
|
|
|
//! Range over which task will invoke the body.
|
|
const Index index;
|
|
|
|
//! Body to invoke over the range.
|
|
const Body body;
|
|
|
|
#if _WIN32||_WIN64
|
|
static unsigned __stdcall thread_function( void* object )
|
|
#else
|
|
static void* thread_function(void* object)
|
|
#endif
|
|
{
|
|
NativeParallelForTask& self = *static_cast<NativeParallelForTask*>(object);
|
|
(self.body)(self.index);
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
//! Execute body(i) in parallel for i in the interval [0,n).
|
|
/** Each iteration is performed by a separate thread. */
|
|
template<typename Index, typename Body>
|
|
void NativeParallelFor( Index n, const Body& body ) {
|
|
typedef NativeParallelForTask<Index,Body> task;
|
|
|
|
if( n>0 ) {
|
|
// Allocate array to hold the tasks
|
|
task* array = static_cast<task*>(operator new( n*sizeof(task) ));
|
|
|
|
// Construct the tasks
|
|
for( Index i=0; i!=n; ++i )
|
|
new( &array[i] ) task(i,body);
|
|
|
|
// Start the tasks
|
|
for( Index i=0; i!=n; ++i )
|
|
array[i].start();
|
|
|
|
// Wait for the tasks to finish and destroy each one.
|
|
for( Index i=n; i; --i ) {
|
|
array[i-1].wait_to_finish();
|
|
array[i-1].~task();
|
|
}
|
|
|
|
// Deallocate the task array
|
|
operator delete(array);
|
|
}
|
|
}
|
|
|
|
//! The function to zero-initialize arrays; useful to avoid warnings
|
|
template <typename T>
|
|
void zero_fill(void* array, size_t n) {
|
|
memset(array, 0, sizeof(T)*n);
|
|
}
|
|
|
|
#if __SUNPRO_CC && defined(min)
|
|
#undef min
|
|
#undef max
|
|
#endif
|
|
|
|
#ifndef min
|
|
//! Utility template function returning lesser of the two values.
|
|
/** Provided here to avoid including not strict safe <algorithm>.\n
|
|
In case operands cause signed/unsigned or size mismatch warnings it is caller's
|
|
responsibility to do the appropriate cast before calling the function. **/
|
|
template<typename T1, typename T2>
|
|
T1 min ( const T1& val1, const T2& val2 ) {
|
|
return val1 < val2 ? val1 : val2;
|
|
}
|
|
#endif /* !min */
|
|
|
|
#ifndef max
|
|
//! Utility template function returning greater of the two values.
|
|
/** Provided here to avoid including not strict safe <algorithm>.\n
|
|
In case operands cause signed/unsigned or size mismatch warnings it is caller's
|
|
responsibility to do the appropriate cast before calling the function. **/
|
|
template<typename T1, typename T2>
|
|
T1 max ( const T1& val1, const T2& val2 ) {
|
|
return val1 < val2 ? val2 : val1;
|
|
}
|
|
#endif /* !max */
|
|
|
|
#if __linux__
|
|
inline unsigned LinuxKernelVersion()
|
|
{
|
|
unsigned digit1, digit2, digit3;
|
|
struct utsname utsnameBuf;
|
|
|
|
if (-1 == uname(&utsnameBuf)) {
|
|
REPORT_FATAL_ERROR("Can't call uname: errno %d\n", errno);
|
|
exit(1);
|
|
}
|
|
if (3 != sscanf(utsnameBuf.release, "%u.%u.%u", &digit1, &digit2, &digit3)) {
|
|
REPORT_FATAL_ERROR("Unable to parse OS release '%s'\n", utsnameBuf.release);
|
|
exit(1);
|
|
}
|
|
return 1000000*digit1+1000*digit2+digit3;
|
|
}
|
|
#endif
|
|
|
|
namespace Harness {
|
|
|
|
#if !HARNESS_NO_ASSERT
|
|
//! Base class that asserts that no operations are made with the object after its destruction.
|
|
class NoAfterlife {
|
|
protected:
|
|
enum state_t {
|
|
LIVE=0x56781234,
|
|
DEAD=0xDEADBEEF
|
|
} m_state;
|
|
|
|
public:
|
|
NoAfterlife() : m_state(LIVE) {}
|
|
NoAfterlife( const NoAfterlife& src ) : m_state(LIVE) {
|
|
ASSERT( src.IsLive(), "Constructing from the dead source" );
|
|
}
|
|
~NoAfterlife() {
|
|
ASSERT( IsLive(), "Repeated destructor call" );
|
|
m_state = DEAD;
|
|
}
|
|
const NoAfterlife& operator=( const NoAfterlife& src ) {
|
|
ASSERT( IsLive(), NULL );
|
|
ASSERT( src.IsLive(), NULL );
|
|
return *this;
|
|
}
|
|
void AssertLive() const {
|
|
ASSERT( IsLive(), "Already dead" );
|
|
}
|
|
bool IsLive() const {
|
|
return m_state == LIVE;
|
|
}
|
|
}; // NoAfterlife
|
|
#endif /* !HARNESS_NO_ASSERT */
|
|
|
|
#if _WIN32 || _WIN64
|
|
void Sleep ( int ms ) {
|
|
#if !__TBB_WIN8UI_SUPPORT
|
|
::Sleep(ms);
|
|
#else
|
|
std::chrono::milliseconds sleep_time( ms );
|
|
std::this_thread::sleep_for( sleep_time );
|
|
#endif
|
|
|
|
}
|
|
|
|
typedef DWORD tid_t;
|
|
tid_t CurrentTid () { return GetCurrentThreadId(); }
|
|
|
|
#else /* !WIN */
|
|
|
|
void Sleep ( int ms ) {
|
|
timespec requested = { ms / 1000, (ms % 1000)*1000000 };
|
|
timespec remaining = { 0, 0 };
|
|
nanosleep(&requested, &remaining);
|
|
}
|
|
|
|
typedef pthread_t tid_t;
|
|
tid_t CurrentTid () { return pthread_self(); }
|
|
#endif /* !WIN */
|
|
|
|
} // namespace Harness
|
|
|
|
#endif /* tbb_tests_harness_H */
|