+++ /dev/null
-/*
- Copyright 2005-2010 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"
-
-namespace Harness {
- enum TestResult {
- Done,
- Skipped
- };
-}
-
-//! 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 Verbose, 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 ();
-
-#define __TBB_LAMBDAS_PRESENT ( _MSC_VER >= 1600 && !__INTEL_COMPILER || __INTEL_COMPILER > 1100 && _TBB_CPP0X )
-
-#if defined(_MSC_VER) && _MSC_VER < 1400
- #define __TBB_EXCEPTION_TYPE_INFO_BROKEN 1
-#else
- #define __TBB_EXCEPTION_TYPE_INFO_BROKEN 0
-#endif
-
-#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>
-
- #define HARNESS_EXPORT
- #define REPORT_FATAL_ERROR REPORT
-
-#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
- #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);
-#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
-#define ASSERT(p,msg) ((void)0)
-#define ASSERT_WARNING(p,msg) ((void)0)
-#endif /* HARNESS_NO_ASSERT */
-
-#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_CUSTOM_MAIN
-
-HARNESS_EXPORT
-#if HARNESS_NO_PARSE_COMMAND_LINE
-int main() {
-#else
-int main(int argc, char* argv[]) {
- ParseCommandLine( argc, argv );
-#endif
- int res = TestMain ();
- ASSERT( res==Harness::Done || res==Harness::Skipped, "Wrong return code by TestMain");
- REPORT( res==Harness::Done ? "done\n" : "skip\n" );
- 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;
- thread_handle = (HANDLE)_beginthreadex( NULL, 0, thread_function, this, 0, &thread_id );
- 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).
- const size_t MByte = 1<<20;
-#if __i386__||__i386
- 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 = WaitForSingleObject( thread_handle, INFINITE );
- ASSERT( status!=WAIT_FAILED, "WaitForSingleObject failed" );
- CloseHandle( thread_handle );
-#else
- int status = pthread_join( thread_id, NULL );
- ASSERT( !status, "pthread_join failed" );
-#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 ) { ::Sleep(ms); }
-#else /* !WIN */
- void Sleep ( int ms ) {
- timespec requested = { ms / 1000, (ms % 1000)*1000000 };
- timespec remaining = { 0, 0 };
- nanosleep(&requested, &remaining);
- }
-#endif /* !WIN */
-
-} // namespace Harness
-
-#endif /* tbb_tests_harness_H */
+++ /dev/null
-/*
- Copyright 2005-2010 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 simple estimate of the memory being used by a program.
-// Not yet implemented for Mac.
-// This header is an optional part of the test harness.
-// It assumes that "harness_assert.h" has already been included.
-
-#if __linux__ || __APPLE__ || __sun
-#include <unistd.h>
-#elif _WIN32
-#include "tbb/machine/windows_api.h"
-#endif /* OS specific */
-#include <new>
-
-#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 <stdexcept>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#include "tbb/atomic.h"
-
-#if __SUNPRO_CC
-using std::printf;
-#endif
-
-#if defined(_MSC_VER) && defined(_Wp64)
- // Workaround for overzealous compiler warnings in /Wp64 mode
- #pragma warning (push)
- #pragma warning (disable: 4267)
-#endif
-
-
-template <typename base_alloc_t, typename count_t = tbb::atomic<size_t> >
-class static_counting_allocator : public base_alloc_t
-{
-public:
- typedef typename base_alloc_t::pointer pointer;
- typedef typename base_alloc_t::const_pointer const_pointer;
- typedef typename base_alloc_t::reference reference;
- typedef typename base_alloc_t::const_reference const_reference;
- typedef typename base_alloc_t::value_type value_type;
- typedef typename base_alloc_t::size_type size_type;
- typedef typename base_alloc_t::difference_type difference_type;
- template<typename U> struct rebind {
- typedef static_counting_allocator<typename base_alloc_t::template rebind<U>::other,count_t> other;
- };
-
- static size_t max_items;
- static count_t items_allocated;
- static count_t items_freed;
- static count_t allocations;
- static count_t frees;
- static bool verbose, throwing;
-
- static_counting_allocator() throw() { }
-
- static_counting_allocator(const static_counting_allocator& src) throw()
- : base_alloc_t(src) { }
-
- template<typename U, typename C>
- static_counting_allocator(const static_counting_allocator<U, C>& src) throw()
- : base_alloc_t(src) { }
-
- bool operator==(const static_counting_allocator &a) const
- { return true; }
-
- pointer allocate(const size_type n)
- {
- if(verbose) printf("\t+%d|", int(n));
- if(max_items && items_allocated + n >= max_items) {
- if(verbose) printf("items limit hits!");
- if(throwing)
- __TBB_THROW( std::bad_alloc() );
- return NULL;
- }
- allocations++;
- items_allocated += n;
- return base_alloc_t::allocate(n, pointer(0));
- }
-
- pointer allocate(const size_type n, const void * const)
- { return allocate(n); }
-
- void deallocate(const pointer ptr, const size_type n)
- {
- if(verbose) printf("\t-%d|", int(n));
- frees++;
- items_freed += n;
- base_alloc_t::deallocate(ptr, n);
- }
-
- static void init_counters(bool v = false) {
- verbose = v;
- if(verbose) printf("\n------------------------------------------- Allocations:\n");
- items_allocated = 0;
- items_freed = 0;
- allocations = 0;
- frees = 0;
- max_items = 0;
- }
-
- static void set_limits(size_type max = 0, bool do_throw = true) {
- max_items = max;
- throwing = do_throw;
- }
-};
-
-template <typename base_alloc_t, typename count_t>
-size_t static_counting_allocator<base_alloc_t, count_t>::max_items;
-template <typename base_alloc_t, typename count_t>
-count_t static_counting_allocator<base_alloc_t, count_t>::items_allocated;
-template <typename base_alloc_t, typename count_t>
-count_t static_counting_allocator<base_alloc_t, count_t>::items_freed;
-template <typename base_alloc_t, typename count_t>
-count_t static_counting_allocator<base_alloc_t, count_t>::allocations;
-template <typename base_alloc_t, typename count_t>
-count_t static_counting_allocator<base_alloc_t, count_t>::frees;
-template <typename base_alloc_t, typename count_t>
-bool static_counting_allocator<base_alloc_t, count_t>::verbose;
-template <typename base_alloc_t, typename count_t>
-bool static_counting_allocator<base_alloc_t, count_t>::throwing;
-
-template <typename base_alloc_t, typename count_t = tbb::atomic<size_t> >
-class local_counting_allocator : public base_alloc_t
-{
-public:
- typedef typename base_alloc_t::pointer pointer;
- typedef typename base_alloc_t::const_pointer const_pointer;
- typedef typename base_alloc_t::reference reference;
- typedef typename base_alloc_t::const_reference const_reference;
- typedef typename base_alloc_t::value_type value_type;
- typedef typename base_alloc_t::size_type size_type;
- typedef typename base_alloc_t::difference_type difference_type;
- template<typename U> struct rebind {
- typedef local_counting_allocator<typename base_alloc_t::template rebind<U>::other,count_t> other;
- };
-
- count_t items_allocated;
- count_t items_freed;
- count_t allocations;
- count_t frees;
- size_t max_items;
-
- local_counting_allocator() throw() {
- items_allocated = 0;
- items_freed = 0;
- allocations = 0;
- frees = 0;
- max_items = 0;
- }
-
- local_counting_allocator(const local_counting_allocator &a) throw()
- : base_alloc_t(a)
- , items_allocated(a.items_allocated)
- , items_freed(a.items_freed)
- , allocations(a.allocations)
- , frees(a.frees)
- , max_items(a.max_items)
- { }
-
- template<typename U, typename C>
- local_counting_allocator(const static_counting_allocator<U,C> &) throw() {
- items_allocated = static_counting_allocator<U,C>::items_allocated;
- items_freed = static_counting_allocator<U,C>::items_freed;
- allocations = static_counting_allocator<U,C>::allocations;
- frees = static_counting_allocator<U,C>::frees;
- max_items = static_counting_allocator<U,C>::max_items;
- }
-
- template<typename U, typename C>
- local_counting_allocator(const local_counting_allocator<U,C> &a) throw()
- : items_allocated(a.items_allocated)
- , items_freed(a.items_freed)
- , allocations(a.allocations)
- , frees(a.frees)
- , max_items(a.max_items)
- { }
-
- bool operator==(const local_counting_allocator &a) const
- { return &a == this; }
-
- pointer allocate(const size_type n)
- {
- if(max_items && items_allocated + n >= max_items)
- __TBB_THROW( std::bad_alloc() );
- ++allocations;
- items_allocated += n;
- return base_alloc_t::allocate(n, pointer(0));
- }
-
- pointer allocate(const size_type n, const void * const)
- { return allocate(n); }
-
- void deallocate(const pointer ptr, const size_type n)
- {
- ++frees;
- items_freed += n;
- base_alloc_t::deallocate(ptr, n);
- }
-
- void set_limits(size_type max = 0) {
- max_items = max;
- }
-};
-
-template <typename T, template<typename X> class Allocator = std::allocator>
-class debug_allocator : public Allocator<T>
-{
-public:
- typedef Allocator<T> base_allocator_type;
- typedef typename base_allocator_type::value_type value_type;
- typedef typename base_allocator_type::pointer pointer;
- typedef typename base_allocator_type::const_pointer const_pointer;
- typedef typename base_allocator_type::reference reference;
- typedef typename base_allocator_type::const_reference const_reference;
- typedef typename base_allocator_type::size_type size_type;
- typedef typename base_allocator_type::difference_type difference_type;
- template<typename U> struct rebind {
- typedef debug_allocator<U, Allocator> other;
- };
-
- debug_allocator() throw() { }
- debug_allocator(const debug_allocator &a) throw() : base_allocator_type( a ) { }
- template<typename U>
- debug_allocator(const debug_allocator<U> &a) throw() : base_allocator_type( Allocator<U>( a ) ) { }
-
- pointer allocate(const size_type n, const void *hint = 0 ) {
- pointer ptr = base_allocator_type::allocate( n, hint );
- std::memset( ptr, 0xE3E3E3E3, n * sizeof(value_type) );
- return ptr;
- }
-};
-
-//! Analogous to std::allocator<void>, as defined in ISO C++ Standard, Section 20.4.1
-/** @ingroup memory_allocation */
-template<template<typename T> class Allocator>
-class debug_allocator<void, Allocator> : public Allocator<void> {
-public:
- typedef Allocator<void> base_allocator_type;
- typedef typename base_allocator_type::value_type value_type;
- typedef typename base_allocator_type::pointer pointer;
- typedef typename base_allocator_type::const_pointer const_pointer;
- template<typename U> struct rebind {
- typedef debug_allocator<U, Allocator> other;
- };
-};
-
-template<typename T1, template<typename X1> class B1, typename T2, template<typename X2> class B2>
-inline bool operator==( const debug_allocator<T1,B1> &a, const debug_allocator<T2,B2> &b) {
- return static_cast< B1<T1> >(a) == static_cast< B2<T2> >(b);
-}
-template<typename T1, template<typename X1> class B1, typename T2, template<typename X2> class B2>
-inline bool operator!=( const debug_allocator<T1,B1> &a, const debug_allocator<T2,B2> &b) {
- return static_cast< B1<T1> >(a) != static_cast< B2<T2> >(b);
-}
-
-#if defined(_MSC_VER) && defined(_Wp64)
- // Workaround for overzealous compiler warnings in /Wp64 mode
- #pragma warning (pop)
-#endif // warning 4267 is back
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Just the assertion portion of the harness.
-// This is useful for writing portions of tests that include
-// the minimal number of necessary header files.
-//
-// The full "harness.h" must be included later.
-
-#ifndef harness_assert_H
-#define harness_assert_H
-
-void ReportError( const char* filename, int line, const char* expression, const char* message);
-void ReportWarning( const char* filename, int line, const char* expression, const char* message);
-
-#define ASSERT(p,message) ((p)?(void)0:ReportError(__FILE__,__LINE__,#p,message))
-#define ASSERT_WARNING(p,message) ((p)?(void)0:ReportWarning(__FILE__,__LINE__,#p,message))
-
-//! Compile-time error if x and y have different types
-template<typename T>
-void AssertSameType( const T& /*x*/, const T& /*y*/ ) {}
-
-#endif /* harness_assert_H */
+++ /dev/null
-/*
- Copyright 2005-2010 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 checking __TBB_ASSERT checks inside TBB.
-// This header is an optional part of the test harness.
-// It assumes that "harness.h" has already been included.
-
-#define TRY_BAD_EXPR_ENABLED (TBB_USE_ASSERT && TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN)
-
-#if TRY_BAD_EXPR_ENABLED
-
-//! Check that expression x raises assertion failure with message containing given substring.
-/** Assumes that tbb::set_assertion_handler( AssertionFailureHandler ) was called earlier. */
-#define TRY_BAD_EXPR(x,substr) \
- { \
- const char* message = NULL; \
- bool okay = false; \
- try { \
- x; \
- } catch( AssertionFailure a ) { \
- okay = true; \
- message = a.message; \
- } \
- CheckAssertionFailure(__LINE__,#x,okay,message,substr); \
- }
-
-//! Exception object that holds a message.
-struct AssertionFailure {
- const char* message;
- AssertionFailure( const char* filename, int line, const char* expression, const char* comment );
-};
-
-AssertionFailure::AssertionFailure( const char* filename, int line, const char* expression, const char* comment ) :
- message(comment)
-{
- ASSERT(filename,"missing filename");
- ASSERT(0<line,"line number must be positive");
- // All of our current files have fewer than 4000 lines.
- ASSERT(line<5000,"dubiously high line number");
- ASSERT(expression,"missing expression");
-}
-
-void AssertionFailureHandler( const char* filename, int line, const char* expression, const char* comment ) {
- throw AssertionFailure(filename,line,expression,comment);
-}
-
-void CheckAssertionFailure( int line, const char* expression, bool okay, const char* message, const char* substr ) {
- if( !okay ) {
- REPORT("Line %d, %s failed to fail\n", line, expression );
- abort();
- } else if( !message ) {
- REPORT("Line %d, %s failed without a message\n", line, expression );
- abort();
- } else if( strstr(message,substr)==0 ) {
- REPORT("Line %d, %s failed with message '%s' missing substring '%s'\n", __LINE__, expression, message, substr );
- abort();
- }
-}
-
-#endif /* TRY_BAD_EXPR_ENABLED */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/atomic.h"
-
-#ifndef harness_barrier_H
-#define harness_barrier_H
-
-namespace Harness {
-
-class SpinBarrier
-{
- unsigned numThreads;
- tbb::atomic<unsigned> numThreadsFinished; /* threads reached barrier in this epoch */
- tbb::atomic<unsigned> epoch; /* how many times this barrier used - XXX move to a separate cache line */
-
- struct DummyCallback {
- void operator() () const {}
- };
-
- SpinBarrier( const SpinBarrier& ); // no copy ctor
- void operator=( const SpinBarrier& ); // no assignment
-public:
- SpinBarrier( unsigned nthreads = 0 ) { initialize(nthreads); };
-
- void initialize( unsigned nthreads ) {
- numThreads = nthreads;
- numThreadsFinished = 0;
- epoch = 0;
- };
-
- // onOpenBarrierCallback is called by last thread arrived on a barrier
- template<typename Callback>
- bool wait(const Callback &onOpenBarrierCallback)
- { // return true if last thread
- unsigned myEpoch = epoch;
- int threadsLeft = numThreads - numThreadsFinished.fetch_and_increment() - 1;
- ASSERT(threadsLeft>=0, "Broken barrier");
- if (threadsLeft > 0) {
- /* not the last threading reaching barrier, wait until epoch changes & return 0 */
- tbb::internal::spin_wait_while_eq(epoch, myEpoch);
- return false;
- }
- /* No more threads left to enter, so I'm the last one reaching this epoch;
- reset the barrier, increment epoch, and return non-zero */
- onOpenBarrierCallback();
- numThreadsFinished = 0;
- epoch = myEpoch+1; /* wakes up threads waiting to exit this epoch */
- return true;
- }
- bool wait()
- {
- return wait(DummyCallback());
- }
-};
-
-}
-
-#endif //harness_barrier_H
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#ifndef tbb_tests_harness_concurrency_tracker_H
-#define tbb_tests_harness_concurrency_tracker_H
-
-#include "harness.h"
-#include "tbb/atomic.h"
-#include "../tbb/tls.h"
-
-namespace Harness {
-
-static tbb::atomic<unsigned> ctInstantParallelism;
-static tbb::atomic<unsigned> ctPeakParallelism;
-static tbb::internal::tls<uintptr_t> ctNested;
-
-class ConcurrencyTracker {
- bool m_Outer;
-
- static void Started () {
- unsigned p = ++ctInstantParallelism;
- unsigned q = ctPeakParallelism;
- while( q<p ) {
- q = ctPeakParallelism.compare_and_swap(p,q);
- }
- }
-
- static void Stopped () {
- ASSERT ( ctInstantParallelism > 0, "Mismatched call to ConcurrencyTracker::Stopped()" );
- --ctInstantParallelism;
- }
-public:
- ConcurrencyTracker() : m_Outer(false) {
- uintptr_t nested = ctNested;
- ASSERT (nested == 0 || nested == 1, NULL);
- if ( !ctNested ) {
- Started();
- m_Outer = true;
- ctNested = 1;
- }
- }
- ~ConcurrencyTracker() {
- if ( m_Outer ) {
- Stopped();
- ctNested = 0;
- }
- }
-
- static unsigned PeakParallelism() { return ctPeakParallelism; }
- static unsigned InstantParallelism() { return ctInstantParallelism; }
-
- static void Reset() {
- ASSERT (ctInstantParallelism == 0, "Reset cannot be called when concurrency tracking is underway");
- ctInstantParallelism = ctPeakParallelism = 0;
- }
-}; // ConcurrencyTracker
-
-} // namespace Harness
-
-#endif /* tbb_tests_harness_concurrency_tracker_H */
+++ /dev/null
-/*
- Copyright 2005-2010 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 simple estimate of CPU time being used by a program.
-// This header is an optional part of the test harness.
-// It assumes that "harness_assert.h" has already been included.
-
-#if _WIN32
-#if !_XBOX
- #include <windows.h>
-#endif
-#else
- #include <sys/time.h>
- #include <sys/resource.h>
-#endif
-
-//! Return time (in seconds) spent by the current process in user mode.
-/* Returns 0 if not implemented on platform. */
-static double GetCPUUserTime() {
-#if _XBOX
- return 0;
-#elif _WIN32
- FILETIME my_times[4];
- bool status = GetProcessTimes(GetCurrentProcess(), my_times, my_times+1, my_times+2, my_times+3)!=0;
- ASSERT( status, NULL );
- LARGE_INTEGER usrtime;
- usrtime.LowPart = my_times[3].dwLowDateTime;
- usrtime.HighPart = my_times[3].dwHighDateTime;
- return double(usrtime.QuadPart)*1E-7;
-#else
- // Generic UNIX, including __APPLE__
-
- // On Linux, there is no good way to get CPU usage info for the current process:
- // getrusage(RUSAGE_SELF, ...) that is used now only returns info for the calling thread;
- // getrusage(RUSAGE_CHILDREN, ...) only counts for finished children threads;
- // tms_utime and tms_cutime got with times(struct tms*) are equivalent to the above items;
- // finally, /proc/self/task/<task_id>/stat doesn't exist on older kernels
- // and it isn't quite convenient to read it for every task_id.
-
- struct rusage resources;
- bool status = getrusage(RUSAGE_SELF, &resources)==0;
- ASSERT( status, NULL );
- return (double(resources.ru_utime.tv_sec)*1E6 + double(resources.ru_utime.tv_usec))*1E-6;
-#endif
-}
-
-#include "tbb/tick_count.h"
-#include <cstdio>
-
-// The resolution of GetCPUUserTime is 10-15 ms or so; waittime should be a few times bigger.
-const double WAITTIME = 0.1; // in seconds, i.e. 100 ms
-const double THRESHOLD = WAITTIME/100;
-
-static void TestCPUUserTime( int nthreads, int nactive = 1 ) {
- // The test will always pass on Linux; read the comments in GetCPUUserTime for details
- // Also it will not detect spinning issues on systems with only one processing core.
-
- int nworkers = nthreads-nactive;
- if( !nworkers ) return;
- double lastusrtime = GetCPUUserTime();
- if( !lastusrtime ) return;
-
- static double minimal_waittime = WAITTIME,
- maximal_waittime = WAITTIME * 10;
- double usrtime;
- double waittime;
- tbb::tick_count stamp = tbb::tick_count::now();
- // wait for GetCPUUserTime update
- while( (usrtime=GetCPUUserTime())-lastusrtime < THRESHOLD ) {
- volatile intptr_t k = (intptr_t)&usrtime;
- for ( int i = 0; i < 1000; ++i ) ++k;
- if ( (waittime = (tbb::tick_count::now()-stamp).seconds()) > maximal_waittime ) {
- REPORT( "Warning: %.2f sec elapsed but user mode time is still below its threshold (%g < %g)\n",
- waittime, usrtime - lastusrtime, THRESHOLD );
- break;
- }
- }
- lastusrtime = usrtime;
-
- // Wait for workers to go sleep
- stamp = tbb::tick_count::now();
- while( ((waittime=(tbb::tick_count::now()-stamp).seconds()) < minimal_waittime)
- || ((usrtime=GetCPUUserTime()-lastusrtime) < THRESHOLD) )
- {
- if ( waittime > maximal_waittime ) {
- REPORT( "Warning: %.2f sec elapsed but GetCPUUserTime reported only %g sec\n", waittime, usrtime );
- break;
- }
- }
-
- // Test that all workers sleep when no work.
- while( nactive>1 && usrtime-nactive*waittime<0 ) {
- // probably the number of active threads was mispredicted
- --nactive; ++nworkers;
- }
- double avg_worker_usrtime = (usrtime-nactive*waittime)/nworkers;
-
- if( avg_worker_usrtime > waittime/2 )
- REPORT( "ERROR: %d worker threads are spinning; waittime: %g; usrtime: %g; avg worker usrtime: %g\n",
- nworkers, waittime, usrtime, avg_worker_usrtime);
- else
- REMARK("%d worker threads; waittime: %g; usrtime: %g; avg worker usrtime: %g\n",
- nworkers, waittime, usrtime, avg_worker_usrtime);
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include <typeinfo>
-#include "tbb/atomic.h"
-#include "harness.h"
-#include "harness_concurrency_tracker.h"
-
-namespace Harness {
-#if _WIN32 || _WIN64
- typedef DWORD tid_t;
- tid_t CurrentTid () { return GetCurrentThreadId(); }
-#else /* !WIN */
- typedef pthread_t tid_t;
- tid_t CurrentTid () { return pthread_self(); }
-#endif /* !WIN */
-} // namespace Harness
-
-int g_NumThreads = 0;
-Harness::tid_t g_Master = 0;
-
-tbb::atomic<intptr_t> g_CurExecuted,
- g_ExecutedAtCatch,
- g_ExceptionsThrown;
-volatile bool g_ExceptionCaught = false,
- g_UnknownException = false;
-
-volatile bool g_ThrowException = true,
- g_Flog = false;
-
-bool g_ExceptionInMaster = false;
-bool g_SolitaryException = false;
-
-//! Number of exceptions propagated into the user code (i.e. intercepted by the tests)
-tbb::atomic<intptr_t> g_Exceptions;
-
-inline void ResetEhGlobals ( bool throwException = true, bool flog = false ) {
- Harness::ConcurrencyTracker::Reset();
- g_CurExecuted = g_ExecutedAtCatch = 0;
- g_ExceptionCaught = false;
- g_UnknownException = false;
- g_ThrowException = throwException;
- g_Flog = flog;
- g_ExceptionsThrown = g_Exceptions = 0;
-}
-
-#if TBB_USE_EXCEPTIONS
-class test_exception : public std::exception {
- const char* my_description;
-public:
- test_exception ( const char* description ) : my_description(description) {}
-
- const char* what() const throw() { return my_description; }
-};
-
-class solitary_test_exception : public test_exception {
-public:
- solitary_test_exception ( const char* description ) : test_exception(description) {}
-};
-
-#if TBB_USE_CAPTURED_EXCEPTION
- typedef tbb::captured_exception PropagatedException;
- #define EXCEPTION_NAME(e) e.name()
-#else
- typedef test_exception PropagatedException;
- #define EXCEPTION_NAME(e) typeid(e).name()
-#endif
-
-#define EXCEPTION_DESCR "Test exception"
-
-#if HARNESS_EH_SIMPLE_MODE
-
-static void ThrowTestException () {
- ++g_ExceptionsThrown;
- throw test_exception(EXCEPTION_DESCR);
-}
-
-#else /* !HARNESS_EH_SIMPLE_MODE */
-
-static void ThrowTestException ( intptr_t threshold ) {
- if ( !g_ThrowException || (!g_Flog && (g_ExceptionInMaster ^ (Harness::CurrentTid() == g_Master))) )
- return;
- while ( Existed() < threshold )
- __TBB_Yield();
- if ( !g_SolitaryException ) {
- ++g_ExceptionsThrown;
- throw test_exception(EXCEPTION_DESCR);
- }
- if ( g_ExceptionsThrown.compare_and_swap(1, 0) == 0 )
- throw solitary_test_exception(EXCEPTION_DESCR);
-}
-#endif /* !HARNESS_EH_SIMPLE_MODE */
-
-#define CATCH() \
- } catch ( PropagatedException& e ) { \
- g_ExecutedAtCatch = g_CurExecuted; \
- ASSERT( e.what(), "Empty what() string" ); \
- ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(EXCEPTION_NAME(e), (g_SolitaryException ? typeid(solitary_test_exception) : typeid(test_exception)).name() ) == 0, "Unexpected original exception name"); \
- ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR) == 0, "Unexpected original exception info"); \
- g_ExceptionCaught = exceptionCaught = true; \
- ++g_Exceptions; \
- } catch ( tbb::tbb_exception& e ) { \
- REPORT("Unexpected %s\n", e.name()); \
- ASSERT (g_UnknownException && !g_UnknownException, "Unexpected tbb::tbb_exception" ); \
- } catch ( std::exception& e ) { \
- REPORT("Unexpected %s\n", typeid(e).name()); \
- ASSERT (g_UnknownException && !g_UnknownException, "Unexpected std::exception" ); \
- } catch ( ... ) { \
- g_ExceptionCaught = exceptionCaught = true; \
- g_UnknownException = unknownException = true; \
- } \
- if ( !g_SolitaryException ) \
- REMARK_ONCE ("Multiple exceptions mode: %d throws", (intptr_t)g_ExceptionsThrown);
-
-#define ASSERT_EXCEPTION() \
- ASSERT (g_ExceptionsThrown ? g_ExceptionCaught : true, "throw without catch"); \
- ASSERT (!g_ExceptionsThrown ? !g_ExceptionCaught : true, "catch without throw"); \
- ASSERT (g_ExceptionCaught, "no exception occurred"); \
- ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || !g_UnknownException, "unknown exception was caught")
-
-#define CATCH_AND_ASSERT() \
- CATCH() \
- ASSERT_EXCEPTION()
-
-#else /* !TBB_USE_EXCEPTIONS */
-
-inline void ThrowTestException ( intptr_t ) {}
-
-#endif /* !TBB_USE_EXCEPTIONS */
-
-#define TRY() \
- bool exceptionCaught = false, unknownException = false; \
- __TBB_TRY {
-
-// "exceptionCaught || unknownException" is used only to "touch" otherwise unused local variables
-#define CATCH_AND_FAIL() } __TBB_CATCH(...) { \
- ASSERT (false, "Canceling tasks must not cause any exceptions"); \
- (void)(exceptionCaught && unknownException); \
- }
-
-const int c_Timeout = 1000000;
-
-void WaitUntilConcurrencyPeaks ( int expected_peak ) {
- if ( g_Flog )
- return;
- int n = 0;
-retry:
- while ( ++n < c_Timeout && (int)Harness::ConcurrencyTracker::PeakParallelism() < expected_peak )
- __TBB_Yield();
- ASSERT_WARNING( n < c_Timeout, "Missed wakeup or machine is overloaded?" );
- // Workaround in case a missed wakeup takes place
- if ( n == c_Timeout ) {
- tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task();
- r.spawn(r);
- n = 0;
- goto retry;
- }
-}
-
-inline void WaitUntilConcurrencyPeaks () { WaitUntilConcurrencyPeaks(g_NumThreads); }
-
-inline bool IsMaster() {
- return Harness::CurrentTid() == g_Master;
-}
-
-inline bool IsThrowingThread() {
- return g_ExceptionInMaster ^ IsMaster() ? true : false;
-}
-
-class CancellatorTask : public tbb::task {
- static volatile bool s_Ready;
- tbb::task_group_context &m_groupToCancel;
- intptr_t m_cancellationThreshold;
-
- tbb::task* execute () {
- Harness::ConcurrencyTracker ct;
- s_Ready = true;
- while ( g_CurExecuted < m_cancellationThreshold )
- __TBB_Yield();
- m_groupToCancel.cancel_group_execution();
- g_ExecutedAtCatch = g_CurExecuted;
- return NULL;
- }
-public:
- CancellatorTask ( tbb::task_group_context& ctx, intptr_t threshold )
- : m_groupToCancel(ctx), m_cancellationThreshold(threshold)
- {
- s_Ready = false;
- }
-
- static void Reset () { s_Ready = false; }
-
- static bool WaitUntilReady () {
- const intptr_t limit = 10000000;
- intptr_t n = 0;
- do {
- __TBB_Yield();
- } while( !s_Ready && ++n < limit );
- ASSERT( s_Ready || n == limit, NULL );
- return s_Ready;
- }
-};
-
-volatile bool CancellatorTask::s_Ready = false;
-
-template<class LauncherTaskT, class CancellatorTaskT>
-void RunCancellationTest ( intptr_t threshold = 1 )
-{
- tbb::task_group_context ctx;
- tbb::empty_task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
- r.set_ref_count(3);
- r.spawn( *new( r.allocate_child() ) CancellatorTaskT(ctx, threshold) );
- __TBB_Yield();
- r.spawn( *new( r.allocate_child() ) LauncherTaskT(ctx) );
- TRY();
- r.wait_for_all();
- CATCH_AND_FAIL();
- r.destroy(r);
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Used in tests that work with TBB scheduler but do not link to the TBB library.
-// In other words it embeds the TBB library core into the test executable.
-
-#ifndef harness_inject_scheduler_H
-#define harness_inject_scheduler_H
-
-// Suppress usage of #pragma comment
-#define __TBB_NO_IMPLICIT_LINKAGE 1
-
-#define __TBB_TASK_CPP_DIRECTLY_INCLUDED 1
-#include "../tbb/tbb_main.cpp"
-
-// Tasking subsystem files
-#include "../tbb/governor.cpp"
-#if __TBB_ARENA_PER_MASTER
-#include "../tbb/market.cpp"
-#endif /* __TBB_ARENA_PER_MASTER */
-#include "../tbb/arena.cpp"
-#include "../tbb/scheduler.cpp"
-#include "../tbb/observer_proxy.cpp"
-#include "../tbb/task.cpp"
-#include "../tbb/task_group_context.cpp"
-
-// Other dependencies
-#include "../tbb/cache_aligned_allocator.cpp"
-#include "../tbb/dynamic_link.cpp"
-#include "../tbb/tbb_thread.cpp"
-#include "../tbb/mutex.cpp"
-#include "../tbb/spin_rw_mutex.cpp"
-#include "../tbb/spin_mutex.cpp"
-#include "../tbb/private_server.cpp"
-#include "../rml/client/rml_tbb.cpp"
-
-#endif /* harness_inject_scheduler_H */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#ifndef harness_iterator_H
-#define harness_iterator_H
-
-#include <iterator>
-#include <memory>
-
-namespace Harness {
-
-template <class T>
-class InputIterator {
- T * my_ptr;
-public:
-#if HARNESS_EXTENDED_STD_COMPLIANCE
- typedef std::input_iterator_tag iterator_category;
- typedef T value_type;
- typedef typename std::allocator<T>::difference_type difference_type;
- typedef typename std::allocator<T>::pointer pointer;
- typedef typename std::allocator<T>::reference reference;
-#endif /* HARNESS_EXTENDED_STD_COMPLIANCE */
-
- explicit InputIterator( T * ptr): my_ptr(ptr){}
-
- T& operator* () { return *my_ptr; }
-
- InputIterator& operator++ () { ++my_ptr; return *this; }
-
- bool operator== ( const InputIterator& r ) { return my_ptr == r.my_ptr; }
-};
-
-template <class T>
-class ForwardIterator {
- T * my_ptr;
-public:
-#if HARNESS_EXTENDED_STD_COMPLIANCE
- typedef std::forward_iterator_tag iterator_category;
- typedef T value_type;
- typedef typename std::allocator<T>::difference_type difference_type;
- typedef typename std::allocator<T>::pointer pointer;
- typedef typename std::allocator<T>::reference reference;
-#endif /* HARNESS_EXTENDED_STD_COMPLIANCE */
-
- explicit ForwardIterator ( T * ptr ) : my_ptr(ptr){}
-
- ForwardIterator ( const ForwardIterator& r ) : my_ptr(r.my_ptr){}
-
- T& operator* () { return *my_ptr; }
-
- ForwardIterator& operator++ () { ++my_ptr; return *this; }
-
- bool operator== ( const ForwardIterator& r ) { return my_ptr == r.my_ptr; }
-};
-
-template <class T>
-class RandomIterator {
- T * my_ptr;
-#if !HARNESS_EXTENDED_STD_COMPLIANCE
- typedef typename std::allocator<T>::difference_type difference_type;
-#endif
-
-public:
-#if HARNESS_EXTENDED_STD_COMPLIANCE
- typedef std::random_access_iterator_tag iterator_category;
- typedef T value_type;
- typedef typename std::allocator<T>::pointer pointer;
- typedef typename std::allocator<T>::reference reference;
- typedef typename std::allocator<T>::difference_type difference_type;
-#endif /* HARNESS_EXTENDED_STD_COMPLIANCE */
-
- explicit RandomIterator ( T * ptr ) : my_ptr(ptr){}
- RandomIterator ( const RandomIterator& r ) : my_ptr(r.my_ptr){}
- T& operator* () { return *my_ptr; }
- RandomIterator& operator++ () { ++my_ptr; return *this; }
- bool operator== ( const RandomIterator& r ) { return my_ptr == r.my_ptr; }
- difference_type operator- (const RandomIterator &r) {return my_ptr - r.my_ptr;}
- RandomIterator operator+ (difference_type n) {return RandomIterator(my_ptr + n);}
-};
-
-} // namespace Harness
-
-#if !HARNESS_EXTENDED_STD_COMPLIANCE
-namespace std {
- template<typename T>
- struct iterator_traits< Harness::InputIterator<T> > {
- typedef std::input_iterator_tag iterator_category;
- typedef T value_type;
- };
-
- template<typename T>
- struct iterator_traits< Harness::ForwardIterator<T> > {
- typedef std::forward_iterator_tag iterator_category;
- typedef T value_type;
- };
-
- template<typename T>
- struct iterator_traits< Harness::RandomIterator<T> > {
- typedef std::random_access_iterator_tag iterator_category;
- typedef T value_type;
- };
-} // namespace std
-#endif /* !HARNESS_EXTENDED_STD_COMPLIANCE */
-
-#endif //harness_iterator_H
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Header that sets HAVE_m128 if we have type __m128
-
-#if (__SSE__||_M_IX86) && !defined(__sun)
-#include <xmmintrin.h>
-#define HAVE_m128 1
-
-//! Class for testing safety of using __m128
-/** Uses circuitous logic forces compiler to put __m128 objects on stack while
- executing various methods, and thus tempt it to use aligned loads and stores
- on the stack. */
-// Do not create file-scope objects of the class, because MinGW (as of May 2010)
-// did not always provide proper stack alignment in destructors of such objects.
-class ClassWithSSE {
- static const int n = 16;
- __m128 field[n];
- void init( int start );
-public:
- ClassWithSSE() {init(-n);}
- ClassWithSSE( int i ) {init(i);}
- void operator=( const ClassWithSSE& src ) {
- __m128 stack[n];
- for( int i=0; i<n; ++i )
- stack[i^5] = src.field[i];
- for( int i=0; i<n; ++i )
- field[i^5] = stack[i];
- }
- ~ClassWithSSE() {init(-2*n);}
- friend bool operator==( const ClassWithSSE& x, const ClassWithSSE& y ) {
- for( int i=0; i<4*n; ++i )
- if( ((const float*)x.field)[i]!=((const float*)y.field)[i] )
- return false;
- return true;
- }
- friend bool operator!=( const ClassWithSSE& x, const ClassWithSSE& y ) {
- return !(x==y);
- }
-};
-
-void ClassWithSSE::init( int start ) {
- __m128 stack[n];
- for( int i=0; i<n; ++i ) {
- // Declaring value as a one-element array instead of a scalar quites
- // gratuitous warnings about possible use of "value" before it was set.
- __m128 value[1];
- for( int j=0; j<4; ++j )
- ((float*)value)[j] = float(n*start+4*i+j);
- stack[i^5] = value[0];
- }
- for( int i=0; i<n; ++i )
- field[i^5] = stack[i];
-}
-
-#endif /* __SSE__||_M_IX86 */
+++ /dev/null
-/*
- Copyright 2005-2010 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 simple estimate of the memory being used by a program.
-// Not yet implemented for Mac.
-// This header is an optional part of the test harness.
-// It assumes that "harness_assert.h" has already been included.
-
-#if __linux__ || __sun
-#include <sys/resource.h>
-#include <unistd.h>
-
-#elif __APPLE__
-#include <unistd.h>
-#include <mach/mach.h>
-#include <AvailabilityMacros.h>
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
-#include <mach/shared_region.h>
-#else
-#include <mach/shared_memory_server.h>
-#endif
-#if SHARED_TEXT_REGION_SIZE || SHARED_DATA_REGION_SIZE
-const size_t shared_size = SHARED_TEXT_REGION_SIZE+SHARED_DATA_REGION_SIZE;
-#else
-const size_t shared_size = 0;
-#endif
-
-#elif _WIN32 && !_XBOX
-#include <windows.h>
-#include <psapi.h>
-#if _MSC_VER
-#pragma comment(lib, "psapi")
-#endif
-
-#endif /* OS selection */
-
-//! Return estimate of number of bytes of memory that this program is currently using.
-/* Returns 0 if not implemented on platform. */
-size_t GetMemoryUsage() {
-#if _XBOX
- return 0;
-#elif _WIN32
- PROCESS_MEMORY_COUNTERS mem;
- bool status = GetProcessMemoryInfo(GetCurrentProcess(), &mem, sizeof(mem))!=0;
- ASSERT(status, NULL);
- return mem.PagefileUsage;
-#elif __linux__
- FILE* statsfile = fopen("/proc/self/statm","r");
- size_t pagesize = getpagesize();
- ASSERT(statsfile, NULL);
- long total_mem;
- int n = fscanf(statsfile,"%lu",&total_mem);
- if( n!=1 ) {
- REPORT("Warning: memory usage statistics wasn't obtained\n");
- return 0;
- }
- fclose(statsfile);
- return total_mem*pagesize;
-#elif __APPLE__
- kern_return_t status;
- task_basic_info info;
- mach_msg_type_number_t msg_type = TASK_BASIC_INFO_COUNT;
- status = task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&info), &msg_type);
- ASSERT(status==KERN_SUCCESS, NULL);
- return info.virtual_size - shared_size;
-#else
- return 0;
-#endif
-}
-
-//! Use approximately a specified amount of stack space.
-/** Recursion is used here instead of alloca because some implementations of alloca do not use the stack. */
-void UseStackSpace( size_t amount, char* top=0 ) {
- char x[1000];
- memset( x, -1, sizeof(x) );
- if( !top )
- top = x;
- ASSERT( x<=top, "test assumes that stacks grow downwards" );
- if( size_t(top-x)<amount )
- UseStackSpace( amount, top );
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Just the tracing portion of the harness.
-//
-// This header defines TRACE and TRCAENL macros, which use REPORT like syntax and
-// are useful for duplicating trace output to the standard debug output on Windows.
-// It is possible to add the ability of automatic extending messages with additional
-// info (file, line, function, time, thread ID, ...).
-//
-// Macros output nothing when test app runs in non-verbose mode (default).
-//
-// The full "harness.h" must be included before this header.
-
-#ifndef tbb_tests_harness_report_H
-#define tbb_tests_harness_report_H
-
-#if defined(MAX_TRACE_SIZE) && MAX_TRACE_SIZE < 1024
- #undef MAX_TRACE_SIZE
-#endif
-#ifndef MAX_TRACE_SIZE
- #define MAX_TRACE_SIZE 1024
-#endif
-
-#if __SUNPRO_CC
-#include <stdio.h>
-#else
-#include <cstdio>
-#endif
-
-#include <cstdarg>
-
-
-#ifdef HARNESS_INCOMPLETE_SOURCES
-#error Source files are not complete. Check the build environment
-#endif
-
-#if _MSC_VER
- #define snprintf _snprintf
-#if _MSC_VER<=1400
- #define vsnprintf _vsnprintf
-#endif
-#endif
-
-namespace Harness {
- namespace internal {
-
-#ifndef TbbHarnessReporter
- struct TbbHarnessReporter {
- void Report ( const char* msg ) {
- printf( "%s", msg );
- fflush(stdout);
-#ifdef _WINDOWS_
- OutputDebugStringA(msg);
-#endif
- }
- }; // struct TbbHarnessReporter
-#endif /* !TbbHarnessReporter */
-
- class Tracer {
- int m_flags;
- const char *m_file;
- const char *m_func;
- size_t m_line;
-
- TbbHarnessReporter m_reporter;
-
- public:
- enum {
- prefix = 1,
- need_lf = 2
- };
-
- Tracer* set_trace_info ( int flags, const char *file, size_t line, const char *func ) {
- m_flags = flags;
- m_line = line;
- m_file = file;
- m_func = func;
- return this;
- }
-
- void trace ( const char* fmt, ... ) {
- char msg[MAX_TRACE_SIZE];
- char msg_fmt_buf[MAX_TRACE_SIZE];
- const char *msg_fmt = fmt;
- if ( m_flags & prefix ) {
- snprintf (msg_fmt_buf, MAX_TRACE_SIZE, "[%s] %s", m_func, fmt);
- msg_fmt = msg_fmt_buf;
- }
- std::va_list argptr;
- va_start (argptr, fmt);
- int len = vsnprintf (msg, MAX_TRACE_SIZE, msg_fmt, argptr);
- va_end (argptr);
- if ( m_flags & need_lf &&
- len < MAX_TRACE_SIZE - 1 && msg_fmt[len-1] != '\n' )
- {
- msg[len] = '\n';
- msg[len + 1] = 0;
- }
- m_reporter.Report(msg);
- }
- }; // class Tracer
-
- static Tracer tracer;
-
- template<int>
- bool not_the_first_call () {
- static bool first_call = false;
- bool res = first_call;
- first_call = true;
- return res;
- }
-
- } // namespace internal
-} // namespace Harness
-
-#if defined(_MSC_VER) && _MSC_VER >= 1300 || defined(__GNUC__) || defined(__GNUG__)
- #define HARNESS_TRACE_ORIG_INFO __FILE__, __LINE__, __FUNCTION__
-#else
- #define HARNESS_TRACE_ORIG_INFO __FILE__, __LINE__, ""
- #define __FUNCTION__ ""
-#endif
-
-
-//! printf style tracing macro
-/** This variant of TRACE adds trailing line-feed (new line) character, if it is absent. **/
-#define TRACE Harness::internal::tracer.set_trace_info(Harness::internal::Tracer::need_lf, HARNESS_TRACE_ORIG_INFO)->trace
-
-//! printf style tracing macro without automatic new line character adding
-#define TRACENL Harness::internal::tracer.set_trace_info(0, HARNESS_TRACE_ORIG_INFO)->trace
-
-//! printf style tracing macro with additional information prefix (e.g. current function name)
-#define TRACEP Harness::internal::tracer.set_trace_info(Harness::internal::Tracer::prefix | \
- Harness::internal::Tracer::need_lf, HARNESS_TRACE_ORIG_INFO)->trace
-
-//! printf style remark macro
-/** Produces output only when the test is run with the -v (verbose) option. **/
-#define REMARK !Verbose ? (void)0 : TRACENL
-
-//! printf style remark macro
-/** Produces output only when invoked first time.
- Only one instance of this macro is allowed per source code line. **/
-#define REMARK_ONCE (!Verbose || Harness::internal::not_the_first_call<__LINE__>()) ? (void)0 : TRACE
-
-//! printf style reporting macro
-/** On heterogeneous platforms redirects its output to the host side. **/
-#define REPORT TRACENL
-
-#endif /* tbb_tests_harness_report_H */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test whether scalable_allocator complies with the requirements in 20.1.5 of ISO C++ Standard (1998).
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-
-#include "tbb/scalable_allocator.h"
-
-// the actual body of the test is there:
-#include "test_allocator.h"
-
-#if _MSC_VER
-#include "tbb/machine/windows_api.h"
-#endif /* _MSC_VER */
-
-int TestMain () {
-#if _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE
- #ifdef _DEBUG
- ASSERT(!GetModuleHandle("tbbmalloc.dll") && GetModuleHandle("tbbmalloc_debug.dll"),
- "test linked with wrong (non-debug) tbbmalloc library");
- #else
- ASSERT(!GetModuleHandle("tbbmalloc_debug.dll") && GetModuleHandle("tbbmalloc.dll"),
- "test linked with wrong (debug) tbbmalloc library");
- #endif
-#endif /* _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE */
- int result = TestMain<tbb::scalable_allocator<void> >();
- ASSERT( !result, NULL );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test whether scalable_allocator works with some of the host's STL containers.
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "tbb/scalable_allocator.h"
-
-// The actual body of the test is there:
-#include "test_allocator_STL.h"
-
-int TestMain () {
- TestAllocatorWithSTL<tbb::scalable_allocator<void> >();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/tbb_config.h"
-
-#if __TBB_GCC_WARNING_SUPPRESSION_ENABLED
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-#endif
-
-//! Wrapper around T where all members are private.
-/** Used to prove that aligned_space<T,N> never calls member of T. */
-template<typename T>
-class Minimal {
- Minimal();
- Minimal( Minimal& min );
- ~Minimal();
- void operator=( const Minimal& );
- T pad;
- template<typename U>
- friend void AssignToCheckAlignment( Minimal<U>& dst, const Minimal<U>& src ) ;
-};
-
-template<typename T>
-void AssignToCheckAlignment( Minimal<T>& dst, const Minimal<T>& src ) {
- dst.pad = src.pad;
-}
-
-#include "tbb/aligned_space.h"
-#include "harness_assert.h"
-
-static bool SpaceWasted;
-
-template<typename U, size_t N>
-void TestAlignedSpaceN() {
- typedef Minimal<U> T;
- struct {
- //! Pad byte increases chance that subsequent member will be misaligned if there is a problem.
- char pad;
- tbb::aligned_space<T ,N> space;
- } x;
- AssertSameType( static_cast< T *>(0), x.space.begin() );
- AssertSameType( static_cast< T *>(0), x.space.end() );
- ASSERT( reinterpret_cast<void *>(x.space.begin())==reinterpret_cast< void *>(&x.space), NULL );
- ASSERT( x.space.end()-x.space.begin()==N, NULL );
- ASSERT( reinterpret_cast<void *>(x.space.begin())>=reinterpret_cast< void *>(&x.space), NULL );
- ASSERT( x.space.end()<=reinterpret_cast< T *>(&x.space+1), NULL );
- // Though not required, a good implementation of aligned_space<T,N> does not use any more space than a T[N].
- SpaceWasted |= sizeof(x.space)!=sizeof(T)*N;
- for( size_t k=1; k<N; ++k )
- AssignToCheckAlignment( x.space.begin()[k-1], x.space.begin()[k] );
-}
-
-static void PrintSpaceWastingWarning( const char* type_name );
-
-#include <typeinfo>
-
-template<typename T>
-void TestAlignedSpace() {
- SpaceWasted = false;
- TestAlignedSpaceN<T,1>();
- TestAlignedSpaceN<T,2>();
- TestAlignedSpaceN<T,3>();
- TestAlignedSpaceN<T,4>();
- TestAlignedSpaceN<T,5>();
- TestAlignedSpaceN<T,6>();
- TestAlignedSpaceN<T,7>();
- TestAlignedSpaceN<T,8>();
- if( SpaceWasted )
- PrintSpaceWastingWarning( typeid(T).name() );
-}
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-
-#include "harness_m128.h"
-
-int TestMain () {
- TestAlignedSpace<char>();
- TestAlignedSpace<short>();
- TestAlignedSpace<int>();
- TestAlignedSpace<float>();
- TestAlignedSpace<double>();
- TestAlignedSpace<long double>();
- TestAlignedSpace<size_t>();
-#if HAVE_m128
- TestAlignedSpace<__m128>();
-#endif /* HAVE_m128 */
- return Harness::Done;
-}
-
-static void PrintSpaceWastingWarning( const char* type_name ) {
- REPORT("Consider rewriting aligned_space<%s,N> to waste less space\n", type_name );
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Basic testing of an allocator
-// Tests against requirements in 20.1.5 of ISO C++ Standard (1998).
-// Does not check for thread safety or false sharing issues.
-//
-// Tests for compatibility with the host's STL are in
-// test_Allocator_STL.h. Those tests are in a separate file
-// because they bring in lots of STL headers, and the tests here
-// are supposed to work in the abscense of STL.
-
-#include "harness.h"
-
-template<typename A>
-struct is_zero_filling {
- static const bool value = false;
-};
-
-int NumberOfFoo;
-
-template<typename T, size_t N>
-struct Foo {
- T foo_array[N];
- Foo() {
- zero_fill<T>(foo_array, N);
- ++NumberOfFoo;
- }
- Foo( const Foo& x ) {
- *this = x;
- ++NumberOfFoo;
- }
- ~Foo() {
- --NumberOfFoo;
- }
-};
-
-inline char PseudoRandomValue( size_t j, size_t k ) {
- return char(j*3 ^ j>>4 ^ k);
-}
-
-//! T is type and A is allocator for that type
-template<typename T, typename A>
-void TestBasic( A& a ) {
- T x;
- const T cx = T();
-
- // See Table 32 in ISO ++ Standard
- typename A::pointer px = &x;
- typename A::const_pointer pcx = &cx;
-
- typename A::reference rx = x;
- ASSERT( &rx==&x, NULL );
-
- typename A::const_reference rcx = cx;
- ASSERT( &rcx==&cx, NULL );
-
- typename A::value_type v = x;
-
- typename A::size_type size;
- size = 0;
- --size;
- ASSERT( size>0, "not an unsigned integral type?" );
-
- typename A::difference_type difference;
- difference = 0;
- --difference;
- ASSERT( difference<0, "not an signed integral type?" );
-
- // "rebind" tested by our caller
-
- ASSERT( a.address(rx)==px, NULL );
-
- ASSERT( a.address(rcx)==pcx, NULL );
-
- typename A::pointer array[100];
- size_t sizeof_T = sizeof(T);
- for( size_t k=0; k<100; ++k ) {
- array[k] = k&1 ? a.allocate(k,array[0]) : a.allocate(k);
- char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
- for( size_t j=0; j<k*sizeof_T; ++j )
- s[j] = PseudoRandomValue(j,k);
- }
-
- // Test hint argument. This can't be compiled when hint is void*, It should be const void*
- typename A::pointer a_ptr;
- const void * const_hint = NULL;
- a_ptr = a.allocate (1, const_hint);
- a.deallocate(a_ptr, 1);
-
- // Test "a.deallocate(p,n)
- for( size_t k=0; k<100; ++k ) {
- char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
- for( size_t j=0; j<k*sizeof_T; ++j )
- ASSERT( s[j] == PseudoRandomValue(j,k), NULL );
- a.deallocate(array[k],k);
- }
-
- // Test "a.max_size()"
- AssertSameType( a.max_size(), typename A::size_type(0) );
- // Following assertion catches case where max_size() is so large that computation of
- // number of bytes for such an allocation would overflow size_type.
- ASSERT( a.max_size()*typename A::size_type(sizeof(T))>=a.max_size(), "max_size larger than reasonable" );
-
- // Test "a1==a2"
- A a1, a2;
- ASSERT( a1==a2, NULL );
-
- // Test "a1!=a2"
- ASSERT( !(a1!=a2), NULL );
-
- // Test "a.construct(p,t)"
- int n = NumberOfFoo;
- typename A::pointer p = a.allocate(1);
- a.construct( p, cx );
- ASSERT( NumberOfFoo==n+1, "constructor for Foo not called?" );
-
- // Test "a.destroy(p)"
- a.destroy( p );
- ASSERT( NumberOfFoo==n, "destructor for Foo not called?" );
- a.deallocate(p,1);
-}
-
-#include "tbb/blocked_range.h"
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Workaround for erroneous "conditional expression is constant" warning in method check_allocate.
- #pragma warning (disable: 4127)
-#endif
-
-// A is an allocator for some type
-template<typename A>
-struct Body: NoAssign {
- static const size_t max_k = 100000;
- A &a;
- Body(A &a_) : a(a_) {}
- void check_allocate( typename A::pointer array[], size_t i, size_t t ) const
- {
- ASSERT(array[i] == 0, NULL);
- size_t size = i * (i&3);
- array[i] = i&1 ? a.allocate(size, array[i>>3]) : a.allocate(size);
- char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
- for( size_t j=0; j<size*sizeof(A); ++j ) {
- if(is_zero_filling<typename A::template rebind<void>::other>::value)
- ASSERT( !s[j], NULL);
- s[j] = PseudoRandomValue(i, t);
- }
- }
-
- void check_deallocate( typename A::pointer array[], size_t i, size_t t ) const
- {
- ASSERT(array[i] != 0, NULL);
- size_t size = i * (i&3);
- char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
- for( size_t j=0; j<size*sizeof(A); ++j )
- ASSERT( s[j] == PseudoRandomValue(i, t), "Thread safety test failed" );
- a.deallocate(array[i], size);
- array[i] = 0;
- }
-
- void operator()( size_t thread_id ) const {
- typename A::pointer array[256];
-
- for( size_t k=0; k<256; ++k )
- array[k] = 0;
- for( size_t k=0; k<max_k; ++k ) {
- size_t i = static_cast<unsigned char>(PseudoRandomValue(k,thread_id));
- if(!array[i]) check_allocate(array, i, thread_id);
- else check_deallocate(array, i, thread_id);
- }
- for( size_t k=0; k<256; ++k )
- if(array[k])
- check_deallocate(array, k, thread_id);
- }
-};
-
-// A is an allocator for some type, and U is another type
-template<typename A, typename U>
-void Test() {
- typename A::template rebind<U>::other b;
- TestBasic<U>(b);
-
- A a(b);
- TestBasic<typename A::value_type>(a);
-
- // thread safety
- int n = NumberOfFoo;
- NativeParallelFor( 4, Body<A>(a) );
- ASSERT( NumberOfFoo==n, "Allocate/deallocate count mismatched" );
-
- ASSERT( a==b, NULL );
- ASSERT( !(a!=b), NULL );
-}
-
-template<typename Allocator>
-int TestMain() {
- Test<typename Allocator::template rebind<Foo<char,1> >::other, Foo<int,17> >();
- Test<typename Allocator::template rebind<Foo<double,1> >::other, Foo<float,23> >();
- return 0;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Tests for compatibility with the host's STL.
-
-#include "harness.h"
-
-template<typename Container>
-void TestSequence() {
- Container c;
- for( int i=0; i<1000; ++i )
- c.push_back(i*i);
- typename Container::const_iterator p = c.begin();
- for( int i=0; i<1000; ++i ) {
- ASSERT( *p==i*i, NULL );
- ++p;
- }
-}
-
-template<typename Set>
-void TestSet() {
- Set s;
- typedef typename Set::value_type value_type;
- for( int i=0; i<100; ++i )
- s.insert(value_type(3*i));
- for( int i=0; i<300; ++i ) {
- ASSERT( s.erase(i)==size_t(i%3==0), NULL );
- }
-}
-
-template<typename Map>
-void TestMap() {
- Map m;
- typedef typename Map::value_type value_type;
- for( int i=0; i<100; ++i )
- m.insert(value_type(i,i*i));
- for( int i=0; i<100; ++i )
- ASSERT( m.find(i)->second==i*i, NULL );
-}
-
-#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 <deque>
-#include <list>
-#include <map>
-#include <set>
-#include <vector>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-template<typename Allocator>
-void TestAllocatorWithSTL() {
- typedef typename Allocator::template rebind<int>::other Ai;
- typedef typename Allocator::template rebind<const int>::other Aci;
- typedef typename Allocator::template rebind<std::pair<const int, int> >::other Acii;
- typedef typename Allocator::template rebind<std::pair<int, int> >::other Aii;
-
- // Sequenced containers
- TestSequence<std::deque <int,Ai> >();
- TestSequence<std::list <int,Ai> >();
- TestSequence<std::vector<int,Ai> >();
-
- // Associative containers
- TestSet<std::set <int, std::less<int>, Ai> >();
- TestSet<std::multiset<int, std::less<int>, Ai> >();
- TestMap<std::map <int, int, std::less<int>, Acii> >();
- TestMap<std::multimap<int, int, std::less<int>, Acii> >();
-
-#if _MSC_VER
- // Test compatibility with Microsoft's implementation of std::allocator for some cases that
- // are undefined according to the ISO standard but permitted by Microsoft.
- TestSequence<std::deque <const int,Aci> >();
-#if _CPPLIB_VER>=500
- TestSequence<std::list <const int,Aci> >();
-#endif
- TestSequence<std::vector<const int,Aci> >();
- TestSet<std::set<const int, std::less<int>, Aci> >();
- TestMap<std::map<int, int, std::less<int>, Aii> >();
- TestMap<std::map<const int, int, std::less<int>, Acii> >();
- TestMap<std::multimap<int, int, std::less<int>, Aii> >();
- TestMap<std::multimap<const int, int, std::less<int>, Acii> >();
-#endif /* _MSC_VER */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Program for basic correctness testing of assembly-language routines.
-
-#include "tbb/task.h"
-
-#include <new>
-#include "harness.h"
-
-using tbb::internal::reference_count;
-
-//! Test __TBB_CompareAndSwapW
-static void TestCompareExchange() {
- ASSERT( intptr_t(-10)<10, "intptr_t not a signed integral type?" );
- REMARK("testing __TBB_CompareAndSwapW\n");
- for( intptr_t a=-10; a<10; ++a )
- for( intptr_t b=-10; b<10; ++b )
- for( intptr_t c=-10; c<10; ++c ) {
-// Workaround for a bug in GCC 4.3.0; and one more is below.
-#if __GNUC__==4&&__GNUC_MINOR__==3&&__GNUC_PATCHLEVEL__==0
- intptr_t x;
- __TBB_store_with_release( x, a );
-#else
- intptr_t x = a;
-#endif
- intptr_t y = __TBB_CompareAndSwapW(&x,b,c);
- ASSERT( y==a, NULL );
- if( a==c )
- ASSERT( x==b, NULL );
- else
- ASSERT( x==a, NULL );
- }
-}
-
-//! Test __TBB___TBB_FetchAndIncrement and __TBB___TBB_FetchAndDecrement
-static void TestAtomicCounter() {
- // "canary" is a value used to detect illegal overwrites.
- const reference_count canary = ~(uintptr_t)0/3;
- REMARK("testing __TBB_FetchAndIncrement\n");
- struct {
- reference_count prefix, i, suffix;
- } x;
- x.prefix = canary;
- x.i = 0;
- x.suffix = canary;
- for( int k=0; k<10; ++k ) {
- reference_count j = __TBB_FetchAndIncrementWacquire((volatile void *)&x.i);
- ASSERT( x.prefix==canary, NULL );
- ASSERT( x.suffix==canary, NULL );
- ASSERT( x.i==k+1, NULL );
- ASSERT( j==k, NULL );
- }
- REMARK("testing __TBB_FetchAndDecrement\n");
- x.i = 10;
- for( int k=10; k>0; --k ) {
- reference_count j = __TBB_FetchAndDecrementWrelease((volatile void *)&x.i);
- ASSERT( j==k, NULL );
- ASSERT( x.i==k-1, NULL );
- ASSERT( x.prefix==canary, NULL );
- ASSERT( x.suffix==canary, NULL );
- }
-}
-
-static void TestTinyLock() {
- REMARK("testing __TBB_LockByte\n");
- unsigned char flags[16];
- for( int i=0; i<16; ++i )
- flags[i] = (unsigned char)i;
-#if __GNUC__==4&&__GNUC_MINOR__==3&&__GNUC_PATCHLEVEL__==0
- __TBB_store_with_release( flags[8], 0 );
-#else
- flags[8] = 0;
-#endif
- __TBB_LockByte(flags[8]);
- for( int i=0; i<16; ++i )
- #ifdef __sparc
- ASSERT( flags[i]==(i==8?0xff:i), NULL );
- #else
- ASSERT( flags[i]==(i==8?1:i), NULL );
- #endif
-}
-
-static void TestLog2() {
- REMARK("testing __TBB_Log2\n");
- for( uintptr_t i=1; i; i<<=1 ) {
- for( uintptr_t j=1; j<1<<16; ++j ) {
- if( uintptr_t k = i*j ) {
- uintptr_t actual = __TBB_Log2(k);
- const uintptr_t ONE = 1; // warning suppression again
- ASSERT( k >= ONE<<actual, NULL );
- ASSERT( k>>1 < ONE<<actual, NULL );
- }
- }
- }
-}
-
-static void TestPause() {
- REMARK("testing __TBB_Pause\n");
- __TBB_Pause(1);
-}
-
-
-int TestMain () {
- __TBB_TRY {
- TestLog2();
- TestTinyLock();
- TestCompareExchange();
- TestAtomicCounter();
- TestPause();
- } __TBB_CATCH(...) {
- ASSERT(0,"unexpected exception");
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Put tbb/atomic.h first, so if it is missing a prerequisite header, we find out about it.
-// The tests here do *not* test for atomicity, just serial correctness. */
-
-#include "tbb/atomic.h"
-#include "harness_assert.h"
-#include <string.h> // memcmp
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // unary minus operator applied to unsigned type, result still unsigned
- #pragma warning( push )
- #pragma warning( disable: 4310 )
-#endif
-
-//! Structure that holds an atomic<T> and some guard bytes around it.
-template<typename T>
-struct TestStruct {
- typedef unsigned char byte_type;
- T prefix;
- tbb::atomic<T> counter;
- T suffix;
- TestStruct( T i ) {
- ASSERT( sizeof(*this)==3*sizeof(T), NULL );
- for (size_t j = 0; j < sizeof(T); ++j) {
- reinterpret_cast<byte_type*>(&prefix)[j] = byte_type(0x11*(j+1));
- reinterpret_cast<byte_type*>(&suffix)[sizeof(T)-j-1] = byte_type(0x11*(j+1));
- }
- counter = i;
- }
- ~TestStruct() {
- // Check for writes outside the counter.
- for (size_t j = 0; j < sizeof(T); ++j) {
- ASSERT( reinterpret_cast<byte_type*>(&prefix)[j] == byte_type(0x11*(j+1)), NULL );
- ASSERT( reinterpret_cast<byte_type*>(&suffix)[sizeof(T)-j-1] == byte_type(0x11*(j+1)), NULL );
- }
- }
-};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-#if defined(__INTEL_COMPILER)
- // reference to EBX in a function requiring stack alignment
- #pragma warning( disable: 998 )
-#endif
-
-//! Test compare_and_swap template members of class atomic<T> for memory_semantics=M
-template<typename T,tbb::memory_semantics M>
-void TestCompareAndSwapAcquireRelease( T i, T j, T k ) {
- ASSERT( i!=k, "values must be distinct" );
- // Test compare_and_swap that should fail
- TestStruct<T> x(i);
- T old = x.counter.template compare_and_swap<M>( j, k );
- ASSERT( old==i, NULL );
- ASSERT( x.counter==i, "old value not retained" );
- // Test compare and swap that should suceed
- old = x.counter.template compare_and_swap<M>( j, i );
- ASSERT( old==i, NULL );
- ASSERT( x.counter==j, "value not updated?" );
-}
-
-//! i, j, k must be different values
-template<typename T>
-void TestCompareAndSwap( T i, T j, T k ) {
- ASSERT( i!=k, "values must be distinct" );
- // Test compare_and_swap that should fail
- TestStruct<T> x(i);
- T old = x.counter.compare_and_swap( j, k );
- ASSERT( old==i, NULL );
- ASSERT( x.counter==i, "old value not retained" );
- // Test compare and swap that should suceed
- old = x.counter.compare_and_swap( j, i );
- ASSERT( old==i, NULL );
- if( x.counter==i ) {
- ASSERT( x.counter==j, "value not updated?" );
- } else {
- ASSERT( x.counter==j, "value trashed" );
- }
- TestCompareAndSwapAcquireRelease<T,tbb::acquire>(i,j,k);
- TestCompareAndSwapAcquireRelease<T,tbb::release>(i,j,k);
-}
-
-//! memory_semantics variation on TestFetchAndStore
-template<typename T, tbb::memory_semantics M>
-void TestFetchAndStoreAcquireRelease( T i, T j ) {
- ASSERT( i!=j, "values must be distinct" );
- TestStruct<T> x(i);
- T old = x.counter.template fetch_and_store<M>( j );
- ASSERT( old==i, NULL );
- ASSERT( x.counter==j, NULL );
-}
-
-//! i and j must be different values
-template<typename T>
-void TestFetchAndStore( T i, T j ) {
- ASSERT( i!=j, "values must be distinct" );
- TestStruct<T> x(i);
- T old = x.counter.fetch_and_store( j );
- ASSERT( old==i, NULL );
- ASSERT( x.counter==j, NULL );
- TestFetchAndStoreAcquireRelease<T,tbb::acquire>(i,j);
- TestFetchAndStoreAcquireRelease<T,tbb::release>(i,j);
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // conversion from <bigger integer> to <smaller integer>, possible loss of data
- // the warning seems a complete nonsense when issued for e.g. short+=short
- #pragma warning( push )
- #pragma warning( disable: 4244 )
-#endif
-
-//! Test fetch_and_add members of class atomic<T> for memory_semantics=M
-template<typename T,tbb::memory_semantics M>
-void TestFetchAndAddAcquireRelease( T i ) {
- TestStruct<T> x(i);
- T actual;
- T expected = i;
-
- // Test fetch_and_add member template
- for( int j=0; j<10; ++j ) {
- actual = x.counter.fetch_and_add(j);
- ASSERT( actual==expected, NULL );
- expected += j;
- }
- for( int j=0; j<10; ++j ) {
- actual = x.counter.fetch_and_add(-j);
- ASSERT( actual==expected, NULL );
- expected -= j;
- }
-
- // Test fetch_and_increment member template
- ASSERT( x.counter==i, NULL );
- actual = x.counter.template fetch_and_increment<M>();
- ASSERT( actual==i, NULL );
- ASSERT( x.counter==T(i+1), NULL );
-
- // Test fetch_and_decrement member template
- actual = x.counter.template fetch_and_decrement<M>();
- ASSERT( actual==T(i+1), NULL );
- ASSERT( x.counter==i, NULL );
-}
-
-//! Test fetch_and_add and related operators
-template<typename T>
-void TestFetchAndAdd( T i ) {
- TestStruct<T> x(i);
- T value;
- value = ++x.counter;
- ASSERT( value==T(i+1), NULL );
- value = x.counter++;
- ASSERT( value==T(i+1), NULL );
- value = x.counter--;
- ASSERT( value==T(i+2), NULL );
- value = --x.counter;
- ASSERT( value==i, NULL );
- T actual;
- T expected = i;
- for( int j=-100; j<=100; ++j ) {
- expected += j;
- actual = x.counter += j;
- ASSERT( actual==expected, NULL );
- }
- for( int j=-100; j<=100; ++j ) {
- expected -= j;
- actual = x.counter -= j;
- ASSERT( actual==expected, NULL );
- }
- // Test fetch_and_increment
- ASSERT( x.counter==i, NULL );
- actual = x.counter.fetch_and_increment();
- ASSERT( actual==i, NULL );
- ASSERT( x.counter==T(i+1), NULL );
-
- // Test fetch_and_decrement
- actual = x.counter.fetch_and_decrement();
- ASSERT( actual==T(i+1), NULL );
- ASSERT( x.counter==i, NULL );
- x.counter = i;
- ASSERT( x.counter==i, NULL );
-
- TestFetchAndAddAcquireRelease<T,tbb::acquire>(i);
- TestFetchAndAddAcquireRelease<T,tbb::release>(i);
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif // warning 4244 is back
-
-//! A type with unknown size.
-class IncompleteType;
-
-void TestFetchAndAdd( IncompleteType* ) {
- // There are no fetch-and-add operations on a IncompleteType*.
-}
-void TestFetchAndAdd( void* ) {
- // There are no fetch-and-add operations on a void*.
-}
-
-void TestFetchAndAdd( bool ) {
- // There are no fetch-and-add operations on a bool.
-}
-
-template<typename T>
-void TestConst( T i ) {
- // Try const
- const TestStruct<T> x(i);
- ASSERT( memcmp( &i, &x.counter, sizeof(T) )==0, "write to atomic<T> broken?" );;
- ASSERT( x.counter==i, "read of atomic<T> broken?" );
-}
-
-template<typename T>
-void TestOperations( T i, T j, T k ) {
- TestConst(i);
- TestCompareAndSwap(i,j,k);
- TestFetchAndStore(i,k); // Pass i,k instead of i,j, because callee requires two distinct values.
-}
-
-template<typename T>
-void TestParallel( const char* name );
-
-bool ParallelError;
-
-template<typename T>
-struct AlignmentChecker {
- char c;
- tbb::atomic<T> i;
-};
-
-#include "harness.h"
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // unary minus operator applied to unsigned type, result still unsigned
- #pragma warning( push )
- #pragma warning( disable: 4146 )
-#endif
-
-/** T is an integral type. */
-template<typename T>
-void TestAtomicInteger( const char* name ) {
- REMARK("testing atomic<%s> (size=%d)\n",name,sizeof(tbb::atomic<T>));
-#if ( __linux__ && __TBB_x86_32 && __GNUC__==3 && __GNUC_MINOR__==3 ) || defined(__SUNPRO_CC)
- // gcc 3.3 has known problem for 32-bit Linux, so only warn if there is a problem.
- // SUNPRO_CC does have this problem as well
- if( sizeof(T)==8 ) {
- if( sizeof(AlignmentChecker<T>)!=2*sizeof(tbb::atomic<T>) ) {
- REPORT("Known issue: alignment for atomic<%s> is wrong with gcc 3.3 and sunCC 5.9 2008/01/28 for IA32\n",name);
- }
- } else
-#endif /* ( __linux__ && __TBB_x86_32 && __GNUC__==3 && __GNUC_MINOR__==3 ) || defined(__SUNPRO_CC) */
- ASSERT( sizeof(AlignmentChecker<T>)==2*sizeof(tbb::atomic<T>), NULL );
- TestOperations<T>(0L,T(-T(1)),T(1));
- for( int k=0; k<int(sizeof(long))*8-1; ++k ) {
- TestOperations<T>(T(1L<<k),T(~(1L<<k)),T(1-(1L<<k)));
- TestOperations<T>(T(-1L<<k),T(~(-1L<<k)),T(1-(-1L<<k)));
- TestFetchAndAdd<T>(T(-1L<<k));
- }
- TestParallel<T>( name );
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-
-template<typename T>
-struct Foo {
- T x, y, z;
-};
-
-
-template<typename T>
-void TestIndirection() {
- Foo<T> item;
- tbb::atomic<Foo<T>*> pointer;
- pointer = &item;
- for( int k=-10; k<=10; ++k ) {
- // Test various syntaxes for indirection to fields with non-zero offset.
- T value1=T(), value2=T();
- for( size_t j=0; j<sizeof(T); ++j ) {
- *(char*)&value1 = char(k^j);
- *(char*)&value2 = char(k^j*j);
- }
- pointer->y = value1;
- (*pointer).z = value2;
- T result1 = (*pointer).y;
- T result2 = pointer->z;
- ASSERT( memcmp(&value1,&result1,sizeof(T))==0, NULL );
- ASSERT( memcmp(&value2,&result2,sizeof(T))==0, NULL );
- }
-}
-
-//! Test atomic<T*>
-template<typename T>
-void TestAtomicPointer() {
- REMARK("testing atomic pointer (%d)\n",int(sizeof(T)));
- T array[1000];
- TestOperations<T*>(&array[500],&array[250],&array[750]);
- TestFetchAndAdd<T*>(&array[500]);
- TestIndirection<T>();
- TestParallel<T*>( "pointer" );
-}
-
-//! Test atomic<Ptr> where Ptr is a pointer to a type of unknown size
-template<typename Ptr>
-void TestAtomicPointerToTypeOfUnknownSize( const char* name ) {
- REMARK("testing atomic<%s>\n",name);
- char array[1000];
- TestOperations<Ptr>((Ptr)(void*)&array[500],(Ptr)(void*)&array[250],(Ptr)(void*)&array[750]);
- TestParallel<Ptr>( name );
-}
-
-void TestAtomicBool() {
- REMARK("testing atomic<bool>\n");
- TestOperations<bool>(true,true,false);
- TestOperations<bool>(false,false,true);
- TestParallel<bool>( "bool" );
-}
-
-enum Color {Red=0,Green=1,Blue=-1};
-
-void TestAtomicEnum() {
- REMARK("testing atomic<Color>\n");
- TestOperations<Color>(Red,Green,Blue);
- TestParallel<Color>( "Color" );
-}
-
-template<typename T>
-void TestAtomicFloat( const char* name ) {
- REMARK("testing atomic<%s>\n", name );
- TestOperations<T>(0.5,3.25,10.75);
- TestParallel<T>( name );
-}
-
-const int numMaskedOperations = 100000;
-const int testSpaceSize = 8;
-int prime[testSpaceSize] = {3,5,7,11,13,17,19,23};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // "possible loss of data" warning suppressed again
- #pragma warning( push )
- #pragma warning( disable: 4244 )
-#endif
-
-template<typename T>
-class TestMaskedCAS_Body: NoAssign {
- T* test_space_uncontended;
- T* test_space_contended;
-public:
- TestMaskedCAS_Body( T* _space1, T* _space2 ) : test_space_uncontended(_space1), test_space_contended(_space2) {}
- void operator()( int my_idx ) const {
- using tbb::internal::__TBB_MaskedCompareAndSwap;
- const T my_prime = T(prime[my_idx]);
- T* const my_ptr = test_space_uncontended+my_idx;
- T old_value=0;
- for( int i=0; i<numMaskedOperations; ++i, old_value+=my_prime ){
- T result;
- // Test uncontended case
- T new_value = old_value + my_prime;
- // The following CAS should always fail
- result = __TBB_MaskedCompareAndSwap<sizeof(T),T>(my_ptr,new_value,old_value-1);
- ASSERT(result!=old_value-1, "masked CAS succeeded while it should fail");
- ASSERT(result==*my_ptr, "masked CAS result mismatch with real value");
- // The following one should succeed
- result = __TBB_MaskedCompareAndSwap<sizeof(T),T>(my_ptr,new_value,old_value);
- ASSERT(result==old_value && *my_ptr==new_value, "masked CAS failed while it should succeed");
- // The following one should fail again
- result = __TBB_MaskedCompareAndSwap<sizeof(T),T>(my_ptr,new_value,old_value);
- ASSERT(result!=old_value, "masked CAS succeeded while it should fail");
- ASSERT(result==*my_ptr, "masked CAS result mismatch with real value");
- // Test contended case
- for( int j=0; j<testSpaceSize; ++j ){
- // try adding my_prime until success
- T value;
- do {
- value = test_space_contended[j];
- result = __TBB_MaskedCompareAndSwap<sizeof(T),T>(test_space_contended+j,value+my_prime,value);
- } while( result!=value );
- }
- }
- }
-};
-
-template<typename T>
-struct intptr_as_array_of
-{
- static const int how_many_Ts = sizeof(intptr_t)/sizeof(T);
- union {
- intptr_t result;
- T space[ how_many_Ts ];
- };
-};
-
-template<typename T>
-intptr_t getCorrectUncontendedValue(int slot_idx) {
- intptr_as_array_of<T> slot;
- slot.result = 0;
- for( int i=0; i<slot.how_many_Ts; ++i ) {
- const T my_prime = T(prime[slot_idx*slot.how_many_Ts + i]);
- for( int j=0; j<numMaskedOperations; ++j )
- slot.space[i] += my_prime;
- }
- return slot.result;
-}
-
-template<typename T>
-intptr_t getCorrectContendedValue() {
- intptr_as_array_of<T> slot;
- slot.result = 0;
- for( int i=0; i<slot.how_many_Ts; ++i )
- for( int primes=0; primes<testSpaceSize; ++primes )
- for( int j=0; j<numMaskedOperations; ++j )
- slot.space[i] += prime[primes];
- return slot.result;
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif // warning 4244 is back again
-
-template<typename T>
-void TestMaskedCAS() {
- REMARK("testing masked CAS<%d>\n",int(sizeof(T)));
-
- const int num_slots = sizeof(T)*testSpaceSize/sizeof(intptr_t);
- intptr_t arr1[num_slots+2]; // two more "canary" slots at boundaries
- intptr_t arr2[num_slots+2];
- for(int i=0; i<num_slots+2; ++i)
- arr2[i] = arr1[i] = 0;
- T* test_space_uncontended = (T*)(arr1+1);
- T* test_space_contended = (T*)(arr2+1);
-
- NativeParallelFor( testSpaceSize, TestMaskedCAS_Body<T>(test_space_uncontended, test_space_contended) );
-
- ASSERT( arr1[0]==0 && arr1[num_slots+1]==0 && arr2[0]==0 && arr2[num_slots+1]==0 , "adjacent memory was overwritten" );
- const intptr_t correctContendedValue = getCorrectContendedValue<T>();
- for(int i=0; i<num_slots; ++i) {
- ASSERT( arr1[i+1]==getCorrectUncontendedValue<T>(i), "unexpected value in an uncontended slot" );
- ASSERT( arr2[i+1]==correctContendedValue, "unexpected value in a contended slot" );
- }
-}
-
-template<unsigned N>
-class ArrayElement {
- char item[N];
-};
-
-int TestMain () {
- TestAtomicInteger<unsigned long long>("unsigned long long");
- TestAtomicInteger<long long>("long long");
- TestAtomicInteger<unsigned long>("unsigned long");
- TestAtomicInteger<long>("long");
- TestAtomicInteger<unsigned int>("unsigned int");
- TestAtomicInteger<int>("int");
- TestAtomicInteger<unsigned short>("unsigned short");
- TestAtomicInteger<short>("short");
- TestAtomicInteger<signed char>("signed char");
- TestAtomicInteger<unsigned char>("unsigned char");
- TestAtomicInteger<char>("char");
- TestAtomicInteger<wchar_t>("wchar_t");
- TestAtomicInteger<size_t>("size_t");
- TestAtomicInteger<ptrdiff_t>("ptrdiff_t");
- TestAtomicPointer<ArrayElement<1> >();
- TestAtomicPointer<ArrayElement<2> >();
- TestAtomicPointer<ArrayElement<3> >();
- TestAtomicPointer<ArrayElement<4> >();
- TestAtomicPointer<ArrayElement<5> >();
- TestAtomicPointer<ArrayElement<6> >();
- TestAtomicPointer<ArrayElement<7> >();
- TestAtomicPointer<ArrayElement<8> >();
- TestAtomicPointerToTypeOfUnknownSize<IncompleteType*>( "IncompleteType*" );
- TestAtomicPointerToTypeOfUnknownSize<void*>( "void*" );
- TestAtomicBool();
- TestAtomicEnum();
- TestAtomicFloat<float>("float");
- TestAtomicFloat<double>("double");
- ASSERT( !ParallelError, NULL );
- TestMaskedCAS<unsigned char>();
- TestMaskedCAS<unsigned short>();
- return Harness::Done;
-}
-
-template<typename T>
-struct FlagAndMessage {
- //! 0 if message not set yet, 1 if message is set.
- tbb::atomic<T> flag;
- /** Force flag and message to be on distinct cache lines for machines with cache line size <= 4096 bytes */
- char pad[4096/sizeof(T)];
- //! Non-zero if message is ready
- T message;
-};
-
-// A special template function used for summation.
-// Actually it is only necessary because of its specialization for void*
-template<typename T>
-T special_sum(intptr_t arg1, intptr_t arg2) {
- return (T)((T)arg1 + arg2);
-}
-
-// The specialization for IncompleteType* is required
-// because pointer arithmetic (+) is impossible with IncompleteType*
-template<>
-IncompleteType* special_sum<IncompleteType*>(intptr_t arg1, intptr_t arg2) {
- return (IncompleteType*)(arg1 + arg2);
-}
-
-// The specialization for void* is required
-// because pointer arithmetic (+) is impossible with void*
-template<>
-void* special_sum<void*>(intptr_t arg1, intptr_t arg2) {
- return (void*)(arg1 + arg2);
-}
-
-// The specialization for bool is required to shut up gratuitous compiler warnings,
-// because some compilers warn about casting int to bool.
-template<>
-bool special_sum<bool>(intptr_t arg1, intptr_t arg2) {
- return ((arg1!=0) + arg2)!=0;
-}
-
-volatile int One = 1;
-
-template<typename T>
-class HammerLoadAndStoreFence: NoAssign {
- FlagAndMessage<T>* fam;
- const int n;
- const int p;
- const int trial;
- const char* name;
- mutable T accum;
-public:
- HammerLoadAndStoreFence( FlagAndMessage<T>* fam_, int n_, int p_, const char* name_, int trial_ ) : fam(fam_), n(n_), p(p_), trial(trial_), name(name_) {}
- void operator()( int k ) const {
- int one = One;
- FlagAndMessage<T>* s = fam+k;
- FlagAndMessage<T>* s_next = fam + (k+1)%p;
- for( int i=0; i<n; ++i ) {
- // The inner for loop is a spin-wait loop, which is normally considered very bad style.
- // But we must use it here because we are interested in examining subtle hardware effects.
- for(unsigned short cnt=1; ; ++cnt) {
- if( !cnt ) // to help 1-core systems complete the test, yield every 2^16 iterations
- __TBB_Yield();
- // Compilers typically generate non-trivial sequence for division by a constant.
- // The expression here is dependent on the loop index i, so it cannot be hoisted.
-#define COMPLICATED_ZERO (i*(one-1)/100)
- // Read flag and then the message
- T flag, message;
- if( trial&1 ) {
- // COMPLICATED_ZERO here tempts compiler to hoist load of message above reading of flag.
- flag = (s+COMPLICATED_ZERO)->flag;
- message = s->message;
- } else {
- flag = s->flag;
- message = s->message;
- }
- if( flag ) {
- if( flag!=(T)-1 ) {
- REPORT("ERROR: flag!=(T)-1 k=%d i=%d trial=%x type=%s (atomicity problem?)\n", k, i, trial, name );
- ParallelError = true;
- }
- if( message!=(T)-1 ) {
- REPORT("ERROR: message!=(T)-1 k=%d i=%d trial=%x type=%s (memory fence problem?)\n", k, i, trial, name );
- ParallelError = true;
- }
- s->message = T(0);
- s->flag = T(0);
- // Set message and then the flag
- if( trial&2 ) {
- // COMPLICATED_ZERO here tempts compiler to sink store below setting of flag
- s_next->message = special_sum<T>(-1, COMPLICATED_ZERO);
- s_next->flag = (T)-1;
- } else {
- s_next->message = (T)-1;
- s_next->flag = (T)-1;
- }
- break;
- } else {
- // Force compiler to use message anyway, so it cannot sink read of s->message below the if.
- accum = message;
- }
- }
- }
- }
-};
-
-//! Test that atomic<T> has acquire semantics for loads and release semantics for stores.
-/** Test performs round-robin passing of message among p processors,
- where p goes from MinThread to MaxThread. */
-template<typename T>
-void TestLoadAndStoreFences( const char* name ) {
- for( int p=MinThread<2 ? 2 : MinThread; p<=MaxThread; ++p ) {
- FlagAndMessage<T>* fam = new FlagAndMessage<T>[p];
- // Each of four trials excercise slightly different expresion pattern within the test.
- // See occurrences of COMPLICATED_ZERO for details.
- for( int trial=0; trial<4; ++trial ) {
- memset( fam, 0, p*sizeof(FlagAndMessage<T>) );
- fam->message = (T)-1;
- fam->flag = (T)-1;
- NativeParallelFor( p, HammerLoadAndStoreFence<T>( fam, 100, p, name, trial ) );
- for( int k=0; k<p; ++k ) {
- ASSERT( fam[k].message==(k==0 ? (T)-1 : 0), "incomplete round-robin?" );
- ASSERT( fam[k].flag==(k==0 ? (T)-1 : 0), "incomplete round-robin?" );
- }
- }
- delete[] fam;
- }
-}
-
-//! Sparse set of values of integral type T.
-/** Set is designed so that if a value is read or written non-atomically,
- the resulting intermediate value is likely to not be a member of the set. */
-template<typename T>
-class SparseValueSet {
- T factor;
-public:
- SparseValueSet() {
- // Compute factor such that:
- // 1. It has at least one 1 in most of its bytes.
- // 2. The bytes are typically different.
- // 3. When multiplied by any value <=127, the product does not overflow.
- factor = T(0);
- for( unsigned i=0; i<sizeof(T)*8-7; i+=7 )
- factor = T(factor | T(1)<<i);
- }
- //! Get ith member of set
- T get( int i ) const {
- // Create multiple of factor. The & prevents overflow of the product.
- return T((i&0x7F)*factor);
- }
- //! True if set contains x
- bool contains( T x ) const {
- // True if
- return (x%factor)==0;
- }
-};
-
-//! Specialization for pointer types. The pointers are random and should not be dereferenced.
-template<typename T>
-class SparseValueSet<T*> {
- SparseValueSet<ptrdiff_t> my_set;
-public:
- T* get( int i ) const {return reinterpret_cast<T*>(my_set.get(i));}
- bool contains( T* x ) const {return my_set.contains(reinterpret_cast<ptrdiff_t>(x));}
-};
-
-//! Specialization for bool.
-/** Checking bool for atomic read/write is pointless in practice, because
- there is no way to *not* atomically read or write a bool value. */
-template<>
-class SparseValueSet<bool> {
-public:
- bool get( int i ) const {return i&1;}
- bool contains( bool ) const {return true;}
-};
-
-#if _MSC_VER==1500 && !defined(__INTEL_COMPILER)
- // VS2008/VC9 seems to have an issue; limits pull in math.h
- #pragma warning( push )
- #pragma warning( disable: 4985 )
-#endif
-#include <limits> /* Need std::numeric_limits */
-#if _MSC_VER==1500 && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-//! Commonality inherited by specializations for floating-point types.
-template<typename T>
-class SparseFloatSet: NoAssign {
- const T epsilon;
-public:
- SparseFloatSet() : epsilon(std::numeric_limits<T>::epsilon()) {}
- T get( int i ) const {
- return i==0 ? T(0) : 1/T((i&0x7F)+1);
- }
- bool contains( T x ) const {
- if( x==T(0) ) {
- return true;
- } else {
- int j = int(1/x+T(0.5));
- if( 0<j && j<=128 ) {
- T error = x*T(j)-T(1);
- // In the calculation above, if x was indeed generated by method get, the error should be
- // at most epsilon, because x is off by at most 1/2 ulp from its infinitely precise value,
- // j is exact, and the multiplication incurs at most another 1/2 ulp of round-off error.
- if( -epsilon<=error && error<=epsilon ) {
- return true;
- } else {
- REPORT("Warning: excessive floating-point error encountered j=%d x=%.15g error=%.15g\n",j,x,error);
- }
- }
- return false;
- }
- };
-};
-
-template<>
-class SparseValueSet<float>: public SparseFloatSet<float> {};
-
-template<>
-class SparseValueSet<double>: public SparseFloatSet<double> {};
-
-template<typename T>
-class HammerAssignment: NoAssign {
- tbb::atomic<T>& x;
- const char* name;
- SparseValueSet<T> set;
-public:
- HammerAssignment( tbb::atomic<T>& x_, const char* name_ ) : x(x_), name(name_) {}
- void operator()( int k ) const {
- const int n = 1000000;
- if( k ) {
- tbb::atomic<T> z;
- AssertSameType( z=x, z ); // Check that return type from assignment is correct
- for( int i=0; i<n; ++i ) {
- // Read x atomically into z.
- z = x;
- if( !set.contains(z) ) {
- REPORT("ERROR: assignment of atomic<%s> is not atomic\n", name);
- ParallelError = true;
- return;
- }
- }
- } else {
- tbb::atomic<T> y;
- for( int i=0; i<n; ++i ) {
- // Get pseudo-random value.
- y = set.get(i);
- // Write y atomically into x.
- x = y;
- }
- }
- }
-};
-
-// Compile-time check that a class method has the required signature.
-// Intended to check the assignment operator of tbb::atomic.
-template<typename T> void TestAssignmentSignature( T& (T::*)(const T&) ) {}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Suppress "conditional expression is constant" warning.
- #pragma warning( push )
- #pragma warning( disable: 4127 )
-#endif
-
-template<typename T>
-void TestAssignment( const char* name ) {
- TestAssignmentSignature( &tbb::atomic<T>::operator= );
- tbb::atomic<T> x;
- x = T(0);
- NativeParallelFor( 2, HammerAssignment<T>( x, name ) );
-#if __TBB_x86_32 && (__linux__ || __FreeBSD__ || _WIN32)
- if( sizeof(T)==8 ) {
- // Some compilers for IA-32 fail to provide 8-byte alignment of objects on the stack,
- // even if the object specifies 8-byte alignment. On such platforms, the IA-32 implementation
- // of atomic<long long> and atomic<unsigned long long> use different tactics depending upon
- // whether the object is properly aligned or not. The following abusive test ensures that we
- // cover both the proper and improper alignment cases, one with the x above and the other with
- // the y below, perhaps not respectively.
-
- // Allocate space big enough to always contain 8-byte locations that are aligned and misaligned.
- char raw_space[15];
- // Set delta to 0 if x is aligned, 4 otherwise.
- uintptr_t delta = ((reinterpret_cast<uintptr_t>(&x)&7) ? 0 : 4);
- // y crosses 8-byte boundary if and only if x does not cross.
- tbb::atomic<T>& y = *reinterpret_cast<tbb::atomic<T>*>((reinterpret_cast<uintptr_t>(&raw_space[7+delta])&~7u) - delta);
- // Assertion checks that y really did end up somewhere inside "raw_space".
- ASSERT( raw_space<=reinterpret_cast<char*>(&y), "y starts before raw_space" );
- ASSERT( reinterpret_cast<char*>(&y+1) <= raw_space+sizeof(raw_space), "y starts after raw_space" );
- y = T(0);
- NativeParallelFor( 2, HammerAssignment<T>( y, name ) );
- }
-#endif /* __TBB_x86_32 && (__linux__ || __FreeBSD__ || _WIN32) */
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-template<typename T>
-void TestParallel( const char* name ) {
- TestLoadAndStoreFences<T>(name);
- TestAssignment<T>(name);
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/blocked_range.h"
-#include "harness_assert.h"
-
-// First test as much as we can without including other headers.
-// Doing so should catch problems arising from failing to include headers.
-
-class AbstractValueType {
- AbstractValueType() {}
- int value;
-public:
- friend AbstractValueType MakeAbstractValueType( int i );
- friend int GetValueOf( const AbstractValueType& v ) {return v.value;}
-};
-
-AbstractValueType MakeAbstractValueType( int i ) {
- AbstractValueType x;
- x.value = i;
- return x;
-}
-
-std::size_t operator-( const AbstractValueType& u, const AbstractValueType& v ) {
- return GetValueOf(u)-GetValueOf(v);
-}
-
-bool operator<( const AbstractValueType& u, const AbstractValueType& v ) {
- return GetValueOf(u)<GetValueOf(v);
-}
-
-AbstractValueType operator+( const AbstractValueType& u, std::size_t offset ) {
- return MakeAbstractValueType(GetValueOf(u)+int(offset));
-}
-
-static void SerialTest() {
- for( int x=-10; x<10; ++x )
- for( int y=-10; y<10; ++y ) {
- AbstractValueType i = MakeAbstractValueType(x);
- AbstractValueType j = MakeAbstractValueType(y);
- for( std::size_t k=1; k<10; ++k ) {
- typedef tbb::blocked_range<AbstractValueType> range_type;
- range_type r( i, j, k );
- AssertSameType( r.empty(), true );
- AssertSameType( range_type::size_type(), std::size_t() );
- AssertSameType( static_cast<range_type::const_iterator*>(0), static_cast<AbstractValueType*>(0) );
- AssertSameType( r.begin(), MakeAbstractValueType(0) );
- AssertSameType( r.end(), MakeAbstractValueType(0) );
- ASSERT( r.empty()==(y<=x), NULL );
- ASSERT( r.grainsize()==k, NULL );
- if( x<=y ) {
- AssertSameType( r.is_divisible(), true );
- ASSERT( r.is_divisible()==(std::size_t(y-x)>k), NULL );
- ASSERT( r.size()==std::size_t(y-x), NULL );
- if( r.is_divisible() ) {
- tbb::blocked_range<AbstractValueType> r2(r,tbb::split());
- ASSERT( GetValueOf(r.begin())==x, NULL );
- ASSERT( GetValueOf(r.end())==GetValueOf(r2.begin()), NULL );
- ASSERT( GetValueOf(r2.end())==y, NULL );
- ASSERT( r.grainsize()==k, NULL );
- ASSERT( r2.grainsize()==k, NULL );
- }
- }
- }
- }
-}
-
-#include "tbb/parallel_for.h"
-#include "harness.h"
-
-const int N = 1<<22;
-
-unsigned char Array[N];
-
-struct Striker {
- // Note: we use <int> here instead of <long> in order to test for Quad 407676
- void operator()( const tbb::blocked_range<int>& r ) const {
- for( tbb::blocked_range<int>::const_iterator i=r.begin(); i!=r.end(); ++i )
- ++Array[i];
- }
-};
-
-void ParallelTest() {
- for( int i=0; i<N; i=i<3 ? i+1 : i*3 ) {
- const tbb::blocked_range<int> r( 0, i, 10 );
- tbb::parallel_for( r, Striker() );
- for( int k=0; k<N; ++k ) {
- ASSERT( Array[k]==(k<i), NULL );
- Array[k] = 0;
- }
- }
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- SerialTest();
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init(p);
- ParallelTest();
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/blocked_range2d.h"
-#include "harness_assert.h"
-
-// First test as much as we can without including other headers.
-// Doing so should catch problems arising from failing to include headers.
-
-template<typename Tag>
-class AbstractValueType {
- AbstractValueType() {}
- int value;
-public:
- template<typename OtherTag>
- friend AbstractValueType<OtherTag> MakeAbstractValueType( int i );
-
- template<typename OtherTag>
- friend int GetValueOf( const AbstractValueType<OtherTag>& v ) ;
-};
-
-template<typename Tag>
-AbstractValueType<Tag> MakeAbstractValueType( int i ) {
- AbstractValueType<Tag> x;
- x.value = i;
- return x;
-}
-
-template<typename Tag>
-int GetValueOf( const AbstractValueType<Tag>& v ) {return v.value;}
-
-template<typename Tag>
-bool operator<( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
- return GetValueOf(u)<GetValueOf(v);
-}
-
-template<typename Tag>
-std::size_t operator-( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
- return GetValueOf(u)-GetValueOf(v);
-}
-
-template<typename Tag>
-AbstractValueType<Tag> operator+( const AbstractValueType<Tag>& u, std::size_t offset ) {
- return MakeAbstractValueType<Tag>(GetValueOf(u)+int(offset));
-}
-
-struct RowTag {};
-struct ColTag {};
-
-static void SerialTest() {
- typedef AbstractValueType<RowTag> row_type;
- typedef AbstractValueType<ColTag> col_type;
- typedef tbb::blocked_range2d<row_type,col_type> range_type;
- for( int rowx=-10; rowx<10; ++rowx ) {
- for( int rowy=rowx; rowy<10; ++rowy ) {
- row_type rowi = MakeAbstractValueType<RowTag>(rowx);
- row_type rowj = MakeAbstractValueType<RowTag>(rowy);
- for( int rowg=1; rowg<10; ++rowg ) {
- for( int colx=-10; colx<10; ++colx ) {
- for( int coly=colx; coly<10; ++coly ) {
- col_type coli = MakeAbstractValueType<ColTag>(colx);
- col_type colj = MakeAbstractValueType<ColTag>(coly);
- for( int colg=1; colg<10; ++colg ) {
- range_type r( rowi, rowj, rowg, coli, colj, colg );
- AssertSameType( r.is_divisible(), true );
- AssertSameType( r.empty(), true );
- AssertSameType( static_cast<range_type::row_range_type::const_iterator*>(0), static_cast<row_type*>(0) );
- AssertSameType( static_cast<range_type::col_range_type::const_iterator*>(0), static_cast<col_type*>(0) );
- AssertSameType( r.rows(), tbb::blocked_range<row_type>( rowi, rowj, 1 ));
- AssertSameType( r.cols(), tbb::blocked_range<col_type>( coli, colj, 1 ));
- ASSERT( r.empty()==(rowx==rowy||colx==coly), NULL );
- ASSERT( r.is_divisible()==(rowy-rowx>rowg||coly-colx>colg), NULL );
- if( r.is_divisible() ) {
- range_type r2(r,tbb::split());
- if( GetValueOf(r2.rows().begin())==GetValueOf(r.rows().begin()) ) {
- ASSERT( GetValueOf(r2.rows().end())==GetValueOf(r.rows().end()), NULL );
- ASSERT( GetValueOf(r2.cols().begin())==GetValueOf(r.cols().end()), NULL );
- } else {
- ASSERT( GetValueOf(r2.cols().end())==GetValueOf(r.cols().end()), NULL );
- ASSERT( GetValueOf(r2.rows().begin())==GetValueOf(r.rows().end()), NULL );
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-#include "tbb/parallel_for.h"
-#include "harness.h"
-
-const int N = 1<<10;
-
-unsigned char Array[N][N];
-
-struct Striker {
- // Note: we use <int> here instead of <long> in order to test for problems similar to Quad 407676
- void operator()( const tbb::blocked_range2d<int>& r ) const {
- for( tbb::blocked_range<int>::const_iterator i=r.rows().begin(); i!=r.rows().end(); ++i )
- for( tbb::blocked_range<int>::const_iterator j=r.cols().begin(); j!=r.cols().end(); ++j )
- ++Array[i][j];
- }
-};
-
-void ParallelTest() {
- for( int i=0; i<N; i=i<3 ? i+1 : i*3 ) {
- for( int j=0; j<N; j=j<3 ? j+1 : j*3 ) {
- const tbb::blocked_range2d<int> r( 0, i, 7, 0, j, 5 );
- tbb::parallel_for( r, Striker() );
- for( int k=0; k<N; ++k ) {
- for( int l=0; l<N; ++l ) {
- ASSERT( Array[k][l]==(k<i && l<j), NULL );
- Array[k][l] = 0;
- }
- }
- }
- }
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- SerialTest();
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init(p);
- ParallelTest();
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/blocked_range3d.h"
-#include "harness_assert.h"
-
-// First test as much as we can without including other headers.
-// Doing so should catch problems arising from failing to include headers.
-
-template<typename Tag>
-class AbstractValueType {
- AbstractValueType() {}
- int value;
-public:
- template<typename OtherTag>
- friend AbstractValueType<OtherTag> MakeAbstractValueType( int i );
-
- template<typename OtherTag>
- friend int GetValueOf( const AbstractValueType<OtherTag>& v ) ;
-};
-
-template<typename Tag>
-AbstractValueType<Tag> MakeAbstractValueType( int i ) {
- AbstractValueType<Tag> x;
- x.value = i;
- return x;
-}
-
-template<typename Tag>
-int GetValueOf( const AbstractValueType<Tag>& v ) {return v.value;}
-
-template<typename Tag>
-bool operator<( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
- return GetValueOf(u)<GetValueOf(v);
-}
-
-template<typename Tag>
-std::size_t operator-( const AbstractValueType<Tag>& u, const AbstractValueType<Tag>& v ) {
- return GetValueOf(u)-GetValueOf(v);
-}
-
-template<typename Tag>
-AbstractValueType<Tag> operator+( const AbstractValueType<Tag>& u, std::size_t offset ) {
- return MakeAbstractValueType<Tag>(GetValueOf(u)+int(offset));
-}
-
-struct PageTag {};
-struct RowTag {};
-struct ColTag {};
-
-static void SerialTest() {
- typedef AbstractValueType<PageTag> page_type;
- typedef AbstractValueType<RowTag> row_type;
- typedef AbstractValueType<ColTag> col_type;
- typedef tbb::blocked_range3d<page_type,row_type,col_type> range_type;
- for( int pagex=-4; pagex<4; ++pagex ) {
- for( int pagey=pagex; pagey<4; ++pagey ) {
- page_type pagei = MakeAbstractValueType<PageTag>(pagex);
- page_type pagej = MakeAbstractValueType<PageTag>(pagey);
- for( int pageg=1; pageg<4; ++pageg ) {
- for( int rowx=-4; rowx<4; ++rowx ) {
- for( int rowy=rowx; rowy<4; ++rowy ) {
- row_type rowi = MakeAbstractValueType<RowTag>(rowx);
- row_type rowj = MakeAbstractValueType<RowTag>(rowy);
- for( int rowg=1; rowg<4; ++rowg ) {
- for( int colx=-4; colx<4; ++colx ) {
- for( int coly=colx; coly<4; ++coly ) {
- col_type coli = MakeAbstractValueType<ColTag>(colx);
- col_type colj = MakeAbstractValueType<ColTag>(coly);
- for( int colg=1; colg<4; ++colg ) {
- range_type r( pagei, pagej, pageg, rowi, rowj, rowg, coli, colj, colg );
- AssertSameType( r.is_divisible(), true );
-
- AssertSameType( r.empty(), true );
-
- AssertSameType( static_cast<range_type::page_range_type::const_iterator*>(0), static_cast<page_type*>(0) );
- AssertSameType( static_cast<range_type::row_range_type::const_iterator*>(0), static_cast<row_type*>(0) );
- AssertSameType( static_cast<range_type::col_range_type::const_iterator*>(0), static_cast<col_type*>(0) );
-
- AssertSameType( r.pages(), tbb::blocked_range<page_type>( pagei, pagej, 1 ));
- AssertSameType( r.rows(), tbb::blocked_range<row_type>( rowi, rowj, 1 ));
- AssertSameType( r.cols(), tbb::blocked_range<col_type>( coli, colj, 1 ));
-
- ASSERT( r.empty()==(pagex==pagey||rowx==rowy||colx==coly), NULL );
-
- ASSERT( r.is_divisible()==(pagey-pagex>pageg||rowy-rowx>rowg||coly-colx>colg), NULL );
-
- if( r.is_divisible() ) {
- range_type r2(r,tbb::split());
- if( (GetValueOf(r2.pages().begin())==GetValueOf(r.pages().begin())) && (GetValueOf(r2.rows().begin())==GetValueOf(r.rows().begin())) ) {
- ASSERT( GetValueOf(r2.pages().end())==GetValueOf(r.pages().end()), NULL );
- ASSERT( GetValueOf(r2.rows().end())==GetValueOf(r.rows().end()), NULL );
- ASSERT( GetValueOf(r2.cols().begin())==GetValueOf(r.cols().end()), NULL );
- } else {
- if ( (GetValueOf(r2.pages().begin())==GetValueOf(r.pages().begin())) && (GetValueOf(r2.cols().begin())==GetValueOf(r.cols().begin())) ) {
- ASSERT( GetValueOf(r2.pages().end())==GetValueOf(r.pages().end()), NULL );
- ASSERT( GetValueOf(r2.cols().end())==GetValueOf(r.cols().end()), NULL );
- ASSERT( GetValueOf(r2.rows().begin())==GetValueOf(r.rows().end()), NULL );
- } else {
- ASSERT( GetValueOf(r2.rows().end())==GetValueOf(r.rows().end()), NULL );
- ASSERT( GetValueOf(r2.cols().end())==GetValueOf(r.cols().end()), NULL );
- ASSERT( GetValueOf(r2.pages().begin())==GetValueOf(r.pages().end()), NULL );
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-#include "tbb/parallel_for.h"
-#include "harness.h"
-
-const int N = 1<<5;
-
-unsigned char Array[N][N][N];
-
-struct Striker {
- // Note: we use <int> here instead of <long> in order to test for problems similar to Quad 407676
- void operator()( const tbb::blocked_range3d<int>& r ) const {
- for( tbb::blocked_range<int>::const_iterator i=r.pages().begin(); i!=r.pages().end(); ++i )
- for( tbb::blocked_range<int>::const_iterator j=r.rows().begin(); j!=r.rows().end(); ++j )
- for( tbb::blocked_range<int>::const_iterator k=r.cols().begin(); k!=r.cols().end(); ++k )
- ++Array[i][j][k];
- }
-};
-
-void ParallelTest() {
- for( int i=0; i<N; i=i<3 ? i+1 : i*3 ) {
- for( int j=0; j<N; j=j<3 ? j+1 : j*3 ) {
- for( int k=0; k<N; k=k<3 ? k+1 : k*3 ) {
- const tbb::blocked_range3d<int> r( 0, i, 5, 0, j, 3, 0, k, 1 );
- tbb::parallel_for( r, Striker() );
- for( int l=0; l<N; ++l ) {
- for( int m=0; m<N; ++m ) {
- for( int n=0; n<N; ++n ) {
- ASSERT( Array[l][m][n]==(l<i && m<j && n<k), NULL );
- Array[l][m][n] = 0;
- }
- }
- }
- }
- }
- }
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- SerialTest();
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init(p);
- ParallelTest();
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test whether cache_aligned_allocator works with some of the host's STL containers.
-
-#include "tbb/cache_aligned_allocator.h"
-#include "tbb/tbb_allocator.h"
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-// the real body of the test is there:
-#include "test_allocator.h"
-
-template<>
-struct is_zero_filling<tbb::zero_allocator<void> > {
- static const bool value = true;
-};
-
-int TestMain () {
- int result = TestMain<tbb::cache_aligned_allocator<void> >();
- result += TestMain<tbb::tbb_allocator<void> >();
- result += TestMain<tbb::zero_allocator<void> >();
- ASSERT( !result, NULL );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test whether cache_aligned_allocator works with some of the host's STL containers.
-
-#include "tbb/cache_aligned_allocator.h"
-#include "tbb/tbb_allocator.h"
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "test_allocator_STL.h"
-
-int TestMain () {
- TestAllocatorWithSTL<tbb::cache_aligned_allocator<void> >();
- TestAllocatorWithSTL<tbb::tbb_allocator<void> >();
- TestAllocatorWithSTL<tbb::zero_allocator<void> >();
- return Harness::Done;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/tbb_config.h"
-#include "harness.h"
-
-#if __TBB_SURVIVE_THREAD_SWITCH && __INTEL_COMPILER >= 1200
-
-static const int N = 14;
-static const int P_outer = 4;
-static const int P_nested = 2;
-
-#include <cilk/cilk.h>
-#define private public
-#include "tbb/task.h"
-#undef private
-#include "tbb/task_scheduler_init.h"
-#include <cstdio>
-#include <cassert>
-
-enum tbb_sched_injection_mode_t {
- tbbsched_none = 0,
- tbbsched_explicit_only = 1,
- tbbsched_auto_only = 2,
- tbbsched_mixed = 3
-};
-
-tbb_sched_injection_mode_t g_sim = tbbsched_none;
-
-bool g_sandwich = false;
-
-// A time delay routine
-void Delay( int n ) {
- static volatile int Global;
- for( int k=0; k<10000; ++k )
- for( int i=0; i<n; ++i )
- ++Global;
-}
-
-int SerialFib( int n ) {
- int a=0, b=1;
- for( int i=0; i<n; ++i ) {
- b += a;
- a = b-a;
- }
- return a;
-}
-
-int F = SerialFib(N);
-
-int Fib ( int n ) {
- if( n < 2 ) {
- if ( g_sim ) {
- tbb::task_scheduler_init tsi(P_nested);
- }
- return n;
- } else {
- tbb::task_scheduler_init *tsi = NULL;
- tbb::task *cur = NULL;
- if ( g_sim ) {
- if ( n % 2 == 0 ) {
- if ( g_sim == tbbsched_auto_only || (g_sim == tbbsched_mixed && n % 4 == 0) ) {
- // Trigger TBB scheduler auto-initialization
- cur = &tbb::task::self();
- }
- else {
- ASSERT ( g_sim == tbbsched_explicit_only || (g_sim == tbbsched_mixed && n % 4 != 0), NULL );
- // Initialize TBB scheduler explicitly
- tsi = new tbb::task_scheduler_init(P_nested);
- }
- }
- }
- int x, y;
- x = cilk_spawn Fib(n-2);
- y = cilk_spawn Fib(n-1);
- cilk_sync;
- if ( tsi )
- delete tsi;
- return x+y;
- }
-}
-
-int TBB_Fib( int n );
-
-class FibCilkSubtask: public tbb::task {
- int n;
- int& result;
- /*override*/ task* execute() {
- if( n<2 ) {
- result = n;
- } else {
- int x, y;
- x = cilk_spawn TBB_Fib(n-2);
- y = cilk_spawn TBB_Fib(n-1);
- cilk_sync;
- result = x+y;
- }
- return NULL;
- }
-public:
- FibCilkSubtask( int& result_, int n_ ) : result(result_), n(n_) {}
-};
-
-class FibTask: public tbb::task {
- int n;
- int& result;
- /*override*/ task* execute() {
- if( !g_sandwich && n<2 ) {
- result = n;
- } else {
- int x,y;
- tbb::task_scheduler_init init(P_nested);
- task* self0 = &task::self();
- set_ref_count( 3 );
- if ( g_sandwich ) {
- spawn (*new( allocate_child() ) FibCilkSubtask(x,n-1));
- spawn (*new( allocate_child() ) FibCilkSubtask(y,n-2));
- }
- else {
- spawn (*new( allocate_child() ) FibTask(x,n-1));
- spawn (*new( allocate_child() ) FibTask(y,n-2));
- }
- wait_for_all();
- task* self1 = &task::self();
- ASSERT( self0 == self1, "failed to preserve TBB TLS" );
- result = x+y;
- }
- return NULL;
- }
-public:
- FibTask( int& result_, int n_ ) : result(result_), n(n_) {}
-};
-
-int TBB_Fib( int n ) {
- if( n<2 ) {
- return n;
- } else {
- int result;
- tbb::task_scheduler_init init(P_nested);
- tbb::task::spawn_root_and_wait(*new( tbb::task::allocate_root()) FibTask(result,n) );
- return result;
- }
-}
-
-void RunCilkOnly ( tbb_sched_injection_mode_t sim ) {
- g_sim = sim;
- int m = Fib(N);
- ASSERT( m == F, NULL );
-}
-
-struct FibBody : NoAssign, Harness::NoAfterlife {
- void operator() ( int ) const {
- int m = Fib(N);
- ASSERT( m == F, NULL );
- }
-};
-
-void RunCilkOnlyConcurrently ( tbb_sched_injection_mode_t sim ) {
- g_sim = sim;
- NativeParallelFor( P_outer, FibBody() );
-}
-
-void RunSandwich( bool sandwich ) {
- g_sandwich = sandwich;
- tbb::task_scheduler_init init(P_outer);
- int m = TBB_Fib(N);
- ASSERT( g_sandwich == sandwich, "Memory corruption detected" );
- ASSERT( m == F, NULL );
-}
-
-int TestMain () {
- for ( int i = 0; i < 100; ++i )
- RunCilkOnlyConcurrently( tbbsched_none );
- RunCilkOnly( tbbsched_none );
- RunCilkOnly( tbbsched_explicit_only );
- RunCilkOnly( tbbsched_auto_only );
- RunCilkOnly( tbbsched_mixed );
- RunSandwich( false );
- for ( int i = 0; i < 10; ++i )
- RunSandwich( true );
- __cilkrts_end_cilk();
- return Harness::Done;
-}
-
-#else /* No Cilk interop */
-
-int TestMain () {
- return Harness::Skipped;
-}
-
-#endif /* No Cilk interop */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define __TBB_EXTRA_DEBUG 1 // for concurrent_hash_map
-#include "tbb/combinable.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/parallel_for.h"
-#include "tbb/parallel_reduce.h"
-#include "tbb/blocked_range.h"
-#include "tbb/tick_count.h"
-#include "tbb/tbb_allocator.h"
-#include "tbb/tbb_thread.h"
-
-#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>
-#include <vector>
-#include <utility>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#include "harness_assert.h"
-#include "harness.h"
-
-#if __TBB_GCC_WARNING_SUPPRESSION_ENABLED
-#pragma GCC diagnostic ignored "-Wuninitialized"
-#endif
-
-static tbb::atomic<int> construction_counter;
-static tbb::atomic<int> destruction_counter;
-
-const int REPETITIONS = 10;
-const int N = 100000;
-const int VALID_NUMBER_OF_KEYS = 100;
-const double EXPECTED_SUM = (REPETITIONS + 1) * N;
-
-//
-// A minimal class
-// Define: default and copy constructor, and allow implicit operator&
-// also operator=
-//
-
-class minimal {
-private:
- int my_value;
-public:
- minimal(int val=0) : my_value(val) { ++construction_counter; }
- minimal( const minimal &m ) : my_value(m.my_value) { ++construction_counter; }
- minimal& operator=(const minimal& other) { my_value = other.my_value; return *this; }
- minimal& operator+=(const minimal& other) { my_value += other.my_value; return *this; }
- operator int() const { return my_value; }
- ~minimal() { ++destruction_counter; }
- void set_value( const int i ) { my_value = i; }
- int value( ) const { return my_value; }
-};
-
-//// functors for initialization and combine
-
-// Addition
-template <typename T>
-struct FunctorAddFinit {
- T operator()() { return 0; }
-};
-
-template <typename T>
-struct FunctorAddFinit7 {
- T operator()() { return 7; }
-};
-
-template <typename T>
-struct FunctorAddCombine {
- T operator()(T left, T right ) const {
- return left + right;
- }
-};
-
-template <typename T>
-struct FunctorAddCombineRef {
- T operator()(const T& left, const T& right ) const {
- return left + right;
- }
-};
-
-template <typename T>
-T my_finit( ) { return 0; }
-
-template <typename T>
-T my_combine( T left, T right) { return left + right; }
-
-template <typename T>
-T my_combine_ref( const T &left, const T &right) { return left + right; }
-
-template <typename T>
-class CombineEachHelper {
-public:
- CombineEachHelper(T& _result) : my_result(_result) {}
- void operator()(const T& new_bit) { my_result += new_bit; }
- CombineEachHelper& operator=(const CombineEachHelper& other) {
- my_result = other;
- return *this;
- }
-private:
- T& my_result;
-};
-
-template <typename T>
-class CombineEachHelperCnt {
-public:
- CombineEachHelperCnt(T& _result, int& _nbuckets) : my_result(_result), nBuckets(_nbuckets) {}
- void operator()(const T& new_bit) { my_result += new_bit; ++nBuckets; }
- CombineEachHelperCnt& operator=(const CombineEachHelperCnt& other) {
- my_result = other.my_result;
- nBuckets = other.nBuckets;
- return *this;
- }
-private:
- T& my_result;
- int& nBuckets;
-};
-
-template <typename T>
-class CombineEachVectorHelper {
-public:
- typedef std::vector<T, tbb::tbb_allocator<T> > ContainerType;
- CombineEachVectorHelper(T& _result) : my_result(_result) { }
- void operator()(const ContainerType& new_bit) {
- for(typename ContainerType::const_iterator ci = new_bit.begin(); ci != new_bit.end(); ++ci) {
- my_result += *ci;
- }
- }
- CombineEachVectorHelper& operator=(const CombineEachVectorHelper& other) { my_result=other.my_result; return *this;}
-private:
- T& my_result;
-};
-
-
-
-//// end functors
-
-template< typename T >
-void run_serial_scalar_tests(const char *test_name) {
- tbb::tick_count t0;
- T sum = 0;
-
- REMARK("Testing serial %s... ", test_name);
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
- for (int i = 0; i < N; ++i) {
- sum += 1;
- }
- }
-
- double ResultValue = sum;
- ASSERT( EXPECTED_SUM == ResultValue, NULL);
- REMARK("done\nserial %s, 0, %g, %g\n", test_name, ResultValue, ( tbb::tick_count::now() - t0).seconds());
-}
-
-
-template <typename T>
-class ParallelScalarBody: NoAssign {
-
- tbb::combinable<T> &sums;
-
-public:
-
- ParallelScalarBody ( tbb::combinable<T> &_sums ) : sums(_sums) { }
-
- void operator()( const tbb::blocked_range<int> &r ) const {
- for (int i = r.begin(); i != r.end(); ++i) {
- bool was_there;
- T& my_local = sums.local(was_there);
- if(!was_there) my_local = 0;
- my_local += 1 ;
- }
- }
-
-};
-
-// parallel body with no test for first access.
-template <typename T>
-class ParallelScalarBodyNoInit: NoAssign {
-
- tbb::combinable<T> &sums;
-
-public:
-
- ParallelScalarBodyNoInit ( tbb::combinable<T> &_sums ) : sums(_sums) { }
-
- void operator()( const tbb::blocked_range<int> &r ) const {
- for (int i = r.begin(); i != r.end(); ++i) {
- sums.local() += 1 ;
- }
- }
-
-};
-
-template< typename T >
-void RunParallelScalarTests(const char *test_name) {
-
- tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred);
-
- for (int p = MinThread; p <= MaxThread; ++p) {
-
-
- if (p == 0) continue;
-
- REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
- init.initialize(p);
-
- tbb::tick_count t0;
-
- T assign_sum(0);
-
- T combine_sum(0);
-
- T combine_ref_sum(0);
-
- T combine_each_sum(0);
-
- T combine_finit_sum(0);
-
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
-
- tbb::combinable<T> sums;
- FunctorAddFinit<T> my_finit_decl;
- tbb::combinable<T> finit_combinable(my_finit_decl);
-
-
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), ParallelScalarBodyNoInit<T>( finit_combinable ) );
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), ParallelScalarBody<T>( sums ) );
-
- // Use combine
- combine_sum += sums.combine(my_combine<T>);
- combine_ref_sum += sums.combine(my_combine_ref<T>);
-
- CombineEachHelper<T> my_helper(combine_each_sum);
- sums.combine_each(my_helper);
-
- // test assignment
- tbb::combinable<T> assigned;
- assigned = sums;
-
- assign_sum += assigned.combine(my_combine<T>);
-
- combine_finit_sum += finit_combinable.combine(my_combine<T>);
- }
-
- ASSERT( EXPECTED_SUM == combine_sum, NULL);
- ASSERT( EXPECTED_SUM == combine_ref_sum, NULL);
- ASSERT( EXPECTED_SUM == assign_sum, NULL);
- ASSERT( EXPECTED_SUM == combine_finit_sum, NULL);
-
- REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, static_cast<double>(combine_sum),
- ( tbb::tick_count::now() - t0).seconds());
- init.terminate();
- }
-}
-
-
-template <typename T>
-class ParallelVectorForBody: NoAssign {
-
- tbb::combinable< std::vector<T, tbb::tbb_allocator<T> > > &locals;
-
-public:
-
- ParallelVectorForBody ( tbb::combinable< std::vector<T, tbb::tbb_allocator<T> > > &_locals ) : locals(_locals) { }
-
- void operator()( const tbb::blocked_range<int> &r ) const {
- T one = 1;
-
- for (int i = r.begin(); i < r.end(); ++i) {
- locals.local().push_back( one );
- }
- }
-
-};
-
-template< typename T >
-void RunParallelVectorTests(const char *test_name) {
- tbb::tick_count t0;
- tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred);
- typedef std::vector<T, tbb::tbb_allocator<T> > ContainerType;
-
- for (int p = MinThread; p <= MaxThread; ++p) {
-
- if (p == 0) continue;
- REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
- init.initialize(p);
-
- T sum = 0;
- T sum2 = 0;
- T sum3 = 0;
-
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
- typedef typename tbb::combinable< ContainerType > CombinableType;
- CombinableType vs;
-
- tbb::parallel_for ( tbb::blocked_range<int> (0, N, 10000), ParallelVectorForBody<T>( vs ) );
-
- // copy construct
- CombinableType vs2(vs); // this causes an assertion failure, related to allocators...
-
- // assign
- CombinableType vs3;
- vs3 = vs;
-
- CombineEachVectorHelper<T> MyCombineEach(sum);
- vs.combine_each(MyCombineEach);
-
- CombineEachVectorHelper<T> MyCombineEach2(sum2);
- vs2.combine_each(MyCombineEach2);
-
- CombineEachVectorHelper<T> MyCombineEach3(sum3);
- vs2.combine_each(MyCombineEach3);
- // combine_each sums all elements of each vector into the result.
- }
-
- double ResultValue = sum;
- ASSERT( EXPECTED_SUM == ResultValue, NULL);
- ResultValue = sum2;
- ASSERT( EXPECTED_SUM == ResultValue, NULL);
- ResultValue = sum3;
- ASSERT( EXPECTED_SUM == ResultValue, NULL);
- REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, ResultValue, ( tbb::tick_count::now() - t0).seconds());
- init.terminate();
- }
-}
-
-#include "harness_barrier.h"
-
-Harness::SpinBarrier sBarrier;
-
-struct Body : NoAssign {
- tbb::combinable<int>* locals;
- const int nthread;
- const int nIters;
- Body( int nthread_, int niters_ ) : nthread(nthread_), nIters(niters_) { sBarrier.initialize(nthread_); }
-
-
- void operator()(int thread_id ) const {
- bool existed;
- sBarrier.wait();
- for(int i = 0; i < nIters; ++i ) {
- existed = thread_id & 1;
- int oldval = locals->local(existed);
- ASSERT(existed == (i > 0), "Error on first reference");
- ASSERT(!existed || (oldval == thread_id), "Error on fetched value");
- existed = thread_id & 1;
- locals->local(existed) = thread_id;
- ASSERT(existed, "Error on assignment");
- }
- }
-};
-
-void
-TestLocalAllocations( int nthread ) {
- ASSERT(nthread > 0, "nthread must be positive");
-#define NITERATIONS 1000
- Body myBody(nthread, NITERATIONS);
- tbb::combinable<int> myCombinable;
- myBody.locals = &myCombinable;
-
- NativeParallelFor( nthread, myBody );
-
- int mySum = 0;
- int mySlots = 0;
- CombineEachHelperCnt<int> myCountCombine(mySum, mySlots);
- myCombinable.combine_each(myCountCombine);
-
- ASSERT(nthread == mySlots, "Incorrect number of slots");
- ASSERT(mySum == (nthread - 1) * nthread / 2, "Incorrect values in result");
-}
-
-
-void
-RunParallelTests() {
- RunParallelScalarTests<int>("int");
- RunParallelScalarTests<double>("double");
- RunParallelScalarTests<minimal>("minimal");
- RunParallelVectorTests<int>("std::vector<int, tbb::tbb_allocator<int> >");
- RunParallelVectorTests<double>("std::vector<double, tbb::tbb_allocator<double> >");
-}
-
-template <typename T>
-void
-RunAssignmentAndCopyConstructorTest(const char *test_name) {
- REMARK("Testing assignment and copy construction for %s\n", test_name);
-
- // test creation with finit function (combine returns finit return value if no threads have created locals)
- FunctorAddFinit7<T> my_finit7_decl;
- tbb::combinable<T> create2(my_finit7_decl);
- ASSERT(7 == create2.combine(my_combine<T>), NULL);
-
- // test copy construction with function initializer
- tbb::combinable<T> copy2(create2);
- ASSERT(7 == copy2.combine(my_combine<T>), NULL);
-
- // test copy assignment with function initializer
- FunctorAddFinit<T> my_finit_decl;
- tbb::combinable<T> assign2(my_finit_decl);
- assign2 = create2;
- ASSERT(7 == assign2.combine(my_combine<T>), NULL);
-}
-
-void
-RunAssignmentAndCopyConstructorTests() {
- REMARK("Running assignment and copy constructor tests\n");
- RunAssignmentAndCopyConstructorTest<int>("int");
- RunAssignmentAndCopyConstructorTest<double>("double");
- RunAssignmentAndCopyConstructorTest<minimal>("minimal");
-}
-
-int TestMain () {
- if (MaxThread > 0) {
- RunParallelTests();
- }
- RunAssignmentAndCopyConstructorTests();
- for(int i = 1 <= MinThread ? MinThread : 1; i <= MaxThread; ++i) {
- REMARK("Testing local() allocation with nthreads=%d\n", i);
- for(int j = 0; j < 100; ++j) {
- TestLocalAllocations(i);
- }
- }
- return Harness::Done;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#ifndef TBB_USE_PERFORMANCE_WARNINGS
-#define TBB_USE_PERFORMANCE_WARNINGS 1
-#endif
-
-// Our tests usually include the header under test first. But this test needs
-// to use the preprocessor to edit the identifier runtime_warning in concurrent_hash_map.h.
-// Hence we include a few other headers before doing the abusive edit.
-#include "tbb/tbb_stddef.h" /* Defines runtime_warning */
-#include "harness_assert.h" /* Prerequisite for defining hooked_warning */
-
-// The symbol internal::runtime_warning is normally an entry point into the TBB library.
-// Here for sake of testing, we define it to be hooked_warning, a routine peculiar to this unit test.
-#define runtime_warning hooked_warning
-
-static bool bad_hashing = false;
-
-namespace tbb {
- namespace internal {
- static void hooked_warning( const char* /*format*/, ... ) {
- ASSERT(bad_hashing, "unexpected runtime_warning: bad hashing");
- }
- } // namespace internal
-} // namespace tbb
-#define __TBB_EXTRA_DEBUG 1 // enables additional checks
-#include "tbb/concurrent_hash_map.h"
-
-// Restore runtime_warning as an entry point into the TBB library.
-#undef runtime_warning
-
-namespace Jungle {
- struct Tiger {};
- size_t tbb_hasher( const Tiger& ) {return 0;}
-}
-
-#if !defined(_MSC_VER) || _MSC_VER>=1400 || __INTEL_COMPILER
-void test_ADL() {
- tbb::tbb_hash_compare<Jungle::Tiger>::hash(Jungle::Tiger()); // Instantiation chain finds tbb_hasher via Argument Dependent Lookup
-}
-#endif
-
-struct UserDefinedKeyType {
-};
-
-namespace tbb {
- // Test whether tbb_hash_compare can be partially specialized as stated in Reference manual.
- template<> struct tbb_hash_compare<UserDefinedKeyType> {
- size_t hash( UserDefinedKeyType ) const {return 0;}
- bool equal( UserDefinedKeyType /*x*/, UserDefinedKeyType /*y*/ ) {return true;}
- };
-}
-
-tbb::concurrent_hash_map<UserDefinedKeyType,int> TestInstantiationWithUserDefinedKeyType;
-
-// Test whether a sufficient set of headers were included to instantiate a concurernt_hash_map. OSS Bug #120 (& #130):
-// http://www.threadingbuildingblocks.org/bug_desc.php?id=120
-tbb::concurrent_hash_map<std::pair<std::pair<int,std::string>,const char*>,int> TestInstantiation;
-
-#include "tbb/parallel_for.h"
-#include "tbb/blocked_range.h"
-#include "tbb/atomic.h"
-#include "tbb/tick_count.h"
-#include "harness.h"
-#include "harness_allocator.h"
-
-class MyException : public std::bad_alloc {
-public:
- virtual const char *what() const throw() { return "out of items limit"; }
- virtual ~MyException() throw() {}
-};
-
-/** Has tightly controlled interface so that we can verify
- that concurrent_hash_map uses only the required interface. */
-class MyKey {
-private:
- void operator=( const MyKey& ); // Deny access
- int key;
- friend class MyHashCompare;
- friend class YourHashCompare;
-public:
- static MyKey make( int i ) {
- MyKey result;
- result.key = i;
- return result;
- }
- int value_of() const {return key;}
-};
-
-tbb::atomic<long> MyDataCount;
-long MyDataCountLimit = 0;
-
-class MyData {
-protected:
- friend class MyData2;
- int data;
- enum state_t {
- LIVE=0x1234,
- DEAD=0x5678
- } my_state;
- void operator=( const MyData& ); // Deny access
-public:
- MyData(int i = 0) {
- my_state = LIVE;
- data = i;
- if(MyDataCountLimit && MyDataCount + 1 >= MyDataCountLimit)
- __TBB_THROW( MyException() );
- ++MyDataCount;
- }
- MyData( const MyData& other ) {
- ASSERT( other.my_state==LIVE, NULL );
- my_state = LIVE;
- data = other.data;
- if(MyDataCountLimit && MyDataCount + 1 >= MyDataCountLimit)
- __TBB_THROW( MyException() );
- ++MyDataCount;
- }
- ~MyData() {
- --MyDataCount;
- my_state = DEAD;
- }
- static MyData make( int i ) {
- MyData result;
- result.data = i;
- return result;
- }
- int value_of() const {
- ASSERT( my_state==LIVE, NULL );
- return data;
- }
- void set_value( int i ) {
- ASSERT( my_state==LIVE, NULL );
- data = i;
- }
- bool operator==( const MyData& other ) const {
- ASSERT( other.my_state==LIVE, NULL );
- ASSERT( my_state==LIVE, NULL );
- return data == other.data;
- }
-};
-
-class MyData2 : public MyData {
-public:
- MyData2( ) {}
- MyData2( const MyData& other ) {
- ASSERT( other.my_state==LIVE, NULL );
- ASSERT( my_state==LIVE, NULL );
- data = other.data;
- }
- void operator=( const MyData& other ) {
- ASSERT( other.my_state==LIVE, NULL );
- ASSERT( my_state==LIVE, NULL );
- data = other.data;
- }
- void operator=( const MyData2& other ) {
- ASSERT( other.my_state==LIVE, NULL );
- ASSERT( my_state==LIVE, NULL );
- data = other.data;
- }
- bool operator==( const MyData2& other ) const {
- ASSERT( other.my_state==LIVE, NULL );
- ASSERT( my_state==LIVE, NULL );
- return data == other.data;
- }
-};
-
-class MyHashCompare {
-public:
- bool equal( const MyKey& j, const MyKey& k ) const {
- return j.key==k.key;
- }
- unsigned long hash( const MyKey& k ) const {
- return k.key;
- }
-};
-
-class YourHashCompare {
-public:
- bool equal( const MyKey& j, const MyKey& k ) const {
- return j.key==k.key;
- }
- unsigned long hash( const MyKey& ) const {
- return 1;
- }
-};
-
-typedef local_counting_allocator<std::allocator<MyData> > MyAllocator;
-typedef tbb::concurrent_hash_map<MyKey,MyData,MyHashCompare,MyAllocator> MyTable;
-typedef tbb::concurrent_hash_map<MyKey,MyData2,MyHashCompare> MyTable2;
-typedef tbb::concurrent_hash_map<MyKey,MyData,YourHashCompare> YourTable;
-
-template<typename MyTable>
-inline void CheckAllocator(MyTable &table, size_t expected_allocs, size_t expected_frees, bool exact = true) {
- size_t items_allocated = table.get_allocator().items_allocated, items_freed = table.get_allocator().items_freed;
- size_t allocations = table.get_allocator().allocations, frees = table.get_allocator().frees;
- REMARK("checking allocators: items %u/%u, allocs %u/%u\n",
- unsigned(items_allocated), unsigned(items_freed), unsigned(allocations), unsigned(frees) );
- ASSERT( items_allocated == allocations, NULL); ASSERT( items_freed == frees, NULL);
- if(exact) {
- ASSERT( allocations == expected_allocs, NULL); ASSERT( frees == expected_frees, NULL);
- } else {
- ASSERT( allocations >= expected_allocs, NULL); ASSERT( frees >= expected_frees, NULL);
- ASSERT( allocations - frees == expected_allocs - expected_frees, NULL );
- }
-}
-
-inline bool UseKey( size_t i ) {
- return (i&3)!=3;
-}
-
-struct Insert {
- static void apply( MyTable& table, int i ) {
- if( UseKey(i) ) {
- if( i&4 ) {
- MyTable::accessor a;
- table.insert( a, MyKey::make(i) );
- if( i&1 )
- (*a).second.set_value(i*i);
- else
- a->second.set_value(i*i);
- } else
- if( i&1 ) {
- MyTable::accessor a;
- table.insert( a, std::make_pair(MyKey::make(i), MyData(i*i)) );
- ASSERT( (*a).second.value_of()==i*i, NULL );
- } else {
- MyTable::const_accessor ca;
- table.insert( ca, std::make_pair(MyKey::make(i), MyData(i*i)) );
- ASSERT( ca->second.value_of()==i*i, NULL );
- }
- }
- }
-};
-
-struct Find {
- static void apply( MyTable& table, int i ) {
- MyTable::accessor a;
- const MyTable::accessor& ca = a;
- bool b = table.find( a, MyKey::make(i) );
- ASSERT( b==!a.empty(), NULL );
- if( b ) {
- if( !UseKey(i) )
- REPORT("Line %d: unexpected key %d present\n",__LINE__,i);
- AssertSameType( &*a, static_cast<MyTable::value_type*>(0) );
- ASSERT( ca->second.value_of()==i*i, NULL );
- ASSERT( (*ca).second.value_of()==i*i, NULL );
- if( i&1 )
- ca->second.set_value( ~ca->second.value_of() );
- else
- (*ca).second.set_value( ~ca->second.value_of() );
- } else {
- if( UseKey(i) )
- REPORT("Line %d: key %d missing\n",__LINE__,i);
- }
- }
-};
-
-struct FindConst {
- static void apply( const MyTable& table, int i ) {
- MyTable::const_accessor a;
- const MyTable::const_accessor& ca = a;
- bool b = table.find( a, MyKey::make(i) );
- ASSERT( b==(table.count(MyKey::make(i))>0), NULL );
- ASSERT( b==!a.empty(), NULL );
- ASSERT( b==UseKey(i), NULL );
- if( b ) {
- AssertSameType( &*ca, static_cast<const MyTable::value_type*>(0) );
- ASSERT( ca->second.value_of()==~(i*i), NULL );
- ASSERT( (*ca).second.value_of()==~(i*i), NULL );
- }
- }
-};
-
-tbb::atomic<int> EraseCount;
-
-struct Erase {
- static void apply( MyTable& table, int i ) {
- bool b;
- if(i&4) {
- if(i&8) {
- MyTable::const_accessor a;
- b = table.find( a, MyKey::make(i) ) && table.erase( a );
- } else {
- MyTable::accessor a;
- b = table.find( a, MyKey::make(i) ) && table.erase( a );
- }
- } else
- b = table.erase( MyKey::make(i) );
- if( b ) ++EraseCount;
- ASSERT( table.count(MyKey::make(i)) == 0, NULL );
- }
-};
-
-static const int IE_SIZE = 2;
-tbb::atomic<YourTable::size_type> InsertEraseCount[IE_SIZE];
-
-struct InsertErase {
- static void apply( YourTable& table, int i ) {
- if ( i%3 ) {
- int key = i%IE_SIZE;
- if ( table.insert( std::make_pair(MyKey::make(key), MyData2()) ) )
- ++InsertEraseCount[key];
- } else {
- int key = i%IE_SIZE;
- if( i&1 ) {
- YourTable::accessor res;
- if(table.find( res, MyKey::make(key) ) && table.erase( res ) )
- --InsertEraseCount[key];
- } else {
- YourTable::const_accessor res;
- if(table.find( res, MyKey::make(key) ) && table.erase( res ) )
- --InsertEraseCount[key];
- }
- }
- }
-};
-
-// Test for the deadlock discussed at:
-// http://softwarecommunity.intel.com/isn/Community/en-US/forums/permalink/30253302/30253302/ShowThread.aspx#30253302
-struct InnerInsert {
- static void apply( YourTable& table, int i ) {
- YourTable::accessor a1, a2;
- if(i&1) __TBB_Yield();
- table.insert( a1, MyKey::make(1) );
- __TBB_Yield();
- table.insert( a2, MyKey::make(1 + (1<<30)) ); // the same chain
- table.erase( a2 ); // if erase by key it would lead to deadlock for single thread
- }
-};
-
-template<typename Op, typename MyTable>
-class TableOperation: NoAssign {
- MyTable& my_table;
-public:
- void operator()( const tbb::blocked_range<int>& range ) const {
- for( int i=range.begin(); i!=range.end(); ++i )
- Op::apply(my_table,i);
- }
- TableOperation( MyTable& table ) : my_table(table) {}
-};
-
-template<typename Op, typename TableType>
-void DoConcurrentOperations( TableType& table, int n, const char* what, int nthread ) {
- REMARK("testing %s with %d threads\n",what,nthread);
- tbb::tick_count t0 = tbb::tick_count::now();
- tbb::parallel_for( tbb::blocked_range<int>(0,n,100), TableOperation<Op,TableType>(table) );
- tbb::tick_count t1 = tbb::tick_count::now();
- REMARK("time for %s = %g with %d threads\n",what,(t1-t0).seconds(),nthread);
-}
-
-//! Test traversing the table with an iterator.
-void TraverseTable( MyTable& table, size_t n, size_t expected_size ) {
- REMARK("testing traversal\n");
- size_t actual_size = table.size();
- ASSERT( actual_size==expected_size, NULL );
- size_t count = 0;
- bool* array = new bool[n];
- memset( array, 0, n*sizeof(bool) );
- const MyTable& const_table = table;
- MyTable::const_iterator ci = const_table.begin();
- for( MyTable::iterator i = table.begin(); i!=table.end(); ++i ) {
- // Check iterator
- int k = i->first.value_of();
- ASSERT( UseKey(k), NULL );
- ASSERT( (*i).first.value_of()==k, NULL );
- ASSERT( 0<=k && size_t(k)<n, "out of bounds key" );
- ASSERT( !array[k], "duplicate key" );
- array[k] = true;
- ++count;
-
- // Check lower/upper bounds
- std::pair<MyTable::iterator, MyTable::iterator> er = table.equal_range(i->first);
- std::pair<MyTable::const_iterator, MyTable::const_iterator> cer = const_table.equal_range(i->first);
- ASSERT(cer.first == er.first && cer.second == er.second, NULL);
- ASSERT(cer.first == i, NULL);
- ASSERT(std::distance(cer.first, cer.second) == 1, NULL);
-
- // Check const_iterator
- MyTable::const_iterator cic = ci++;
- ASSERT( cic->first.value_of()==k, NULL );
- ASSERT( (*cic).first.value_of()==k, NULL );
- }
- ASSERT( ci==const_table.end(), NULL );
- delete[] array;
- if( count!=expected_size ) {
- REPORT("Line %d: count=%ld but should be %ld\n",__LINE__,long(count),long(expected_size));
- }
-}
-
-typedef tbb::atomic<unsigned char> AtomicByte;
-
-template<typename RangeType>
-struct ParallelTraverseBody: NoAssign {
- const size_t n;
- AtomicByte* const array;
- ParallelTraverseBody( AtomicByte array_[], size_t n_ ) :
- n(n_),
- array(array_)
- {}
- void operator()( const RangeType& range ) const {
- for( typename RangeType::iterator i = range.begin(); i!=range.end(); ++i ) {
- int k = i->first.value_of();
- ASSERT( 0<=k && size_t(k)<n, NULL );
- ++array[k];
- }
- }
-};
-
-void Check( AtomicByte array[], size_t n, size_t expected_size ) {
- if( expected_size )
- for( size_t k=0; k<n; ++k ) {
- if( array[k] != int(UseKey(k)) ) {
- REPORT("array[%d]=%d != %d=UseKey(%d)\n",
- int(k), int(array[k]), int(UseKey(k)), int(k));
- ASSERT(false,NULL);
- }
- }
-}
-
-//! Test travering the tabel with a parallel range
-void ParallelTraverseTable( MyTable& table, size_t n, size_t expected_size ) {
- REMARK("testing parallel traversal\n");
- ASSERT( table.size()==expected_size, NULL );
- AtomicByte* array = new AtomicByte[n];
-
- memset( array, 0, n*sizeof(AtomicByte) );
- MyTable::range_type r = table.range(10);
- tbb::parallel_for( r, ParallelTraverseBody<MyTable::range_type>( array, n ));
- Check( array, n, expected_size );
-
- const MyTable& const_table = table;
- memset( array, 0, n*sizeof(AtomicByte) );
- MyTable::const_range_type cr = const_table.range(10);
- tbb::parallel_for( cr, ParallelTraverseBody<MyTable::const_range_type>( array, n ));
- Check( array, n, expected_size );
-
- delete[] array;
-}
-
-void TestInsertFindErase( int nthread ) {
- int n=250000;
-
- // compute m = number of unique keys
- int m = 0;
- for( int i=0; i<n; ++i )
- m += UseKey(i);
-
- MyAllocator a; a.items_freed = a.frees = 100;
- ASSERT( MyDataCount==0, NULL );
- MyTable table(a);
- TraverseTable(table,n,0);
- ParallelTraverseTable(table,n,0);
- CheckAllocator(table, 0, 100);
-
- DoConcurrentOperations<Insert,MyTable>(table,n,"insert",nthread);
- ASSERT( MyDataCount==m, NULL );
- TraverseTable(table,n,m);
- ParallelTraverseTable(table,n,m);
- CheckAllocator(table, m, 100);
-
- DoConcurrentOperations<Find,MyTable>(table,n,"find",nthread);
- ASSERT( MyDataCount==m, NULL );
- CheckAllocator(table, m, 100);
-
- DoConcurrentOperations<FindConst,MyTable>(table,n,"find(const)",nthread);
- ASSERT( MyDataCount==m, NULL );
- CheckAllocator(table, m, 100);
-
- EraseCount=0;
- DoConcurrentOperations<Erase,MyTable>(table,n,"erase",nthread);
- ASSERT( EraseCount==m, NULL );
- ASSERT( MyDataCount==0, NULL );
- TraverseTable(table,n,0);
- CheckAllocator(table, m, m+100);
-
- bad_hashing = true;
- table.clear();
- bad_hashing = false;
-
- if(nthread > 1) {
- YourTable ie_table;
- for( int i=0; i<IE_SIZE; ++i )
- InsertEraseCount[i] = 0;
- DoConcurrentOperations<InsertErase,YourTable>(ie_table,n/2,"insert_erase",nthread);
- for( int i=0; i<IE_SIZE; ++i )
- ASSERT( InsertEraseCount[i]==ie_table.count(MyKey::make(i)), NULL );
-
- DoConcurrentOperations<InnerInsert,YourTable>(ie_table,2000,"inner insert",nthread);
- }
-}
-
-volatile int Counter;
-
-class AddToTable: NoAssign {
- MyTable& my_table;
- const int my_nthread;
- const int my_m;
-public:
- AddToTable( MyTable& table, int nthread, int m ) : my_table(table), my_nthread(nthread), my_m(m) {}
- void operator()( int ) const {
- for( int i=0; i<my_m; ++i ) {
- // Busy wait to synchronize threads
- int j = 0;
- while( Counter<i ) {
- if( ++j==1000000 ) {
- // If Counter<i after a million iterations, then we almost surely have
- // more logical threads than physical threads, and should yield in
- // order to let suspended logical threads make progress.
- j = 0;
- __TBB_Yield();
- }
- }
- // Now all threads attempt to simultaneously insert a key.
- int k;
- {
- MyTable::accessor a;
- MyKey key = MyKey::make(i);
- if( my_table.insert( a, key ) )
- a->second.set_value( 1 );
- else
- a->second.set_value( a->second.value_of()+1 );
- k = a->second.value_of();
- }
- if( k==my_nthread )
- Counter=i+1;
- }
- }
-};
-
-class RemoveFromTable: NoAssign {
- MyTable& my_table;
- const int my_nthread;
- const int my_m;
-public:
- RemoveFromTable( MyTable& table, int nthread, int m ) : my_table(table), my_nthread(nthread), my_m(m) {}
- void operator()(int) const {
- for( int i=0; i<my_m; ++i ) {
- bool b;
- if(i&4) {
- if(i&8) {
- MyTable::const_accessor a;
- b = my_table.find( a, MyKey::make(i) ) && my_table.erase( a );
- } else {
- MyTable::accessor a;
- b = my_table.find( a, MyKey::make(i) ) && my_table.erase( a );
- }
- } else
- b = my_table.erase( MyKey::make(i) );
- if( b ) ++EraseCount;
- }
- }
-};
-
-//! Test for memory leak in concurrent_hash_map (TR #153).
-void TestConcurrency( int nthread ) {
- REMARK("testing multiple insertions/deletions of same key with %d threads\n", nthread);
- {
- ASSERT( MyDataCount==0, NULL );
- MyTable table;
- const int m = 1000;
- Counter = 0;
- tbb::tick_count t0 = tbb::tick_count::now();
- NativeParallelFor( nthread, AddToTable(table,nthread,m) );
- tbb::tick_count t1 = tbb::tick_count::now();
- REMARK("time for %u insertions = %g with %d threads\n",unsigned(MyDataCount),(t1-t0).seconds(),nthread);
- ASSERT( MyDataCount==m, "memory leak detected" );
-
- EraseCount = 0;
- t0 = tbb::tick_count::now();
- NativeParallelFor( nthread, RemoveFromTable(table,nthread,m) );
- t1 = tbb::tick_count::now();
- REMARK("time for %u deletions = %g with %d threads\n",unsigned(EraseCount),(t1-t0).seconds(),nthread);
- ASSERT( MyDataCount==0, "memory leak detected" );
- ASSERT( EraseCount==m, "return value of erase() is broken" );
-
- CheckAllocator(table, m, m, /*exact*/nthread <= 1);
- }
- ASSERT( MyDataCount==0, "memory leak detected" );
-}
-
-void TestTypes() {
- AssertSameType( static_cast<MyTable::key_type*>(0), static_cast<MyKey*>(0) );
- AssertSameType( static_cast<MyTable::mapped_type*>(0), static_cast<MyData*>(0) );
- AssertSameType( static_cast<MyTable::value_type*>(0), static_cast<std::pair<const MyKey,MyData>*>(0) );
- AssertSameType( static_cast<MyTable::accessor::value_type*>(0), static_cast<MyTable::value_type*>(0) );
- AssertSameType( static_cast<MyTable::const_accessor::value_type*>(0), static_cast<const MyTable::value_type*>(0) );
- AssertSameType( static_cast<MyTable::size_type*>(0), static_cast<size_t*>(0) );
- AssertSameType( static_cast<MyTable::difference_type*>(0), static_cast<ptrdiff_t*>(0) );
-}
-
-template<typename Iterator, typename T>
-void TestIteratorTraits() {
- AssertSameType( static_cast<typename Iterator::difference_type*>(0), static_cast<ptrdiff_t*>(0) );
- AssertSameType( static_cast<typename Iterator::value_type*>(0), static_cast<T*>(0) );
- AssertSameType( static_cast<typename Iterator::pointer*>(0), static_cast<T**>(0) );
- AssertSameType( static_cast<typename Iterator::iterator_category*>(0), static_cast<std::forward_iterator_tag*>(0) );
- T x;
- typename Iterator::reference xr = x;
- typename Iterator::pointer xp = &x;
- ASSERT( &xr==xp, NULL );
-}
-
-template<typename Iterator1, typename Iterator2>
-void TestIteratorAssignment( Iterator2 j ) {
- Iterator1 i(j), k;
- ASSERT( i==j, NULL ); ASSERT( !(i!=j), NULL );
- k = j;
- ASSERT( k==j, NULL ); ASSERT( !(k!=j), NULL );
-}
-
-template<typename Range1, typename Range2>
-void TestRangeAssignment( Range2 r2 ) {
- Range1 r1(r2); r1 = r2;
-}
-//------------------------------------------------------------------------
-// Test for copy constructor and assignment
-//------------------------------------------------------------------------
-
-template<typename MyTable>
-static void FillTable( MyTable& x, int n ) {
- for( int i=1; i<=n; ++i ) {
- MyKey key( MyKey::make(-i) ); // hash values must not be specified in direct order
- typename MyTable::accessor a;
- bool b = x.insert(a,key);
- ASSERT(b, NULL);
- a->second.set_value( i*i );
- }
-}
-
-template<typename MyTable>
-static void CheckTable( const MyTable& x, int n ) {
- ASSERT( x.size()==size_t(n), "table is different size than expected" );
- ASSERT( x.empty()==(n==0), NULL );
- ASSERT( x.size()<=x.max_size(), NULL );
- for( int i=1; i<=n; ++i ) {
- MyKey key( MyKey::make(-i) );
- typename MyTable::const_accessor a;
- bool b = x.find(a,key);
- ASSERT( b, NULL );
- ASSERT( a->second.value_of()==i*i, NULL );
- }
- int count = 0;
- int key_sum = 0;
- for( typename MyTable::const_iterator i(x.begin()); i!=x.end(); ++i ) {
- ++count;
- key_sum += -i->first.value_of();
- }
- ASSERT( count==n, NULL );
- ASSERT( key_sum==n*(n+1)/2, NULL );
-}
-
-static void TestCopy() {
- REMARK("testing copy\n");
- MyTable t1;
- for( int i=0; i<10000; i=(i<100 ? i+1 : i*3) ) {
- MyDataCount = 0;
-
- FillTable(t1,i);
- // Do not call CheckTable(t1,i) before copying, it enforces rehashing
-
- MyTable t2(t1);
- // Check that copy constructor did not mangle source table.
- CheckTable(t1,i);
- swap(t1, t2);
- CheckTable(t1,i);
- ASSERT( !(t1 != t2), NULL );
-
- // Clear original table
- t2.clear();
- swap(t2, t1);
- CheckTable(t1,0);
-
- // Verify that copy of t1 is correct, even after t1 is cleared.
- CheckTable(t2,i);
- t2.clear();
- t1.swap( t2 );
- CheckTable(t1,0);
- CheckTable(t2,0);
- ASSERT( MyDataCount==0, "data leak?" );
- }
-}
-
-void TestAssignment() {
- REMARK("testing assignment\n");
- for( int i=0; i<1000; i=(i<30 ? i+1 : i*5) ) {
- for( int j=0; j<1000; j=(j<30 ? j+1 : j*7) ) {
- MyTable t1;
- MyTable t2;
- FillTable(t1,i);
- FillTable(t2,j);
- ASSERT( (t1 == t2) == (i == j), NULL );
- CheckTable(t2,j);
-
- MyTable& tref = t2=t1;
- ASSERT( &tref==&t2, NULL );
- ASSERT( t1 == t2, NULL );
- CheckTable(t1,i);
- CheckTable(t2,i);
-
- t1.clear();
- CheckTable(t1,0);
- CheckTable(t2,i);
- ASSERT( MyDataCount==i, "data leak?" );
-
- t2.clear();
- CheckTable(t1,0);
- CheckTable(t2,0);
- ASSERT( MyDataCount==0, "data leak?" );
- }
- }
-}
-
-void TestIteratorsAndRanges() {
- REMARK("testing iterators compliance\n");
- TestIteratorTraits<MyTable::iterator,MyTable::value_type>();
- TestIteratorTraits<MyTable::const_iterator,const MyTable::value_type>();
-
- MyTable v;
- MyTable const &u = v;
-
- TestIteratorAssignment<MyTable::const_iterator>( u.begin() );
- TestIteratorAssignment<MyTable::const_iterator>( v.begin() );
- TestIteratorAssignment<MyTable::iterator>( v.begin() );
- // doesn't compile as expected: TestIteratorAssignment<typename V::iterator>( u.begin() );
-
- // check for non-existing
- ASSERT(v.equal_range(MyKey::make(-1)) == std::make_pair(v.end(), v.end()), NULL);
- ASSERT(u.equal_range(MyKey::make(-1)) == std::make_pair(u.end(), u.end()), NULL);
-
- REMARK("testing ranges compliance\n");
- TestRangeAssignment<MyTable::const_range_type>( u.range() );
- TestRangeAssignment<MyTable::const_range_type>( v.range() );
- TestRangeAssignment<MyTable::range_type>( v.range() );
- // doesn't compile as expected: TestRangeAssignment<typename V::range_type>( u.range() );
-
- REMARK("testing construction and insertion from iterators range\n");
- FillTable( v, 1000 );
- MyTable2 t(v.begin(), v.end());
- v.rehash();
- CheckTable(t, 1000);
- t.insert(v.begin(), v.end()); // do nothing
- CheckTable(t, 1000);
- t.clear();
- t.insert(v.begin(), v.end()); // restore
- CheckTable(t, 1000);
-
- REMARK("testing comparison\n");
- typedef tbb::concurrent_hash_map<MyKey,MyData2,YourHashCompare,MyAllocator> YourTable1;
- typedef tbb::concurrent_hash_map<MyKey,MyData2,YourHashCompare> YourTable2;
- YourTable1 t1;
- FillTable( t1, 10 );
- CheckTable(t1, 10 );
- YourTable2 t2(t1.begin(), t1.end());
- MyKey key( MyKey::make(-5) ); MyData2 data;
- ASSERT(t2.erase(key), NULL);
- YourTable2::accessor a;
- ASSERT(t2.insert(a, key), NULL);
- data.set_value(0); a->second = data;
- ASSERT( t1 != t2, NULL);
- data.set_value(5*5); a->second = data;
- ASSERT( t1 == t2, NULL);
-}
-
-void TestRehash() {
- REMARK("testing rehashing\n");
- MyTable w;
- w.insert( std::make_pair(MyKey::make(-5), MyData()) );
- w.rehash(); // without this, assertion will fail
- MyTable::iterator it = w.begin();
- int i = 0; // check for non-rehashed buckets
- for( ; it != w.end(); i++ )
- w.count( (it++)->first );
- ASSERT( i == 1, NULL );
- for( i=0; i<1000; i=(i<29 ? i+1 : i*2) ) {
- for( int j=max(256+i, i*2); j<10000; j*=3 ) {
- MyTable v;
- FillTable( v, i );
- ASSERT(int(v.size()) == i, NULL);
- ASSERT(int(v.bucket_count()) <= j, NULL);
- v.rehash( j );
- ASSERT(int(v.bucket_count()) >= j, NULL);
- CheckTable( v, i );
- }
- }
-}
-
-#if TBB_USE_EXCEPTIONS
-void TestExceptions() {
- typedef local_counting_allocator<tbb::tbb_allocator<MyData2> > allocator_t;
- typedef tbb::concurrent_hash_map<MyKey,MyData2,MyHashCompare,allocator_t> ThrowingTable;
- enum methods {
- zero_method = 0,
- ctor_copy, op_assign, op_insert,
- all_methods
- };
- REMARK("testing exception-safety guarantees\n");
- ThrowingTable src;
- FillTable( src, 1000 );
- ASSERT( MyDataCount==1000, NULL );
-
- try {
- for(int t = 0; t < 2; t++) // exception type
- for(int m = zero_method+1; m < all_methods; m++)
- {
- allocator_t a;
- if(t) MyDataCountLimit = 101;
- else a.set_limits(101);
- ThrowingTable victim(a);
- MyDataCount = 0;
-
- try {
- switch(m) {
- case ctor_copy: {
- ThrowingTable acopy(src, a);
- } break;
- case op_assign: {
- victim = src;
- } break;
- case op_insert: {
- FillTable( victim, 1000 );
- } break;
- default:;
- }
- ASSERT(false, "should throw an exception");
- } catch(std::bad_alloc &e) {
- MyDataCountLimit = 0;
- size_t size = victim.size();
- switch(m) {
- case op_assign:
- ASSERT( MyDataCount==100, "data leak?" );
- ASSERT( size>=100, NULL );
- CheckAllocator(victim, 100+t, t);
- case ctor_copy:
- CheckTable(src, 1000);
- break;
- case op_insert:
- ASSERT( size==size_t(100-t), NULL );
- ASSERT( MyDataCount==100-t, "data leak?" );
- CheckTable(victim, 100-t);
- CheckAllocator(victim, 100, t);
- break;
-
- default:; // nothing to check here
- }
- REMARK("Exception %d: %s\t- ok ()\n", m, e.what());
- }
- catch ( ... ) {
- ASSERT ( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unrecognized exception" );
- }
- }
- } catch(...) {
- ASSERT(false, "unexpected exception");
- }
- src.clear(); MyDataCount = 0;
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-//------------------------------------------------------------------------
-// Test driver
-//------------------------------------------------------------------------
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- if( MinThread<0 ) {
- REPORT("ERROR: must use at least one thread\n");
- exit(1);
- }
-
- // Do serial tests
- TestTypes();
- TestCopy();
- TestRehash();
- TestAssignment();
- TestIteratorsAndRanges();
-#if TBB_USE_EXCEPTIONS
- TestExceptions();
-#endif /* TBB_USE_EXCEPTIONS */
-
- // Do concurrency tests.
- for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) {
- tbb::task_scheduler_init init( nthread );
- TestInsertFindErase( nthread );
- TestConcurrency( nthread );
- }
- // check linking
- if(bad_hashing) { //should be false
- tbb::internal::runtime_warning("none\nERROR: it must not be executed");
- }
-
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/concurrent_monitor.h"
-#include "tbb/atomic.h"
-#include "tbb/parallel_for.h"
-#include "tbb/blocked_range.h"
-#include "harness.h"
-#include "tbb/concurrent_monitor.cpp"
-
-using namespace tbb;
-
-//! Queuing lock with concurrent_monitor; to test concurrent_monitor::notify( Predicate p )
-class QueuingMutex {
-public:
- //! Construct unacquired mutex.
- QueuingMutex() { q_tail = NULL; }
-
- //! The scoped locking pattern
- class ScopedLock: internal::no_copy {
- void Initialize() { mutex = NULL; }
- public:
- ScopedLock() {Initialize();}
- ScopedLock( QueuingMutex& m ) { Initialize(); Acquire(m); }
- ~ScopedLock() { if( mutex ) Release(); }
- void Acquire( QueuingMutex& m );
- void Release();
- void SleepPerhaps();
-
- private:
- QueuingMutex* mutex;
- ScopedLock* next;
- uintptr_t going;
- internal::concurrent_monitor::thread_context thr_ctx;
- };
-
- friend class ScopedLock;
-private:
- //! The last competitor requesting the lock
- atomic<ScopedLock*> q_tail;
- internal::concurrent_monitor waitq;
-};
-
-struct PredicateEq {
- void* p;
- PredicateEq( void* p_ ) : p(p_) {}
- bool operator() ( void* v ) const {return p==v;}
-};
-
-//! A method to acquire QueuingMutex lock
-void QueuingMutex::ScopedLock::Acquire( QueuingMutex& m )
-{
- // Must set all fields before the fetch_and_store, because once the
- // fetch_and_store executes, *this becomes accessible to other threads.
- mutex = &m;
- next = NULL;
- going = 0;
-
- // The fetch_and_store must have release semantics, because we are
- // "sending" the fields initialized above to other processors.
- ScopedLock* pred = m.q_tail.fetch_and_store<tbb::release>(this);
- if( pred ) {
- __TBB_ASSERT( !pred->next, "the predecessor has another successor!");
- pred->next = this;
- for( int i=0; i<16; ++i ) {
- if( going!=0ul ) break;
- __TBB_Yield();
- }
- SleepPerhaps();
- }
-
- // Force acquire so that user's critical section receives correct values
- // from processor that was previously in the user's critical section.
- __TBB_load_with_acquire(going);
-}
-
-//! A method to release QueuingMutex lock
-void QueuingMutex::ScopedLock::Release( )
-{
- if( !next ) {
- if( this == mutex->q_tail.compare_and_swap<tbb::release>(NULL, this) ) {
- // this was the only item in the queue, and the queue is now empty.
- goto done;
- }
- // Someone in the queue
- spin_wait_while_eq( next, (ScopedLock*)0 );
- }
- __TBB_store_with_release(next->going, 1);
- mutex->waitq.notify( PredicateEq(next) );
-done:
- Initialize();
-}
-
-//! Yield and block; go to sleep
-void QueuingMutex::ScopedLock::SleepPerhaps()
-{
- bool slept = false;
- internal::concurrent_monitor& mq = mutex->waitq;
- mq.prepare_wait( thr_ctx, this );
- while( going==0ul ) {
- if( (slept=mq.commit_wait( thr_ctx ))==true )
- break;
- mq.prepare_wait( thr_ctx, this );
- }
- if( !slept )
- mq.cancel_wait( thr_ctx );
-}
-
-// Spin lock with concurrent_monitor; to test concurrent_monitor::notify_all() and concurrent_monitor::notify()
-class SpinMutex {
-public:
- //! Construct unacquired mutex.
- SpinMutex() : toggle(false) { flag = 0; }
-
- //! The scoped locking pattern
- class ScopedLock: internal::no_copy {
- void Initialize() { mutex = NULL; }
- public:
- ScopedLock() {Initialize();}
- ScopedLock( SpinMutex& m ) { Initialize(); Acquire(m); }
- ~ScopedLock() { if( mutex ) Release(); }
- void Acquire( SpinMutex& m );
- void Release();
- void SleepPerhaps();
-
- private:
- SpinMutex* mutex;
- internal::concurrent_monitor::thread_context thr_ctx;
- };
-
- friend class ScopedLock;
-private:
- tbb::atomic<unsigned> flag;
- bool toggle;
- internal::concurrent_monitor waitq;
-};
-
-//! A method to acquire SpinMutex lock
-void SpinMutex::ScopedLock::Acquire( SpinMutex& m )
-{
- mutex = &m;
-retry:
- if( m.flag.compare_and_swap( 1, 0 )!=0 ) {
- SleepPerhaps();
- goto retry;
- }
-}
-
-//! A method to release SpinMutex lock
-void SpinMutex::ScopedLock::Release()
-{
- bool old_toggle = mutex->toggle;
- mutex->toggle = !mutex->toggle;
- mutex->flag = 0;
- if( old_toggle )
- mutex->waitq.notify_one();
- else
- mutex->waitq.notify_all();
-}
-
-//! Yield and block; go to sleep
-void SpinMutex::ScopedLock::SleepPerhaps()
-{
- bool slept = false;
- internal::concurrent_monitor& mq = mutex->waitq;
- mq.prepare_wait( thr_ctx, this );
- while( mutex->flag ) {
- if( (slept=mq.commit_wait( thr_ctx ))==true )
- break;
- mq.prepare_wait( thr_ctx, this );
- }
- if( !slept )
- mq.cancel_wait( thr_ctx );
-}
-
-template<typename M>
-struct Counter {
- typedef M mutex_type;
- M mutex;
- volatile long value;
-};
-
-//! Function object for use with parallel_for.h.
-template<typename C>
-struct AddOne: NoAssign {
- C& counter;
- /** Increments counter once for each iteration in the iteration space. */
- void operator()( tbb::blocked_range<size_t>& range ) const {
- for( size_t i=range.begin(); i!=range.end(); ++i ) {
- typename C::mutex_type::ScopedLock lock(counter.mutex);
- counter.value = counter.value+1;
- }
- }
- AddOne( C& counter_ ) : counter(counter_) {}
-};
-
-//! Generic test of a TBB mutex type M.
-/** Does not test features specific to reader-writer locks. */
-template<typename M>
-void Test() {
- Counter<M> counter;
- counter.value = 0;
- const int n = 100000;
- tbb::parallel_for(tbb::blocked_range<size_t>(0,n,n/10),AddOne<Counter<M> >(counter));
- if( counter.value!=n )
- REPORT("ERROR : counter.value=%ld\n",counter.value);
-}
-
-int TestMain () {
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK( "testing with %d workers\n", static_cast<int>(p) );
- // test the predicated notify
- Test<QueuingMutex>();
- // test the notify_all method
- Test<SpinMutex>();
- REMARK( "calling destructor for task_scheduler_init\n" );
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/concurrent_queue.h"
-#include "tbb/atomic.h"
-#include "tbb/tick_count.h"
-#include "harness.h"
-#include "harness_allocator.h"
-
-static tbb::atomic<long> FooConstructed;
-static tbb::atomic<long> FooDestroyed;
-
-class Foo {
- enum state_t{
- LIVE=0x1234,
- DEAD=0xDEAD
- };
- state_t state;
-public:
- int thread_id;
- int serial;
- Foo() : state(LIVE), thread_id(0), serial(0) {
- ++FooConstructed;
- }
- Foo( const Foo& item ) : state(LIVE) {
- ASSERT( item.state==LIVE, NULL );
- ++FooConstructed;
- thread_id = item.thread_id;
- serial = item.serial;
- }
- ~Foo() {
- ASSERT( state==LIVE, NULL );
- ++FooDestroyed;
- state=DEAD;
- thread_id=0xDEAD;
- serial=0xDEAD;
- }
- void operator=( const Foo& item ) {
- ASSERT( item.state==LIVE, NULL );
- ASSERT( state==LIVE, NULL );
- thread_id = item.thread_id;
- serial = item.serial;
- }
- bool is_const() {return false;}
- bool is_const() const {return true;}
-};
-
-// problem size
-static const int N = 50000; // # of bytes
-
-#if TBB_USE_EXCEPTIONS
-//! Exception for concurrent_queue
-class Foo_exception : public std::bad_alloc {
-public:
- virtual const char *what() const throw() { return "out of Foo limit"; }
- virtual ~Foo_exception() throw() {}
-};
-
-static tbb::atomic<long> FooExConstructed;
-static tbb::atomic<long> FooExDestroyed;
-static tbb::atomic<long> serial_source;
-static long MaxFooCount = 0;
-static const long Threshold = 400;
-
-class FooEx {
- enum state_t{
- LIVE=0x1234,
- DEAD=0xDEAD
- };
- state_t state;
-public:
- int serial;
- FooEx() : state(LIVE) {
- ++FooExConstructed;
- serial = serial_source++;
- }
-
- FooEx( const FooEx& item ) : state(LIVE) {
- ++FooExConstructed;
- if( MaxFooCount && (FooExConstructed-FooExDestroyed) >= MaxFooCount ) // in push()
- throw Foo_exception();
- serial = item.serial;
- }
- ~FooEx() {
- ASSERT( state==LIVE, NULL );
- ++FooExDestroyed;
- state=DEAD;
- serial=0xDEAD;
- }
- void operator=( FooEx& item ) {
- ASSERT( item.state==LIVE, NULL );
- ASSERT( state==LIVE, NULL );
- serial = item.serial;
- if( MaxFooCount==2*Threshold && (FooExConstructed-FooExDestroyed) <= MaxFooCount/4 ) // in pop()
- throw Foo_exception();
- }
-} ;
-#endif /* TBB_USE_EXCEPTIONS */
-
-const size_t MAXTHREAD = 256;
-
-static int Sum[MAXTHREAD];
-
-//! Count of various pop operations
-/** [0] = pop_if_present that failed
- [1] = pop_if_present that succeeded
- [2] = pop */
-static tbb::atomic<long> PopKind[3];
-
-const int M = 10000;
-
-#if TBB_DEPRECATED
-#define CALL_BLOCKING_POP(q,v) (q)->pop(v)
-#define CALL_TRY_POP(q,v,i) (((i)&0x2)?q->try_pop(v):q->pop_if_present(v))
-#define SIZE() size()
-#else
-#define CALL_BLOCKING_POP(q,v) while( !(q)->try_pop(v) ) __TBB_Yield()
-#define CALL_TRY_POP(q,v,i) q->try_pop(v)
-#define SIZE() unsafe_size()
-#endif
-
-struct Body: NoAssign {
- tbb::concurrent_queue<Foo>* queue;
- const int nthread;
- Body( int nthread_ ) : nthread(nthread_) {}
- void operator()( int thread_id ) const {
- long pop_kind[3] = {0,0,0};
- int serial[MAXTHREAD+1];
- memset( serial, 0, nthread*sizeof(int) );
- ASSERT( thread_id<nthread, NULL );
-
- long sum = 0;
- for( long j=0; j<M; ++j ) {
- Foo f;
- f.thread_id = 0xDEAD;
- f.serial = 0xDEAD;
- bool prepopped = false;
- if( j&1 ) {
- prepopped = CALL_TRY_POP(queue,f,j);
- ++pop_kind[prepopped];
- }
- Foo g;
- g.thread_id = thread_id;
- g.serial = j+1;
- queue->push( g );
- if( !prepopped ) {
- CALL_BLOCKING_POP(queue,f);
- ++pop_kind[2];
- }
- ASSERT( f.thread_id<=nthread, NULL );
- ASSERT( f.thread_id==nthread || serial[f.thread_id]<f.serial, "partial order violation" );
- serial[f.thread_id] = f.serial;
- sum += f.serial-1;
- }
- Sum[thread_id] = sum;
- for( int k=0; k<3; ++k )
- PopKind[k] += pop_kind[k];
- }
-};
-
-void TestPushPop( size_t prefill, ptrdiff_t capacity, int nthread ) {
- ASSERT( nthread>0, "nthread must be positive" );
-#if TBB_DEPRECATED
- ptrdiff_t signed_prefill = ptrdiff_t(prefill);
- if( signed_prefill+1>=capacity )
- return;
-#endif
- bool success = false;
- for( int k=0; k<3; ++k )
- PopKind[k] = 0;
- for( int trial=0; !success; ++trial ) {
- FooConstructed = 0;
- FooDestroyed = 0;
- Body body(nthread);
- tbb::concurrent_queue<Foo> queue;
-#if TBB_DEPRECATED
- queue.set_capacity( capacity );
-#endif
- body.queue = &queue;
- for( size_t i=0; i<prefill; ++i ) {
- Foo f;
- f.thread_id = nthread;
- f.serial = 1+int(i);
- queue.push(f);
- ASSERT( unsigned(queue.SIZE())==i+1, NULL );
- ASSERT( !queue.empty(), NULL );
- }
- tbb::tick_count t0 = tbb::tick_count::now();
- NativeParallelFor( nthread, body );
- tbb::tick_count t1 = tbb::tick_count::now();
- double timing = (t1-t0).seconds();
- REMARK("prefill=%d capacity=%d threads=%d time = %g = %g nsec/operation\n", int(prefill), int(capacity), nthread, timing, timing/(2*M*nthread)*1.E9);
- int sum = 0;
- for( int k=0; k<nthread; ++k )
- sum += Sum[k];
- int expected = int(nthread*((M-1)*M/2) + ((prefill-1)*prefill)/2);
- for( int i=int(prefill); --i>=0; ) {
- ASSERT( !queue.empty(), NULL );
- Foo f;
- bool result = queue.try_pop(f);
- ASSERT( result, NULL );
- ASSERT( int(queue.SIZE())==i, NULL );
- sum += f.serial-1;
- }
- ASSERT( queue.empty(), NULL );
- ASSERT( queue.SIZE()==0, NULL );
- if( sum!=expected )
- REPORT("sum=%d expected=%d\n",sum,expected);
- ASSERT( FooConstructed==FooDestroyed, NULL );
- // TODO: checks by counting allocators
-
- success = true;
- if( nthread>1 && prefill==0 ) {
- // Check that pop_if_present got sufficient exercise
- for( int k=0; k<2; ++k ) {
-#if (_WIN32||_WIN64)
- // The TBB library on Windows seems to have a tough time generating
- // the desired interleavings for pop_if_present, so the code tries longer, and settles
- // for fewer desired interleavings.
- const int max_trial = 100;
- const int min_requirement = 20;
-#else
- const int min_requirement = 100;
- const int max_trial = 20;
-#endif /* _WIN32||_WIN64 */
- if( PopKind[k]<min_requirement ) {
- if( trial>=max_trial ) {
- if( Verbose )
- REPORT("Warning: %d threads had only %ld pop_if_present operations %s after %d trials (expected at least %d). "
- "This problem may merely be unlucky scheduling. "
- "Investigate only if it happens repeatedly.\n",
- nthread, long(PopKind[k]), k==0?"failed":"succeeded", max_trial, min_requirement);
- else
- REPORT("Warning: the number of %s pop_if_present operations is less than expected for %d threads. Investigate if it happens repeatedly.\n",
- k==0?"failed":"succeeded", nthread );
-
- } else {
- success = false;
- }
- }
- }
- }
- }
-}
-
-class Bar {
- enum state_t {
- LIVE=0x1234,
- DEAD=0xDEAD
- };
- state_t state;
-public:
- ptrdiff_t my_id;
- Bar() : state(LIVE), my_id(-1) {}
- Bar(size_t _i) : state(LIVE), my_id(_i) {}
- Bar( const Bar& a_bar ) : state(LIVE) {
- ASSERT( a_bar.state==LIVE, NULL );
- my_id = a_bar.my_id;
- }
- ~Bar() {
- ASSERT( state==LIVE, NULL );
- state = DEAD;
- my_id = DEAD;
- }
- void operator=( const Bar& a_bar ) {
- ASSERT( a_bar.state==LIVE, NULL );
- ASSERT( state==LIVE, NULL );
- my_id = a_bar.my_id;
- }
- friend bool operator==(const Bar& bar1, const Bar& bar2 ) ;
-} ;
-
-bool operator==(const Bar& bar1, const Bar& bar2) {
- ASSERT( bar1.state==Bar::LIVE, NULL );
- ASSERT( bar2.state==Bar::LIVE, NULL );
- return bar1.my_id == bar2.my_id;
-}
-
-class BarIterator
-{
- Bar* bar_ptr;
- BarIterator(Bar* bp_) : bar_ptr(bp_) {}
-public:
- ~BarIterator() {}
- BarIterator& operator=( const BarIterator& other ) {
- bar_ptr = other.bar_ptr;
- return *this;
- }
- Bar& operator*() const {
- return *bar_ptr;
- }
- BarIterator& operator++() {
- ++bar_ptr;
- return *this;
- }
- Bar* operator++(int) {
- Bar* result = &operator*();
- operator++();
- return result;
- }
- friend bool operator==(const BarIterator& bia, const BarIterator& bib) ;
- friend bool operator!=(const BarIterator& bia, const BarIterator& bib) ;
- friend void TestConstructors ();
-} ;
-
-bool operator==(const BarIterator& bia, const BarIterator& bib) {
- return bia.bar_ptr==bib.bar_ptr;
-}
-
-bool operator!=(const BarIterator& bia, const BarIterator& bib) {
- return bia.bar_ptr!=bib.bar_ptr;
-}
-
-#if TBB_USE_EXCEPTIONS
-class Bar_exception : public std::bad_alloc {
-public:
- virtual const char *what() const throw() { return "making the entry invalid"; }
- virtual ~Bar_exception() throw() {}
-};
-
-class BarEx {
- enum state_t {
- LIVE=0x1234,
- DEAD=0xDEAD
- };
- static int count;
-public:
- state_t state;
- typedef enum {
- PREPARATION,
- COPY_CONSTRUCT
- } mode_t;
- static mode_t mode;
- ptrdiff_t my_id;
- ptrdiff_t my_tilda_id;
- static int button;
- BarEx() : state(LIVE), my_id(-1), my_tilda_id(-1) {}
- BarEx(size_t _i) : state(LIVE), my_id(_i), my_tilda_id(my_id^(-1)) {}
- BarEx( const BarEx& a_bar ) : state(LIVE) {
- ASSERT( a_bar.state==LIVE, NULL );
- my_id = a_bar.my_id;
- if( mode==PREPARATION )
- if( !( ++count % 100 ) )
- throw Bar_exception();
- my_tilda_id = a_bar.my_tilda_id;
- }
- ~BarEx() {
- ASSERT( state==LIVE, NULL );
- state = DEAD;
- my_id = DEAD;
- }
- static void set_mode( mode_t m ) { mode = m; }
- void operator=( const BarEx& a_bar ) {
- ASSERT( a_bar.state==LIVE, NULL );
- ASSERT( state==LIVE, NULL );
- my_id = a_bar.my_id;
- my_tilda_id = a_bar.my_tilda_id;
- }
- friend bool operator==(const BarEx& bar1, const BarEx& bar2 ) ;
-} ;
-
-int BarEx::count = 0;
-BarEx::mode_t BarEx::mode = BarEx::PREPARATION;
-
-bool operator==(const BarEx& bar1, const BarEx& bar2) {
- ASSERT( bar1.state==BarEx::LIVE, NULL );
- ASSERT( bar2.state==BarEx::LIVE, NULL );
- ASSERT( (bar1.my_id ^ bar1.my_tilda_id) == -1, NULL );
- ASSERT( (bar2.my_id ^ bar2.my_tilda_id) == -1, NULL );
- return bar1.my_id==bar2.my_id && bar1.my_tilda_id==bar2.my_tilda_id;
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-#if TBB_DEPRECATED
-#define CALL_BEGIN(q,i) (((i)&0x1)?q.begin():q.unsafe_begin())
-#define CALL_END(q,i) (((i)&0x1)?q.end():q.unsafe_end())
-#else
-#define CALL_BEGIN(q,i) q.unsafe_begin()
-#define CALL_END(q,i) q.unsafe_end()
-#endif
-
-void TestConstructors ()
-{
- tbb::concurrent_queue<Bar> src_queue;
- tbb::concurrent_queue<Bar>::const_iterator dqb;
- tbb::concurrent_queue<Bar>::const_iterator dqe;
- tbb::concurrent_queue<Bar>::const_iterator iter;
-
- for( size_t size=0; size<1001; ++size ) {
- for( size_t i=0; i<size; ++i )
- src_queue.push(Bar(i+(i^size)));
- tbb::concurrent_queue<Bar>::const_iterator sqb( CALL_BEGIN(src_queue,size) );
- tbb::concurrent_queue<Bar>::const_iterator sqe( CALL_END(src_queue,size));
-
- tbb::concurrent_queue<Bar> dst_queue(sqb, sqe);
-
- ASSERT(src_queue.SIZE()==dst_queue.SIZE(), "different size");
-
- src_queue.clear();
- }
-
- Bar bar_array[1001];
- for( size_t size=0; size<1001; ++size ) {
- for( size_t i=0; i<size; ++i )
- bar_array[i] = Bar(i+(i^size));
-
- const BarIterator sab(bar_array+0);
- const BarIterator sae(bar_array+size);
-
- tbb::concurrent_queue<Bar> dst_queue2(sab, sae);
-
- ASSERT( size==unsigned(dst_queue2.SIZE()), NULL );
- ASSERT( sab==BarIterator(bar_array+0), NULL );
- ASSERT( sae==BarIterator(bar_array+size), NULL );
-
- dqb = CALL_BEGIN(dst_queue2,size);
- dqe = CALL_END(dst_queue2,size);
- BarIterator v_iter(sab);
- for( ; dqb != dqe; ++dqb, ++v_iter )
- ASSERT( *dqb == *v_iter, "unexpected element" );
- ASSERT( v_iter==sae, "different size?" );
- }
-
- src_queue.clear();
-
- tbb::concurrent_queue<Bar> dst_queue3( src_queue );
- ASSERT( src_queue.SIZE()==dst_queue3.SIZE(), NULL );
- ASSERT( 0==dst_queue3.SIZE(), NULL );
-
- int k=0;
- for( size_t i=0; i<1001; ++i ) {
- Bar tmp_bar;
- src_queue.push(Bar(++k));
- src_queue.push(Bar(++k));
- src_queue.try_pop(tmp_bar);
-
- tbb::concurrent_queue<Bar> dst_queue4( src_queue );
-
- ASSERT( src_queue.SIZE()==dst_queue4.SIZE(), NULL );
-
- dqb = CALL_BEGIN(dst_queue4,i);
- dqe = CALL_END(dst_queue4,i);
- iter = CALL_BEGIN(src_queue,i);
-
- for( ; dqb != dqe; ++dqb, ++iter )
- ASSERT( *dqb == *iter, "unexpected element" );
-
- ASSERT( iter==CALL_END(src_queue,i), "different size?" );
- }
-
- tbb::concurrent_queue<Bar> dst_queue5( src_queue );
-
- ASSERT( src_queue.SIZE()==dst_queue5.SIZE(), NULL );
- dqb = dst_queue5.unsafe_begin();
- dqe = dst_queue5.unsafe_end();
- iter = src_queue.unsafe_begin();
- for( ; dqb != dqe; ++dqb, ++iter )
- ASSERT( *dqb == *iter, "unexpected element" );
-
- for( size_t i=0; i<100; ++i) {
- Bar tmp_bar;
- src_queue.push(Bar(i+1000));
- src_queue.push(Bar(i+1000));
- src_queue.try_pop(tmp_bar);
-
- dst_queue5.push(Bar(i+1000));
- dst_queue5.push(Bar(i+1000));
- dst_queue5.try_pop(tmp_bar);
- }
-
- ASSERT( src_queue.SIZE()==dst_queue5.SIZE(), NULL );
- dqb = dst_queue5.unsafe_begin();
- dqe = dst_queue5.unsafe_end();
- iter = src_queue.unsafe_begin();
- for( ; dqb != dqe; ++dqb, ++iter )
- ASSERT( *dqb == *iter, "unexpected element" );
- ASSERT( iter==src_queue.unsafe_end(), "different size?" );
-
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN || __TBB_PLACEMENT_NEW_EXCEPTION_SAFETY_BROKEN
- REPORT("Known issue: part of the constructor test is skipped.\n");
-#elif TBB_USE_EXCEPTIONS
- k = 0;
-#if TBB_DEPRECATED==0
- unsigned
-#endif
- int n_elements=0;
- tbb::concurrent_queue<BarEx> src_queue_ex;
- for( size_t size=0; size<1001; ++size ) {
- BarEx tmp_bar_ex;
- int n_successful_pushes=0;
- BarEx::set_mode( BarEx::PREPARATION );
- try {
- src_queue_ex.push(BarEx(k+(k^size)));
- ++n_successful_pushes;
- } catch (...) {
- }
- ++k;
- try {
- src_queue_ex.push(BarEx(k+(k^size)));
- ++n_successful_pushes;
- } catch (...) {
- }
- ++k;
- src_queue_ex.try_pop(tmp_bar_ex);
- n_elements += (n_successful_pushes - 1);
- ASSERT( src_queue_ex.SIZE()==n_elements, NULL);
-
- BarEx::set_mode( BarEx::COPY_CONSTRUCT );
- tbb::concurrent_queue<BarEx> dst_queue_ex( src_queue_ex );
-
- ASSERT( src_queue_ex.SIZE()==dst_queue_ex.SIZE(), NULL );
-
- tbb::concurrent_queue<BarEx>::const_iterator dqb_ex = CALL_BEGIN(dst_queue_ex, size);
- tbb::concurrent_queue<BarEx>::const_iterator dqe_ex = CALL_END(dst_queue_ex, size);
- tbb::concurrent_queue<BarEx>::const_iterator iter_ex = CALL_BEGIN(src_queue_ex, size);
-
- for( ; dqb_ex != dqe_ex; ++dqb_ex, ++iter_ex )
- ASSERT( *dqb_ex == *iter_ex, "unexpected element" );
- ASSERT( iter_ex==CALL_END(src_queue_ex,size), "different size?" );
- }
-#endif /* TBB_USE_EXCEPTIONS */
-}
-
-template<typename Iterator1, typename Iterator2>
-void TestIteratorAux( Iterator1 i, Iterator2 j, int size ) {
- // Now test iteration
- Iterator1 old_i;
- for( int k=0; k<size; ++k ) {
- ASSERT( i!=j, NULL );
- ASSERT( !(i==j), NULL );
- Foo f;
- if( k&1 ) {
- // Test pre-increment
- f = *old_i++;
- // Test assignment
- i = old_i;
- } else {
- // Test post-increment
- f=*i++;
- if( k<size-1 ) {
- // Test "->"
- ASSERT( k+2==i->serial, NULL );
- }
- // Test assignment
- old_i = i;
- }
- ASSERT( k+1==f.serial, NULL );
- }
- ASSERT( !(i!=j), NULL );
- ASSERT( i==j, NULL );
-}
-
-template<typename Iterator1, typename Iterator2>
-void TestIteratorAssignment( Iterator2 j ) {
- Iterator1 i(j);
- ASSERT( i==j, NULL );
- ASSERT( !(i!=j), NULL );
- Iterator1 k;
- k = j;
- ASSERT( k==j, NULL );
- ASSERT( !(k!=j), NULL );
-}
-
-template<typename Iterator, typename T>
-void TestIteratorTraits() {
- AssertSameType( static_cast<typename Iterator::difference_type*>(0), static_cast<ptrdiff_t*>(0) );
- AssertSameType( static_cast<typename Iterator::value_type*>(0), static_cast<T*>(0) );
- AssertSameType( static_cast<typename Iterator::pointer*>(0), static_cast<T**>(0) );
- AssertSameType( static_cast<typename Iterator::iterator_category*>(0), static_cast<std::forward_iterator_tag*>(0) );
- T x;
- typename Iterator::reference xr = x;
- typename Iterator::pointer xp = &x;
- ASSERT( &xr==xp, NULL );
-}
-
-//! Test the iterators for concurrent_queue
-void TestIterator() {
- tbb::concurrent_queue<Foo> queue;
- const tbb::concurrent_queue<Foo>& const_queue = queue;
- for( int j=0; j<500; ++j ) {
- TestIteratorAux( CALL_BEGIN(queue,j) , CALL_END(queue,j) , j );
- TestIteratorAux( CALL_BEGIN(const_queue,j), CALL_END(const_queue,j), j );
- TestIteratorAux( CALL_BEGIN(const_queue,j), CALL_END(queue,j) , j );
- TestIteratorAux( CALL_BEGIN(queue,j) , CALL_END(const_queue,j), j );
- Foo f;
- f.serial = j+1;
- queue.push(f);
- }
- TestIteratorAssignment<tbb::concurrent_queue<Foo>::const_iterator>( const_queue.unsafe_begin() );
- TestIteratorAssignment<tbb::concurrent_queue<Foo>::const_iterator>( queue.unsafe_begin() );
- TestIteratorAssignment<tbb::concurrent_queue<Foo>::iterator>( queue.unsafe_begin() );
- TestIteratorTraits<tbb::concurrent_queue<Foo>::const_iterator, const Foo>();
- TestIteratorTraits<tbb::concurrent_queue<Foo>::iterator, Foo>();
-}
-
-void TestConcurrentQueueType() {
- AssertSameType( tbb::concurrent_queue<Foo>::value_type(), Foo() );
- Foo f;
- const Foo g;
- tbb::concurrent_queue<Foo>::reference r = f;
- ASSERT( &r==&f, NULL );
- ASSERT( !r.is_const(), NULL );
- tbb::concurrent_queue<Foo>::const_reference cr = g;
- ASSERT( &cr==&g, NULL );
- ASSERT( cr.is_const(), NULL );
-}
-
-template<typename T>
-void TestEmptyQueue() {
- const tbb::concurrent_queue<T> queue;
- ASSERT( queue.SIZE()==0, NULL );
-#if TBB_DEPRECATED
- ASSERT( queue.capacity()>0, NULL );
- ASSERT( size_t(queue.capacity())>=size_t(-1)/(sizeof(void*)+sizeof(T)), NULL );
-#endif
-}
-
-#if TBB_DEPRECATED
-#define CALL_TRY_PUSH(q,f,i) (((i)&0x1)?(q).push_if_not_full(f):(q).try_push(f))
-void TestFullQueue() {
- for( int n=0; n<10; ++n ) {
- FooConstructed = 0;
- FooDestroyed = 0;
- tbb::concurrent_queue<Foo> queue;
- queue.set_capacity(n);
- for( int i=0; i<=n; ++i ) {
- Foo f;
- f.serial = i;
- bool result = CALL_TRY_PUSH(queue, f, i );
- ASSERT( result==(i<n), NULL );
- }
- for( int i=0; i<=n; ++i ) {
- Foo f;
- bool result = queue.pop_if_present( f );
- ASSERT( result==(i<n), NULL );
- ASSERT( !result || f.serial==i, NULL );
- }
- ASSERT( FooConstructed==FooDestroyed, NULL );
- }
-}
-#endif /* if TBB_DEPRECATED */
-
-#if TBB_DEPRECATED
-#define CALL_PUSH_IF_NOT_FULL(q,v,i) (((i)&0x1)?q.push_if_not_full(v):(q.push(v), true))
-#else
-#define CALL_PUSH_IF_NOT_FULL(q,v,i) (q.push(v), true)
-#endif
-
-void TestClear() {
- FooConstructed = 0;
- FooDestroyed = 0;
- const unsigned int n=5;
-
- tbb::concurrent_queue<Foo> queue;
-#if TBB_DEPRECATED
- const int q_capacity=10;
- queue.set_capacity(q_capacity);
-#endif
- for( size_t i=0; i<n; ++i ) {
- Foo f;
- f.serial = int(i);
- bool result = CALL_PUSH_IF_NOT_FULL(queue, f, i);
- ASSERT( result, NULL );
- }
- ASSERT( unsigned(queue.SIZE())==n, NULL );
- queue.clear();
- ASSERT( queue.SIZE()==0, NULL );
- for( size_t i=0; i<n; ++i ) {
- Foo f;
- f.serial = int(i);
- bool result = CALL_PUSH_IF_NOT_FULL(queue, f, i);
- ASSERT( result, NULL );
- }
- ASSERT( unsigned(queue.SIZE())==n, NULL );
- queue.clear();
- ASSERT( queue.SIZE()==0, NULL );
- for( size_t i=0; i<n; ++i ) {
- Foo f;
- f.serial = int(i);
- bool result = CALL_PUSH_IF_NOT_FULL(queue, f, i);
- ASSERT( result, NULL );
- }
- ASSERT( unsigned(queue.SIZE())==n, NULL );
-}
-
-#if TBB_DEPRECATED
-template<typename T>
-struct TestNegativeQueueBody: NoAssign {
- tbb::concurrent_queue<T>& queue;
- const int nthread;
- TestNegativeQueueBody( tbb::concurrent_queue<T>& q, int n ) : queue(q), nthread(n) {}
- void operator()( int k ) const {
- if( k==0 ) {
- int number_of_pops = nthread-1;
- // Wait for all pops to pend.
- while( queue.size()>-number_of_pops ) {
- __TBB_Yield();
- }
- for( int i=0; ; ++i ) {
- ASSERT( queue.size()==i-number_of_pops, NULL );
- ASSERT( queue.empty()==(queue.size()<=0), NULL );
- if( i==number_of_pops ) break;
- // Satisfy another pop
- queue.push( T() );
- }
- } else {
- // Pop item from queue
- T item;
- queue.pop(item);
- }
- }
-};
-
-//! Test a queue with a negative size.
-template<typename T>
-void TestNegativeQueue( int nthread ) {
- tbb::concurrent_queue<T> queue;
- NativeParallelFor( nthread, TestNegativeQueueBody<T>(queue,nthread) );
-}
-#endif /* if TBB_DEPRECATED */
-
-#if TBB_USE_EXCEPTIONS
-void TestExceptions() {
- typedef static_counting_allocator<std::allocator<FooEx>, size_t> allocator_t;
- typedef static_counting_allocator<std::allocator<char>, size_t> allocator_char_t;
- typedef tbb::concurrent_queue<FooEx, allocator_t> concur_queue_t;
-
- enum methods {
- m_push = 0,
- m_pop
- };
-
- REMARK("Testing exception safety\n");
- // verify 'clear()' on exception; queue's destructor calls its clear()
- // Do test on queues of two different types at the same time to
- // catch problem with incorrect sharing between templates.
- {
- concur_queue_t queue0;
- tbb::concurrent_queue<int,allocator_t> queue1;
- for( int i=0; i<2; ++i ) {
- bool caught = false;
- try {
- allocator_char_t::init_counters();
- allocator_char_t::set_limits(N/2);
- for( int k=0; k<N; k++ ) {
- if( i==0 )
- queue0.push( FooEx() );
- else
- queue1.push( k );
- }
- } catch (...) {
- caught = true;
- }
- ASSERT( caught, "call to push should have thrown exception" );
- }
- }
- REMARK("... queue destruction test passed\n");
-
- try {
- int n_pushed=0, n_popped=0;
- for(int t = 0; t <= 1; t++)// exception type -- 0 : from allocator(), 1 : from Foo's constructor
- {
- concur_queue_t queue_test;
- for( int m=m_push; m<=m_pop; m++ ) {
- // concurrent_queue internally rebinds the allocator to one with 'char'
- allocator_char_t::init_counters();
-
- if(t) MaxFooCount = MaxFooCount + 400;
- else allocator_char_t::set_limits(N/2);
-
- try {
- switch(m) {
- case m_push:
- for( int k=0; k<N; k++ ) {
- queue_test.push( FooEx() );
- n_pushed++;
- }
- break;
- case m_pop:
- n_popped=0;
- for( int k=0; k<n_pushed; k++ ) {
- FooEx elt;
- queue_test.try_pop( elt );
- n_popped++;
- }
- n_pushed = 0;
- allocator_char_t::set_limits();
- break;
- }
- if( !t && m==m_push ) ASSERT(false, "should throw an exception");
- } catch ( Foo_exception & ) {
- switch(m) {
- case m_push: {
- ASSERT( ptrdiff_t(queue_test.SIZE())==n_pushed, "incorrect queue size" );
- long tc = MaxFooCount;
- MaxFooCount = 0;
- for( int k=0; k<(int)tc; k++ ) {
- queue_test.push( FooEx() );
- n_pushed++;
- }
- MaxFooCount = tc;
- }
- break;
- case m_pop:
- MaxFooCount = 0; // disable exception
- n_pushed -= (n_popped+1); // including one that threw an exception
- ASSERT( n_pushed>=0, "n_pushed cannot be less than 0" );
- for( int k=0; k<1000; k++ ) {
- queue_test.push( FooEx() );
- n_pushed++;
- }
- ASSERT( !queue_test.empty(), "queue must not be empty" );
- ASSERT( ptrdiff_t(queue_test.SIZE())==n_pushed, "queue size must be equal to n pushed" );
- for( int k=0; k<n_pushed; k++ ) {
- FooEx elt;
- queue_test.try_pop( elt );
- }
- ASSERT( queue_test.empty(), "queue must be empty" );
- ASSERT( queue_test.SIZE()==0, "queue must be empty" );
- break;
- }
- } catch ( std::bad_alloc & ) {
- allocator_char_t::set_limits(); // disable exception from allocator
- size_t size = queue_test.SIZE();
- switch(m) {
- case m_push:
- ASSERT( size>0, "incorrect queue size");
- break;
- case m_pop:
- if( !t ) ASSERT( false, "should not throw an exceptin" );
- break;
- }
- }
- REMARK("... for t=%d and m=%d, exception test passed\n", t, m);
- }
- }
- } catch(...) {
- ASSERT(false, "unexpected exception");
- }
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-template<typename T>
-struct TestQueueElements: NoAssign {
- tbb::concurrent_queue<T>& queue;
- const int nthread;
- TestQueueElements( tbb::concurrent_queue<T>& q, int n ) : queue(q), nthread(n) {}
- void operator()( int k ) const {
- for( int i=0; i<1000; ++i ) {
- if( (i&0x1)==0 ) {
- __TBB_ASSERT( T(k)<T(nthread), NULL );
- queue.push( T(k) );
- } else {
- // Pop item from queue
- T item;
- queue.try_pop(item);
- __TBB_ASSERT( item<=T(nthread), NULL );
- }
- }
- }
-};
-
-//! Test concurrent queue with primitive data type
-template<typename T>
-void TestPrimitiveTypes( int nthread, T exemplar )
-{
- tbb::concurrent_queue<T> queue;
- for( int i=0; i<100; ++i )
- queue.push( exemplar );
- NativeParallelFor( nthread, TestQueueElements<T>(queue,nthread) );
-}
-
-#include "harness_m128.h"
-
-#if HAVE_m128
-
-//! Test concurrent queue with SSE type
-/** Type Queue should be a queue of ClassWithSSE. */
-template<typename Queue>
-void TestSSE() {
- Queue q1;
- for( int i=0; i<100; ++i )
- q1.push(ClassWithSSE(i));
-
- // Copy the queue
- Queue q2 = q1;
- // Check that elements of the copy are correct
- typename Queue::const_iterator ci = q2.unsafe_begin();
- for( int i=0; i<100; ++i ) {
- ClassWithSSE foo = *ci;
- ASSERT( *ci==ClassWithSSE(i), NULL );
- ++ci;
- }
-
- for( int i=0; i<101; ++i ) {
- ClassWithSSE tmp;
- bool b = q1.try_pop( tmp );
- ASSERT( b==(i<100), NULL );
- ASSERT( !b || tmp==ClassWithSSE(i), NULL );
- }
-}
-#endif /* HAVE_m128 */
-
-int TestMain () {
- TestEmptyQueue<char>();
- TestEmptyQueue<Foo>();
-#if TBB_DEPRECATED
- TestFullQueue();
-#endif
- TestClear();
- TestConcurrentQueueType();
- TestIterator();
- TestConstructors();
-
- TestPrimitiveTypes( MaxThread, (char)1 );
- TestPrimitiveTypes( MaxThread, (int)-12 );
- TestPrimitiveTypes( MaxThread, (float)-1.2f );
- TestPrimitiveTypes( MaxThread, (double)-4.3 );
-#if HAVE_m128
- TestSSE<tbb::concurrent_queue<ClassWithSSE> >();
- TestSSE<tbb::concurrent_bounded_queue<ClassWithSSE> >();
-#endif /* HAVE_m128 */
-
- // Test concurrent operations
- for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) {
-#if TBB_DEPRECATED
- TestNegativeQueue<Foo>(nthread);
-#endif
- for( size_t prefill=0; prefill<64; prefill+=(1+prefill/3) ) {
- TestPushPop(prefill,ptrdiff_t(-1),nthread);
- TestPushPop(prefill,ptrdiff_t(1),nthread);
- TestPushPop(prefill,ptrdiff_t(2),nthread);
- TestPushPop(prefill,ptrdiff_t(10),nthread);
- TestPushPop(prefill,ptrdiff_t(100),nthread);
- }
- }
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception safety test is skipped.\n");
-#elif TBB_USE_EXCEPTIONS
- TestExceptions();
-#endif /* TBB_USE_EXCEPTIONS */
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-/* Some tests in this source file are based on PPL tests provided by Microsoft. */
-
-#define __TBB_EXTRA_DEBUG 1
-#include "tbb/concurrent_unordered_map.h"
-#include "tbb/parallel_for.h"
-#include "tbb/tick_count.h"
-#include <stdio.h>
-#include "harness.h"
-#include "harness_allocator.h"
-
-using namespace std;
-
-typedef local_counting_allocator<debug_allocator<std::pair<const int,int>,std::allocator> > MyAllocator;
-typedef tbb::concurrent_unordered_map<int, int, tbb::tbb_hash<int>, std::equal_to<int>, MyAllocator> Mycumap;
-//typedef tbb::concurrent_unordered_map<int, int> Mycumap;
-//typedef concurrent_unordered_multimap<int, int> Mycummap;
-
-#define CheckAllocatorE(t,a,f) CheckAllocator(t,a,f,true,__LINE__)
-#define CheckAllocatorA(t,a,f) CheckAllocator(t,a,f,false,__LINE__)
-template<typename MyTable>
-inline void CheckAllocator(MyTable &table, size_t expected_allocs, size_t expected_frees, bool exact = true, int line = 0) {
- typename MyTable::allocator_type a = table.get_allocator();
- REMARK("#%d checking allocators: items %u/%u, allocs %u/%u\n", line,
- unsigned(a.items_allocated), unsigned(a.items_freed), unsigned(a.allocations), unsigned(a.frees) );
- ASSERT( a.items_allocated == a.allocations, NULL); ASSERT( a.items_freed == a.frees, NULL);
- if(exact) {
- ASSERT( a.allocations == expected_allocs, NULL); ASSERT( a.frees == expected_frees, NULL);
- } else {
- ASSERT( a.allocations >= expected_allocs, NULL); ASSERT( a.frees >= expected_frees, NULL);
- ASSERT( a.allocations - a.frees == expected_allocs - expected_frees, NULL );
- }
-}
-
-template <typename K, typename V = std::pair<const K, K> >
-struct ValueFactory {
- static V make(const K &value) { return V(value, value); }
- static K get(const V& value) { return value.second; }
-};
-
-template <typename T>
-struct ValueFactory<T, T> {
- static T make(const T &value) { return value; }
- static T get(const T &value) { return value; }
-};
-
-template <typename T>
-struct Value : ValueFactory<typename T::key_type, typename T::value_type> {};
-
-#if _MSC_VER
-#pragma warning(disable: 4189) // warning 4189 -- local variable is initialized but not referenced
-#pragma warning(disable: 4127) // warning 4127 -- while (true) has a constant expression in it
-#endif
-
-template<typename Iterator, typename RangeType>
-std::pair<int,int> CheckRecursiveRange(RangeType range) {
- std::pair<int,int> sum(0, 0); // count, sum
- for( Iterator i = range.begin(), e = range.end(); i != e; ++i ) {
- ++sum.first; sum.second += i->second;
- }
- if( range.is_divisible() ) {
- RangeType range2( range, tbb::split() );
- std::pair<int,int> sum1 = CheckRecursiveRange<Iterator, RangeType>( range );
- std::pair<int,int> sum2 = CheckRecursiveRange<Iterator, RangeType>( range2 );
- sum1.first += sum2.first; sum1.second += sum2.second;
- ASSERT( sum == sum1, "Mismatched ranges after division");
- }
- return sum;
-}
-
-template <typename T>
-struct SpecialTests {
- static void Test() {}
-};
-
-template <>
-struct SpecialTests <Mycumap>
-{
- static void Test()
- {
- Mycumap cont(0);
- const Mycumap &ccont(cont);
-
- // mapped_type& operator[](const key_type& k);
- cont[1] = 2;
-
- // bool empty() const;
- ASSERT(!ccont.empty(), "Concurrent container empty after adding an element");
-
- // size_type size() const;
- ASSERT(ccont.size() == 1, "Concurrent container size incorrect");
-
- ASSERT(cont[1] == 2, "Concurrent container size incorrect");
-
- // mapped_type& at( const key_type& k );
- // const mapped_type& at(const key_type& k) const;
- ASSERT(cont.at(1) == 2, "Concurrent container size incorrect");
- ASSERT(ccont.at(1) == 2, "Concurrent container size incorrect");
-
- // iterator find(const key_type& k);
- Mycumap::const_iterator it = cont.find(1);
- ASSERT(it != cont.end() && Value<Mycumap>::get(*(it)) == 2, "Element with key 1 not properly found");
-
- REMARK("passed -- specialized concurrent unordered map tests\n");
- }
-};
-
-template<typename T>
-void test_basic(const char * str)
-{
- T cont;
- const T &ccont(cont);
-
- // bool empty() const;
- ASSERT(ccont.empty(), "Concurrent container not empty after construction");
-
- // size_type size() const;
- ASSERT(ccont.size() == 0, "Concurrent container not empty after construction");
-
- // size_type max_size() const;
- ASSERT(ccont.max_size() > 0, "Concurrent container max size invalid");
-
- //iterator begin();
- //iterator end();
- ASSERT(cont.begin() == cont.end(), "Concurrent container iterators invalid after construction");
- ASSERT(ccont.begin() == ccont.end(), "Concurrent container iterators invalid after construction");
- ASSERT(cont.cbegin() == cont.cend(), "Concurrent container iterators invalid after construction");
-
- //std::pair<iterator, bool> insert(const value_type& obj);
- std::pair<typename T::iterator, bool> ins = cont.insert(Value<T>::make(1));
- ASSERT(ins.second == true && Value<T>::get(*(ins.first)) == 1, "Element 1 not properly inserted");
-
- // bool empty() const;
- ASSERT(!ccont.empty(), "Concurrent container empty after adding an element");
-
- // size_type size() const;
- ASSERT(ccont.size() == 1, "Concurrent container size incorrect");
-
- std::pair<typename T::iterator, bool> ins2 = cont.insert(Value<T>::make(1));
-
- if (T::allow_multimapping)
- {
- // std::pair<iterator, bool> insert(const value_type& obj);
- ASSERT(ins2.second == true && Value<T>::get(*(ins2.first)) == 1, "Element 1 not properly inserted");
-
- // size_type size() const;
- ASSERT(ccont.size() == 2, "Concurrent container size incorrect");
-
- // size_type count(const key_type& k) const;
- ASSERT(ccont.count(1) == 2, "Concurrent container count(1) incorrect");
-
- // std::pair<iterator, iterator> equal_range(const key_type& k);
- std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1);
- typename T::iterator it = range.first;
- ASSERT(it != cont.end() && Value<T>::get(*it) == 1, "Element 1 not properly found");
- unsigned int count = 0;
- for (; it != range.second; it++)
- {
- count++;
- ASSERT(Value<T>::get(*it) == 1, "Element 1 not properly found");
- }
-
- ASSERT(count == 2, "Range doesn't have the right number of elements");
- }
- else
- {
- // std::pair<iterator, bool> insert(const value_type& obj);
- ASSERT(ins2.second == false && ins2.first == ins.first, "Element 1 should not be re-inserted");
-
- // size_type size() const;
- ASSERT(ccont.size() == 1, "Concurrent container size incorrect");
-
- // size_type count(const key_type& k) const;
- ASSERT(ccont.count(1) == 1, "Concurrent container count(1) incorrect");
-
- // std::pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
- // std::pair<iterator, iterator> equal_range(const key_type& k);
- std::pair<typename T::iterator, typename T::iterator> range = cont.equal_range(1);
- typename T::iterator i = range.first;
- ASSERT(i != cont.end() && Value<T>::get(*i) == 1, "Element 1 not properly found");
- ASSERT(++i == range.second, "Range doesn't have the right number of elements");
- }
-
- // const_iterator find(const key_type& k) const;
- // iterator find(const key_type& k);
- typename T::iterator it = cont.find(1);
- ASSERT(it != cont.end() && Value<T>::get(*(it)) == 1, "Element 1 not properly found");
- ASSERT(ccont.find(1) == it, "Element 1 not properly found");
-
- // iterator insert(const_iterator hint, const value_type& obj);
- typename T::iterator it2 = cont.insert(ins.first, Value<T>::make(2));
- ASSERT(Value<T>::get(*it2) == 2, "Element 2 not properly inserted");
-
- // T(const T& _Umap)
- T newcont = ccont;
- ASSERT(T::allow_multimapping ? (newcont.size() == 3) : (newcont.size() == 2), "Copy construction did not copy the elements properly");
-
- // size_type unsafe_erase(const key_type& k);
- typename T::size_type size = cont.unsafe_erase(1);
- ASSERT(T::allow_multimapping ? (size == 2) : (size == 1), "Erase did not remove the right number of elements");
-
- // iterator unsafe_erase(const_iterator position);
- typename T::iterator it4 = cont.unsafe_erase(cont.find(2));
- ASSERT(it4 == cont.end() && cont.size() == 0, "Erase did not remove the last element properly");
-
- // template<class InputIterator> void insert(InputIterator first, InputIterator last);
- cont.insert(newcont.begin(), newcont.end());
- ASSERT(T::allow_multimapping ? (cont.size() == 3) : (cont.size() == 2), "Range insert did not copy the elements properly");
-
- // iterator unsafe_erase(const_iterator first, const_iterator last);
- std::pair<typename T::iterator, typename T::iterator> range2 = newcont.equal_range(1);
- newcont.unsafe_erase(range2.first, range2.second);
- ASSERT(newcont.size() == 1, "Range erase did not erase the elements properly");
-
- // void clear();
- newcont.clear();
- ASSERT(newcont.begin() == newcont.end() && newcont.size() == 0, "Clear did not clear the container");
-
- // T& operator=(const T& _Umap)
- newcont = ccont;
- ASSERT(T::allow_multimapping ? (newcont.size() == 3) : (newcont.size() == 2), "Assignment operator did not copy the elements properly");
-
- // void rehash(size_type n);
- newcont.rehash(16);
- ASSERT(T::allow_multimapping ? (newcont.size() == 3) : (newcont.size() == 2), "Rehash should not affect the container elements");
-
- // float load_factor() const;
- // float max_load_factor() const;
- ASSERT(ccont.load_factor() <= ccont.max_load_factor(), "Load factor invalid");
-
- // void max_load_factor(float z);
- cont.max_load_factor(16.0f);
- ASSERT(ccont.max_load_factor() == 16.0f, "Max load factor not properly changed");
-
- // hasher hash_function() const;
- ccont.hash_function();
-
- // key_equal key_eq() const;
- ccont.key_eq();
-
- cont.clear();
- CheckAllocatorA(cont, 1, 0); // one dummy is always allocated
- for (int i = 0; i < 256; i++)
- {
- std::pair<typename T::iterator, bool> ins3 = cont.insert(Value<T>::make(i));
- ASSERT(ins3.second == true && Value<T>::get(*(ins3.first)) == i, "Element 1 not properly inserted");
- }
- ASSERT(cont.size() == 256, "Wrong number of elements inserted");
- ASSERT(256 == CheckRecursiveRange<typename T::iterator>(cont.range()).first, NULL);
- ASSERT(256 == CheckRecursiveRange<typename T::const_iterator>(ccont.range()).first, NULL);
-
- // size_type unsafe_bucket_count() const;
- ASSERT(ccont.unsafe_bucket_count() == 16, "Wrong number of buckets");
-
- // size_type unsafe_max_bucket_count() const;
- ASSERT(ccont.unsafe_max_bucket_count() > 65536, "Wrong max number of buckets");
-
- for (unsigned int i = 0; i < 256; i++)
- {
- typename T::size_type buck = ccont.unsafe_bucket(i);
-
- // size_type unsafe_bucket(const key_type& k) const;
- ASSERT(buck < 16, "Wrong bucket mapping");
- }
-
- for (unsigned int i = 0; i < 16; i++)
- {
- // size_type unsafe_bucket_size(size_type n);
- ASSERT(cont.unsafe_bucket_size(i) == 16, "Wrong number elements in a bucket");
-
- // local_iterator unsafe_begin(size_type n);
- // const_local_iterator unsafe_begin(size_type n) const;
- // local_iterator unsafe_end(size_type n);
- // const_local_iterator unsafe_end(size_type n) const;
- // const_local_iterator unsafe_cbegin(size_type n) const;
- // const_local_iterator unsafe_cend(size_type n) const;
- unsigned int count = 0;
- for (typename T::iterator bit = cont.unsafe_begin(i); bit != cont.unsafe_end(i); bit++)
- {
- count++;
- }
- ASSERT(count == 16, "Bucket iterators are invalid");
- }
-
- // void swap(T&);
- cont.swap(newcont);
- ASSERT(newcont.size() == 256, "Wrong number of elements after swap");
- ASSERT(newcont.count(200) == 1, "Element with key 200 not present after swap");
- ASSERT(newcont.count(16) == 1, "Element with key 16 not present after swap");
- ASSERT(newcont.count(99) == 1, "Element with key 99 not present after swap");
- ASSERT(T::allow_multimapping ? (cont.size() == 3) : (cont.size() == 2), "Wrong number of elements after swap");
-
- REMARK("passed -- basic %S tests\n", str);
-
-#if defined (VERBOSE)
- REMARK("container dump debug:\n");
- cont._Dump();
- REMARK("container dump release:\n");
- cont.dump();
- REMARK("\n");
-#endif
-
- SpecialTests<T>::Test();
-}
-
-void test_machine() {
- ASSERT(__TBB_ReverseByte(0)==0, NULL );
- ASSERT(__TBB_ReverseByte(1)==0x80, NULL );
- ASSERT(__TBB_ReverseByte(0xFE)==0x7F, NULL );
- ASSERT(__TBB_ReverseByte(0xFF)==0xFF, NULL );
-}
-
-template<typename T>
-class FillTable: NoAssign {
- T &table;
- const int items;
- typedef std::pair<typename T::iterator, bool> pairIB;
-public:
- FillTable(T &t, int i) : table(t), items(i) {
- ASSERT( !(items&1) && items > 100, NULL);
- }
- void operator()(int threadn) const {
- if( threadn == 0 ) { // Fill even keys forward (single thread)
- bool last_inserted = true;
- for( int i = 0; i < items; i+=2 ) {
- pairIB pib = table.insert(Value<T>::make(i));
- ASSERT(Value<T>::get(*(pib.first)) == i, "Element not properly inserted");
- ASSERT( last_inserted || !pib.second, "Previous key was not inserted but this is inserted" );
- last_inserted = pib.second;
- }
- } else if( threadn == 1 ) { // Fill even keys backward (single thread)
- bool last_inserted = true;
- for( int i = items-2; i >= 0; i-=2 ) {
- pairIB pib = table.insert(Value<T>::make(i));
- ASSERT(Value<T>::get(*(pib.first)) == i, "Element not properly inserted");
- ASSERT( last_inserted || !pib.second, "Previous key was not inserted but this is inserted" );
- last_inserted = pib.second;
- }
- } else if( !(threadn&1) ) { // Fill odd keys forward (multiple threads)
- for( int i = 1; i < items; i+=2 ) {
- pairIB pib = table.insert(Value<T>::make(i));
- ASSERT(Value<T>::get(*(pib.first)) == i, "Element not properly inserted");
- }
- } else { // Check odd keys backward (multiple threads)
- bool last_found = false;
- for( int i = items-1; i >= 0; i-=2 ) {
- typename T::iterator it = table.find(i);
- if( it != table.end() ) { // found
- ASSERT(Value<T>::get(*it) == i, "Element not properly inserted");
- last_found = true;
- } else ASSERT( !last_found, "Previous key was found but this is not" );
- }
- }
- }
-};
-
-typedef tbb::atomic<unsigned char> AtomicByte;
-
-template<typename RangeType>
-struct ParallelTraverseBody: NoAssign {
- const int n;
- AtomicByte* const array;
- ParallelTraverseBody( AtomicByte an_array[], int a_n ) :
- n(a_n), array(an_array)
- {}
- void operator()( const RangeType& range ) const {
- for( typename RangeType::iterator i = range.begin(); i!=range.end(); ++i ) {
- int k = i->first;
- ASSERT( k == i->second, NULL );
- ASSERT( 0<=k && k<n, NULL );
- array[k]++;
- }
- }
-};
-
-void CheckRange( AtomicByte array[], int n ) {
- for( int k=0; k<n; ++k ) {
- if( array[k] != 1 ) {
- REPORT("array[%d]=%d\n", k, int(array[k]));
- ASSERT(false,NULL);
- }
- }
-}
-
-template<typename T>
-class CheckTable: NoAssign {
- T &table;
-public:
- CheckTable(T &t) : NoAssign(), table(t) {}
- void operator()(int i) const {
- int c = (int)table.count( i );
- ASSERT( c, "must exist" );
- }
-};
-
-template<typename T>
-void test_concurrent(const char *tablename) {
-#if TBB_USE_ASSERT
- int items = 2000;
-#else
- int items = 100000;
-#endif
- T table(items/1000);
- tbb::tick_count t0 = tbb::tick_count::now();
- NativeParallelFor( 16/*min 6*/, FillTable<T>(table, items) );
- tbb::tick_count t1 = tbb::tick_count::now();
- REMARK( "time for filling '%s' by %d items = %g\n", tablename, items, (t1-t0).seconds() );
- ASSERT( int(table.size()) == items, NULL);
-
- AtomicByte* array = new AtomicByte[items];
- memset( array, 0, items*sizeof(AtomicByte) );
-
- typename T::range_type r = table.range();
- ASSERT(items == CheckRecursiveRange<typename T::iterator>(r).first, NULL);
- tbb::parallel_for( r, ParallelTraverseBody<typename T::const_range_type>( array, items ));
- CheckRange( array, items );
-
- const T &const_table = table;
- memset( array, 0, items*sizeof(AtomicByte) );
- typename T::const_range_type cr = const_table.range();
- ASSERT(items == CheckRecursiveRange<typename T::const_iterator>(cr).first, NULL);
- tbb::parallel_for( cr, ParallelTraverseBody<typename T::const_range_type>( array, items ));
- CheckRange( array, items );
- delete[] array;
-
- tbb::parallel_for( 0, items, CheckTable<T>( table ) );
-
- table.clear();
- CheckAllocatorA(table, items+1, items); // one dummy is always allocated
-}
-
-int TestMain () {
- test_machine();
- test_basic<Mycumap>("concurrent unordered map");
- test_concurrent<Mycumap>("concurrent unordered map");
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/concurrent_vector.h"
-#include "tbb/tbb_allocator.h"
-#include "tbb/cache_aligned_allocator.h"
-#include "tbb/tbb_exception.h"
-#include <cstdio>
-#include <cstdlib>
-#include "harness_report.h"
-#include "harness_assert.h"
-#include "harness_allocator.h"
-
-#if TBB_USE_EXCEPTIONS
-static bool known_issue_verbose = false;
-#define KNOWN_ISSUE(msg) if(!known_issue_verbose) known_issue_verbose = true, REPORT(msg)
-#endif /* TBB_USE_EXCEPTIONS */
-
-tbb::atomic<long> FooCount;
-long MaxFooCount = 0;
-
-//! Problem size
-const size_t N = 500000;
-
-//! Exception for concurrent_vector
-class Foo_exception : public std::bad_alloc {
-public:
- virtual const char *what() const throw() { return "out of Foo limit"; }
- virtual ~Foo_exception() throw() {}
-};
-
-static const int initial_value_of_bar = 42;
-struct Foo {
- int my_bar;
-public:
- enum State {
- ZeroInitialized=0,
- DefaultInitialized=0xDEFAUL,
- CopyInitialized=0xC0314,
- Destroyed=0xDEADF00
- } state;
- bool is_valid() const {
- return state==DefaultInitialized||state==CopyInitialized;
- }
- bool is_valid_or_zero() const {
- return is_valid()||(state==ZeroInitialized && !my_bar);
- }
- int& zero_bar() {
- ASSERT( is_valid_or_zero(), NULL );
- return my_bar;
- }
- int& bar() {
- ASSERT( is_valid(), NULL );
- return my_bar;
- }
- int bar() const {
- ASSERT( is_valid(), NULL );
- return my_bar;
- }
- Foo( int barr = initial_value_of_bar ) {
- my_bar = barr;
- if(MaxFooCount && FooCount >= MaxFooCount)
- __TBB_THROW( Foo_exception() );
- FooCount++;
- state = DefaultInitialized;
- }
- Foo( const Foo& foo ) {
- my_bar = foo.my_bar;
- ASSERT( foo.is_valid_or_zero(), "bad source for copy" );
- if(MaxFooCount && FooCount >= MaxFooCount)
- __TBB_THROW( Foo_exception() );
- FooCount++;
- state = CopyInitialized;
- }
- ~Foo() {
- ASSERT( is_valid_or_zero(), NULL );
- my_bar = ~initial_value_of_bar;
- if(state != ZeroInitialized) --FooCount;
- state = Destroyed;
- }
- bool operator==(const Foo &f) const { return my_bar == f.my_bar; }
- bool operator<(const Foo &f) const { return my_bar < f.my_bar; }
- bool is_const() const {return true;}
- bool is_const() {return false;}
-protected:
- char reserve[1];
- void operator=( const Foo& ) {}
-};
-
-class FooWithAssign: public Foo {
-public:
- void operator=( const FooWithAssign& x ) {
- my_bar = x.my_bar;
- ASSERT( x.is_valid_or_zero(), "bad source for assignment" );
- ASSERT( is_valid(), NULL );
- }
- bool operator==(const Foo &f) const { return my_bar == f.my_bar; }
- bool operator<(const Foo &f) const { return my_bar < f.my_bar; }
-};
-
-class FooIterator: public std::iterator<std::input_iterator_tag,FooWithAssign> {
- int x_bar;
-public:
- FooIterator(int x) {
- x_bar = x;
- }
- FooIterator &operator++() {
- x_bar++; return *this;
- }
- FooWithAssign operator*() {
- FooWithAssign foo; foo.bar() = x_bar;
- return foo;
- }
- bool operator!=(const FooIterator &i) { return x_bar != i.x_bar; }
-};
-
-inline void NextSize( int& s ) {
- if( s<=32 ) ++s;
- else s += s/10;
-}
-
-//! Check vector have expected size and filling
-template<typename vector_t>
-static void CheckVector( const vector_t& cv, size_t expected_size, size_t old_size ) {
- ASSERT( cv.capacity()>=expected_size, NULL );
- ASSERT( cv.size()==expected_size, NULL );
- ASSERT( cv.empty()==(expected_size==0), NULL );
- for( int j=0; j<int(expected_size); ++j ) {
- if( cv[j].bar()!=~j )
- REPORT("ERROR on line %d for old_size=%ld expected_size=%ld j=%d\n",__LINE__,long(old_size),long(expected_size),j);
- }
-}
-
-//! Test of assign, grow, copying with various sizes
-void TestResizeAndCopy() {
- typedef static_counting_allocator<debug_allocator<Foo,std::allocator>, std::size_t> allocator_t;
- typedef tbb::concurrent_vector<Foo, allocator_t> vector_t;
- allocator_t::init_counters();
- for( int old_size=0; old_size<=128; NextSize( old_size ) ) {
- for( int new_size=0; new_size<=1280; NextSize( new_size ) ) {
- long count = FooCount;
- vector_t v;
- ASSERT( count==FooCount, NULL );
- v.assign(old_size/2, Foo() );
- ASSERT( count+old_size/2==FooCount, NULL );
- for( int j=0; j<old_size/2; ++j )
- ASSERT( v[j].state == Foo::CopyInitialized, NULL);
- v.assign(FooIterator(0), FooIterator(old_size));
- v.resize(new_size, Foo(33) );
- ASSERT( count+new_size==FooCount, NULL );
- for( int j=0; j<new_size; ++j ) {
- int expected = j<old_size ? j : 33;
- if( v[j].bar()!=expected )
- REPORT("ERROR on line %d for old_size=%ld new_size=%ld v[%ld].bar()=%d != %d\n",__LINE__,long(old_size),long(new_size),long(j),v[j].bar(), expected);
- }
- ASSERT( v.size()==size_t(new_size), NULL );
- for( int j=0; j<new_size; ++j ) {
- v[j].bar() = ~j;
- }
- const vector_t& cv = v;
- // Try copy constructor
- vector_t copy_of_v(cv);
- CheckVector(cv,new_size,old_size);
- ASSERT( !(v != copy_of_v), NULL );
- v.clear();
- ASSERT( v.empty(), NULL );
- swap(v, copy_of_v);
- ASSERT( copy_of_v.empty(), NULL );
- CheckVector(v,new_size,old_size);
- }
- }
- ASSERT( allocator_t::items_allocated == allocator_t::items_freed, NULL);
- ASSERT( allocator_t::allocations == allocator_t::frees, NULL);
-}
-
-//! Test reserve, compact, capacity
-void TestCapacity() {
- typedef static_counting_allocator<debug_allocator<Foo,tbb::cache_aligned_allocator>, std::size_t> allocator_t;
- typedef tbb::concurrent_vector<Foo, allocator_t> vector_t;
- allocator_t::init_counters();
- for( size_t old_size=0; old_size<=11000; old_size=(old_size<5 ? old_size+1 : 3*old_size) ) {
- for( size_t new_size=0; new_size<=11000; new_size=(new_size<5 ? new_size+1 : 3*new_size) ) {
- long count = FooCount;
- {
- vector_t v; v.reserve(old_size);
- ASSERT( v.capacity()>=old_size, NULL );
- v.reserve( new_size );
- ASSERT( v.capacity()>=old_size, NULL );
- ASSERT( v.capacity()>=new_size, NULL );
- ASSERT( v.empty(), NULL );
- size_t fill_size = 2*new_size;
- for( size_t i=0; i<fill_size; ++i ) {
- ASSERT( size_t(FooCount)==count+i, NULL );
-#if TBB_DEPRECATED
- size_t j = v.grow_by(1);
-#else
- size_t j = v.grow_by(1) - v.begin();
-#endif
- ASSERT( j==i, NULL );
- v[j].bar() = int(~j);
- }
- vector_t copy_of_v(v); // should allocate first segment with same size as for shrink_to_fit()
- if(__TBB_Log2(/*reserved size*/old_size|1) > __TBB_Log2(fill_size|1) )
- ASSERT( v.capacity() != copy_of_v.capacity(), NULL );
- v.shrink_to_fit();
- ASSERT( v.capacity() == copy_of_v.capacity(), NULL );
- CheckVector(v, new_size*2, old_size); // check vector correctness
- ASSERT( v==copy_of_v, NULL ); // TODO: check also segments layout equality
- }
- ASSERT( FooCount==count, NULL );
- }
- }
- ASSERT( allocator_t::items_allocated == allocator_t::items_freed, NULL);
- ASSERT( allocator_t::allocations == allocator_t::frees, NULL);
-}
-
-struct AssignElement {
- typedef tbb::concurrent_vector<int>::range_type::iterator iterator;
- iterator base;
- void operator()( const tbb::concurrent_vector<int>::range_type& range ) const {
- for( iterator i=range.begin(); i!=range.end(); ++i ) {
- if( *i!=0 )
- REPORT("ERROR for v[%ld]\n", long(i-base));
- *i = int(i-base);
- }
- }
- AssignElement( iterator base_ ) : base(base_) {}
-};
-
-struct CheckElement {
- typedef tbb::concurrent_vector<int>::const_range_type::iterator iterator;
- iterator base;
- void operator()( const tbb::concurrent_vector<int>::const_range_type& range ) const {
- for( iterator i=range.begin(); i!=range.end(); ++i )
- if( *i != int(i-base) )
- REPORT("ERROR for v[%ld]\n", long(i-base));
- }
- CheckElement( iterator base_ ) : base(base_) {}
-};
-
-#include "tbb/tick_count.h"
-#include "tbb/parallel_for.h"
-#include "harness.h"
-
-//! Test parallel access by iterators
-void TestParallelFor( int nthread ) {
- typedef tbb::concurrent_vector<int> vector_t;
- vector_t v;
- v.resize(N);
- tbb::tick_count t0 = tbb::tick_count::now();
- REMARK("Calling parallel_for with %ld threads\n",long(nthread));
- tbb::parallel_for( v.range(10000), AssignElement(v.begin()) );
- tbb::tick_count t1 = tbb::tick_count::now();
- const vector_t& u = v;
- tbb::parallel_for( u.range(10000), CheckElement(u.begin()) );
- tbb::tick_count t2 = tbb::tick_count::now();
- REMARK("Time for parallel_for: assign time = %8.5f, check time = %8.5f\n",
- (t1-t0).seconds(),(t2-t1).seconds());
- for( long i=0; size_t(i)<v.size(); ++i )
- if( v[i]!=i )
- REPORT("ERROR for v[%ld]\n", i);
-}
-
-template<typename Iterator1, typename Iterator2>
-void TestIteratorAssignment( Iterator2 j ) {
- Iterator1 i(j);
- ASSERT( i==j, NULL );
- ASSERT( !(i!=j), NULL );
- Iterator1 k;
- k = j;
- ASSERT( k==j, NULL );
- ASSERT( !(k!=j), NULL );
-}
-
-template<typename Range1, typename Range2>
-void TestRangeAssignment( Range2 r2 ) {
- Range1 r1(r2); r1 = r2;
-}
-
-template<typename Iterator, typename T>
-void TestIteratorTraits() {
- AssertSameType( static_cast<typename Iterator::difference_type*>(0), static_cast<ptrdiff_t*>(0) );
- AssertSameType( static_cast<typename Iterator::value_type*>(0), static_cast<T*>(0) );
- AssertSameType( static_cast<typename Iterator::pointer*>(0), static_cast<T**>(0) );
- AssertSameType( static_cast<typename Iterator::iterator_category*>(0), static_cast<std::random_access_iterator_tag*>(0) );
- T x;
- typename Iterator::reference xr = x;
- typename Iterator::pointer xp = &x;
- ASSERT( &xr==xp, NULL );
-}
-
-template<typename Vector, typename Iterator>
-void CheckConstIterator( const Vector& u, int i, const Iterator& cp ) {
- typename Vector::const_reference pref = *cp;
- if( pref.bar()!=i )
- REPORT("ERROR for u[%ld] using const_iterator\n", long(i));
- typename Vector::difference_type delta = cp-u.begin();
- ASSERT( delta==i, NULL );
- if( u[i].bar()!=i )
- REPORT("ERROR for u[%ld] using subscripting\n", long(i));
- ASSERT( u.begin()[i].bar()==i, NULL );
-}
-
-template<typename Iterator1, typename Iterator2, typename V>
-void CheckIteratorComparison( V& u ) {
- V u2 = u;
- Iterator1 i = u.begin();
-
- for( int i_count=0; i_count<100; ++i_count ) {
- Iterator2 j = u.begin();
- Iterator2 i2 = u2.begin();
- for( int j_count=0; j_count<100; ++j_count ) {
- ASSERT( (i==j)==(i_count==j_count), NULL );
- ASSERT( (i!=j)==(i_count!=j_count), NULL );
- ASSERT( (i-j)==(i_count-j_count), NULL );
- ASSERT( (i<j)==(i_count<j_count), NULL );
- ASSERT( (i>j)==(i_count>j_count), NULL );
- ASSERT( (i<=j)==(i_count<=j_count), NULL );
- ASSERT( (i>=j)==(i_count>=j_count), NULL );
- ASSERT( !(i==i2), NULL );
- ASSERT( i!=i2, NULL );
- ++j;
- ++i2;
- }
- ++i;
- }
-}
-
-//! Test sequential iterators for vector type V.
-/** Also does timing. */
-template<typename T>
-void TestSequentialFor() {
- typedef tbb::concurrent_vector<FooWithAssign> V;
- V v(N);
- ASSERT(v.grow_by(0) == v.grow_by(0, FooWithAssign()), NULL);
-
- // Check iterator
- tbb::tick_count t0 = tbb::tick_count::now();
- typename V::iterator p = v.begin();
- ASSERT( !(*p).is_const(), NULL );
- ASSERT( !p->is_const(), NULL );
- for( int i=0; size_t(i)<v.size(); ++i, ++p ) {
- if( (*p).state!=Foo::DefaultInitialized )
- REPORT("ERROR for v[%ld]\n", long(i));
- typename V::reference pref = *p;
- pref.bar() = i;
- typename V::difference_type delta = p-v.begin();
- ASSERT( delta==i, NULL );
- ASSERT( -delta<=0, "difference type not signed?" );
- }
- tbb::tick_count t1 = tbb::tick_count::now();
-
- // Check const_iterator going forwards
- const V& u = v;
- typename V::const_iterator cp = u.begin();
- ASSERT( cp == v.cbegin(), NULL );
- ASSERT( (*cp).is_const(), NULL );
- ASSERT( cp->is_const(), NULL );
- ASSERT( *cp == v.front(), NULL);
- for( int i=0; size_t(i)<u.size(); ++i ) {
- CheckConstIterator(u,i,cp);
- V::const_iterator &cpr = ++cp;
- ASSERT( &cpr == &cp, "preincrement not returning a reference?");
- }
- tbb::tick_count t2 = tbb::tick_count::now();
- REMARK("Time for serial for: assign time = %8.5f, check time = %8.5f\n",
- (t1-t0).seconds(),(t2-t1).seconds());
-
- // Now go backwards
- cp = u.end();
- ASSERT( cp == v.cend(), NULL );
- for( int i=int(u.size()); i>0; ) {
- --i;
- V::const_iterator &cpr = --cp;
- ASSERT( &cpr == &cp, "predecrement not returning a reference?");
- if( i>0 ) {
- typename V::const_iterator cp_old = cp--;
- int here = (*cp_old).bar();
- ASSERT( here==u[i].bar(), NULL );
- typename V::const_iterator cp_new = cp++;
- int prev = (*cp_new).bar();
- ASSERT( prev==u[i-1].bar(), NULL );
- }
- CheckConstIterator(u,i,cp);
- }
-
- // Now go forwards and backwards
- ptrdiff_t k = 0;
- cp = u.begin();
- for( size_t i=0; i<u.size(); ++i ) {
- CheckConstIterator(u,int(k),cp);
- typename V::difference_type delta = i*3 % u.size();
- if( 0<=k+delta && size_t(k+delta)<u.size() ) {
- V::const_iterator &cpr = (cp += delta);
- ASSERT( &cpr == &cp, "+= not returning a reference?");
- k += delta;
- }
- delta = i*7 % u.size();
- if( 0<=k-delta && size_t(k-delta)<u.size() ) {
- if( i&1 ) {
- V::const_iterator &cpr = (cp -= delta);
- ASSERT( &cpr == &cp, "-= not returning a reference?");
- } else
- cp = cp - delta; // Test operator-
- k -= delta;
- }
- }
-
- for( int i=0; size_t(i)<u.size(); i=(i<50?i+1:i*3) )
- for( int j=-i; size_t(i+j)<u.size(); j=(j<50?j+1:j*5) ) {
- ASSERT( (u.begin()+i)[j].bar()==i+j, NULL );
- ASSERT( (v.begin()+i)[j].bar()==i+j, NULL );
- ASSERT((v.cbegin()+i)[j].bar()==i+j, NULL );
- ASSERT( (i+u.begin())[j].bar()==i+j, NULL );
- ASSERT( (i+v.begin())[j].bar()==i+j, NULL );
- ASSERT((i+v.cbegin())[j].bar()==i+j, NULL );
- }
-
- CheckIteratorComparison<typename V::iterator, typename V::iterator>(v);
- CheckIteratorComparison<typename V::iterator, typename V::const_iterator>(v);
- CheckIteratorComparison<typename V::const_iterator, typename V::iterator>(v);
- CheckIteratorComparison<typename V::const_iterator, typename V::const_iterator>(v);
-
- TestIteratorAssignment<typename V::const_iterator>( u.begin() );
- TestIteratorAssignment<typename V::const_iterator>( v.begin() );
- TestIteratorAssignment<typename V::const_iterator>( v.cbegin() );
- TestIteratorAssignment<typename V::iterator>( v.begin() );
- // doesn't compile as expected: TestIteratorAssignment<typename V::iterator>( u.begin() );
-
- TestRangeAssignment<typename V::const_range_type>( u.range() );
- TestRangeAssignment<typename V::const_range_type>( v.range() );
- TestRangeAssignment<typename V::range_type>( v.range() );
- // doesn't compile as expected: TestRangeAssignment<typename V::range_type>( u.range() );
-
- // Check reverse_iterator
- typename V::reverse_iterator rp = v.rbegin();
- for( size_t i=v.size(); i>0; --i, ++rp ) {
- typename V::reference pref = *rp;
- ASSERT( size_t(pref.bar())==i-1, NULL );
- ASSERT( rp!=v.rend(), NULL );
- }
- ASSERT( rp==v.rend(), NULL );
-
- // Check const_reverse_iterator
- typename V::const_reverse_iterator crp = u.rbegin();
- ASSERT( crp == v.crbegin(), NULL );
- ASSERT( *crp == v.back(), NULL);
- for( size_t i=v.size(); i>0; --i, ++crp ) {
- typename V::const_reference cpref = *crp;
- ASSERT( size_t(cpref.bar())==i-1, NULL );
- ASSERT( crp!=u.rend(), NULL );
- }
- ASSERT( crp == u.rend(), NULL );
- ASSERT( crp == v.crend(), NULL );
-
- TestIteratorAssignment<typename V::const_reverse_iterator>( u.rbegin() );
- TestIteratorAssignment<typename V::reverse_iterator>( v.rbegin() );
-
- // test compliance with C++ Standard 2003, clause 23.1.1p9
- {
- tbb::concurrent_vector<int> v1, v2(1, 100);
- v1.assign(1, 100); ASSERT(v1 == v2, NULL);
- ASSERT(v1.size() == 1 && v1[0] == 100, "used integral iterators");
- }
-
- // cross-allocator tests
-#if !defined(_WIN64) || defined(_CPPLIB_VER)
- typedef local_counting_allocator<std::allocator<int>, size_t> allocator1_t;
- typedef tbb::cache_aligned_allocator<void> allocator2_t;
- typedef tbb::concurrent_vector<FooWithAssign, allocator1_t> V1;
- typedef tbb::concurrent_vector<FooWithAssign, allocator2_t> V2;
- V1 v1( v ); // checking cross-allocator copying
- V2 v2( 10 ); v2 = v1; // checking cross-allocator assignment
- ASSERT( (v1 == v) && !(v2 != v), NULL);
- ASSERT( !(v1 < v) && !(v2 > v), NULL);
- ASSERT( (v1 <= v) && (v2 >= v), NULL);
-#endif
-}
-
-static const size_t Modulus = 7;
-
-typedef static_counting_allocator<debug_allocator<Foo> > MyAllocator;
-typedef tbb::concurrent_vector<Foo, MyAllocator> MyVector;
-
-template<typename MyVector>
-class GrowToAtLeast: NoAssign {
- MyVector& my_vector;
-public:
- void operator()( const tbb::blocked_range<size_t>& range ) const {
- for( size_t i=range.begin(); i!=range.end(); ++i ) {
- size_t n = my_vector.size();
- size_t req = (i % (2*n+1))+1;
-#if TBB_DEPRECATED
- my_vector.grow_to_at_least(req);
-#else
- typename MyVector::iterator p = my_vector.grow_to_at_least(req);
- if( p-my_vector.begin() < typename MyVector::difference_type(req) )
- ASSERT( p->state == Foo::DefaultInitialized || p->state == Foo::ZeroInitialized, NULL);
-#endif
- ASSERT( my_vector.size()>=req, NULL );
- }
- }
- GrowToAtLeast( MyVector& vector ) : my_vector(vector) {}
-};
-
-void TestConcurrentGrowToAtLeast() {
- typedef static_counting_allocator< tbb::zero_allocator<Foo> > MyAllocator;
- typedef tbb::concurrent_vector<Foo, MyAllocator> MyVector;
- MyAllocator::init_counters();
- MyVector v(2, Foo(), MyAllocator());
- for( size_t s=1; s<1000; s*=10 ) {
- tbb::parallel_for( tbb::blocked_range<size_t>(0,10000*s,s), GrowToAtLeast<MyVector>(v), tbb::simple_partitioner() );
- }
- v.clear();
- ASSERT( 0 == v.get_allocator().frees, NULL);
- v.shrink_to_fit();
- size_t items_allocated = v.get_allocator().items_allocated,
- items_freed = v.get_allocator().items_freed;
- size_t allocations = v.get_allocator().allocations,
- frees = v.get_allocator().frees;
- ASSERT( items_allocated == items_freed, NULL);
- ASSERT( allocations == frees, NULL);
-}
-
-//! Test concurrent invocations of method concurrent_vector::grow_by
-template<typename MyVector>
-class GrowBy: NoAssign {
- MyVector& my_vector;
-public:
- void operator()( const tbb::blocked_range<int>& range ) const {
- ASSERT( range.begin() < range.end(), NULL );
-#if TBB_DEPRECATED
- for( int i=range.begin(); i!=range.end(); ++i )
-#else
- int i = range.begin(), h = (range.end() - i) / 2;
- typename MyVector::iterator s = my_vector.grow_by(h);
- for( h += i; i < h; ++i, ++s )
- s->bar() = i;
- for(; i!=range.end(); ++i )
-#endif
- {
- if( i&1 ) {
-#if TBB_DEPRECATED
- typename MyVector::reference element = my_vector[my_vector.grow_by(1)];
- element.bar() = i;
-#else
- my_vector.grow_by(1)->bar() = i;
-#endif
- } else {
- typename MyVector::value_type f;
- f.bar() = i;
-#if TBB_DEPRECATED
- size_t r;
-#else
- typename MyVector::iterator r;
-#endif
- if( i&2 )
- r = my_vector.push_back( f );
- else
- r = my_vector.grow_by(1, f);
-#if TBB_DEPRECATED
- ASSERT( my_vector[r].bar()==i, NULL );
-#else
- ASSERT( r->bar()==i, NULL );
-#endif
- }
- }
- }
- GrowBy( MyVector& vector ) : my_vector(vector) {}
-};
-
-//! Test concurrent invocations of method concurrent_vector::grow_by
-void TestConcurrentGrowBy( int nthread ) {
- MyAllocator::init_counters();
- {
- int m = 100000; MyAllocator a;
- MyVector v( a );
- tbb::parallel_for( tbb::blocked_range<int>(0,m,100), GrowBy<MyVector>(v), tbb::simple_partitioner() );
- ASSERT( v.size()==size_t(m), NULL );
-
- // Verify that v is a permutation of 0..m
- int inversions = 0, def_inits = 0, copy_inits = 0;
- bool* found = new bool[m];
- memset( found, 0, m );
- for( int i=0; i<m; ++i ) {
- if( v[i].state == Foo::DefaultInitialized ) ++def_inits;
- else if( v[i].state == Foo::CopyInitialized ) ++copy_inits;
- else {
- REMARK("i: %d ", i);
- ASSERT( false, "v[i] seems not initialized");
- }
- int index = v[i].bar();
- ASSERT( !found[index], NULL );
- found[index] = true;
- if( i>0 )
- inversions += v[i].bar()<v[i-1].bar();
- }
- for( int i=0; i<m; ++i ) {
- ASSERT( found[i], NULL );
- ASSERT( nthread>1 || v[i].bar()==i, "sequential execution is wrong" );
- }
- delete[] found;
- REMARK("Initialization by default constructor: %d, by copy: %d\n", def_inits, copy_inits);
- ASSERT( def_inits >= m/2, NULL );
- ASSERT( copy_inits >= m/4, NULL );
- if( nthread>1 && inversions<m/20 )
- REPORT("Warning: not much concurrency in TestConcurrentGrowBy (%d inversions)\n", inversions);
- }
- size_t items_allocated = MyAllocator::items_allocated,
- items_freed = MyAllocator::items_freed;
- size_t allocations = MyAllocator::allocations,
- frees = MyAllocator::frees;
- ASSERT( items_allocated == items_freed, NULL);
- ASSERT( allocations == frees, NULL);
-}
-
-//! Test the assignment operator and swap
-void TestAssign() {
- typedef tbb::concurrent_vector<FooWithAssign, local_counting_allocator<std::allocator<FooWithAssign>, size_t > > vector_t;
- local_counting_allocator<std::allocator<FooWithAssign>, size_t > init_alloc;
- init_alloc.allocations = 100;
- for( int dst_size=1; dst_size<=128; NextSize( dst_size ) ) {
- for( int src_size=2; src_size<=128; NextSize( src_size ) ) {
- vector_t u(FooIterator(0), FooIterator(src_size), init_alloc);
- for( int i=0; i<src_size; ++i )
- ASSERT( u[i].bar()==i, NULL );
- vector_t v(dst_size, FooWithAssign(), init_alloc);
- for( int i=0; i<dst_size; ++i ) {
- ASSERT( v[i].state==Foo::CopyInitialized, NULL );
- v[i].bar() = ~i;
- }
- ASSERT( v != u, NULL);
- v.swap(u);
- CheckVector(u, dst_size, src_size);
- u.swap(v);
- // using assignment
- v = u;
- ASSERT( v == u, NULL);
- u.clear();
- ASSERT( u.size()==0, NULL );
- ASSERT( v.size()==size_t(src_size), NULL );
- for( int i=0; i<src_size; ++i )
- ASSERT( v[i].bar()==i, NULL );
- ASSERT( 0 == u.get_allocator().frees, NULL);
- u.shrink_to_fit(); // deallocate unused memory
- size_t items_allocated = u.get_allocator().items_allocated,
- items_freed = u.get_allocator().items_freed;
- size_t allocations = u.get_allocator().allocations,
- frees = u.get_allocator().frees + 100;
- ASSERT( items_allocated == items_freed, NULL);
- ASSERT( allocations == frees, NULL);
- }
- }
-}
-
-// Test the comparison operators
-#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 <string>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-void TestComparison() {
- std::string str[3]; str[0] = "abc";
- str[1].assign("cba");
- str[2].assign("abc"); // same as 0th
- tbb::concurrent_vector<char> var[3];
- var[0].assign(str[0].begin(), str[0].end());
- var[1].assign(str[0].rbegin(), str[0].rend());
- var[2].assign(var[1].rbegin(), var[1].rend()); // same as 0th
- for (int i = 0; i < 3; ++i) {
- for (int j = 0; j < 3; ++j) {
- ASSERT( (var[i] == var[j]) == (str[i] == str[j]), NULL );
- ASSERT( (var[i] != var[j]) == (str[i] != str[j]), NULL );
- ASSERT( (var[i] < var[j]) == (str[i] < str[j]), NULL );
- ASSERT( (var[i] > var[j]) == (str[i] > str[j]), NULL );
- ASSERT( (var[i] <= var[j]) == (str[i] <= str[j]), NULL );
- ASSERT( (var[i] >= var[j]) == (str[i] >= str[j]), NULL );
- }
- }
-}
-
-//------------------------------------------------------------------------
-// Regression test for problem where on oversubscription caused
-// concurrent_vector::grow_by to run very slowly (TR#196).
-//------------------------------------------------------------------------
-
-#include "tbb/task_scheduler_init.h"
-#include <math.h>
-
-typedef unsigned long Number;
-
-static tbb::concurrent_vector<Number> Primes;
-
-class FindPrimes {
- bool is_prime( Number val ) const {
- int limit, factor = 3;
- if( val<5u )
- return val==2;
- else {
- limit = long(sqrtf(float(val))+0.5f);
- while( factor<=limit && val % factor )
- ++factor;
- return factor>limit;
- }
- }
-public:
- void operator()( const tbb::blocked_range<Number>& r ) const {
- for( Number i=r.begin(); i!=r.end(); ++i ) {
- if( i%2 && is_prime(i) ) {
-#if TBB_DEPRECATED
- Primes[Primes.grow_by(1)] = i;
-#else
- Primes.push_back( i );
-#endif
- }
- }
- }
-};
-
-double TimeFindPrimes( int nthread ) {
- Primes.clear();
- Primes.reserve(1000000);// TODO: or compact()?
- tbb::task_scheduler_init init(nthread);
- tbb::tick_count t0 = tbb::tick_count::now();
- tbb::parallel_for( tbb::blocked_range<Number>(0,1000000,500), FindPrimes() );
- tbb::tick_count t1 = tbb::tick_count::now();
- return (t1-t0).seconds();
-}
-
-void TestFindPrimes() {
- // Time fully subscribed run.
- double t2 = TimeFindPrimes( tbb::task_scheduler_init::automatic );
-
- // Time parallel run that is very likely oversubscribed.
-#if _XBOX
- double t128 = TimeFindPrimes(32); //XBOX360 can't handle too many threads
-#else
- double t128 = TimeFindPrimes(128);
-#endif
- REMARK("TestFindPrimes: t2==%g t128=%g k=%g\n", t2, t128, t128/t2);
-
- // We allow the 128-thread run a little extra time to allow for thread overhead.
- // Theoretically, following test will fail on machine with >128 processors.
- // But that situation is not going to come up in the near future,
- // and the generalization to fix the issue is not worth the trouble.
- if( t128 > 1.3*t2 ) {
- REPORT("Warning: grow_by is pathetically slow: t2==%g t128=%g k=%g\n", t2, t128, t128/t2);
- }
-}
-
-//------------------------------------------------------------------------
-// Test compatibility with STL sort.
-//------------------------------------------------------------------------
-
-#include <algorithm>
-
-void TestSort() {
- for( int n=0; n<100; n=n*3+1 ) {
- tbb::concurrent_vector<int> array(n);
- for( int i=0; i<n; ++i )
- array.at(i) = (i*7)%n;
- std::sort( array.begin(), array.end() );
- for( int i=0; i<n; ++i )
- ASSERT( array[i]==i, NULL );
- }
-}
-
-#if TBB_USE_EXCEPTIONS
-//------------------------------------------------------------------------
-// Test exceptions safety (from allocator and items constructors)
-//------------------------------------------------------------------------
-void TestExceptions() {
- typedef static_counting_allocator<debug_allocator<FooWithAssign>, std::size_t> allocator_t;
- typedef tbb::concurrent_vector<FooWithAssign, allocator_t> vector_t;
-
- enum methods {
- zero_method = 0,
- ctor_copy, ctor_size, assign_nt, assign_ir, op_equ, reserve, compact, grow,
- all_methods
- };
- ASSERT( !FooCount, NULL );
-
- try {
- vector_t src(FooIterator(0), FooIterator(N)); // original data
-
- for(int t = 0; t < 2; ++t) // exception type
- for(int m = zero_method+1; m < all_methods; ++m)
- {
- ASSERT( FooCount == N, "Previous iteration miss some Foo's de-/initialization" );
- allocator_t::init_counters();
- if(t) MaxFooCount = FooCount + N/4;
- else allocator_t::set_limits(N/4);
- vector_t victim;
- try {
- switch(m) {
- case ctor_copy: {
- vector_t acopy(src);
- } break; // auto destruction after exception is checked by ~Foo
- case ctor_size: {
- vector_t sized(N);
- } break; // auto destruction after exception is checked by ~Foo
- // Do not test assignment constructor due to reusing of same methods as below
- case assign_nt: {
- victim.assign(N, FooWithAssign());
- } break;
- case assign_ir: {
- victim.assign(FooIterator(0), FooIterator(N));
- } break;
- case op_equ: {
- victim.reserve(2); victim = src; // fragmented assignment
- } break;
- case reserve: {
- try {
- victim.reserve(victim.max_size()+1);
- } catch(std::length_error &) {
- } catch(...) {
- KNOWN_ISSUE("ERROR: unrecognized exception - known compiler issue\n");
- }
- victim.reserve(N);
- } break;
- case compact: {
- if(t) MaxFooCount = 0; else allocator_t::set_limits(); // reset limits
- victim.reserve(2); victim = src; // fragmented assignment
- if(t) MaxFooCount = FooCount + 10; else allocator_t::set_limits(1, false); // block any allocation, check NULL return from allocator
- victim.shrink_to_fit(); // should start defragmenting first segment
- } break;
- case grow: {
- tbb::task_scheduler_init init(2);
- if(t) MaxFooCount = FooCount + 31; // these numbers help to reproduce the live lock for versions < TBB2.2
- try {
- tbb::parallel_for( tbb::blocked_range<int>(0, N, 70), GrowBy<vector_t>(victim) );
- } catch(...) {
-#if TBB_USE_CAPTURED_EXCEPTION
- throw tbb::bad_last_alloc();
-#else
- throw;
-#endif
- }
- } break;
- default:;
- }
- if(!t || m != reserve) ASSERT(false, "should throw an exception");
- } catch(std::bad_alloc &e) {
- allocator_t::set_limits(); MaxFooCount = 0;
- size_t capacity = victim.capacity();
- size_t size = victim.size();
-#if TBB_DEPRECATED
- size_t req_size = victim.grow_by(0);
-#else
- size_t req_size = victim.grow_by(0) - victim.begin();
-#endif
- ASSERT( size <= capacity, NULL);
- ASSERT( req_size >= size, NULL);
- switch(m) {
- case reserve:
- if(t) ASSERT(false, NULL);
- case assign_nt:
- case assign_ir:
- if(!t) {
- ASSERT(capacity < N/2, "unexpected capacity");
- ASSERT(size == 0, "unexpected size");
- break;
- } else {
- ASSERT(size == N, "unexpected size");
- ASSERT(capacity >= N, "unexpected capacity");
- int i;
- for(i = 1; ; ++i)
- if(!victim[i].zero_bar()) break;
- else ASSERT(victim[i].bar() == (m == assign_ir)? i : initial_value_of_bar, NULL);
- for(; size_t(i) < size; ++i) ASSERT(!victim[i].zero_bar(), NULL);
- ASSERT(size_t(i) == size, NULL);
- break;
- }
- case grow:
- case op_equ:
- if(!t) {
- ASSERT(capacity > 0, NULL);
- ASSERT(capacity < N, "unexpected capacity");
- }
- {
- vector_t copy_of_victim(victim);
- ASSERT(copy_of_victim.size() > 0, NULL);
- for(int i = 0; ; ++i) {
- try {
- FooWithAssign &foo = victim.at(i);
- if( !foo.is_valid_or_zero() ) {
- std::printf("i: %d size: %u req_size: %u state: %d\n", i, unsigned(size), unsigned(req_size), foo.state);
- }
- int bar = foo.zero_bar();
- if(m != grow) ASSERT( bar == i || (t && bar == 0), NULL);
- if(size_t(i) < copy_of_victim.size()) ASSERT( copy_of_victim[i].bar() == bar, NULL);
- } catch(std::range_error &) { // skip broken segment
- ASSERT( size_t(i) < req_size, NULL );
- if(m == op_equ) break;
- } catch(std::out_of_range &){
- ASSERT( i > 0, NULL ); break;
- } catch(...) {
- KNOWN_ISSUE("ERROR: unrecognized exception - known compiler issue\n"); break;
- }
- }
- vector_t copy_of_victim2(10); copy_of_victim2 = victim;
- ASSERT(copy_of_victim == copy_of_victim2, "assignment doesn't match copying");
- if(m == op_equ) {
- try {
- victim = copy_of_victim2;
- } catch(tbb::bad_last_alloc &) { break;
- } catch(...) {
- KNOWN_ISSUE("ERROR: unrecognized exception - known compiler issue\n"); break;
- }
- ASSERT(t, NULL);
- }
- } break;
- case compact:
- ASSERT(capacity > 0, "unexpected capacity");
- ASSERT(victim == src, "shrink_to_fit() is broken");
- break;
-
- default:; // nothing to check here
- }
- REMARK("Exception %d: %s\t- ok\n", m, e.what());
- }
- }
- } catch(...) {
- ASSERT(false, "unexpected exception");
- }
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-//------------------------------------------------------------------------
-// Test SSE
-//------------------------------------------------------------------------
-#include "harness_m128.h"
-
-#if HAVE_m128
-
-void TestSSE() {
- tbb::concurrent_vector<ClassWithSSE> v;
- for( int i=0; i<100; ++i ) {
- v.push_back(ClassWithSSE(i));
- for( int j=0; i<i; ++j )
- ASSERT( v[j]==ClassWithSSE(j), NULL );
- }
-}
-#endif /* HAVE_m128 */
-
-//------------------------------------------------------------------------
-
-int TestMain () {
- if( MinThread<1 ) {
- REPORT("ERROR: MinThread=%d, but must be at least 1\n",MinThread); MinThread = 1;
- }
-#if !TBB_DEPRECATED
- TestIteratorTraits<tbb::concurrent_vector<Foo>::iterator,Foo>();
- TestIteratorTraits<tbb::concurrent_vector<Foo>::const_iterator,const Foo>();
- TestSequentialFor<FooWithAssign> ();
- TestResizeAndCopy();
- TestAssign();
-#if HAVE_m128
- TestSSE();
-#endif /* HAVE_m128 */
-#endif
- TestCapacity();
- ASSERT( !FooCount, NULL );
- for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) {
- tbb::task_scheduler_init init( nthread );
- TestParallelFor( nthread );
- TestConcurrentGrowToAtLeast();
- TestConcurrentGrowBy( nthread );
- }
- ASSERT( !FooCount, NULL );
-#if !TBB_DEPRECATED
- TestComparison();
- TestFindPrimes();
- TestSort();
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception safety test is skipped.\n");
-#elif TBB_USE_EXCEPTIONS
- TestExceptions();
-#endif /* TBB_USE_EXCEPTIONS */
-#endif /* !TBB_DEPRECATED */
- ASSERT( !FooCount, NULL );
- REMARK("sizeof(concurrent_vector<int>) == %d\n", (int)sizeof(tbb::concurrent_vector<int>));
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/compat/condition_variable"
-#include "tbb/mutex.h"
-#include "tbb/recursive_mutex.h"
-#include "tbb/tick_count.h"
-#include "tbb/atomic.h"
-
-#include "harness.h"
-
-// This test deliberately avoids a "using tbb" statement,
-// so that the error of putting types in the wrong namespace will be caught.
-
-using namespace std;
-
-template<typename M>
-struct Counter {
- typedef M mutex_type;
- M mutex;
- volatile long value;
- void flog_once_lock_guard( size_t mode );
- void flog_once_unique_lock( size_t mode );
-};
-
-template<typename M>
-void Counter<M>::flog_once_lock_guard(size_t mode)
-/** Increments counter once for each iteration in the iteration space. */
-{
- if( mode&1 ) {
- // Try acquire and release with implicit lock_guard
- // precondition: if mutex_type is not a recursive mutex, the calling thread does not own the mutex m.
- // if the prcondition is not met, either dead-lock incorrect 'value' would result in.
- lock_guard<M> lg(mutex);
- value = value+1;
- } else {
- // Try acquire and release with adopt lock_quard
- // precodition: the calling thread owns the mutex m.
- // if the prcondition is not met, incorrect 'value' would result in because the thread unlocks
- // mutex that it does not own.
- mutex.lock();
- lock_guard<M> lg( mutex, adopt_lock );
- value = value+1;
- }
-}
-
-template<typename M>
-void Counter<M>::flog_once_unique_lock(size_t mode)
-/** Increments counter once for each iteration in the iteration space. */
-{
- switch( mode&7 ) {
- case 0:
- {// implicitly acquire and release mutex with unique_lock
- unique_lock<M> ul( mutex );
- value = value+1;
- ASSERT( ul==true, NULL );
- }
- break;
- case 1:
- {// unique_lock with defer_lock
- unique_lock<M> ul( mutex, defer_lock );
- ASSERT( ul.owns_lock()==false, NULL );
- ul.lock();
- value = value+1;
- ASSERT( ul.owns_lock()==true, NULL );
- }
- break;
- case 2:
- {// unique_lock::try_lock() with try_to_lock
- unique_lock<M> ul( mutex, try_to_lock );
- if( !ul )
- while( !ul.try_lock() )
- __TBB_Yield();
- value = value+1;
- }
- break;
- case 3:
- {// unique_lock::try_lock_for() with try_to_lock
- unique_lock<M> ul( mutex, defer_lock );
- tbb::tick_count::interval_t i(1.0);
- while( !ul.try_lock_for( i ) )
- ;
- value = value+1;
- ASSERT( ul.owns_lock()==true, NULL );
- }
- break;
- case 4:
- {
- unique_lock<M> ul_o4;
- {// unique_lock with adopt_lock
- mutex.lock();
- unique_lock<M> ul( mutex, adopt_lock );
- value = value+1;
- ASSERT( ul.owns_lock()==true, NULL );
- ASSERT( ul.mutex()==&mutex, NULL );
- ASSERT( ul_o4.owns_lock()==false, NULL );
- ASSERT( ul_o4.mutex()==NULL, NULL );
- swap( ul, ul_o4 );
- ASSERT( ul.owns_lock()==false, NULL );
- ASSERT( ul.mutex()==NULL, NULL );
- ASSERT( ul_o4.owns_lock()==true, NULL );
- ASSERT( ul_o4.mutex()==&mutex, NULL );
- ul_o4.unlock();
- }
- ASSERT( ul_o4.owns_lock()==false, NULL );
- }
- break;
- case 5:
- {
- unique_lock<M> ul_o5;
- {// unique_lock with adopt_lock
- mutex.lock();
- unique_lock<M> ul( mutex, adopt_lock );
- value = value+1;
- ASSERT( ul.owns_lock()==true, NULL );
- ASSERT( ul.mutex()==&mutex, NULL );
- ASSERT( ul_o5.owns_lock()==false, NULL );
- ASSERT( ul_o5.mutex()==NULL, NULL );
- ul_o5.swap( ul );
- ASSERT( ul.owns_lock()==false, NULL );
- ASSERT( ul.mutex()==NULL, NULL );
- ASSERT( ul_o5.owns_lock()==true, NULL );
- ASSERT( ul_o5.mutex()==&mutex, NULL );
- ul_o5.unlock();
- }
- ASSERT( ul_o5.owns_lock()==false, NULL );
- }
- break;
- default:
- {// unique_lock with adopt_lock, and release()
- mutex.lock();
- unique_lock<M> ul( mutex, adopt_lock );
- ASSERT( ul==true, NULL );
- value = value+1;
- M* old_m = ul.release();
- old_m->unlock();
- ASSERT( ul.owns_lock()==false, NULL );
- }
- break;
- }
-}
-
-static tbb::atomic<size_t> Order;
-
-template<typename State, long TestSize>
-struct WorkForLocks: NoAssign {
- static const size_t chunk = 100;
- State& state;
- WorkForLocks( State& state_ ) : state(state_) {}
- void operator()( int ) const {
- size_t step;
- while( (step=Order.fetch_and_add<tbb::acquire>(chunk))<TestSize ) {
- for( size_t i=0; i<chunk && step<TestSize; ++i, ++step ) {
- state.flog_once_lock_guard(step);
- state.flog_once_unique_lock(step);
- }
- }
- }
-};
-
-template<typename M>
-void TestLocks( const char* name, int nthread ) {
- REMARK("testing %s in TestLocks\n",name);
- Counter<M> counter;
- counter.value = 0;
- Order = 0;
- const long test_size = 100000;
- NativeParallelFor( nthread, WorkForLocks<Counter<M>, test_size>(counter) );
-
- if( counter.value!=2*test_size )
- REPORT("ERROR for %s in TestLocks: counter.value=%ld != 2 * %ld=test_size\n",name,counter.value,test_size);
-}
-
-static tbb::atomic<int> barrier;
-
-// Test if the constructor works and if native_handle() works
-template<typename M>
-struct WorkForCondVarCtor: NoAssign {
- condition_variable& my_cv;
- M& my_mtx;
- WorkForCondVarCtor( condition_variable& cv_, M& mtx_ ) : my_cv(cv_), my_mtx(mtx_) {}
- void operator()( int tid ) const {
- ASSERT( tid<=1, NULL ); // test with 2 threads.
- condition_variable::native_handle_type handle = my_cv.native_handle();
- if( tid&1 ) {
- my_mtx.lock();
- ++barrier;
-#if _WIN32||_WIN64
- if( !tbb::interface5::internal::internal_condition_variable_wait( *handle, &my_mtx ) ) {
- int ec = GetLastError();
- ASSERT( ec!=WAIT_TIMEOUT, NULL );
- throw_exception( tbb::internal::eid_condvar_wait_failed );
- }
-#else
- if( pthread_cond_wait( handle, my_mtx.native_handle() ) )
- throw_exception( tbb::internal::eid_condvar_wait_failed );
-#endif
- ++barrier;
- my_mtx.unlock();
- } else {
- bool res;
- while( (res=my_mtx.try_lock())==true && barrier==0 ) {
- my_mtx.unlock();
- __TBB_Yield();
- }
- if( res ) my_mtx.unlock();
- do {
-#if _WIN32||_WIN64
- tbb::interface5::internal::internal_condition_variable_notify_one( *handle );
-#else
- pthread_cond_signal( handle );
-#endif
- __TBB_Yield();
- } while ( barrier<2 );
- }
- }
-};
-
-static condition_variable* test_cv;
-static tbb::atomic<int> n_waiters;
-
-// Test if the destructor works
-template<typename M>
-struct WorkForCondVarDtor: NoAssign {
- int nthread;
- M& my_mtx;
- WorkForCondVarDtor( int n, M& mtx_ ) : nthread(n), my_mtx(mtx_) {}
- void operator()( int tid ) const {
- if( tid==0 ) {
- unique_lock<M> ul( my_mtx, defer_lock );
- test_cv = new condition_variable;
-
- while( n_waiters<nthread-1 )
- __TBB_Yield();
- ul.lock();
- test_cv->notify_all();
- ul.unlock();
- while( n_waiters>0 )
- __TBB_Yield();
- delete test_cv;
- } else {
- while( test_cv==NULL )
- __TBB_Yield();
- unique_lock<M> ul(my_mtx);
- ++n_waiters;
- test_cv->wait( ul );
- --n_waiters;
- }
- }
-};
-
-static const int max_ticket = 100;
-static const int short_delay = 10;
-static const int long_delay = 100;
-
-tbb::atomic<int> n_signaled;
-tbb::atomic<int> n_done, n_done_1, n_done_2;
-tbb::atomic<int> n_timed_out;
-
-static bool false_to_true;
-
-struct TestPredicateFalseToTrue {
- TestPredicateFalseToTrue() {}
- bool operator()() { return false_to_true; }
-};
-
-struct TestPredicateFalse {
- TestPredicateFalse() {}
- bool operator()() { return false; }
-};
-
-struct TestPredicateTrue {
- TestPredicateTrue() {}
- bool operator()() { return true; }
-};
-
-// Test timed wait and timed wait with pred
-template<typename M>
-struct WorkForCondVarTimedWait: NoAssign {
- int nthread;
- condition_variable& test_cv;
- M& my_mtx;
- WorkForCondVarTimedWait( int n_, condition_variable& cv_, M& mtx_ ) : nthread(n_), test_cv(cv_), my_mtx(mtx_) {}
- void operator()( int tid ) const {
- tbb::tick_count t1, t2;
-
- unique_lock<M> ul( my_mtx, defer_lock );
-
- ASSERT( n_timed_out==0, NULL );
- ++barrier;
- while( barrier<nthread ) __TBB_Yield();
-
- // test if a thread times out with wait_for()
- for( int i=1; i<10; ++i ) {
- tbb::tick_count::interval_t intv((double)i*0.0001 /*seconds*/);
- ul.lock();
- cv_status st = no_timeout;
- __TBB_TRY {
- /** Some version of glibc return EINVAL instead 0 when spurious wakeup occurs on pthread_cond_timedwait() **/
- st = test_cv.wait_for( ul, intv );
- } __TBB_CATCH( std::runtime_error& ) {}
- ASSERT( ul, "mutex should have been reacquired" );
- ul.unlock();
- if( st==timeout )
- ++n_timed_out;
- }
-
- ASSERT( n_timed_out>0, "should have been timed-out at least once\n" );
- ++n_done_1;
- while( n_done_1<nthread ) __TBB_Yield();
-
- for( int i=1; i<10; ++i ) {
- tbb::tick_count::interval_t intv((double)i*0.0001 /*seconds*/);
- ul.lock();
- __TBB_TRY {
- /** Some version of glibc return EINVAL instead 0 when spurious wakeup occurs on pthread_cond_timedwait() **/
- ASSERT( false==test_cv.wait_for( ul, intv, TestPredicateFalse()), "incorrect return value" );
- } __TBB_CATCH( std::runtime_error& ) {}
- ASSERT( ul, "mutex should have been reacquired" );
- ul.unlock();
- }
-
- if( tid==0 )
- n_waiters = 0;
- // barrier
- ++n_done_2;
- while( n_done_2<nthread ) __TBB_Yield();
-
- // at this point, we know wait_for() successfully times out.
- // so test if a thread blocked on wait_for() could receive a signal before its waiting time elapses.
- if( tid==0 ) {
- // signaler
- n_signaled = 0;
- ASSERT( n_waiters==0, NULL );
- ++n_done_2; // open gate 1
-
- while( n_waiters<(nthread-1) ) __TBB_Yield(); // wait until all other threads block on cv. flag_1
-
- ul.lock();
- test_cv.notify_all();
- n_waiters = 0;
- ul.unlock();
-
- while( n_done_2<2*nthread ) __TBB_Yield();
- ASSERT( n_signaled>0, "too small an interval?" );
- n_signaled = 0;
-
- } else {
- while( n_done_2<nthread+1 ) __TBB_Yield(); // gate 1
-
- // sleeper
- tbb::tick_count::interval_t intv((double)2.0 /*seconds*/);
- ul.lock();
- ++n_waiters; // raise flag 1/(nthread-1)
- t1 = tbb::tick_count::now();
- cv_status st = test_cv.wait_for( ul, intv ); // gate 2
- t2 = tbb::tick_count::now();
- ul.unlock();
- if( st==no_timeout ) {
- ++n_signaled;
- ASSERT( (t2-t1).seconds()<intv.seconds(), "got a signal after timed-out?" );
- }
- }
-
- ASSERT( n_done==0, NULL );
- ++n_done_2;
-
- if( tid==0 ) {
- ASSERT( n_waiters==0, NULL );
- ++n_done; // open gate 3
-
- while( n_waiters<(nthread-1) ) __TBB_Yield(); // wait until all other threads block on cv.
- for( int i=0; i<2*short_delay; ++i ) __TBB_Yield(); // give some time to waiters so that all of them in the waitq
- ul.lock();
- false_to_true = true;
- test_cv.notify_all(); // open gate 4
- ul.unlock();
-
- while( n_done<nthread ) __TBB_Yield(); // wait until all other threads wake up.
- ASSERT( n_signaled>0, "too small an interval?" );
- } else {
-
- while( n_done<1 ) __TBB_Yield(); // gate 3
-
- tbb::tick_count::interval_t intv((double)2.0 /*seconds*/);
- ul.lock();
- ++n_waiters;
- // wait_for w/ predciate
- t1 = tbb::tick_count::now();
- ASSERT( test_cv.wait_for( ul, intv, TestPredicateFalseToTrue())==true, NULL ); // gate 4
- t2 = tbb::tick_count::now();
- ul.unlock();
- if( (t2-t1).seconds()<intv.seconds() )
- ++n_signaled;
- ++n_done;
- }
- }
-};
-
-tbb::atomic<int> ticket_for_sleep, ticket_for_wakeup, signaled_ticket, wokeup_ticket;
-tbb::atomic<unsigned> n_visit_to_waitq;
-unsigned max_waitq_length;
-
-template<typename M>
-struct WorkForCondVarWaitAndNotifyOne: NoAssign {
- int nthread;
- condition_variable& test_cv;
- M& my_mtx;
- WorkForCondVarWaitAndNotifyOne( int n_, condition_variable& cv_, M& mtx_ ) : nthread(n_), test_cv(cv_), my_mtx(mtx_) {}
- void operator()( int tid ) const {
- if( tid&1 ) {
- // exercise signal part
- while( ticket_for_wakeup<max_ticket ) {
- int my_ticket = ++ticket_for_wakeup; // atomically grab the next ticket
- if( my_ticket>max_ticket )
- break;
-
- for( ;; ) {
- unique_lock<M> ul( my_mtx, defer_lock );
- ul.lock();
- if( n_waiters>0 && my_ticket<=ticket_for_sleep && my_ticket==(wokeup_ticket+1) ) {
- signaled_ticket = my_ticket;
- test_cv.notify_one();
- ++n_signaled;
- ul.unlock();
- break;
- }
- ul.unlock();
- __TBB_Yield();
- }
-
- // give waiters time to go to sleep.
- for( int m=0; m<short_delay; ++m )
- __TBB_Yield();
- }
- } else {
- while( ticket_for_sleep<max_ticket ) {
- unique_lock<M> ul( my_mtx, defer_lock );
- ul.lock();
- // exercise wait part
- int my_ticket = ++ticket_for_sleep; // grab my ticket
- if( my_ticket>max_ticket ) break;
-
- // each waiter should go to sleep at least once
- unsigned nw = ++n_waiters;
- for( ;; ) {
- // update to max_waitq_length
- if( nw>max_waitq_length ) max_waitq_length = nw;
- ++n_visit_to_waitq;
- test_cv.wait( ul );
- // if( ret==false ) ++n_timedout;
- ASSERT( ul, "mutex should have been locked" );
- --n_waiters;
- if( signaled_ticket==my_ticket ) {
- wokeup_ticket = my_ticket;
- break;
- }
- if( n_waiters>0 )
- test_cv.notify_one();
- nw = ++n_waiters; // update to max_waitq_length occurs above
- }
-
- ul.unlock();
- __TBB_Yield(); // give other threads chance to run.
- }
- }
- ++n_done;
- spin_wait_until_eq( n_done, nthread );
- ASSERT( n_signaled==max_ticket, "incorrect number of notifications sent" );
- }
-};
-
-struct TestPredicate1 {
- int target;
- TestPredicate1( int i_ ) : target(i_) {}
- bool operator()( ) { return signaled_ticket==target; }
-};
-
-template<typename M>
-struct WorkForCondVarWaitPredAndNotifyAll: NoAssign {
- int nthread;
- condition_variable& test_cv;
- M& my_mtx;
- int multiple;
- WorkForCondVarWaitPredAndNotifyAll( int n_, condition_variable& cv_, M& mtx_, int m_ ) :
- nthread(n_), test_cv(cv_), my_mtx(mtx_), multiple(m_) {}
- void operator()( int tid ) const {
- if( tid&1 ) {
- while( ticket_for_sleep<max_ticket ) {
- unique_lock<M> ul( my_mtx, defer_lock );
- // exercise wait part
- int my_ticket = ++ticket_for_sleep; // grab my ticket
- if( my_ticket>max_ticket )
- break;
-
- ul.lock();
- ++n_visit_to_waitq;
- unsigned nw = ++n_waiters;
- if( nw>max_waitq_length ) max_waitq_length = nw;
- test_cv.wait( ul, TestPredicate1( my_ticket ) );
- wokeup_ticket = my_ticket;
- --n_waiters;
- ASSERT( ul, "mutex should have been locked" );
- ul.unlock();
-
- __TBB_Yield(); // give other threads chance to run.
- }
- } else {
- // exercise signal part
- while( ticket_for_wakeup<max_ticket ) {
- int my_ticket = ++ticket_for_wakeup; // atomically grab the next ticket
- if( my_ticket>max_ticket )
- break;
-
- for( ;; ) {
- unique_lock<M> ul( my_mtx );
- if( n_waiters>0 && my_ticket<=ticket_for_sleep && my_ticket==(wokeup_ticket+1) ) {
- signaled_ticket = my_ticket;
- test_cv.notify_all();
- ++n_signaled;
- ul.unlock();
- break;
- }
- ul.unlock();
- __TBB_Yield();
- }
-
- // give waiters time to go to sleep.
- for( int m=0; m<long_delay*multiple; ++m )
- __TBB_Yield();
- }
- }
- ++n_done;
- spin_wait_until_eq( n_done, nthread );
- ASSERT( n_signaled==max_ticket, "incorrect number of notifications sent" );
- }
-};
-
-void InitGlobalCounters()
-{
- ticket_for_sleep = ticket_for_wakeup = signaled_ticket = wokeup_ticket = 0;
- n_waiters = 0;
- n_signaled = 0;
- n_done = n_done_1 = n_done_2 = 0;
- n_visit_to_waitq = 0;
- n_timed_out = 0;
-}
-
-template<typename M>
-void TestConditionVariable( const char* name, int nthread )
-{
- REMARK("testing %s in TestConditionVariable\n",name);
- Counter<M> counter;
- M mtx;
-
- ASSERT( nthread>1, "at least two threads are needed for testing condition_variable" );
- REMARK(" - constructor\n" );
- // Test constructor.
- {
- condition_variable cv1;
-#if _WIN32||_WIN64
- condition_variable::native_handle_type handle = cv1.native_handle();
- ASSERT( uintptr_t(&handle->cv_event)==uintptr_t(&handle->cv_native), NULL );
-#endif
- M mtx1;
- barrier = 0;
- NativeParallelFor( 2, WorkForCondVarCtor<M>( cv1, mtx1 ) );
- }
-
- REMARK(" - destructor\n" );
- // Test destructor.
- {
- M mtx2;
- test_cv = NULL;
- n_waiters = 0;
- NativeParallelFor( nthread, WorkForCondVarDtor<M>( nthread, mtx2 ) );
- }
-
- REMARK(" - timed_wait (i.e., wait_for)\n");
- // Test timed wait.
- {
- condition_variable cv_tw;
- M mtx_tw;
- barrier = 0;
- InitGlobalCounters();
- int nthr = nthread>4?4:nthread;
- NativeParallelFor( nthr, WorkForCondVarTimedWait<M>( nthr, cv_tw, mtx_tw ) );
- }
-
- REMARK(" - wait with notify_one\n");
- // Test wait and notify_one
- do {
- condition_variable cv3;
- M mtx3;
- InitGlobalCounters();
- NativeParallelFor( nthread, WorkForCondVarWaitAndNotifyOne<M>( nthread, cv3, mtx3 ) );
- } while( n_visit_to_waitq==0 || max_waitq_length==0 );
-
- REMARK(" - predicated wait with notify_all\n");
- // Test wait_pred and notify_all
- int delay_multiple = 1;
- do {
- condition_variable cv4;
- M mtx4;
- InitGlobalCounters();
- NativeParallelFor( nthread, WorkForCondVarWaitPredAndNotifyAll<M>( nthread, cv4, mtx4, delay_multiple ) );
- if( max_waitq_length<unsigned(nthread/2) )
- ++delay_multiple;
- } while( n_visit_to_waitq<=0 || max_waitq_length<unsigned(nthread/2) );
-}
-
-#if TBB_USE_EXCEPTIONS
-static tbb::atomic<int> err_count;
-
-#define TRY_AND_CATCH_RUNTIME_ERROR(op,msg) \
- try { \
- op; \
- ++err_count; \
- } catch( std::runtime_error& e ) {ASSERT( strstr(e.what(), msg) , NULL );} catch(...) {++err_count;}
-
-template<typename M>
-void TestUniqueLockException( const char * name ) {
- REMARK("testing %s TestUniqueLockException\n",name);
- M mtx;
- unique_lock<M> ul_0;
- err_count = 0;
-
- TRY_AND_CATCH_RUNTIME_ERROR( ul_0.lock(), "Operation not permitted" );
- TRY_AND_CATCH_RUNTIME_ERROR( ul_0.try_lock(), "Operation not permitted" );
-
- unique_lock<M> ul_1( mtx );
-
- TRY_AND_CATCH_RUNTIME_ERROR( ul_1.lock(), "Resource deadlock" );
- TRY_AND_CATCH_RUNTIME_ERROR( ul_1.try_lock(), "Resource deadlock" );
-
- ul_1.unlock();
- TRY_AND_CATCH_RUNTIME_ERROR( ul_1.unlock(), "Operation not permitted" );
-
- ASSERT( !err_count, "Some exceptions are not thrown or incorrect ones are thrown" );
-}
-
-template<typename M>
-void TestConditionVariableException( const char * name ) {
- REMARK("testing %s in TestConditionVariableException; yet to be implemented\n",name);
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-template<typename Mutex, typename RecursiveMutex>
-void DoCondVarTest()
-{
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK( "testing with %d threads\n", p );
- TestLocks<Mutex>( "mutex", p );
- TestLocks<RecursiveMutex>( "recursive_mutex", p );
-
- if( p<=1 ) continue;
-
- // for testing condition_variable, at least one sleeper and one notifier are needed
- TestConditionVariable<Mutex>( "mutex", p );
- }
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception handling tests are skipped.\n");
-#elif TBB_USE_EXCEPTIONS
- TestUniqueLockException<Mutex>( "mutex" );
- TestUniqueLockException<RecursiveMutex>( "recursive_mutex" );
- TestConditionVariableException<Mutex>( "mutex" );
-#endif /* TBB_USE_EXCEPTIONS */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// test critical section
-//
-#include "tbb/critical_section.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/enumerable_thread_specific.h"
-#include "tbb/tick_count.h"
-#include "harness_assert.h"
-#include "harness.h"
-#include <math.h>
-
-#include "harness_barrier.h"
-Harness::SpinBarrier sBarrier;
-tbb::critical_section cs;
-const int MAX_WORK = 300;
-
-struct BusyBody : NoAssign {
- tbb::enumerable_thread_specific<double> &locals;
- const int nThread;
- const int WorkRatiox100;
- int &unprotected_count;
- bool test_throw;
-
- BusyBody( int nThread_, int workRatiox100_, tbb::enumerable_thread_specific<double> &locals_, int &unprotected_count_, bool test_throw_) :
- locals(locals_),
- nThread(nThread_),
- WorkRatiox100(workRatiox100_),
- unprotected_count(unprotected_count_),
- test_throw(test_throw_) {
- sBarrier.initialize(nThread_);
- }
-
- void operator()(const int /* threadID */ ) const {
- int nIters = MAX_WORK/nThread;
- sBarrier.wait();
- tbb::tick_count t0 = tbb::tick_count::now();
- for(int j = 0; j < nIters; j++) {
-
- for(int i = 0; i < MAX_WORK * (100 - WorkRatiox100); i++) {
- locals.local() += 1.0;
- }
- cs.lock();
- ASSERT( !cs.try_lock(), "recursive try_lock must fail" );
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- if(test_throw && j == (nIters / 2)) {
- bool was_caught = false,
- unknown_exception = false;
- try {
- cs.lock();
- }
- catch(tbb::improper_lock& e) {
- ASSERT( e.what(), "Error message is absent" );
- was_caught = true;
- }
- catch(...) {
- was_caught = unknown_exception = true;
- }
- ASSERT(was_caught, "Recursive lock attempt did not throw");
- ASSERT(!unknown_exception, "tbb::improper_lock exception is expected");
- }
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- for(int i = 0; i < MAX_WORK * WorkRatiox100; i++) {
- locals.local() += 1.0;
- }
- unprotected_count++;
- cs.unlock();
- }
- locals.local() = (tbb::tick_count::now() - t0).seconds();
- }
-};
-
-struct BusyBodyScoped : NoAssign {
- tbb::enumerable_thread_specific<double> &locals;
- const int nThread;
- const int WorkRatiox100;
- int &unprotected_count;
- bool test_throw;
-
- BusyBodyScoped( int nThread_, int workRatiox100_, tbb::enumerable_thread_specific<double> &locals_, int &unprotected_count_, bool test_throw_) :
- locals(locals_),
- nThread(nThread_),
- WorkRatiox100(workRatiox100_),
- unprotected_count(unprotected_count_),
- test_throw(test_throw_) {
- sBarrier.initialize(nThread_);
- }
-
- void operator()(const int /* threadID */ ) const {
- int nIters = MAX_WORK/nThread;
- sBarrier.wait();
- tbb::tick_count t0 = tbb::tick_count::now();
- for(int j = 0; j < nIters; j++) {
-
- for(int i = 0; i < MAX_WORK * (100 - WorkRatiox100); i++) {
- locals.local() += 1.0;
- }
- {
- tbb::critical_section::scoped_lock my_lock(cs);
- for(int i = 0; i < MAX_WORK * WorkRatiox100; i++) {
- locals.local() += 1.0;
- }
- unprotected_count++;
- }
- }
- locals.local() = (tbb::tick_count::now() - t0).seconds();
- }
-};
-
-void
-RunOneCriticalSectionTest(int nThreads, int csWorkRatio, bool test_throw) {
- tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred);
- tbb::enumerable_thread_specific<double> test_locals;
- int myCount = 0;
- BusyBody myBody(nThreads, csWorkRatio, test_locals, myCount, test_throw);
- BusyBodyScoped myScopedBody(nThreads, csWorkRatio, test_locals, myCount, test_throw);
- init.initialize(nThreads);
- tbb::tick_count t0;
- {
- t0 = tbb::tick_count::now();
- myCount = 0;
- NativeParallelFor(nThreads, myBody);
- ASSERT(myCount == (MAX_WORK - (MAX_WORK % nThreads)), NULL);
- REMARK("%d threads, work ratio %d per cent, time %g", nThreads, csWorkRatio, (tbb::tick_count::now() - t0).seconds());
- if (nThreads > 1) {
- double etsSum = 0;
- double etsMax = 0;
- double etsMin = 0;
- double etsSigmaSq = 0;
- double etsSigma = 0;
-
- for(tbb::enumerable_thread_specific<double>::const_iterator ci = test_locals.begin(); ci != test_locals.end(); ci++) {
- etsSum += *ci;
- if(etsMax==0.0) {
- etsMin = *ci;
- }
- else {
- if(etsMin > *ci) etsMin = *ci;
- }
- if(etsMax < *ci) etsMax = *ci;
- }
- double etsAvg = etsSum / (double)nThreads;
- for(tbb::enumerable_thread_specific<double>::const_iterator ci = test_locals.begin(); ci != test_locals.end(); ci++) {
- etsSigma = etsAvg - *ci;
- etsSigmaSq += etsSigma * etsSigma;
- }
- // an attempt to gauge the "fairness" of the scheduling of the threads. We figure
- // the standard deviation, and compare it with the maximum deviation from the
- // average time. If the difference is 0 that means all threads finished in the same
- // amount of time. If non-zero, the difference is divided by the time, and the
- // negative log is taken. If > 2, then the difference is on the order of 0.01*t
- // where T is the average time. We aritrarily define this as "fair."
- etsSigma = sqrt(etsSigmaSq/double(nThreads));
- etsMax -= etsAvg; // max - a == delta1
- etsMin = etsAvg - etsMin; // a - min == delta2
- if(etsMax < etsMin) etsMax = etsMin;
- etsMax -= etsSigma;
- // ASSERT(etsMax >= 0, NULL); // shouldn't the maximum difference from the mean be > the stddev?
- etsMax = (etsMax > 0.0) ? etsMax : 0.0; // possible rounding error
- double fairness = etsMax / etsAvg;
- if(fairness == 0.0) {
- fairness = 100.0;
- }
- else fairness = - log10(fairness);
- if(fairness > 2.0 ) {
- REMARK(" Fair (%g)\n", fairness);
- }
- else {
- REMARK(" Unfair (%g)\n", fairness);
- }
- }
- myCount = 0;
- NativeParallelFor(nThreads, myScopedBody);
- ASSERT(myCount == (MAX_WORK - (MAX_WORK % nThreads)), NULL);
-
- }
-
- init.terminate();
-}
-
-void
-RunParallelTests() {
- for(int p = MinThread; p <= MaxThread; p++) {
- for(int cs_ratio = 1; cs_ratio < 95; cs_ratio *= 2) {
- RunOneCriticalSectionTest(p, cs_ratio, /*test_throw*/true);
- }
- }
-}
-
-int TestMain () {
- if(MinThread <= 0) MinThread = 1;
-
- if(MaxThread > 0) {
- RunParallelTests();
- }
-
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include <limits.h> // for INT_MAX
-#include "tbb/task_scheduler_init.h"
-#include "tbb/tbb_exception.h"
-#include "tbb/task.h"
-#include "tbb/atomic.h"
-#include "tbb/parallel_for.h"
-#include "tbb/parallel_reduce.h"
-#include "tbb/parallel_do.h"
-#include "tbb/pipeline.h"
-#include "tbb/parallel_scan.h"
-#include "tbb/blocked_range.h"
-#include "harness_assert.h"
-
-#if __TBB_TASK_GROUP_CONTEXT
-
-#define FLAT_RANGE 100000
-#define FLAT_GRAIN 100
-#define NESTING_RANGE 100
-#define NESTING_GRAIN 10
-#define NESTED_RANGE (FLAT_RANGE / NESTING_RANGE)
-#define NESTED_GRAIN (FLAT_GRAIN / NESTING_GRAIN)
-
-tbb::atomic<intptr_t> g_FedTasksCount; // number of tasks added by parallel_do feeder
-
-inline intptr_t Existed () { return INT_MAX; }
-
-#include "harness_eh.h"
-
-inline void ResetGlobals ( bool throwException = true, bool flog = false ) {
- ResetEhGlobals( throwException, flog );
- g_FedTasksCount = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Tests for tbb::parallel_for and tbb::parallel_reduce
-
-typedef size_t count_type;
-typedef tbb::blocked_range<count_type> range_type;
-
-inline intptr_t NumSubranges ( intptr_t length, intptr_t grain ) {
- intptr_t n = 1;
- for( ; length > grain; length -= length >> 1 )
- n *= 2;
- return n;
-}
-
-template<class Body>
-intptr_t TestNumSubrangesCalculation ( intptr_t length, intptr_t grain, intptr_t nested_length, intptr_t nested_grain ) {
- ResetGlobals();
- g_ThrowException = false;
- intptr_t nestingCalls = NumSubranges(length, grain),
- nestedCalls = NumSubranges(nested_length, nested_grain),
- maxExecuted = nestingCalls * (nestedCalls + 1);
- tbb::parallel_for( range_type(0, length, grain), Body() );
- ASSERT (g_CurExecuted == maxExecuted, "Wrong estimation of bodies invocation count");
- return maxExecuted;
-}
-
-class NoThrowParForBody {
-public:
- void operator()( const range_type& r ) const {
- volatile long x;
- count_type end = r.end();
- for( count_type i=r.begin(); i<end; ++i )
- x = 0;
- }
-};
-
-#if TBB_USE_EXCEPTIONS
-
-void Test0 () {
- ResetGlobals();
- tbb::simple_partitioner p;
- for( size_t i=0; i<10; ++i ) {
- tbb::parallel_for( range_type(0, 0, 1), NoThrowParForBody() );
- tbb::parallel_for( range_type(0, 0, 1), NoThrowParForBody(), p );
- tbb::parallel_for( range_type(0, 128, 8), NoThrowParForBody() );
- tbb::parallel_for( range_type(0, 128, 8), NoThrowParForBody(), p );
- }
-} // void Test0 ()
-
-//! Template that creates a functor suitable for parallel_reduce from a functor for parallel_for.
-template<typename ParForBody>
-class SimpleParReduceBody: NoAssign {
- ParForBody m_Body;
-public:
- void operator()( const range_type& r ) const { m_Body(r); }
- SimpleParReduceBody() {}
- SimpleParReduceBody( SimpleParReduceBody& left, tbb::split ) : m_Body(left.m_Body) {}
- void join( SimpleParReduceBody& /*right*/ ) {}
-}; // SimpleParReduceBody
-
-//! Test parallel_for and parallel_reduce for a given partitioner.
-/** The Body need only be suitable for a parallel_for. */
-template<typename ParForBody, typename Partitioner>
-void TestParallelLoopAux( Partitioner& partitioner ) {
- for( int i=0; i<2; ++i ) {
- ResetGlobals();
- TRY();
- if( i==0 )
- tbb::parallel_for( range_type(0, FLAT_RANGE, FLAT_GRAIN), ParForBody(), partitioner );
- else {
- SimpleParReduceBody<ParForBody> rb;
- tbb::parallel_reduce( range_type(0, FLAT_RANGE, FLAT_GRAIN), rb, partitioner );
- }
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "No exception thrown from the nesting parallel_for");
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
- }
-}
-
-//! Test with parallel_for and parallel_reduce, over all three kinds of partitioners.
-/** The Body only needs to be suitable for tbb::parallel_for. */
-template<typename Body>
-void TestParallelLoop() {
- // The simple and auto partitioners should be const, but not the affinity partitioner.
- const tbb::simple_partitioner p0;
- TestParallelLoopAux<Body>( p0 );
- const tbb::auto_partitioner p1;
- TestParallelLoopAux<Body>( p1 );
-}
-
-class SimpleParForBody: NoAssign {
-public:
- void operator()( const range_type& r ) const {
- Harness::ConcurrencyTracker ct;
- volatile long x;
- for( count_type i = r.begin(); i != r.end(); ++i )
- x = 0;
- ++g_CurExecuted;
- WaitUntilConcurrencyPeaks();
- ThrowTestException(1);
- }
-};
-
-void Test1() {
- TestParallelLoop<SimpleParForBody>();
-} // void Test1 ()
-
-class NestingParForBody: NoAssign {
-public:
- void operator()( const range_type& ) const {
- Harness::ConcurrencyTracker ct;
- ++g_CurExecuted;
- tbb::parallel_for( tbb::blocked_range<size_t>(0, NESTED_RANGE, NESTED_GRAIN), SimpleParForBody() );
- }
-};
-
-//! Uses parallel_for body containing a nested parallel_for with the default context not wrapped by a try-block.
-/** Nested algorithms are spawned inside the new bound context by default. Since
- exceptions thrown from the nested parallel_for are not handled by the caller
- (nesting parallel_for body) in this test, they will cancel all the sibling nested
- algorithms. **/
-void Test2 () {
- TestParallelLoop<NestingParForBody>();
-} // void Test2 ()
-
-class NestingParForBodyWithIsolatedCtx {
-public:
- void operator()( const range_type& ) const {
- tbb::task_group_context ctx(tbb::task_group_context::isolated);
- ++g_CurExecuted;
- tbb::parallel_for( tbb::blocked_range<size_t>(0, NESTED_RANGE, NESTED_GRAIN), SimpleParForBody(), tbb::simple_partitioner(), ctx );
- }
-};
-
-//! Uses parallel_for body invoking a nested parallel_for with an isolated context without a try-block.
-/** Even though exceptions thrown from the nested parallel_for are not handled
- by the caller in this test, they will not affect sibling nested algorithms
- already running because of the isolated contexts. However because the first
- exception cancels the root parallel_for only the first g_NumThreads subranges
- will be processed (which launch nested parallel_fors) **/
-void Test3 () {
- ResetGlobals();
- typedef NestingParForBodyWithIsolatedCtx body_type;
- intptr_t nestedCalls = NumSubranges(NESTED_RANGE, NESTED_GRAIN),
- minExecuted = (g_NumThreads - 1) * nestedCalls;
- TRY();
- tbb::parallel_for( range_type(0, NESTING_RANGE, NESTING_GRAIN), body_type() );
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "No exception thrown from the nesting parallel_for");
- if ( g_SolitaryException ) {
- ASSERT (g_CurExecuted > minExecuted, "Too few tasks survived exception");
- ASSERT (g_CurExecuted <= minExecuted + (g_ExecutedAtCatch + g_NumThreads), "Too many tasks survived exception");
- }
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-} // void Test3 ()
-
-class NestingParForExceptionSafeBody {
-public:
- void operator()( const range_type& ) const {
- tbb::task_group_context ctx(tbb::task_group_context::isolated);
- TRY();
- tbb::parallel_for( tbb::blocked_range<size_t>(0, NESTED_RANGE, NESTED_GRAIN), SimpleParForBody(), tbb::simple_partitioner(), ctx );
- CATCH();
- }
-};
-
-//! Uses parallel_for body invoking a nested parallel_for (with default bound context) inside a try-block.
-/** Since exception(s) thrown from the nested parallel_for are handled by the caller
- in this test, they do not affect neither other tasks of the the root parallel_for
- nor sibling nested algorithms. **/
-void Test4 () {
- ResetGlobals( true, true );
- intptr_t nestedCalls = NumSubranges(NESTED_RANGE, NESTED_GRAIN),
- nestingCalls = NumSubranges(NESTING_RANGE, NESTING_GRAIN),
- maxExecuted = nestingCalls * nestedCalls;
- TRY();
- tbb::parallel_for( range_type(0, NESTING_RANGE, NESTING_GRAIN), NestingParForExceptionSafeBody() );
- CATCH();
- ASSERT (!exceptionCaught, "All exceptions must have been handled in the parallel_for body");
- intptr_t minExecuted = 0;
- if ( g_SolitaryException ) {
- minExecuted = maxExecuted - nestedCalls;
- ASSERT (g_Exceptions == 1, "No exception registered");
- ASSERT (g_CurExecuted >= minExecuted, "Too few tasks executed");
- ASSERT (g_CurExecuted <= minExecuted + g_NumThreads, "Too many tasks survived exception");
- }
- else {
- minExecuted = g_Exceptions;
- ASSERT (g_Exceptions > 1 && g_Exceptions <= nestingCalls, "Unexpected actual number of exceptions");
- ASSERT (g_CurExecuted >= minExecuted, "Too many executed tasks reported");
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived multiple exceptions");
- ASSERT (g_CurExecuted <= nestingCalls * (1 + g_NumThreads), "Too many tasks survived exception");
- }
-} // void Test4 ()
-
-#endif /* TBB_USE_EXCEPTIONS */
-
-class ParForBodyToCancel {
-public:
- void operator()( const range_type& ) const {
- ++g_CurExecuted;
- CancellatorTask::WaitUntilReady();
- }
-};
-
-template<class B>
-class ParForLauncherTask : public tbb::task {
- tbb::task_group_context &my_ctx;
-
- tbb::task* execute () {
- tbb::parallel_for( range_type(0, FLAT_RANGE, FLAT_GRAIN), B(), tbb::simple_partitioner(), my_ctx );
- return NULL;
- }
-public:
- ParForLauncherTask ( tbb::task_group_context& ctx ) : my_ctx(ctx) {}
-};
-
-//! Test for cancelling an algorithm from outside (from a task running in parallel with the algorithm).
-void TestCancelation1 () {
- ResetGlobals( false );
- RunCancellationTest<ParForLauncherTask<ParForBodyToCancel>, CancellatorTask>( NumSubranges(FLAT_RANGE, FLAT_GRAIN) / 4 );
- ASSERT (g_CurExecuted < g_ExecutedAtCatch + g_NumThreads, "Too many tasks were executed after cancellation");
-}
-
-class CancellatorTask2 : public tbb::task {
- tbb::task_group_context &m_GroupToCancel;
-
- tbb::task* execute () {
- Harness::ConcurrencyTracker ct;
- WaitUntilConcurrencyPeaks();
- m_GroupToCancel.cancel_group_execution();
- g_ExecutedAtCatch = g_CurExecuted;
- return NULL;
- }
-public:
- CancellatorTask2 ( tbb::task_group_context& ctx, intptr_t ) : m_GroupToCancel(ctx) {}
-};
-
-class ParForBodyToCancel2 {
-public:
- void operator()( const range_type& ) const {
- ++g_CurExecuted;
- Harness::ConcurrencyTracker ct;
- // The test will hang (and be timed out by the test system) if is_cancelled() is broken
- while( !tbb::task::self().is_cancelled() )
- __TBB_Yield();
- }
-};
-
-//! Test for cancelling an algorithm from outside (from a task running in parallel with the algorithm).
-/** This version also tests task::is_cancelled() method. **/
-void TestCancelation2 () {
- ResetGlobals();
- RunCancellationTest<ParForLauncherTask<ParForBodyToCancel2>, CancellatorTask2>();
- ASSERT (g_ExecutedAtCatch < g_NumThreads, "Somehow worker tasks started their execution before the cancellator task");
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Some tasks were executed after cancellation");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Regression test based on the contribution by the author of the following forum post:
-// http://softwarecommunity.intel.com/isn/Community/en-US/forums/thread/30254959.aspx
-
-#define LOOP_COUNT 16
-#define MAX_NESTING 3
-#define REDUCE_RANGE 1024
-#define REDUCE_GRAIN 256
-
-class Worker {
-public:
- void DoWork (int & result, int nest);
-};
-
-class RecursiveParReduceBodyWithSharedWorker {
- Worker * m_SharedWorker;
- int m_NestingLevel;
- int m_Result;
-public:
- RecursiveParReduceBodyWithSharedWorker ( RecursiveParReduceBodyWithSharedWorker& src, tbb::split )
- : m_SharedWorker(src.m_SharedWorker)
- , m_NestingLevel(src.m_NestingLevel)
- , m_Result(0)
- {}
- RecursiveParReduceBodyWithSharedWorker ( Worker *w, int nesting )
- : m_SharedWorker(w)
- , m_NestingLevel(nesting)
- , m_Result(0)
- {}
-
- void operator() ( const tbb::blocked_range<size_t>& r ) {
- for (size_t i = r.begin (); i != r.end (); ++i) {
- int subtotal = 0;
- m_SharedWorker->DoWork (subtotal, m_NestingLevel);
- m_Result += subtotal;
- }
- }
- void join (const RecursiveParReduceBodyWithSharedWorker & x) {
- m_Result += x.m_Result;
- }
- int result () { return m_Result; }
-};
-
-void Worker::DoWork ( int& result, int nest ) {
- ++nest;
- if ( nest < MAX_NESTING ) {
- RecursiveParReduceBodyWithSharedWorker rt (this, nest);
- tbb::parallel_reduce (tbb::blocked_range<size_t>(0, REDUCE_RANGE, REDUCE_GRAIN), rt);
- result = rt.result ();
- }
- else
- ++result;
-}
-
-//! Regression test for hanging that occurred with the first version of cancellation propagation
-void TestCancelation3 () {
- Worker w;
- int result = 0;
- w.DoWork (result, 0);
- ASSERT ( result == 1048576, "Wrong calculation result");
-}
-
-void RunParForAndReduceTests () {
- REMARK( "parallel for and reduce tests\n" );
- tbb::task_scheduler_init init (g_NumThreads);
- g_Master = Harness::CurrentTid();
-
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- Test0();
- Test1();
- Test2();
- Test3();
- Test4();
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- TestCancelation1();
- TestCancelation2();
- TestCancelation3();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Tests for tbb::parallel_do
-
-#define ITER_RANGE 1000
-#define ITEMS_TO_FEED 50
-#define NESTED_ITER_RANGE 100
-#define NESTING_ITER_RANGE 50
-
-#define PREPARE_RANGE(Iterator, rangeSize) \
- size_t test_vector[rangeSize + 1]; \
- for (int i =0; i < rangeSize; i++) \
- test_vector[i] = i; \
- Iterator begin(&test_vector[0]); \
- Iterator end(&test_vector[rangeSize])
-
-void Feed ( tbb::parallel_do_feeder<size_t> &feeder, size_t val ) {
- if (g_FedTasksCount < ITEMS_TO_FEED) {
- ++g_FedTasksCount;
- feeder.add(val);
- }
-}
-
-#include "harness_iterator.h"
-
-#if TBB_USE_EXCEPTIONS
-
-// Simple functor object with exception
-class SimpleParDoBody {
-public:
- void operator() ( size_t &value ) const {
- ++g_CurExecuted;
- Harness::ConcurrencyTracker ct;
- value += 1000;
- WaitUntilConcurrencyPeaks();
- ThrowTestException(1);
- }
-};
-
-// Simple functor object with exception and feeder
-class SimpleParDoBodyWithFeeder : SimpleParDoBody {
-public:
- void operator() ( size_t &value, tbb::parallel_do_feeder<size_t> &feeder ) const {
- Feed(feeder, 0);
- SimpleParDoBody::operator()(value);
- }
-};
-
-// Tests exceptions without nesting
-template <class Iterator, class simple_body>
-void Test1_parallel_do () {
- ResetGlobals();
- PREPARE_RANGE(Iterator, ITER_RANGE);
- TRY();
- tbb::parallel_do<Iterator, simple_body>(begin, end, simple_body() );
- CATCH_AND_ASSERT();
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-
-} // void Test1_parallel_do ()
-
-template <class Iterator>
-class NestingParDoBody {
-public:
- void operator()( size_t& /*value*/ ) const {
- ++g_CurExecuted;
- PREPARE_RANGE(Iterator, NESTED_ITER_RANGE);
- tbb::parallel_do<Iterator, SimpleParDoBody>(begin, end, SimpleParDoBody());
- }
-};
-
-template <class Iterator>
-class NestingParDoBodyWithFeeder : NestingParDoBody<Iterator> {
-public:
- void operator()( size_t& value, tbb::parallel_do_feeder<size_t>& feeder ) const {
- Feed(feeder, 0);
- NestingParDoBody<Iterator>::operator()(value);
- }
-};
-
-//! Uses parallel_do body containing a nested parallel_do with the default context not wrapped by a try-block.
-/** Nested algorithms are spawned inside the new bound context by default. Since
- exceptions thrown from the nested parallel_do are not handled by the caller
- (nesting parallel_do body) in this test, they will cancel all the sibling nested
- algorithms. **/
-template <class Iterator, class nesting_body>
-void Test2_parallel_do () {
- ResetGlobals();
- PREPARE_RANGE(Iterator, ITER_RANGE);
- TRY();
- tbb::parallel_do<Iterator, nesting_body >(begin, end, nesting_body() );
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "No exception thrown from the nesting parallel_for");
- //if ( g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-} // void Test2_parallel_do ()
-
-template <class Iterator>
-class NestingParDoBodyWithIsolatedCtx {
-public:
- void operator()( size_t& /*value*/ ) const {
- tbb::task_group_context ctx(tbb::task_group_context::isolated);
- ++g_CurExecuted;
- PREPARE_RANGE(Iterator, NESTED_ITER_RANGE);
- tbb::parallel_do<Iterator, SimpleParDoBody>(begin, end, SimpleParDoBody(), ctx);
- }
-};
-
-template <class Iterator>
-class NestingParDoBodyWithIsolatedCtxWithFeeder : NestingParDoBodyWithIsolatedCtx<Iterator> {
-public:
- void operator()( size_t& value, tbb::parallel_do_feeder<size_t> &feeder ) const {
- Feed(feeder, 0);
- NestingParDoBodyWithIsolatedCtx<Iterator>::operator()(value);
- }
-};
-
-//! Uses parallel_do body invoking a nested parallel_do with an isolated context without a try-block.
-/** Even though exceptions thrown from the nested parallel_do are not handled
- by the caller in this test, they will not affect sibling nested algorithms
- already running because of the isolated contexts. However because the first
- exception cancels the root parallel_do only the first g_NumThreads subranges
- will be processed (which launch nested parallel_dos) **/
-template <class Iterator, class nesting_body>
-void Test3_parallel_do () {
- ResetGlobals();
- PREPARE_RANGE(Iterator, NESTING_ITER_RANGE);
- intptr_t nestedCalls = NESTED_ITER_RANGE,
- minExecuted = (g_NumThreads - 1) * nestedCalls;
- TRY();
- tbb::parallel_do<Iterator, nesting_body >(begin, end, nesting_body());
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "No exception thrown from the nesting parallel_for");
- if ( g_SolitaryException ) {
- ASSERT (g_CurExecuted > minExecuted, "Too few tasks survived exception");
- ASSERT (g_CurExecuted <= minExecuted + (g_ExecutedAtCatch + g_NumThreads), "Too many tasks survived exception");
- }
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-} // void Test3_parallel_do ()
-
-template <class Iterator>
-class NestingParDoWithEhBody {
-public:
- void operator()( size_t& /*value*/ ) const {
- tbb::task_group_context ctx(tbb::task_group_context::isolated);
- PREPARE_RANGE(Iterator, NESTED_ITER_RANGE);
- TRY();
- tbb::parallel_do<Iterator, SimpleParDoBody>(begin, end, SimpleParDoBody(), ctx);
- CATCH();
- }
-};
-
-template <class Iterator>
-class NestingParDoWithEhBodyWithFeeder : NoAssign, NestingParDoWithEhBody<Iterator> {
-public:
- void operator()( size_t &value, tbb::parallel_do_feeder<size_t> &feeder ) const {
- Feed(feeder, 0);
- NestingParDoWithEhBody<Iterator>::operator()(value);
- }
-};
-
-//! Uses parallel_for body invoking a nested parallel_for (with default bound context) inside a try-block.
-/** Since exception(s) thrown from the nested parallel_for are handled by the caller
- in this test, they do not affect neither other tasks of the the root parallel_for
- nor sibling nested algorithms. **/
-template <class Iterator, class nesting_body_with_eh>
-void Test4_parallel_do () {
- ResetGlobals( true, true );
- PREPARE_RANGE(Iterator, NESTING_ITER_RANGE);
- TRY();
- tbb::parallel_do<Iterator, nesting_body_with_eh>(begin, end, nesting_body_with_eh());
- CATCH();
- ASSERT (!exceptionCaught, "All exceptions must have been handled in the parallel_do body");
- intptr_t nestedCalls = NESTED_ITER_RANGE,
- nestingCalls = NESTING_ITER_RANGE + g_FedTasksCount,
- maxExecuted = nestingCalls * nestedCalls,
- minExecuted = 0;
- if ( g_SolitaryException ) {
- minExecuted = maxExecuted - nestedCalls;
- ASSERT (g_Exceptions == 1, "No exception registered");
- ASSERT (g_CurExecuted >= minExecuted, "Too few tasks executed");
- ASSERT (g_CurExecuted <= minExecuted + g_NumThreads, "Too many tasks survived exception");
- }
- else {
- minExecuted = g_Exceptions;
- ASSERT (g_Exceptions > 1 && g_Exceptions <= nestingCalls, "Unexpected actual number of exceptions");
- ASSERT (g_CurExecuted >= minExecuted, "Too many executed tasks reported");
- ASSERT (g_CurExecuted < g_ExecutedAtCatch + g_NumThreads + nestingCalls, "Too many tasks survived multiple exceptions");
- ASSERT (g_CurExecuted <= nestingCalls * (1 + g_NumThreads), "Too many tasks survived exception");
- }
-} // void Test4_parallel_do ()
-
-// This body throws an exception only if the task was added by feeder
-class ParDoBodyWithThrowingFeederTasks {
-public:
- //! This form of the function call operator can be used when the body needs to add more work during the processing
- void operator() ( size_t &value, tbb::parallel_do_feeder<size_t> &feeder ) const {
- ++g_CurExecuted;
- Feed(feeder, 1);
- if (value == 1)
- ThrowTestException(1);
- }
-}; // class ParDoBodyWithThrowingFeederTasks
-
-// Test exception in task, which was added by feeder.
-template <class Iterator>
-void Test5_parallel_do () {
- ResetGlobals();
- PREPARE_RANGE(Iterator, ITER_RANGE);
- TRY();
- tbb::parallel_do<Iterator, ParDoBodyWithThrowingFeederTasks>(begin, end, ParDoBodyWithThrowingFeederTasks());
- CATCH();
- if (g_SolitaryException)
- ASSERT (exceptionCaught, "At least one exception should occur");
-} // void Test5_parallel_do ()
-
-#endif /* TBB_USE_EXCEPTIONS */
-
-class ParDoBodyToCancel {
-public:
- void operator()( size_t& /*value*/ ) const {
- ++g_CurExecuted;
- CancellatorTask::WaitUntilReady();
- }
-};
-
-class ParDoBodyToCancelWithFeeder : ParDoBodyToCancel {
-public:
- void operator()( size_t& value, tbb::parallel_do_feeder<size_t> &feeder ) const {
- Feed(feeder, 0);
- ParDoBodyToCancel::operator()(value);
- }
-};
-
-template<class B, class Iterator>
-class ParDoWorkerTask : public tbb::task {
- tbb::task_group_context &my_ctx;
-
- tbb::task* execute () {
- PREPARE_RANGE(Iterator, NESTED_ITER_RANGE);
- tbb::parallel_do<Iterator, B>( begin, end, B(), my_ctx );
- return NULL;
- }
-public:
- ParDoWorkerTask ( tbb::task_group_context& ctx ) : my_ctx(ctx) {}
-};
-
-//! Test for cancelling an algorithm from outside (from a task running in parallel with the algorithm).
-template <class Iterator, class body_to_cancel>
-void TestCancelation1_parallel_do () {
- ResetGlobals( false );
- intptr_t threshold = 10;
- tbb::task_group_context ctx;
- ctx.reset();
- tbb::empty_task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- r.set_ref_count(3);
- r.spawn( *new( r.allocate_child() ) CancellatorTask(ctx, threshold) );
- __TBB_Yield();
- r.spawn( *new( r.allocate_child() ) ParDoWorkerTask<body_to_cancel, Iterator>(ctx) );
- TRY();
- r.wait_for_all();
- CATCH_AND_FAIL();
- ASSERT (g_CurExecuted < g_ExecutedAtCatch + g_NumThreads, "Too many tasks were executed after cancellation");
- r.destroy(r);
-}
-
-class ParDoBodyToCancel2 {
-public:
- void operator()( size_t& /*value*/ ) const {
- ++g_CurExecuted;
- Harness::ConcurrencyTracker ct;
- // The test will hang (and be timed out by the test system) if is_cancelled() is broken
- while( !tbb::task::self().is_cancelled() )
- __TBB_Yield();
- }
-};
-
-class ParDoBodyToCancel2WithFeeder : ParDoBodyToCancel2 {
-public:
- void operator()( size_t& value, tbb::parallel_do_feeder<size_t> &feeder ) const {
- Feed(feeder, 0);
- ParDoBodyToCancel2::operator()(value);
- }
-};
-
-//! Test for cancelling an algorithm from outside (from a task running in parallel with the algorithm).
-/** This version also tests task::is_cancelled() method. **/
-template <class Iterator, class body_to_cancel>
-void TestCancelation2_parallel_do () {
- ResetGlobals();
- RunCancellationTest<ParDoWorkerTask<body_to_cancel, Iterator>, CancellatorTask2>();
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Some tasks were executed after cancellation");
-}
-
-#define RunWithSimpleBody(func, body) \
- func<Harness::RandomIterator<size_t>, body>(); \
- func<Harness::RandomIterator<size_t>, body##WithFeeder>(); \
- func<Harness::ForwardIterator<size_t>, body>(); \
- func<Harness::ForwardIterator<size_t>, body##WithFeeder>()
-
-#define RunWithTemplatedBody(func, body) \
- func<Harness::RandomIterator<size_t>, body<Harness::RandomIterator<size_t> > >(); \
- func<Harness::RandomIterator<size_t>, body##WithFeeder<Harness::RandomIterator<size_t> > >(); \
- func<Harness::ForwardIterator<size_t>, body<Harness::ForwardIterator<size_t> > >(); \
- func<Harness::ForwardIterator<size_t>, body##WithFeeder<Harness::ForwardIterator<size_t> > >()
-
-void RunParDoTests() {
- REMARK( "parallel do tests\n" );
- tbb::task_scheduler_init init (g_NumThreads);
- g_Master = Harness::CurrentTid();
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- RunWithSimpleBody(Test1_parallel_do, SimpleParDoBody);
- RunWithTemplatedBody(Test2_parallel_do, NestingParDoBody);
- RunWithTemplatedBody(Test3_parallel_do, NestingParDoBodyWithIsolatedCtx);
- RunWithTemplatedBody(Test4_parallel_do, NestingParDoWithEhBody);
- Test5_parallel_do<Harness::ForwardIterator<size_t> >();
- Test5_parallel_do<Harness::RandomIterator<size_t> >();
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- RunWithSimpleBody(TestCancelation1_parallel_do, ParDoBodyToCancel);
- RunWithSimpleBody(TestCancelation2_parallel_do, ParDoBodyToCancel2);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Tests for tbb::pipeline
-
-#define NUM_ITEMS 100
-
-const size_t c_DataEndTag = size_t(~0);
-
-int g_NumTokens = 0;
-
-// Simple input filter class, it assigns 1 to all array members
-// It stops when it receives item equal to -1
-class InputFilter: public tbb::filter {
- tbb::atomic<size_t> m_Item;
- size_t m_Buffer[NUM_ITEMS + 1];
-public:
- InputFilter() : tbb::filter(parallel) {
- m_Item = 0;
- for (size_t i = 0; i < NUM_ITEMS; ++i )
- m_Buffer[i] = 1;
- m_Buffer[NUM_ITEMS] = c_DataEndTag;
- }
-
- void* operator()( void* ) {
- size_t item = m_Item.fetch_and_increment();
- if ( item >= NUM_ITEMS )
- return NULL;
- m_Buffer[item] = 1;
- return &m_Buffer[item];
- }
-
- size_t* buffer() { return m_Buffer; }
-}; // class InputFilter
-
-// Pipeline filter, without exceptions throwing
-class NoThrowFilter : public tbb::filter {
- size_t m_Value;
-public:
- enum operation {
- addition,
- subtraction,
- multiplication
- } m_Operation;
-
- NoThrowFilter(operation _operation, size_t value, bool is_parallel)
- : filter(is_parallel? tbb::filter::parallel : tbb::filter::serial_in_order),
- m_Value(value), m_Operation(_operation)
- {}
- void* operator()(void* item) {
- size_t &value = *(size_t*)item;
- ASSERT(value != c_DataEndTag, "terminator element is being processed");
- switch (m_Operation){
- case addition:
- value += m_Value;
- break;
- case subtraction:
- value -= m_Value;
- break;
- case multiplication:
- value *= m_Value;
- break;
- default:
- ASSERT(0, "Wrong operation parameter passed to NoThrowFilter");
- } // switch (m_Operation)
- return item;
- }
-};
-
-// Test pipeline without exceptions throwing
-void Test0_pipeline () {
- ResetGlobals();
- // Run test when serial filter is the first non-input filter
- InputFilter inputFilter;
- NoThrowFilter filter1(NoThrowFilter::addition, 99, false);
- NoThrowFilter filter2(NoThrowFilter::subtraction, 90, true);
- NoThrowFilter filter3(NoThrowFilter::multiplication, 5, false);
- // Result should be 50 for all items except the last
- tbb::pipeline p;
- p.add_filter(inputFilter);
- p.add_filter(filter1);
- p.add_filter(filter2);
- p.add_filter(filter3);
- p.run(8);
- for (size_t i = 0; i < NUM_ITEMS; ++i)
- ASSERT(inputFilter.buffer()[i] == 50, "pipeline didn't process items properly");
-} // void Test0_pipeline ()
-
-#if TBB_USE_EXCEPTIONS
-
-// Simple filter with exception throwing
-class SimpleFilter : public tbb::filter {
- bool m_canThrow;
-public:
- SimpleFilter (tbb::filter::mode _mode, bool canThrow ) : filter (_mode), m_canThrow(canThrow) {}
-
- void* operator()(void* item) {
- ++g_CurExecuted;
- if ( m_canThrow ) {
- if ( !is_serial() ) {
- Harness::ConcurrencyTracker ct;
- WaitUntilConcurrencyPeaks( min(g_NumTokens, g_NumThreads) );
- }
- ThrowTestException(1);
- }
- return item;
- }
-}; // class SimpleFilter
-
-// This enumeration represents filters order in pipeline
-struct FilterSet {
- tbb::filter::mode mode1,
- mode2;
- bool throw1,
- throw2;
-
- FilterSet( tbb::filter::mode m1, tbb::filter::mode m2, bool t1, bool t2 )
- : mode1(m1), mode2(m2), throw1(t1), throw2(t2)
- {}
-}; // struct FilterSet
-
-FilterSet serial_parallel( tbb::filter::serial, tbb::filter::parallel, false, true );
-
-template<typename InFilter, typename Filter>
-class CustomPipeline : protected tbb::pipeline {
- InFilter inputFilter;
- Filter filter1;
- Filter filter2;
-public:
- CustomPipeline( const FilterSet& filters )
- : filter1(filters.mode1, filters.throw1), filter2(filters.mode2, filters.throw2)
- {
- add_filter(inputFilter);
- add_filter(filter1);
- add_filter(filter2);
- }
- void run () { tbb::pipeline::run(g_NumTokens); }
- void run ( tbb::task_group_context& ctx ) { tbb::pipeline::run(g_NumTokens, ctx); }
-
- using tbb::pipeline::add_filter;
-};
-
-typedef CustomPipeline<InputFilter, SimpleFilter> SimplePipeline;
-
-// Tests exceptions without nesting
-void Test1_pipeline ( const FilterSet& filters ) {
- ResetGlobals();
- SimplePipeline testPipeline(filters);
- TRY();
- testPipeline.run();
- if ( g_CurExecuted == 2 * NUM_ITEMS ) {
- // In case of all serial filters they might be all executed in the thread(s)
- // where exceptions are not allowed by the common test logic. So we just quit.
- return;
- }
- CATCH_AND_ASSERT();
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-
-} // void Test1_pipeline ()
-
-// Filter with nesting
-class NestingFilter : public tbb::filter {
-public:
- NestingFilter (tbb::filter::mode _mode, bool ) : filter (_mode) {}
-
- void* operator()(void* item) {
- ++g_CurExecuted;
- SimplePipeline testPipeline(serial_parallel);
- testPipeline.run();
- return item;
- }
-}; // class NestingFilter
-
-//! Uses pipeline containing a nested pipeline with the default context not wrapped by a try-block.
-/** Nested algorithms are spawned inside the new bound context by default. Since
- exceptions thrown from the nested pipeline are not handled by the caller
- (nesting pipeline body) in this test, they will cancel all the sibling nested
- algorithms. **/
-void Test2_pipeline ( const FilterSet& filters ) {
- ResetGlobals();
- CustomPipeline<InputFilter, NestingFilter> testPipeline(filters);
- TRY();
- testPipeline.run();
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "No exception thrown from the nesting pipeline");
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-} // void Test2_pipeline ()
-
-class NestingFilterWithIsolatedCtx : public tbb::filter {
-public:
- NestingFilterWithIsolatedCtx(tbb::filter::mode m, bool ) : filter(m) {}
-
- void* operator()(void* item) {
- ++g_CurExecuted;
- tbb::task_group_context ctx(tbb::task_group_context::isolated);
- SimplePipeline testPipeline(serial_parallel);
- testPipeline.run(ctx);
- return item;
- }
-}; // class NestingFilterWithIsolatedCtx
-
-//! Uses pipeline invoking a nested pipeline with an isolated context without a try-block.
-/** Even though exceptions thrown from the nested pipeline are not handled
- by the caller in this test, they will not affect sibling nested algorithms
- already running because of the isolated contexts. However because the first
- exception cancels the root parallel_do only the first g_NumThreads subranges
- will be processed (which launch nested pipelines) **/
-void Test3_pipeline ( const FilterSet& filters ) {
- ResetGlobals();
- intptr_t nestedCalls = 100,
- minExecuted = (g_NumThreads - 1) * nestedCalls;
- CustomPipeline<InputFilter, NestingFilterWithIsolatedCtx> testPipeline(filters);
- TRY();
- testPipeline.run();
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "No exception thrown from the nesting parallel_for");
- if ( g_SolitaryException ) {
- ASSERT (g_CurExecuted > minExecuted, "Too few tasks survived exception");
- ASSERT (g_CurExecuted <= minExecuted + (g_ExecutedAtCatch + g_NumThreads), "Too many tasks survived exception");
- }
- ASSERT (g_Exceptions == 1, "No try_blocks in any body expected in this test");
- if ( !g_SolitaryException )
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived exception");
-} // void Test3_pipeline ()
-
-class NestingFilterWithEhBody : public tbb::filter {
-public:
- NestingFilterWithEhBody(tbb::filter::mode m, bool ) : filter(m) {}
-
- void* operator()(void* item) {
- tbb::task_group_context ctx(tbb::task_group_context::isolated);
- SimplePipeline testPipeline(serial_parallel);
- TRY();
- testPipeline.run(ctx);
- CATCH();
- return item;
- }
-}; // class NestingFilterWithEhBody
-
-//! Uses pipeline body invoking a nested pipeline (with default bound context) inside a try-block.
-/** Since exception(s) thrown from the nested pipeline are handled by the caller
- in this test, they do not affect neither other tasks of the the root pipeline
- nor sibling nested algorithms. **/
-void Test4_pipeline ( const FilterSet& filters ) {
-#if __GNUC__ && !__INTEL_COMPILER
- if ( strncmp(__VERSION__, "4.1.0", 5) == 0 ) {
- REMARK_ONCE("Known issue: one of exception handling tests is skipped.\n");
- return;
- }
-#endif
- ResetGlobals( true, true );
- intptr_t nestedCalls = NUM_ITEMS + 1,
- nestingCalls = 2 * (NUM_ITEMS + 1),
- maxExecuted = nestingCalls * nestedCalls;
- CustomPipeline<InputFilter, NestingFilterWithEhBody> testPipeline(filters);
- TRY();
- testPipeline.run();
- CATCH_AND_ASSERT();
- ASSERT (!exceptionCaught, "All exceptions must have been handled in the parallel_do body");
- intptr_t minExecuted = 0;
- if ( g_SolitaryException ) {
- minExecuted = maxExecuted - nestedCalls;
- ASSERT (g_Exceptions != 0, "No exception registered");
- ASSERT (g_CurExecuted <= minExecuted + g_NumThreads, "Too many tasks survived exception");
- }
- else {
- minExecuted = g_Exceptions;
- ASSERT (g_Exceptions > 1 && g_Exceptions <= nestingCalls, "Unexpected actual number of exceptions");
- ASSERT (g_CurExecuted >= minExecuted, "Too many executed tasks reported");
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks survived multiple exceptions");
- }
-} // void Test4_pipeline ()
-
-//! Testing filter::finalize method
-#define BUFFER_SIZE 32
-#define NUM_BUFFERS 1024
-
-tbb::atomic<size_t> g_AllocatedCount; // Number of currently allocated buffers
-tbb::atomic<size_t> g_TotalCount; // Total number of allocated buffers
-
-//! Base class for all filters involved in finalize method testing
-class FinalizationBaseFilter : public tbb::filter {
-public:
- FinalizationBaseFilter ( tbb::filter::mode m ) : filter(m) {}
-
- // Deletes buffers if exception occured
- virtual void finalize( void* item ) {
- size_t* m_Item = (size_t*)item;
- delete[] m_Item;
- --g_AllocatedCount;
- }
-};
-
-//! Input filter to test finalize method
-class InputFilterWithFinalization: public FinalizationBaseFilter {
-public:
- InputFilterWithFinalization() : FinalizationBaseFilter(tbb::filter::serial) {
- g_TotalCount = 0;
- }
- void* operator()( void* ){
- if (g_TotalCount == NUM_BUFFERS)
- return NULL;
- size_t* item = new size_t[BUFFER_SIZE];
- for (int i = 0; i < BUFFER_SIZE; i++)
- item[i] = 1;
- ++g_TotalCount;
- ++g_AllocatedCount;
- return item;
- }
-};
-
-// The filter multiplies each buffer item by 10.
-class ProcessingFilterWithFinalization : public FinalizationBaseFilter {
-public:
- ProcessingFilterWithFinalization (tbb::filter::mode _mode, bool) : FinalizationBaseFilter (_mode) {}
-
- void* operator()( void* item) {
- if (g_TotalCount > NUM_BUFFERS / 2)
- ThrowTestException(1);
- size_t* m_Item = (size_t*)item;
- for (int i = 0; i < BUFFER_SIZE; i++)
- m_Item[i] *= 10;
- return item;
- }
-};
-
-// Output filter deletes previously allocated buffer
-class OutputFilterWithFinalization : public FinalizationBaseFilter {
-public:
- OutputFilterWithFinalization (tbb::filter::mode m) : FinalizationBaseFilter (m) {}
-
- void* operator()( void* item){
- size_t* m_Item = (size_t*)item;
- delete[] m_Item;
- --g_AllocatedCount;
- return NULL;
- }
-};
-
-//! Tests filter::finalize method
-void Test5_pipeline ( const FilterSet& filters ) {
- ResetGlobals();
- g_AllocatedCount = 0;
- CustomPipeline<InputFilterWithFinalization, ProcessingFilterWithFinalization> testPipeline(filters);
- OutputFilterWithFinalization my_output_filter(tbb::filter::parallel);
-
- testPipeline.add_filter(my_output_filter);
- TRY();
- testPipeline.run();
- CATCH();
- ASSERT (g_AllocatedCount == 0, "Memory leak: Some my_object weren't destroyed");
-} // void Test5_pipeline ()
-
-//! Tests pipeline function passed with different combination of filters
-template<void testFunc(const FilterSet&)>
-void TestWithDifferentFilters() {
- const int NumFilterTypes = 3;
- const tbb::filter::mode modes[NumFilterTypes] = {
- tbb::filter::parallel,
- tbb::filter::serial,
- tbb::filter::serial_out_of_order
- };
- for ( int i = 0; i < NumFilterTypes; ++i ) {
- for ( int j = 0; j < NumFilterTypes; ++j ) {
- for ( int k = 0; k < 2; ++k )
- testFunc( FilterSet(modes[i], modes[j], k == 0, k != 0) );
- }
- }
-}
-
-#endif /* TBB_USE_EXCEPTIONS */
-
-class FilterToCancel : public tbb::filter {
-public:
- FilterToCancel(bool is_parallel)
- : filter( is_parallel ? tbb::filter::parallel : tbb::filter::serial_in_order )
- {}
- void* operator()(void* item) {
- ++g_CurExecuted;
- CancellatorTask::WaitUntilReady();
- return item;
- }
-}; // class FilterToCancel
-
-template <class Filter_to_cancel>
-class PipelineLauncherTask : public tbb::task {
- tbb::task_group_context &my_ctx;
-public:
- PipelineLauncherTask ( tbb::task_group_context& ctx ) : my_ctx(ctx) {}
-
- tbb::task* execute () {
- // Run test when serial filter is the first non-input filter
- InputFilter inputFilter;
- Filter_to_cancel filterToCancel(true);
- tbb::pipeline p;
- p.add_filter(inputFilter);
- p.add_filter(filterToCancel);
- p.run(g_NumTokens, my_ctx);
- return NULL;
- }
-};
-
-//! Test for cancelling an algorithm from outside (from a task running in parallel with the algorithm).
-void TestCancelation1_pipeline () {
- ResetGlobals();
- g_ThrowException = false;
- intptr_t threshold = 10;
- tbb::task_group_context ctx;
- ctx.reset();
- tbb::empty_task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- r.set_ref_count(3);
- r.spawn( *new( r.allocate_child() ) CancellatorTask(ctx, threshold) );
- __TBB_Yield();
- r.spawn( *new( r.allocate_child() ) PipelineLauncherTask<FilterToCancel>(ctx) );
- TRY();
- r.wait_for_all();
- CATCH_AND_FAIL();
- r.destroy(r);
- ASSERT (g_CurExecuted < g_ExecutedAtCatch + g_NumThreads, "Too many tasks were executed after cancellation");
-}
-
-class FilterToCancel2 : public tbb::filter {
-public:
- FilterToCancel2(bool is_parallel)
- : filter ( is_parallel ? tbb::filter::parallel : tbb::filter::serial_in_order)
- {}
-
- void* operator()(void* item) {
- ++g_CurExecuted;
- Harness::ConcurrencyTracker ct;
- // The test will hang (and be timed out by the tesst system) if is_cancelled() is broken
- while( !tbb::task::self().is_cancelled() )
- __TBB_Yield();
- return item;
- }
-};
-
-//! Test for cancelling an algorithm from outside (from a task running in parallel with the algorithm).
-/** This version also tests task::is_cancelled() method. **/
-void TestCancelation2_pipeline () {
- ResetGlobals();
- RunCancellationTest<PipelineLauncherTask<FilterToCancel2>, CancellatorTask2>();
- ASSERT (g_CurExecuted <= g_ExecutedAtCatch, "Some tasks were executed after cancellation");
-}
-
-void RunPipelineTests() {
- REMARK( "pipeline tests\n" );
- tbb::task_scheduler_init init (g_NumThreads);
- g_Master = Harness::CurrentTid();
- g_NumTokens = 2 * g_NumThreads;
-
- Test0_pipeline();
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- TestWithDifferentFilters<Test1_pipeline>();
- TestWithDifferentFilters<Test2_pipeline>();
- TestWithDifferentFilters<Test3_pipeline>();
- TestWithDifferentFilters<Test4_pipeline>();
- TestWithDifferentFilters<Test5_pipeline>();
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- TestCancelation1_pipeline();
- TestCancelation2_pipeline();
-}
-
-#endif /* __TBB_TASK_GROUP_CONTEXT */
-
-#if TBB_USE_EXCEPTIONS
-
-class MyCapturedException : public tbb::captured_exception {
-public:
- static int m_refCount;
-
- MyCapturedException () : tbb::captured_exception("MyCapturedException", "test") { ++m_refCount; }
- ~MyCapturedException () throw() { --m_refCount; }
-
- MyCapturedException* move () throw() {
- MyCapturedException* movee = (MyCapturedException*)malloc(sizeof(MyCapturedException));
- return ::new (movee) MyCapturedException;
- }
- void destroy () throw() {
- this->~MyCapturedException();
- free(this);
- }
- void operator delete ( void* p ) { free(p); }
-};
-
-int MyCapturedException::m_refCount = 0;
-
-void DeleteTbbException ( volatile tbb::tbb_exception* pe ) {
- delete pe;
-}
-
-void TestTbbExceptionAPI () {
- const char *name = "Test captured exception",
- *reason = "Unit testing";
- tbb::captured_exception e(name, reason);
- ASSERT (strcmp(e.name(), name) == 0, "Setting captured exception name failed");
- ASSERT (strcmp(e.what(), reason) == 0, "Setting captured exception reason failed");
- tbb::captured_exception c(e);
- ASSERT (strcmp(c.name(), e.name()) == 0, "Copying captured exception name failed");
- ASSERT (strcmp(c.what(), e.what()) == 0, "Copying captured exception reason failed");
- tbb::captured_exception *m = e.move();
- ASSERT (strcmp(m->name(), name) == 0, "Moving captured exception name failed");
- ASSERT (strcmp(m->what(), reason) == 0, "Moving captured exception reason failed");
- ASSERT (!e.name() && !e.what(), "Moving semantics broken");
- m->destroy();
-
- MyCapturedException mce;
- MyCapturedException *mmce = mce.move();
- ASSERT( MyCapturedException::m_refCount == 2, NULL );
- DeleteTbbException(mmce);
- ASSERT( MyCapturedException::m_refCount == 1, NULL );
-}
-
-#endif /* TBB_USE_EXCEPTIONS */
-
-/** If min and max thread numbers specified on the command line are different,
- the test is run only for 2 sizes of the thread pool (MinThread and MaxThread)
- to be able to test the high and low contention modes while keeping the test reasonably fast **/
-int TestMain () {
- REMARK ("Using %s\n", TBB_USE_CAPTURED_EXCEPTION ? "tbb:captured_exception" : "exact exception propagation");
- MinThread = min(tbb::task_scheduler_init::default_num_threads(), max(2, MinThread));
- MaxThread = max(MinThread, min(tbb::task_scheduler_init::default_num_threads(), MaxThread));
- ASSERT (FLAT_RANGE >= FLAT_GRAIN * MaxThread, "Fix defines");
-#if __TBB_TASK_GROUP_CONTEXT
- int step = max((MaxThread - MinThread + 1)/2, 1);
- for ( g_NumThreads = MinThread; g_NumThreads <= MaxThread; g_NumThreads += step ) {
- REMARK ("Number of threads %d\n", g_NumThreads);
- // Execute in all the possible modes
- for ( size_t j = 0; j < 4; ++j ) {
- g_ExceptionInMaster = (j & 1) == 1;
- g_SolitaryException = (j & 2) == 1;
- RunParForAndReduceTests();
- RunParDoTests();
- RunPipelineTests();
- }
- }
-#if TBB_USE_EXCEPTIONS
- TestTbbExceptionAPI();
-#endif
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception handling tests are skipped.\n");
-#endif
- return Harness::Done;
-#else /* !__TBB_TASK_GROUP_CONTEXT */
- return Harness::Skipped;
-#endif /* !__TBB_TASK_GROUP_CONTEXT */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define __TBB_COUNT_TASK_NODES 1
-#include "harness_inject_scheduler.h"
-
-#if __TBB_TASK_GROUP_CONTEXT
-
-#define __TBB_ATOMICS_CODEGEN_BROKEN __SUNPRO_CC
-
-#include "tbb/task_scheduler_init.h"
-#include "tbb/spin_mutex.h"
-#include "tbb/tick_count.h"
-
-#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 <string>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#define NUM_CHILD_TASKS 256
-#define NUM_ROOT_TASKS 32
-#define NUM_ROOTS_IN_GROUP 8
-
-//! Statistics about number of tasks in different states
-class TaskStats {
- typedef tbb::spin_mutex::scoped_lock lock_t;
- //! Number of tasks allocated that was ever allocated
- volatile intptr_t m_Existed;
- //! Number of tasks executed to the moment
- volatile intptr_t m_Executed;
- //! Number of tasks allocated but not yet destroyed to the moment
- volatile intptr_t m_Existing;
-
- mutable tbb::spin_mutex m_Mutex;
-public:
- //! Assumes that assignment is noncontended for the left-hand operand
- const TaskStats& operator= ( const TaskStats& rhs ) {
- if ( this != &rhs ) {
- lock_t lock(rhs.m_Mutex);
- m_Existed = rhs.m_Existed;
- m_Executed = rhs.m_Executed;
- m_Existing = rhs.m_Existing;
- }
- return *this;
- }
- intptr_t Existed() const { return m_Existed; }
- intptr_t Executed() const { return m_Executed; }
- intptr_t Existing() const { return m_Existing; }
- void IncExisted() { lock_t lock(m_Mutex); ++m_Existed; ++m_Existing; }
- void IncExecuted() { lock_t lock(m_Mutex); ++m_Executed; }
- void DecExisting() { lock_t lock(m_Mutex); --m_Existing; }
- //! Assumed to be used in uncontended manner only
- void Reset() { m_Executed = m_Existing = m_Existed = 0; }
-};
-
-TaskStats g_CurStat;
-
-inline intptr_t Existed () { return g_CurStat.Existed(); }
-
-#include "harness_eh.h"
-
-bool g_BoostExecutedCount = true;
-volatile bool g_TaskWasCancelled = false;
-
-inline void ResetGlobals () {
- ResetEhGlobals();
- g_BoostExecutedCount = true;
- g_TaskWasCancelled = false;
- g_CurStat.Reset();
-}
-
-#define ASSERT_TEST_POSTCOND() \
- ASSERT (g_CurStat.Existed() >= g_CurStat.Executed(), "Total number of tasks is less than executed"); \
- ASSERT (!g_CurStat.Existing(), "Not all task objects have been destroyed"); \
- ASSERT (!tbb::task::self().is_cancelled(), "Scheduler's default context has not been cleaned up properly");
-
-inline void WaitForException () {
- int n = 0;
- while ( ++n < c_Timeout && !__TBB_load_with_acquire(g_ExceptionCaught) )
- __TBB_Yield();
- ASSERT_WARNING( n < c_Timeout, "WaitForException failed" );
-}
-
-class TaskBase : public tbb::task {
- tbb::task* execute () {
- tbb::task* t = NULL;
- __TBB_TRY {
- t = do_execute();
- } __TBB_CATCH( ... ) {
- g_CurStat.IncExecuted();
- __TBB_RETHROW();
- }
- g_CurStat.IncExecuted();
- return t;
- }
-protected:
- TaskBase ( bool throwException = true ) : m_Throw(throwException) { g_CurStat.IncExisted(); }
- ~TaskBase () { g_CurStat.DecExisting(); }
-
- virtual tbb::task* do_execute () = 0;
-
- bool m_Throw;
-}; // class TaskBase
-
-class LeafTask : public TaskBase {
- tbb::task* do_execute () {
- Harness::ConcurrencyTracker ct;
- WaitUntilConcurrencyPeaks();
- if ( g_BoostExecutedCount )
- ++g_CurExecuted;
- if ( m_Throw )
- ThrowTestException(NUM_CHILD_TASKS/2);
- if ( !g_ThrowException )
- __TBB_Yield();
- return NULL;
- }
-public:
- LeafTask ( bool throw_exception = true ) : TaskBase(throw_exception) {}
-};
-
-class SimpleRootTask : public TaskBase {
- tbb::task* do_execute () {
- set_ref_count(NUM_CHILD_TASKS + 1);
- for ( size_t i = 0; i < NUM_CHILD_TASKS; ++i )
- spawn( *new( allocate_child() ) LeafTask(m_Throw) );
- wait_for_all();
- return NULL;
- }
-public:
- SimpleRootTask ( bool throw_exception = true ) : TaskBase(throw_exception) {}
-};
-
-#if TBB_USE_EXCEPTIONS
-
-class SimpleThrowingTask : public tbb::task {
-public:
- tbb::task* execute () { throw 0; }
- ~SimpleThrowingTask() {}
-};
-
-//! Checks if innermost running task information is updated correctly during cancellation processing
-void Test0 () {
- tbb::task_scheduler_init init (1);
- tbb::empty_task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- tbb::task_list tl;
- tl.push_back( *new( r.allocate_child() ) SimpleThrowingTask );
- tl.push_back( *new( r.allocate_child() ) SimpleThrowingTask );
- r.set_ref_count( 3 );
- try {
- r.spawn_and_wait_for_all( tl );
- }
- catch (...) {}
- r.destroy( r );
-}
-
-//! Default exception behavior test.
-/** Allocates a root task that spawns a bunch of children, one or several of which throw
- a test exception in a worker or master thread (depending on the global setting). **/
-void Test1 () {
- ResetGlobals();
- tbb::empty_task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- ASSERT (!g_CurStat.Existing() && !g_CurStat.Existed() && !g_CurStat.Executed(),
- "something wrong with the task accounting");
- r.set_ref_count(NUM_CHILD_TASKS + 1);
- for ( int i = 0; i < NUM_CHILD_TASKS; ++i )
- r.spawn( *new( r.allocate_child() ) LeafTask );
- TRY();
- r.wait_for_all();
- CATCH_AND_ASSERT();
- r.destroy(r);
- ASSERT_TEST_POSTCOND();
-} // void Test1 ()
-
-//! Default exception behavior test.
-/** Allocates and spawns root task that runs a bunch of children, one of which throws
- a test exception in a worker thread. (Similar to Test1, except that the root task
- is spawned by the test function, and children are created by the root task instead
- of the test function body.) **/
-void Test2 () {
- ResetGlobals();
- SimpleRootTask &r = *new( tbb::task::allocate_root() ) SimpleRootTask;
- ASSERT (g_CurStat.Existing() == 1 && g_CurStat.Existed() == 1 && !g_CurStat.Executed(),
- "something wrong with the task accounting");
- TRY();
- tbb::task::spawn_root_and_wait(r);
- CATCH_AND_ASSERT();
- ASSERT (g_ExceptionCaught, "no exception occurred");
- ASSERT_TEST_POSTCOND();
-} // void Test2 ()
-
-//! The same as Test2() except the root task has explicit context.
-/** The context is initialized as bound in order to check correctness of its associating
- with a root task. **/
-void Test3 () {
- ResetGlobals();
- tbb::task_group_context ctx(tbb::task_group_context::bound);
- SimpleRootTask &r = *new( tbb::task::allocate_root(ctx) ) SimpleRootTask;
- ASSERT (g_CurStat.Existing() == 1 && g_CurStat.Existed() == 1 && !g_CurStat.Executed(),
- "something wrong with the task accounting");
- TRY();
- tbb::task::spawn_root_and_wait(r);
- CATCH_AND_ASSERT();
- ASSERT (g_ExceptionCaught, "no exception occurred");
- ASSERT_TEST_POSTCOND();
-} // void Test2 ()
-
-class RootLauncherTask : public TaskBase {
- tbb::task_group_context::kind_type m_CtxKind;
-
- tbb::task* do_execute () {
- tbb::task_group_context ctx(m_CtxKind);
- SimpleRootTask &r = *new( allocate_root(ctx) ) SimpleRootTask;
- TRY();
- spawn_root_and_wait(r);
- // Give a child of our siblings a chance to throw the test exception
- WaitForException();
- CATCH();
- ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || !g_UnknownException, "unknown exception was caught");
- return NULL;
- }
-public:
- RootLauncherTask ( tbb::task_group_context::kind_type ctx_kind = tbb::task_group_context::isolated ) : m_CtxKind(ctx_kind) {}
-};
-
-/** Allocates and spawns a bunch of roots, which allocate and spawn new root with
- isolated context, which at last spawns a bunch of children each, one of which
- throws a test exception in a worker thread. **/
-void Test4 () {
- ResetGlobals();
- tbb::task_list tl;
- for ( size_t i = 0; i < NUM_ROOT_TASKS; ++i )
- tl.push_back( *new( tbb::task::allocate_root() ) RootLauncherTask );
- TRY();
- tbb::task::spawn_root_and_wait(tl);
- CATCH_AND_ASSERT();
- ASSERT (!exceptionCaught, "exception in this scope is unexpected");
- intptr_t num_tasks_expected = NUM_ROOT_TASKS * (NUM_CHILD_TASKS + 2);
- ASSERT (g_CurStat.Existed() == num_tasks_expected, "Wrong total number of tasks");
- if ( g_SolitaryException )
- ASSERT (g_CurStat.Executed() >= num_tasks_expected - NUM_CHILD_TASKS, "Unexpected number of executed tasks");
- ASSERT_TEST_POSTCOND();
-} // void Test4 ()
-
-class RootsGroupLauncherTask : public TaskBase {
- tbb::task* do_execute () {
- tbb::task_group_context ctx (tbb::task_group_context::isolated);
- tbb::task_list tl;
- for ( size_t i = 0; i < NUM_ROOT_TASKS; ++i )
- tl.push_back( *new( allocate_root(ctx) ) SimpleRootTask );
- TRY();
- spawn_root_and_wait(tl);
- // Give worker a chance to throw exception
- WaitForException();
- CATCH_AND_ASSERT();
- return NULL;
- }
-};
-
-/** Allocates and spawns a bunch of roots, which allocate and spawn groups of roots
- with an isolated context shared by all group members, which at last spawn a bunch
- of children each, one of which throws a test exception in a worker thread. **/
-void Test5 () {
- ResetGlobals();
- tbb::task_list tl;
- for ( size_t i = 0; i < NUM_ROOTS_IN_GROUP; ++i )
- tl.push_back( *new( tbb::task::allocate_root() ) RootsGroupLauncherTask );
- TRY();
- tbb::task::spawn_root_and_wait(tl);
- CATCH_AND_ASSERT();
- ASSERT (!exceptionCaught, "unexpected exception intercepted");
- if ( g_SolitaryException ) {
- intptr_t num_tasks_expected = NUM_ROOTS_IN_GROUP * (1 + NUM_ROOT_TASKS * (1 + NUM_CHILD_TASKS));
- intptr_t min_num_tasks_executed = num_tasks_expected - NUM_ROOT_TASKS * (NUM_CHILD_TASKS + 1);
- ASSERT (g_CurStat.Executed() >= min_num_tasks_executed, "Too few tasks executed");
- }
- ASSERT_TEST_POSTCOND();
-} // void Test5 ()
-
-class ThrowingRootLauncherTask : public TaskBase {
- tbb::task* do_execute () {
- tbb::task_group_context ctx (tbb::task_group_context::bound);
- SimpleRootTask &r = *new( allocate_root(ctx) ) SimpleRootTask(false);
- TRY();
- spawn_root_and_wait(r);
- CATCH();
- ASSERT (!exceptionCaught, "unexpected exception intercepted");
- ThrowTestException(NUM_CHILD_TASKS);
- g_TaskWasCancelled |= is_cancelled();
- return NULL;
- }
-};
-
-class BoundHierarchyLauncherTask : public TaskBase {
- bool m_Recover;
-
- void alloc_roots ( tbb::task_group_context& ctx, tbb::task_list& tl ) {
- for ( size_t i = 0; i < NUM_ROOT_TASKS; ++i )
- tl.push_back( *new( allocate_root(ctx) ) ThrowingRootLauncherTask );
- }
-
- tbb::task* do_execute () {
- tbb::task_group_context ctx (tbb::task_group_context::isolated);
- tbb::task_list tl;
- alloc_roots(ctx, tl);
- TRY();
- spawn_root_and_wait(tl);
- CATCH_AND_ASSERT();
- ASSERT (exceptionCaught, "no exception occurred");
- ASSERT (!tl.empty(), "task list was cleared somehow");
- if ( g_SolitaryException )
- ASSERT (g_TaskWasCancelled, "No tasks were cancelled despite of exception");
- if ( m_Recover ) {
- // Test task_group_context::unbind and task_group_context::reset methods
- g_ThrowException = false;
- exceptionCaught = false;
- tl.clear();
- alloc_roots(ctx, tl);
- ctx.reset();
- try {
- spawn_root_and_wait(tl);
- }
- catch (...) {
- exceptionCaught = true;
- }
- ASSERT (!exceptionCaught, "unexpected exception occurred");
- }
- return NULL;
- }
-public:
- BoundHierarchyLauncherTask ( bool recover = false ) : m_Recover(recover) {}
-
-}; // class BoundHierarchyLauncherTask
-
-//! Test for bound contexts forming 2 level tree. Exception is thrown on the 1st (root) level.
-/** Allocates and spawns a root that spawns a bunch of 2nd level roots sharing
- the same isolated context, each of which in their turn spawns a single 3rd level
- root with the bound context, and these 3rd level roots spawn bunches of leaves
- in the end. Leaves do not generate exceptions. The test exception is generated
- by one of the 2nd level roots. **/
-void Test6 () {
- ResetGlobals();
- BoundHierarchyLauncherTask &r = *new( tbb::task::allocate_root() ) BoundHierarchyLauncherTask;
- TRY();
- tbb::task::spawn_root_and_wait(r);
- CATCH_AND_ASSERT();
- ASSERT (!exceptionCaught, "unexpected exception intercepted");
- // After the first of the branches (ThrowingRootLauncherTask) completes,
- // the rest of the task tree may be collapsed before having a chance to execute leaves.
- // A number of branches running concurrently with the first one will be able to spawn leaves though.
- /// \todo: If additional checkpoints are added to scheduler the following assertion must weaken
- intptr_t num_tasks_expected = 1 + NUM_ROOT_TASKS * (2 + NUM_CHILD_TASKS);
- intptr_t min_num_tasks_created = 1 + g_NumThreads * 2 + NUM_CHILD_TASKS;
- // 2 stands for BoundHierarchyLauncherTask and SimpleRootTask
- // 1 corresponds to BoundHierarchyLauncherTask
- intptr_t min_num_tasks_executed = 2 + 1 + NUM_CHILD_TASKS;
- ASSERT (g_CurStat.Existed() <= num_tasks_expected, "Number of expected tasks is calculated incorrectly");
- ASSERT (g_CurStat.Existed() >= min_num_tasks_created, "Too few tasks created");
- ASSERT (g_CurStat.Executed() >= min_num_tasks_executed, "Too few tasks executed");
- ASSERT_TEST_POSTCOND();
-} // void Test6 ()
-
-//! Tests task_group_context::unbind and task_group_context::reset methods.
-/** Allocates and spawns a root that spawns a bunch of 2nd level roots sharing
- the same isolated context, each of which in their turn spawns a single 3rd level
- root with the bound context, and these 3rd level roots spawn bunches of leaves
- in the end. Leaves do not generate exceptions. The test exception is generated
- by one of the 2nd level roots. **/
-void Test7 () {
- ResetGlobals();
- BoundHierarchyLauncherTask &r = *new( tbb::task::allocate_root() ) BoundHierarchyLauncherTask;
- TRY();
- tbb::task::spawn_root_and_wait(r);
- CATCH_AND_ASSERT();
- ASSERT (!exceptionCaught, "unexpected exception intercepted");
- ASSERT_TEST_POSTCOND();
-} // void Test6 ()
-
-class BoundHierarchyLauncherTask2 : public TaskBase {
- tbb::task* do_execute () {
- tbb::task_group_context ctx;
- tbb::task_list tl;
- for ( size_t i = 0; i < NUM_ROOT_TASKS; ++i )
- tl.push_back( *new( allocate_root(ctx) ) RootLauncherTask(tbb::task_group_context::bound) );
- TRY();
- spawn_root_and_wait(tl);
- CATCH_AND_ASSERT();
- // Exception must be intercepted by RootLauncherTask
- ASSERT (!exceptionCaught, "no exception occurred");
- return NULL;
- }
-}; // class BoundHierarchyLauncherTask2
-
-//! Test for bound contexts forming 2 level tree. Exception is thrown in the 2nd (outer) level.
-/** Allocates and spawns a root that spawns a bunch of 2nd level roots sharing
- the same isolated context, each of which in their turn spawns a single 3rd level
- root with the bound context, and these 3rd level roots spawn bunches of leaves
- in the end. The test exception is generated by one of the leaves. **/
-void Test8 () {
- ResetGlobals();
- BoundHierarchyLauncherTask2 &r = *new( tbb::task::allocate_root() ) BoundHierarchyLauncherTask2;
- TRY();
- tbb::task::spawn_root_and_wait(r);
- CATCH_AND_ASSERT();
- ASSERT (!exceptionCaught, "unexpected exception intercepted");
- if ( g_SolitaryException ) {
- intptr_t num_tasks_expected = 1 + NUM_ROOT_TASKS * (2 + NUM_CHILD_TASKS);
- intptr_t min_num_tasks_created = 1 + g_NumThreads * (2 + NUM_CHILD_TASKS);
- intptr_t min_num_tasks_executed = num_tasks_expected - (NUM_CHILD_TASKS + 1);
- ASSERT (g_CurStat.Existed() <= num_tasks_expected, "Number of expected tasks is calculated incorrectly");
- ASSERT (g_CurStat.Existed() >= min_num_tasks_created, "Too few tasks created");
- ASSERT (g_CurStat.Executed() >= min_num_tasks_executed, "Too few tasks executed");
- }
- ASSERT_TEST_POSTCOND();
-} // void Test8 ()
-
-template<typename T>
-void ThrowMovableException ( intptr_t threshold, const T& data ) {
- if ( !IsThrowingThread() )
- return;
- if ( !g_SolitaryException ) {
-#if __TBB_ATOMICS_CODEGEN_BROKEN
- g_ExceptionsThrown = g_ExceptionsThrown + 1;
-#else
- ++g_ExceptionsThrown;
-#endif
- throw tbb::movable_exception<T>(data);
- }
- while ( g_CurStat.Existed() < threshold )
- __TBB_Yield();
- if ( g_ExceptionsThrown.compare_and_swap(1, 0) == 0 )
- throw tbb::movable_exception<T>(data);
-}
-
-const int g_IntExceptionData = -375;
-const std::string g_StringExceptionData = "My test string";
-
-// Exception data class implementing minimal requirements of tbb::movable_exception
-class ExceptionData {
- const ExceptionData& operator = ( const ExceptionData& src );
- explicit ExceptionData ( int n ) : m_Int(n), m_String(g_StringExceptionData) {}
-public:
- ExceptionData ( const ExceptionData& src ) : m_Int(src.m_Int), m_String(src.m_String) {}
- ~ExceptionData () {}
-
- int m_Int;
- std::string m_String;
-
- // Simple way to provide an instance when all initializing constructors are private
- // and to avoid memory reclamation problems.
- static ExceptionData s_data;
-};
-
-ExceptionData ExceptionData::s_data(g_IntExceptionData);
-
-typedef tbb::movable_exception<int> SolitaryMovableException;
-typedef tbb::movable_exception<ExceptionData> MultipleMovableException;
-
-class LeafTaskWithMovableExceptions : public TaskBase {
- bool m_IntAsData;
-
- tbb::task* do_execute () {
- Harness::ConcurrencyTracker ct;
- WaitUntilConcurrencyPeaks();
- if ( g_SolitaryException )
- ThrowMovableException<int>(NUM_CHILD_TASKS/2, g_IntExceptionData);
- else
- ThrowMovableException<ExceptionData>(NUM_CHILD_TASKS/2, ExceptionData::s_data);
- return NULL;
- }
-};
-
-void CheckException ( tbb::tbb_exception& e ) {
- ASSERT (strcmp(e.name(), (g_SolitaryException ? typeid(SolitaryMovableException)
- : typeid(MultipleMovableException)).name() ) == 0,
- "Unexpected original exception name");
- ASSERT (strcmp(e.what(), "tbb::movable_exception") == 0, "Unexpected original exception info ");
- if ( g_SolitaryException ) {
- SolitaryMovableException& me = dynamic_cast<SolitaryMovableException&>(e);
- ASSERT (me.data() == g_IntExceptionData, "Unexpected solitary movable_exception data");
- }
- else {
- MultipleMovableException& me = dynamic_cast<MultipleMovableException&>(e);
- ASSERT (me.data().m_Int == g_IntExceptionData, "Unexpected multiple movable_exception int data");
- ASSERT (me.data().m_String == g_StringExceptionData, "Unexpected multiple movable_exception string data");
- }
-}
-
-void CheckException () {
- try {
- throw;
- } catch ( tbb::tbb_exception& e ) {
- CheckException(e);
- }
- catch ( ... ) {
- }
-}
-
-//! Test for movable_exception behavior, and external exception recording.
-/** Allocates a root task that spawns a bunch of children, one or several of which throw
- a movable exception in a worker or master thread (depending on the global settings).
- The test also checks the correctness of multiple rethrowing of the pending exception. **/
-void TestMovableException () {
- ResetGlobals();
- tbb::task_group_context ctx;
- tbb::empty_task *r = new( tbb::task::allocate_root() ) tbb::empty_task;
- ASSERT (!g_CurStat.Existing() && !g_CurStat.Existed() && !g_CurStat.Executed(),
- "something wrong with the task accounting");
- r->set_ref_count(NUM_CHILD_TASKS + 1);
- for ( int i = 0; i < NUM_CHILD_TASKS; ++i )
- r->spawn( *new( r->allocate_child() ) LeafTaskWithMovableExceptions );
- TRY()
- r->wait_for_all();
- } catch ( ... ) {
- ASSERT (!ctx.is_group_execution_cancelled(), "");
- CheckException();
- try {
- throw;
- } catch ( tbb::tbb_exception& e ) {
- CheckException(e);
- g_ExceptionCaught = exceptionCaught = true;
- }
- catch ( ... ) {
- g_ExceptionCaught = true;
- g_UnknownException = unknownException = true;
- }
- ctx.register_pending_exception();
- ASSERT (ctx.is_group_execution_cancelled(), "After exception registration the context must be in the cancelled state");
- }
- r->destroy(*r);
- ASSERT_EXCEPTION();
- ASSERT_TEST_POSTCOND();
-
- r = new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
- r->set_ref_count(1);
- g_ExceptionCaught = g_UnknownException = false;
- try {
- r->wait_for_all();
- } catch ( tbb::tbb_exception& e ) {
- CheckException(e);
- g_ExceptionCaught = true;
- }
- catch ( ... ) {
- g_ExceptionCaught = true;
- g_UnknownException = true;
- }
- ASSERT (g_ExceptionCaught, "no exception occurred");
- ASSERT (__TBB_EXCEPTION_TYPE_INFO_BROKEN || !g_UnknownException, "unknown exception was caught");
- r->destroy(*r);
-} // void Test10 ()
-
-#endif /* TBB_USE_EXCEPTIONS */
-
-template<class T>
-class CtxLauncherTask : public tbb::task {
- tbb::task_group_context &m_Ctx;
-
- tbb::task* execute () {
- tbb::task::spawn_root_and_wait( *new( tbb::task::allocate_root(m_Ctx) ) T );
- return NULL;
- }
-public:
- CtxLauncherTask ( tbb::task_group_context& ctx ) : m_Ctx(ctx) {}
-};
-
-//! Test for cancelling a task hierarchy from outside (from a task running in parallel with it).
-void TestCancelation () {
- ResetGlobals();
- g_ThrowException = false;
- tbb::task_group_context ctx;
- tbb::task_list tl;
- tl.push_back( *new( tbb::task::allocate_root() ) CtxLauncherTask<SimpleRootTask>(ctx) );
- tl.push_back( *new( tbb::task::allocate_root() ) CancellatorTask(ctx, NUM_CHILD_TASKS / 4) );
- TRY();
- tbb::task::spawn_root_and_wait(tl);
- CATCH_AND_FAIL();
- ASSERT (g_CurStat.Executed() <= g_ExecutedAtCatch + g_NumThreads, "Too many tasks were executed after cancellation");
- ASSERT_TEST_POSTCOND();
-} // void Test9 ()
-
-class CtxDestroyerTask : public tbb::task {
- int m_nestingLevel;
-
- tbb::task* execute () {
- ASSERT ( m_nestingLevel >= 0 && m_nestingLevel < MaxNestingDepth, "Wrong nesting level. The test is broken" );
- tbb::task_group_context ctx;
- tbb::task *t = new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
- int level = ++m_nestingLevel;
- if ( level < MaxNestingDepth ) {
- execute();
- }
- else {
- if ( !CancellatorTask::WaitUntilReady() )
- REPORT( "Warning: missing wakeup\n" );
- ++g_CurExecuted;
- }
- if ( ctx.is_group_execution_cancelled() )
- ++s_numCancelled;
- t->destroy(*t);
- return NULL;
- }
-public:
- CtxDestroyerTask () : m_nestingLevel(0) { s_numCancelled = 0; }
-
- static const int MaxNestingDepth = 256;
- static int s_numCancelled;
-};
-
-int CtxDestroyerTask::s_numCancelled = 0;
-
-//! Test for data race between cancellation propagation and context destruction.
-/** If the data race ever occurs, an assertion inside TBB will be triggered. **/
-void TestCtxDestruction () {
- for ( size_t i = 0; i < 10; ++i ) {
- tbb::task_group_context ctx;
- tbb::task_list tl;
- ResetGlobals();
- g_BoostExecutedCount = false;
- g_ThrowException = false;
- CancellatorTask::Reset();
-
- tl.push_back( *new( tbb::task::allocate_root() ) CtxLauncherTask<CtxDestroyerTask>(ctx) );
- tl.push_back( *new( tbb::task::allocate_root() ) CancellatorTask(ctx, 1) );
- tbb::task::spawn_root_and_wait(tl);
- ASSERT( g_CurExecuted == 1, "Test is broken" );
- ASSERT( CtxDestroyerTask::s_numCancelled <= CtxDestroyerTask::MaxNestingDepth, "Test is broken" );
- }
-} // void TestCtxDestruction()
-
-#include <algorithm>
-#include "harness_barrier.h"
-
-class CtxConcurrentDestroyer : NoAssign, Harness::NoAfterlife {
- static const int ContextsPerThread = 512;
-
- static int s_Concurrency;
- static int s_NumContexts;
- static tbb::task_group_context** s_Contexts;
- static char* s_Buffer;
- static Harness::SpinBarrier s_Barrier;
- static Harness::SpinBarrier s_ExitBarrier;
-
- struct Shuffler {
- void operator() () const { std::random_shuffle(s_Contexts, s_Contexts + s_NumContexts); }
- };
-public:
- static void Init ( int p ) {
- s_Concurrency = p;
- s_NumContexts = p * ContextsPerThread;
- s_Contexts = new tbb::task_group_context*[s_NumContexts];
- s_Buffer = new char[s_NumContexts * sizeof(tbb::task_group_context)];
- s_Barrier.initialize( p );
- s_ExitBarrier.initialize( p );
- }
- static void Uninit () {
- for ( int i = 0; i < s_NumContexts; ++i ) {
- tbb::internal::context_list_node_t &node = s_Contexts[i]->my_node;
- ASSERT( !node.my_next && !node.my_prev, "Destroyed context was written to during context chain update" );
- }
- delete s_Contexts;
- delete s_Buffer;
- }
-
- void operator() ( int id ) const {
- int begin = ContextsPerThread * id,
- end = begin + ContextsPerThread;
- for ( int i = begin; i < end; ++i )
- s_Contexts[i] = new( s_Buffer + i * sizeof(tbb::task_group_context) ) tbb::task_group_context;
- s_Barrier.wait( Shuffler() );
- for ( int i = begin; i < end; ++i ) {
- s_Contexts[i]->tbb::task_group_context::~task_group_context();
- memset( s_Contexts[i], 0, sizeof(tbb::task_group_context) );
- }
- s_ExitBarrier.wait();
- }
-}; // class CtxConcurrentDestroyer
-
-int CtxConcurrentDestroyer::s_Concurrency;
-int CtxConcurrentDestroyer::s_NumContexts;
-tbb::task_group_context** CtxConcurrentDestroyer::s_Contexts;
-char* CtxConcurrentDestroyer::s_Buffer;
-Harness::SpinBarrier CtxConcurrentDestroyer::s_Barrier;
-Harness::SpinBarrier CtxConcurrentDestroyer::s_ExitBarrier;
-
-void TestConcurrentCtxDestruction () {
- REMARK( "TestConcurrentCtxDestruction\n" );
- CtxConcurrentDestroyer::Init(g_NumThreads);
- NativeParallelFor( g_NumThreads, CtxConcurrentDestroyer() );
- CtxConcurrentDestroyer::Uninit();
-}
-
-void RunTests () {
- REMARK ("Number of threads %d\n", g_NumThreads);
- tbb::task_scheduler_init init (g_NumThreads);
- g_Master = Harness::CurrentTid();
-#if TBB_USE_EXCEPTIONS
- Test1();
- Test2();
- Test3();
- Test4();
- Test5();
- Test6();
- Test7();
- Test8();
- TestMovableException();
-#endif /* TBB_USE_EXCEPTIONS */
- TestCancelation();
- TestCtxDestruction();
-#if !RML_USE_WCRM
- TestConcurrentCtxDestruction();
-#endif
-}
-#endif /* __TBB_TASK_GROUP_CONTEXT */
-
-int TestMain () {
- REMARK ("Using %s", TBB_USE_CAPTURED_EXCEPTION ? "tbb:captured_exception" : "exact exception propagation");
- MinThread = min(NUM_ROOTS_IN_GROUP, min(tbb::task_scheduler_init::default_num_threads(), max(2, MinThread)));
- MaxThread = min(NUM_ROOTS_IN_GROUP, max(MinThread, min(tbb::task_scheduler_init::default_num_threads(), MaxThread)));
- ASSERT (NUM_ROOTS_IN_GROUP < NUM_ROOT_TASKS, "Fix defines");
-#if __TBB_TASK_GROUP_CONTEXT
-#if TBB_USE_EXCEPTIONS
- // Test0 always runs on one thread
- Test0();
-#endif /* TBB_USE_EXCEPTIONS */
- g_SolitaryException = 0;
- for ( g_NumThreads = MinThread; g_NumThreads <= MaxThread; ++g_NumThreads )
- RunTests();
- return Harness::Done;
-#else
- return Harness::Skipped;
-#endif /* __TBB_TASK_GROUP_CONTEXT */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/enumerable_thread_specific.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/parallel_for.h"
-#include "tbb/parallel_reduce.h"
-#include "tbb/blocked_range.h"
-#include "tbb/tick_count.h"
-#include "tbb/tbb_allocator.h"
-#include "tbb/tbb_thread.h"
-#include "tbb/atomic.h"
-
-#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>
-#include <vector>
-#include <deque>
-#include <list>
-#include <map>
-#include <utility>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#include "harness_assert.h"
-#include "harness.h"
-
-#if __TBB_GCC_WARNING_SUPPRESSION_ENABLED
-#pragma GCC diagnostic ignored "-Wuninitialized"
-#endif
-
-static tbb::atomic<int> construction_counter;
-static tbb::atomic<int> destruction_counter;
-
-const int REPETITIONS = 10;
-const int N = 100000;
-const int VALID_NUMBER_OF_KEYS = 100;
-const double EXPECTED_SUM = (REPETITIONS + 1) * N;
-
-//! A minimal class that occupies N bytes.
-/** Defines default and copy constructor, and allows implicit operator&.
- Hides operator=. */
-template<size_t N=tbb::internal::NFS_MaxLineSize>
-class minimal: NoAssign {
-private:
- int my_value;
- bool is_constructed;
- char pad[N-sizeof(int) - sizeof(bool)];
-public:
- minimal() : NoAssign(), my_value(0) { ++construction_counter; is_constructed = true; }
- minimal( const minimal &m ) : NoAssign(), my_value(m.my_value) { ++construction_counter; is_constructed = true; }
- ~minimal() { ++destruction_counter; ASSERT(is_constructed, NULL); is_constructed = false; }
- void set_value( const int i ) { ASSERT(is_constructed, NULL); my_value = i; }
- int value( ) const { ASSERT(is_constructed, NULL); return my_value; }
-};
-
-//
-// A helper class that simplifies writing the tests since minimal does not
-// define = or + operators.
-//
-
-template< typename T >
-struct test_helper {
- static inline void init(T &e) { e = static_cast<T>(0); }
- static inline void sum(T &e, const int addend ) { e += static_cast<T>(addend); }
- static inline void sum(T &e, const double addend ) { e += static_cast<T>(addend); }
- static inline void set(T &e, const int value ) { e = static_cast<T>(value); }
- static inline double get(const T &e ) { return static_cast<double>(e); }
-};
-
-template<size_t N>
-struct test_helper<minimal<N> > {
- static inline void init(minimal<N> &sum) { sum.set_value( 0 ); }
- static inline void sum(minimal<N> &sum, const int addend ) { sum.set_value( sum.value() + addend); }
- static inline void sum(minimal<N> &sum, const double addend ) { sum.set_value( sum.value() + static_cast<int>(addend)); }
- static inline void sum(minimal<N> &sum, const minimal<N> &addend ) { sum.set_value( sum.value() + addend.value()); }
- static inline void set(minimal<N> &v, const int value ) { v.set_value( static_cast<int>(value) ); }
- static inline double get(const minimal<N> &sum ) { return static_cast<double>(sum.value()); }
-};
-
-//! Tag class used to make certain constructors hard to invoke accidentally.
-struct SecretTagType {} SecretTag;
-
-//// functors and routines for initialization and combine
-
-// Addition
-
-template <typename T>
-struct FunctorAddCombineRef {
- T operator()(const T& left, const T& right) const {
- return left+right;
- }
-};
-
-template <size_t N>
-struct FunctorAddCombineRef<minimal<N> > {
- minimal<N> operator()(const minimal<N>& left, const minimal<N>& right) const {
- minimal<N> result;
- result.set_value( left.value() + right.value() );
- return result;
- }
-};
-
-//! Counts instances of FunctorFinit
-static tbb::atomic<int> FinitCounter;
-
-template <typename T, int Value>
-struct FunctorFinit {
- FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
- FunctorFinit( SecretTagType ) {++FinitCounter;}
- ~FunctorFinit() {--FinitCounter;}
- T operator()() { return Value; }
-};
-
-template <size_t N, int Value>
-struct FunctorFinit<minimal<N>,Value> {
- FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
- FunctorFinit( SecretTagType ) {++FinitCounter;}
- ~FunctorFinit() {--FinitCounter;}
- minimal<N> operator()() {
- minimal<N> result;
- result.set_value( Value );
- return result;
- }
-};
-
-template <typename T>
-struct FunctorAddCombine {
- T operator()(T left, T right ) const {
- return FunctorAddCombineRef<T>()( left, right );
- }
-};
-
-template <typename T>
-T my_combine_ref( const T &left, const T &right) {
- return FunctorAddCombineRef<T>()( left, right );
-}
-
-template <typename T>
-T my_combine( T left, T right) { return my_combine_ref(left,right); }
-
-template <typename T>
-class combine_one_helper {
-public:
- combine_one_helper(T& _result) : my_result(_result) {}
- void operator()(const T& new_bit) { test_helper<T>::sum(my_result, new_bit); }
- combine_one_helper& operator=(const combine_one_helper& other) {
- test_helper<T>::set(my_result, test_helper<T>::get(other));
- return *this;
- }
-private:
- T& my_result;
-};
-
-//// end functors and routines
-
-template< typename T >
-void run_serial_scalar_tests(const char *test_name) {
- tbb::tick_count t0;
- T sum;
- test_helper<T>::init(sum);
-
- REMARK("Testing serial %s... ", test_name);
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
- for (int i = 0; i < N; ++i) {
- test_helper<T>::sum(sum,1);
- }
- }
-
- double result_value = test_helper<T>::get(sum);
- ASSERT( EXPECTED_SUM == result_value, NULL);
- REMARK("done\nserial %s, 0, %g, %g\n", test_name, result_value, ( tbb::tick_count::now() - t0).seconds());
-}
-
-
-template <typename T>
-class parallel_scalar_body: NoAssign {
-
- tbb::enumerable_thread_specific<T> &sums;
-
-public:
-
- parallel_scalar_body ( tbb::enumerable_thread_specific<T> &_sums ) : sums(_sums) { }
-
- void operator()( const tbb::blocked_range<int> &r ) const {
- for (int i = r.begin(); i != r.end(); ++i)
- test_helper<T>::sum( sums.local(), 1 );
- }
-
-};
-
-template< typename T >
-void run_parallel_scalar_tests_nocombine(const char *test_name) {
-
- typedef tbb::enumerable_thread_specific<T> ets_type;
-
- // We assume that static_sums zero-initialized or has a default constructor that zeros it.
- static ets_type static_sums = ets_type( T() );
-
- T exemplar;
- test_helper<T>::init(exemplar);
- T exemplar23;
- test_helper<T>::set(exemplar23,23);
-
- for (int p = MinThread; p <= MaxThread; ++p) {
- REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
- tbb::task_scheduler_init init(p);
- tbb::tick_count t0;
-
- T iterator_sum;
- test_helper<T>::init(iterator_sum);
-
- T finit_ets_sum;
- test_helper<T>::init(finit_ets_sum);
-
- T const_iterator_sum;
- test_helper<T>::init(const_iterator_sum);
-
- T range_sum;
- test_helper<T>::init(range_sum);
-
- T const_range_sum;
- test_helper<T>::init(const_range_sum);
-
- T cconst_sum;
- test_helper<T>::init(cconst_sum);
-
- T assign_sum;
- test_helper<T>::init(assign_sum);
-
- T cassgn_sum;
- test_helper<T>::init(cassgn_sum);
- T non_cassgn_sum;
- test_helper<T>::init(non_cassgn_sum);
-
- T static_sum;
- test_helper<T>::init(static_sum);
-
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
-
- static_sums.clear();
-
- ets_type sums(exemplar);
- FunctorFinit<T,0> my_finit(SecretTag);
- ets_type finit_ets(my_finit);
-
- ASSERT( sums.empty(), NULL);
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( sums ) );
- ASSERT( !sums.empty(), NULL);
-
- ASSERT( finit_ets.empty(), NULL);
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( finit_ets ) );
- ASSERT( !finit_ets.empty(), NULL);
-
- ASSERT(static_sums.empty(), NULL);
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( static_sums ) );
- ASSERT( !static_sums.empty(), NULL);
-
- // use iterator
- typename ets_type::size_type size = 0;
- for ( typename ets_type::iterator i = sums.begin(); i != sums.end(); ++i ) {
- ++size;
- test_helper<T>::sum(iterator_sum, *i);
- }
- ASSERT( sums.size() == size, NULL);
-
- // use const_iterator
- for ( typename ets_type::const_iterator i = sums.begin(); i != sums.end(); ++i ) {
- test_helper<T>::sum(const_iterator_sum, *i);
- }
-
- // use range_type
- typename ets_type::range_type r = sums.range();
- for ( typename ets_type::range_type::const_iterator i = r.begin(); i != r.end(); ++i ) {
- test_helper<T>::sum(range_sum, *i);
- }
-
- // use const_range_type
- typename ets_type::const_range_type cr = sums.range();
- for ( typename ets_type::const_range_type::iterator i = cr.begin(); i != cr.end(); ++i ) {
- test_helper<T>::sum(const_range_sum, *i);
- }
-
- // test copy constructor, with TLS-cached locals
- typedef typename tbb::enumerable_thread_specific<T, tbb::cache_aligned_allocator<T>, tbb::ets_key_per_instance> cached_ets_type;
-
- cached_ets_type cconst(sums);
-
- for ( typename cached_ets_type::const_iterator i = cconst.begin(); i != cconst.end(); ++i ) {
- test_helper<T>::sum(cconst_sum, *i);
- }
-
- // test assignment
- ets_type assigned;
- assigned = sums;
-
- for ( typename ets_type::const_iterator i = assigned.begin(); i != assigned.end(); ++i ) {
- test_helper<T>::sum(assign_sum, *i);
- }
-
- // test assign to and from cached locals
- cached_ets_type cassgn;
- cassgn = sums;
- for ( typename cached_ets_type::const_iterator i = cassgn.begin(); i != cassgn.end(); ++i ) {
- test_helper<T>::sum(cassgn_sum, *i);
- }
-
- ets_type non_cassgn;
- non_cassgn = cassgn;
- for ( typename ets_type::const_iterator i = non_cassgn.begin(); i != non_cassgn.end(); ++i ) {
- test_helper<T>::sum(non_cassgn_sum, *i);
- }
-
- // test finit-initialized ets
- for(typename ets_type::const_iterator i = finit_ets.begin(); i != finit_ets.end(); ++i) {
- test_helper<T>::sum(finit_ets_sum, *i);
- }
-
- // test static ets
- for(typename ets_type::const_iterator i = static_sums.begin(); i != static_sums.end(); ++i) {
- test_helper<T>::sum(static_sum, *i);
- }
-
- }
-
- ASSERT( EXPECTED_SUM == test_helper<T>::get(iterator_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(const_iterator_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(range_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(const_range_sum), NULL);
-
- ASSERT( EXPECTED_SUM == test_helper<T>::get(cconst_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(assign_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(cassgn_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(non_cassgn_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(finit_ets_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(static_sum), NULL);
-
- REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, test_helper<T>::get(iterator_sum),
- ( tbb::tick_count::now() - t0).seconds());
- }
-}
-
-template< typename T >
-void run_parallel_scalar_tests(const char *test_name) {
-
- typedef tbb::enumerable_thread_specific<T> ets_type;
-
- // We assume that static_sums zero-initialized or has a default constructor that zeros it.
- static ets_type static_sums = ets_type( T() );
-
- T exemplar;
- test_helper<T>::init(exemplar);
-
- run_parallel_scalar_tests_nocombine<T>(test_name);
-
- for (int p = MinThread; p <= MaxThread; ++p) {
- REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
- tbb::task_scheduler_init init(p);
- tbb::tick_count t0;
-
- T combine_sum;
- test_helper<T>::init(combine_sum);
-
- T combine_ref_sum;
- test_helper<T>::init(combine_ref_sum);
-
- T combine_one_sum;
- test_helper<T>::init(combine_one_sum);
-
- T static_sum;
- test_helper<T>::init(static_sum);
-
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
-
- static_sums.clear();
-
- ets_type sums(exemplar);
-
- ASSERT( sums.empty(), NULL);
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( sums ) );
- ASSERT( !sums.empty(), NULL);
-
- ASSERT(static_sums.empty(), NULL);
- tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( static_sums ) );
- ASSERT( !static_sums.empty(), NULL);
-
-
- // Use combine
- test_helper<T>::sum(combine_sum, sums.combine(my_combine<T>));
- test_helper<T>::sum(combine_ref_sum, sums.combine(my_combine_ref<T>));
- test_helper<T>::sum(static_sum, static_sums.combine(my_combine<T>));
-
- combine_one_helper<T> my_helper(combine_one_sum);
- sums.combine_each(my_helper);
- }
-
-
- ASSERT( EXPECTED_SUM == test_helper<T>::get(combine_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(combine_ref_sum), NULL);
- ASSERT( EXPECTED_SUM == test_helper<T>::get(static_sum), NULL);
-
- REMARK("done\nparallel combine %s, %d, %g, %g\n", test_name, p, test_helper<T>::get(combine_sum),
- ( tbb::tick_count::now() - t0).seconds());
- }
-}
-
-template <typename T>
-class parallel_vector_for_body: NoAssign {
-
- tbb::enumerable_thread_specific< std::vector<T, tbb::tbb_allocator<T> > > &locals;
-
-public:
-
- parallel_vector_for_body ( tbb::enumerable_thread_specific< std::vector<T, tbb::tbb_allocator<T> > > &_locals ) : locals(_locals) { }
-
- void operator()( const tbb::blocked_range<int> &r ) const {
- T one;
- test_helper<T>::set(one, 1);
-
- for (int i = r.begin(); i < r.end(); ++i) {
- locals.local().push_back( one );
- }
- }
-
-};
-
-template <typename R, typename T>
-struct parallel_vector_reduce_body {
-
- T sum;
- size_t count;
-
- parallel_vector_reduce_body ( ) : count(0) { test_helper<T>::init(sum); }
- parallel_vector_reduce_body ( parallel_vector_reduce_body<R, T> &, tbb::split ) : count(0) { test_helper<T>::init(sum); }
-
- void operator()( const R &r ) {
- for (typename R::iterator ri = r.begin(); ri != r.end(); ++ri) {
- const std::vector< T, tbb::tbb_allocator<T> > &v = *ri;
- ++count;
- for (typename std::vector<T, tbb::tbb_allocator<T> >::const_iterator vi = v.begin(); vi != v.end(); ++vi) {
- test_helper<T>::sum(sum, *vi);
- }
- }
- }
-
- void join( const parallel_vector_reduce_body &b ) {
- test_helper<T>::sum(sum,b.sum);
- count += b.count;
- }
-
-};
-
-template< typename T >
-void run_parallel_vector_tests(const char *test_name) {
- tbb::tick_count t0;
- typedef std::vector<T, tbb::tbb_allocator<T> > container_type;
-
- for (int p = MinThread; p <= MaxThread; ++p) {
- REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
- tbb::task_scheduler_init init(p);
-
- T sum;
- test_helper<T>::init(sum);
-
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
- typedef typename tbb::enumerable_thread_specific< container_type > ets_type;
- ets_type vs;
-
- ASSERT( vs.empty(), NULL);
- tbb::parallel_for ( tbb::blocked_range<int> (0, N, 10000), parallel_vector_for_body<T>( vs ) );
- ASSERT( !vs.empty(), NULL);
-
- // copy construct
- ets_type vs2(vs); // this causes an assertion failure, related to allocators...
-
- // assign
- ets_type vs3;
- vs3 = vs;
-
- parallel_vector_reduce_body< typename tbb::enumerable_thread_specific< std::vector< T, tbb::tbb_allocator<T> > >::const_range_type, T > pvrb;
- tbb::parallel_reduce ( vs.range(1), pvrb );
-
- test_helper<T>::sum(sum, pvrb.sum);
-
- ASSERT( vs.size() == pvrb.count, NULL);
-
- tbb::flattened2d<ets_type> fvs = flatten2d(vs);
- size_t ccount = fvs.size();
- size_t elem_cnt = 0;
- for(typename tbb::flattened2d<ets_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
- ++elem_cnt;
- };
- ASSERT(ccount == elem_cnt, NULL);
-
- elem_cnt = 0;
- for(typename tbb::flattened2d<ets_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) {
- ++elem_cnt;
- };
- ASSERT(ccount == elem_cnt, NULL);
- }
-
- double result_value = test_helper<T>::get(sum);
- ASSERT( EXPECTED_SUM == result_value, NULL);
- REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, result_value, ( tbb::tick_count::now() - t0).seconds());
- }
-}
-
-template<typename T>
-void run_cross_type_vector_tests(const char *test_name) {
- tbb::tick_count t0;
- typedef std::vector<T, tbb::tbb_allocator<T> > container_type;
-
- for (int p = MinThread; p <= MaxThread; ++p) {
- REMARK("Testing parallel %s on %d thread(s)... ", test_name, p);
- tbb::task_scheduler_init init(p);
-
- T sum;
- test_helper<T>::init(sum);
-
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
- typedef typename tbb::enumerable_thread_specific< container_type, tbb::cache_aligned_allocator<container_type>, tbb::ets_no_key > ets_nokey_type;
- typedef typename tbb::enumerable_thread_specific< container_type, tbb::cache_aligned_allocator<container_type>, tbb::ets_key_per_instance > ets_tlskey_type;
- ets_nokey_type vs;
-
- ASSERT( vs.empty(), NULL);
- tbb::parallel_for ( tbb::blocked_range<int> (0, N, 10000), parallel_vector_for_body<T>( vs ) );
- ASSERT( !vs.empty(), NULL);
-
- // copy construct
- ets_tlskey_type vs2(vs);
-
- // assign
- ets_nokey_type vs3;
- vs3 = vs2;
-
- parallel_vector_reduce_body< typename tbb::enumerable_thread_specific< std::vector< T, tbb::tbb_allocator<T> > >::const_range_type, T > pvrb;
- tbb::parallel_reduce ( vs3.range(1), pvrb );
-
- test_helper<T>::sum(sum, pvrb.sum);
-
- ASSERT( vs3.size() == pvrb.count, NULL);
-
- tbb::flattened2d<ets_nokey_type> fvs = flatten2d(vs3);
- size_t ccount = fvs.size();
- size_t elem_cnt = 0;
- for(typename tbb::flattened2d<ets_nokey_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
- ++elem_cnt;
- };
- ASSERT(ccount == elem_cnt, NULL);
-
- elem_cnt = 0;
- for(typename tbb::flattened2d<ets_nokey_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) {
- ++elem_cnt;
- };
- ASSERT(ccount == elem_cnt, NULL);
- }
-
- double result_value = test_helper<T>::get(sum);
- ASSERT( EXPECTED_SUM == result_value, NULL);
- REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, result_value, ( tbb::tick_count::now() - t0).seconds());
- }
-}
-
-template< typename T >
-void run_serial_vector_tests(const char *test_name) {
- tbb::tick_count t0;
- T sum;
- test_helper<T>::init(sum);
- T one;
- test_helper<T>::set(one, 1);
-
- REMARK("Testing serial %s... ", test_name);
- for (int t = -1; t < REPETITIONS; ++t) {
- if (Verbose && t == 0) t0 = tbb::tick_count::now();
- std::vector<T, tbb::tbb_allocator<T> > v;
- for (int i = 0; i < N; ++i) {
- v.push_back( one );
- }
- for (typename std::vector<T, tbb::tbb_allocator<T> >::const_iterator i = v.begin(); i != v.end(); ++i)
- test_helper<T>::sum(sum, *i);
- }
-
- double result_value = test_helper<T>::get(sum);
- ASSERT( EXPECTED_SUM == result_value, NULL);
- REMARK("done\nserial %s, 0, %g, %g\n", test_name, result_value, ( tbb::tick_count::now() - t0).seconds());
-}
-
-const size_t line_size = tbb::internal::NFS_MaxLineSize;
-
-void
-run_serial_tests() {
- run_serial_scalar_tests<int>("int");
- run_serial_scalar_tests<double>("double");
- run_serial_scalar_tests<minimal<> >("minimal<>");
- run_serial_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >");
- run_serial_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >");
-}
-
-void
-run_parallel_tests() {
- run_parallel_scalar_tests<int>("int");
- run_parallel_scalar_tests<double>("double");
- run_parallel_scalar_tests_nocombine<minimal<> >("minimal<>");
- run_parallel_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >");
- run_parallel_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >");
-}
-
-void
-run_cross_type_tests() {
- // cross-type scalar tests are part of run_serial_scalar_tests
- run_cross_type_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >");
- run_parallel_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >");
-}
-
-typedef tbb::enumerable_thread_specific<minimal<line_size> > flogged_ets;
-
-class set_body {
- flogged_ets *a;
-
-public:
- set_body( flogged_ets*_a ) : a(_a) { }
-
- void operator() ( ) const {
- for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
- a[i].local().set_value(i + 1);
- }
- }
-
-};
-
-void do_tbb_threads( int max_threads, flogged_ets a[] ) {
- std::vector< tbb::tbb_thread * > threads;
-
- for (int p = 0; p < max_threads; ++p) {
- threads.push_back( new tbb::tbb_thread ( set_body( a ) ) );
- }
-
- for (int p = 0; p < max_threads; ++p) {
- threads[p]->join();
- }
-
- for(int p = 0; p < max_threads; ++p) {
- delete threads[p];
- }
-}
-
-void
-flog_key_creation_and_deletion() {
- const int FLOG_REPETITIONS = 100;
-
- for (int p = MinThread; p <= MaxThread; ++p) {
- REMARK("Testing repeated deletes on %d threads... ", p);
-
- for (int j = 0; j < FLOG_REPETITIONS; ++j) {
- construction_counter = 0;
- destruction_counter = 0;
-
- // causes VALID_NUMER_OF_KEYS exemplar instances to be constructed
- flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS];
- ASSERT(int(construction_counter) == 0, NULL); // no exemplars or actual locals have been constructed
- ASSERT(int(destruction_counter) == 0, NULL); // and none have been destroyed
-
- // causes p * VALID_NUMBER_OF_KEYS minimals to be created
- do_tbb_threads(p, a);
-
- for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
- int pcnt = 0;
- for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) {
- ASSERT( (*tli).value() == i+1, NULL );
- ++pcnt;
- }
- ASSERT( pcnt == p, NULL); // should be one local per thread.
- }
- delete[] a;
- }
-
- ASSERT( int(construction_counter) == (p)*VALID_NUMBER_OF_KEYS, NULL );
- ASSERT( int(destruction_counter) == (p)*VALID_NUMBER_OF_KEYS, NULL );
-
- REMARK("done\nTesting repeated clears on %d threads... ", p);
-
- construction_counter = 0;
- destruction_counter = 0;
-
- // causes VALID_NUMER_OF_KEYS exemplar instances to be constructed
- flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS];
-
- for (int j = 0; j < FLOG_REPETITIONS; ++j) {
-
- // causes p * VALID_NUMBER_OF_KEYS minimals to be created
- do_tbb_threads(p, a);
-
- for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) {
- for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) {
- ASSERT( (*tli).value() == i+1, NULL );
- }
- a[i].clear();
- ASSERT( static_cast<int>(a[i].end() - a[i].begin()) == 0, NULL );
- }
-
- }
-
- delete[] a;
-
- ASSERT( int(construction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS, NULL );
- ASSERT( int(destruction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS, NULL );
-
- REMARK("done\n");
- }
-
-}
-
-template <typename inner_container>
-void
-flog_segmented_interator() {
-
- bool found_error = false;
- typedef typename inner_container::value_type T;
- typedef std::vector< inner_container > nested_vec;
- inner_container my_inner_container;
- my_inner_container.clear();
- nested_vec my_vec;
-
- // simple nested vector (neither level empty)
- const int maxval = 10;
- for(int i=0; i < maxval; i++) {
- my_vec.push_back(my_inner_container);
- for(int j = 0; j < maxval; j++) {
- my_vec.at(i).push_back((T)(maxval * i + j));
- }
- }
-
- tbb::internal::segmented_iterator<nested_vec, T> my_si(my_vec);
-
- T ii;
- for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
- if((*my_si) != ii) {
- found_error = true;
- REMARK( "*my_si=%d\n", int(*my_si));
- }
- }
-
- // outer level empty
- my_vec.clear();
- for(my_si=my_vec.begin(); my_si != my_vec.end(); ++my_si) {
- found_error = true;
- }
-
- // inner levels empty
- my_vec.clear();
- for(int i =0; i < maxval; ++i) {
- my_vec.push_back(my_inner_container);
- }
- for(my_si = my_vec.begin(); my_si != my_vec.end(); ++my_si) {
- found_error = true;
- }
-
- // every other inner container is empty
- my_vec.clear();
- for(int i=0; i < maxval; ++i) {
- my_vec.push_back(my_inner_container);
- if(i%2) {
- for(int j = 0; j < maxval; ++j) {
- my_vec.at(i).push_back((T)(maxval * (i/2) + j));
- }
- }
- }
- for(my_si = my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
- if((*my_si) != ii) {
- found_error = true;
- REMARK("*my_si=%d, ii=%d\n", (int)(*my_si), (int)ii);
- }
- }
-
- tbb::internal::segmented_iterator<nested_vec, const T> my_csi(my_vec);
- for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
- if((*my_csi) != ii) {
- found_error = true;
- REMARK( "*my_csi=%d\n", int(*my_csi));
- }
- }
-
- // outer level empty
- my_vec.clear();
- for(my_csi=my_vec.begin(); my_csi != my_vec.end(); ++my_csi) {
- found_error = true;
- }
-
- // inner levels empty
- my_vec.clear();
- for(int i =0; i < maxval; ++i) {
- my_vec.push_back(my_inner_container);
- }
- for(my_csi = my_vec.begin(); my_csi != my_vec.end(); ++my_csi) {
- found_error = true;
- }
-
- // every other inner container is empty
- my_vec.clear();
- for(int i=0; i < maxval; ++i) {
- my_vec.push_back(my_inner_container);
- if(i%2) {
- for(int j = 0; j < maxval; ++j) {
- my_vec.at(i).push_back((T)(maxval * (i/2) + j));
- }
- }
- }
- for(my_csi = my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
- if((*my_csi) != ii) {
- found_error = true;
- REMARK("*my_csi=%d, ii=%d\n", (int)(*my_csi), (int)ii);
- }
- }
-
-
- if(found_error) REPORT("segmented_iterator failed\n");
-}
-
-template <typename Key, typename Val>
-void
-flog_segmented_iterator_map() {
- typedef typename std::map<Key, Val> my_map;
- typedef std::vector< my_map > nested_vec;
- my_map my_inner_container;
- my_inner_container.clear();
- nested_vec my_vec;
- my_vec.clear();
- bool found_error = false;
-
- // simple nested vector (neither level empty)
- const int maxval = 4;
- for(int i=0; i < maxval; i++) {
- my_vec.push_back(my_inner_container);
- for(int j = 0; j < maxval; j++) {
- my_vec.at(i).insert(std::make_pair<Key,Val>(maxval * i + j, 2*(maxval*i + j)));
- }
- }
-
- tbb::internal::segmented_iterator<nested_vec, std::pair<const Key, Val> > my_si(my_vec);
- Key ii;
- for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) {
- if(((*my_si).first != ii) || ((*my_si).second != 2*ii)) {
- found_error = true;
- REMARK( "ii=%d, (*my_si).first=%d, second=%d\n",ii, int((*my_si).first), int((*my_si).second));
- }
- }
-
- tbb::internal::segmented_iterator<nested_vec, const std::pair<const Key, Val> > my_csi(my_vec);
- for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) {
- if(((*my_csi).first != ii) || ((*my_csi).second != 2*ii)) {
- found_error = true;
- REMARK( "ii=%d, (*my_csi).first=%d, second=%d\n",ii, int((*my_csi).first), int((*my_csi).second));
- }
- }
-}
-
-void
-run_segmented_iterator_tests() {
- // only the following containers can be used with the segmented iterator.
- REMARK("Running Segmented Iterator Tests\n");
- flog_segmented_interator<std::vector< int > >();
- flog_segmented_interator<std::vector< double > >();
- flog_segmented_interator<std::deque< int > >();
- flog_segmented_interator<std::deque< double > >();
- flog_segmented_interator<std::list< int > >();
- flog_segmented_interator<std::list< double > >();
-
- flog_segmented_iterator_map<int, int>();
- flog_segmented_iterator_map<int, double>();
-}
-
-template <typename T>
-void
-run_assign_and_copy_constructor_test(const char *test_name) {
- REMARK("Testing assignment and copy construction for %s\n", test_name);
-
- // test initializer with exemplar
- T initializer0;
- test_helper<T>::init(initializer0);
- T initializer7;
- test_helper<T>::set(initializer7,7);
- tbb::enumerable_thread_specific<T> create1(initializer7);
- (void) create1.local(); // create an initialized value
- ASSERT(7 == test_helper<T>::get(create1.local()), NULL);
-
- // test copy construction with exemplar initializer
- create1.clear();
- tbb::enumerable_thread_specific<T> copy1(create1);
- (void) copy1.local();
- ASSERT(7 == test_helper<T>::get(copy1.local()), NULL);
-
- // test copy assignment with exemplar initializer
- create1.clear();
- tbb::enumerable_thread_specific<T> assign1(initializer0);
- assign1 = create1;
- (void) assign1.local();
- ASSERT(7 == test_helper<T>::get(assign1.local()), NULL);
-
- // test creation with finit function
- FunctorFinit<T,7> my_finit7(SecretTag);
- tbb::enumerable_thread_specific<T> create2(my_finit7);
- (void) create2.local();
- ASSERT(7 == test_helper<T>::get(create2.local()), NULL);
-
- // test copy construction with function initializer
- create2.clear();
- tbb::enumerable_thread_specific<T> copy2(create2);
- (void) copy2.local();
- ASSERT(7 == test_helper<T>::get(copy2.local()), NULL);
-
- // test copy assignment with function initializer
- create2.clear();
- FunctorFinit<T,0> my_finit(SecretTag);
- tbb::enumerable_thread_specific<T> assign2(my_finit);
- assign2 = create2;
- (void) assign2.local();
- ASSERT(7 == test_helper<T>::get(assign2.local()), NULL);
-}
-
-void
-run_assignment_and_copy_constructor_tests() {
- REMARK("Running assignment and copy constructor tests\n");
- run_assign_and_copy_constructor_test<int>("int");
- run_assign_and_copy_constructor_test<double>("double");
- // Try class sizes that are close to a cache line in size, in order to check padding calculations.
- run_assign_and_copy_constructor_test<minimal<line_size-1> >("minimal<line_size-1>");
- run_assign_and_copy_constructor_test<minimal<line_size> >("minimal<line_size>");
- run_assign_and_copy_constructor_test<minimal<line_size+1> >("minimal<line_size+1>");
- ASSERT(FinitCounter==0, NULL);
-}
-
-// Class with no default constructor
-class HasNoDefaultConstructor {
- HasNoDefaultConstructor();
-public:
- HasNoDefaultConstructor( SecretTagType ) {}
-};
-
-// Initialization functor for a HasNoDefaultConstructor
-struct HasNoDefaultConstructorFinit {
- HasNoDefaultConstructor operator()() {
- return HasNoDefaultConstructor(SecretTag);
- }
-};
-
-struct HasNoDefaultConstructorCombine {
- HasNoDefaultConstructor operator()( HasNoDefaultConstructor, HasNoDefaultConstructor ) {
- return HasNoDefaultConstructor(SecretTag);
- }
-};
-
-//! Test situations where only default constructor or copy constructor is required.
-void TestInstantiation() {
- // Test instantiation is possible when copy constructor is not required.
- tbb::enumerable_thread_specific<NoCopy> ets1;
-
- // Test instantiation when default constructor is not required, because exemplar is provided.
- HasNoDefaultConstructor x(SecretTag);
- tbb::enumerable_thread_specific<HasNoDefaultConstructor> ets2(x);
- ets2.combine(HasNoDefaultConstructorCombine());
-
- // Test instantiation when default constructor is not required, because init function is provided.
- HasNoDefaultConstructorFinit f;
- tbb::enumerable_thread_specific<HasNoDefaultConstructor> ets3(f);
- ets3.combine(HasNoDefaultConstructorCombine());
-}
-
-int TestMain () {
- TestInstantiation();
- run_segmented_iterator_tests();
- flog_key_creation_and_deletion();
-
- if (MinThread == 0) {
- run_serial_tests();
- MinThread = 1;
- }
- if (MaxThread > 0) {
- run_parallel_tests();
- run_cross_type_tests();
- }
-
- run_assignment_and_copy_constructor_tests();
-
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-/**
- The test checks that for different ranges of random numbers (from 0 to
- [MinThread, MaxThread]) generated with different seeds the probability
- of each number in the range deviates from the ideal random distribution
- by no more than AcceptableDeviation percent.
-**/
-
-#include "harness_inject_scheduler.h"
-
-#define HARNESS_DEFAULT_MIN_THREADS 2
-#define HARNESS_DEFAULT_MAX_THREADS 32
-
-#define TEST_TOTAL_SEQUENCE 0
-
-#include "harness.h"
-#include "tbb/atomic.h"
-
-//! Coefficient defining tolerable deviation from ideal random distribution
-const double AcceptableDeviation = 2.1;
-//! Tolerable probability of failure to achieve tolerable distribution
-const double AcceptableProbabilityOfOutliers = 1e-6;
-//! Coefficient defining the length of random numbers series used to estimate the distribution
-/** Number of random values generated per each range element. I.e. the larger is
- the range, the longer is the series of random values. **/
-const uintptr_t SeriesBaseLen = 100;
-//! Number of random numbers series to generate
-const uintptr_t NumSeries = 100;
-//! Number of random number generation series with different seeds
-const uintptr_t NumSeeds = 100;
-
-tbb::atomic<uintptr_t> NumHighOutliers;
-tbb::atomic<uintptr_t> NumLowOutliers;
-
-inline void CheckProbability ( double probability, double expectedProbability, int index, int numIndices ) {
- double lowerBound = expectedProbability / AcceptableDeviation,
- upperBound = expectedProbability * AcceptableDeviation;
- if ( probability < lowerBound ) {
- if ( !NumLowOutliers )
- REMARK( "Warning: Probability %.3f of hitting index %d among %d elements is out of acceptable range (%.3f - %.3f)\n",
- probability, index, numIndices, lowerBound, upperBound );
- ++NumLowOutliers;
- }
- else if ( probability > upperBound ) {
- if ( !NumHighOutliers )
- REMARK( "Warning: Probability %.3f of hitting index %d among %d elements is out of acceptable range (%.3f - %.3f)\n",
- probability, index, numIndices, lowerBound, upperBound );
- ++NumHighOutliers;
- }
-}
-
-struct CheckDistributionBody {
- void operator() ( int id ) const {
- uintptr_t randomRange = id + MinThread;
- uintptr_t *curHits = new uintptr_t[randomRange]
-#if TEST_TOTAL_SEQUENCE
- , *totalHits = new uintptr_t[randomRange]
-#endif
- ;
- double expectedProbability = 1./randomRange;
- // Loop through different seeds
- for ( uintptr_t i = 0; i < NumSeeds; ++i ) {
- // Seed value is selected in two ways, the first of which mimics
- // the one used by the TBB task scheduler
- void* seed = i % 2 ? (char*)&curHits + i * 16 : (void*)(i * 8);
- tbb::internal::FastRandom random( (unsigned)(uintptr_t)seed );
- memset( curHits, 0, randomRange * sizeof(uintptr_t) );
-#if TEST_TOTAL_SEQUENCE
- memset( totalHits, 0, randomRange * sizeof(uintptr_t) );
-#endif
- const uintptr_t seriesLen = randomRange * SeriesBaseLen,
- experimentLen = NumSeries * seriesLen;
- uintptr_t *curSeries = new uintptr_t[seriesLen], // circular buffer
- randsGenerated = 0;
- // Initialize statistics
- while ( randsGenerated < seriesLen ) {
- uintptr_t idx = random.get() % randomRange;
- ++curHits[idx];
-#if TEST_TOTAL_SEQUENCE
- ++totalHits[idx];
-#endif
- curSeries[randsGenerated++] = idx;
- }
- while ( randsGenerated < experimentLen ) {
- for ( uintptr_t j = 0; j < randomRange; ++j ) {
- CheckProbability( double(curHits[j])/seriesLen, expectedProbability, j, randomRange );
-#if TEST_TOTAL_SEQUENCE
- CheckProbability( double(totalHits[j])/randsGenerated, expectedProbability, j, randomRange );
-#endif
- }
- --curHits[curSeries[randsGenerated % seriesLen]];
- int idx = random.get() % randomRange;
- ++curHits[idx];
-#if TEST_TOTAL_SEQUENCE
- ++totalHits[idx];
-#endif
- curSeries[randsGenerated++ % seriesLen] = idx;
- }
- delete [] curSeries;
- }
- delete [] curHits;
-#if TEST_TOTAL_SEQUENCE
- delete [] totalHits;
-#endif
- }
-};
-
-#include "tbb/tbb_thread.h"
-
-int TestMain () {
- ASSERT( AcceptableDeviation < 100, NULL );
- MinThread = max(MinThread, 2);
- MaxThread = max(MinThread, MaxThread);
- double NumChecks = double(NumSeeds) * (MaxThread - MinThread + 1) * (MaxThread + MinThread) / 2.0 * (SeriesBaseLen * NumSeries - SeriesBaseLen);
- REMARK( "Number of distribution quality checks %g\n", NumChecks );
- NumLowOutliers = NumHighOutliers = 0;
- // Parallelism is used in this test only to speed up the long serial checks
- // Essentially it is a loop over random number ranges
- // Ideally tbb::parallel_for could be used to parallelize the outermost loop
- // in CheckDistributionBody, but it is not used to avoid unit test contamination.
- int P = tbb::tbb_thread::hardware_concurrency();
- while ( MinThread <= MaxThread ) {
- NativeParallelFor( min(P, MaxThread - MinThread + 1), CheckDistributionBody() );
- MinThread += P;
- }
- double observedProbabilityOfOutliers = (NumLowOutliers + NumHighOutliers) / NumChecks;
- if ( observedProbabilityOfOutliers > AcceptableProbabilityOfOutliers ) {
- if ( NumLowOutliers )
- REPORT( "Warning: %d cases of too low probability of a given number detected\n", (int)NumLowOutliers );
- if ( NumHighOutliers )
- REPORT( "Warning: %d cases of too high probability of a given number detected\n", (int)NumHighOutliers );
- ASSERT( observedProbabilityOfOutliers <= AcceptableProbabilityOfOutliers, NULL );
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include <cstdio>
-#include <cstdlib>
-#include <cassert>
-#include <utility>
-#include "tbb/task.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/tick_count.h"
-#include "tbb/parallel_for.h"
-#include "tbb/blocked_range.h"
-#include "tbb/mutex.h"
-#include "tbb/spin_mutex.h"
-#include "tbb/queuing_mutex.h"
-#include "harness.h"
-
-using namespace std;
-using namespace tbb;
-
-///////////////////// Parallel methods ////////////////////////
-
-// *** Serial shared by mutexes *** //
-int SharedI = 1, SharedN;
-template<typename M>
-class SharedSerialFibBody: NoAssign {
- M &mutex;
-public:
- SharedSerialFibBody( M &m ) : mutex( m ) {}
- //! main loop
- void operator()( const blocked_range<int>& /*range*/ ) const {
- for(;;) {
- typename M::scoped_lock lock( mutex );
- if(SharedI >= SharedN) break;
- volatile double sum = 7.3;
- sum *= 11.17;
- ++SharedI;
- }
- }
-};
-
-//! Root function
-template<class M>
-void SharedSerialFib(int n)
-{
- SharedI = 1;
- SharedN = n;
- M mutex;
- parallel_for( blocked_range<int>(0,4,1), SharedSerialFibBody<M>( mutex ) );
-}
-
-/////////////////////////// Main ////////////////////////////////////////////////////
-
-double Tsum = 0; int Tnum = 0;
-
-typedef void (*MeasureFunc)(int);
-//! Measure ticks count in loop [2..n]
-void Measure(const char *name, MeasureFunc func, int n)
-{
- tick_count t0;
- tick_count::interval_t T;
- REMARK("%s",name);
- t0 = tick_count::now();
- for(int number = 2; number <= n; number++)
- func(number);
- T = tick_count::now() - t0;
- double avg = Tnum? Tsum/Tnum : 1;
- if (avg == 0.0) avg = 1;
- if(avg * 100 < T.seconds()) {
- REPORT("Warning: halting detected (%g sec, av: %g)\n", T.seconds(), avg);
- ASSERT(avg * 1000 > T.seconds(), "Too long halting period");
- } else {
- Tsum += T.seconds(); Tnum++;
- }
- REMARK("\t- in %f msec\n", T.seconds()*1000);
-}
-
-int TestMain () {
- MinThread = max(2, MinThread);
- int NumbersCount = 100;
- short recycle = 100;
- do {
- for(int threads = MinThread; threads <= MaxThread; threads++) {
- task_scheduler_init scheduler_init(threads);
- REMARK("Threads number is %d\t", threads);
- Measure("Shared serial (wrapper mutex)\t", SharedSerialFib<mutex>, NumbersCount);
- //sum = Measure("Shared serial (spin_mutex)", SharedSerialFib<tbb::spin_mutex>, NumbersCount);
- //sum = Measure("Shared serial (queuing_mutex)", SharedSerialFib<tbb::queuing_mutex>, NumbersCount);
- }
- } while(--recycle);
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Program for basic correctness of handle_perror, which is internal
-// to the TBB shared library.
-
-#include <cerrno>
-
-#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 <stdexcept>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#include "../tbb/tbb_misc.h"
-#include "harness.h"
-
-#if TBB_USE_EXCEPTIONS
-
-static void TestHandlePerror() {
- bool caught = false;
- try {
- tbb::internal::handle_perror( EAGAIN, "apple" );
- } catch( std::runtime_error& e ) {
-#if TBB_USE_EXCEPTIONS
- REMARK("caught runtime_exception('%s')\n",e.what());
- ASSERT( memcmp(e.what(),"apple: ",7)==0, NULL );
- ASSERT( strstr(e.what(),"unavailable")!=NULL, "bad error message?" );
-#endif /* TBB_USE_EXCEPTIONS */
- caught = true;
- }
- ASSERT(caught,NULL);
-}
-
-int TestMain () {
- TestHandlePerror();
- return Harness::Done;
-}
-
-#else /* !TBB_USE_EXCEPTIONS */
-
-int TestMain () {
- return Harness::Skipped;
-}
-
-#endif /* TBB_USE_EXCEPTIONS */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#if __APPLE__
-
-#define HARNESS_CUSTOM_MAIN 1
-#include "harness.h"
-#include <cstdlib>
-#include "tbb/task_scheduler_init.h"
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-
-bool exec_test(const char *self) {
- int status = 1;
- pid_t p = fork();
- if(p < 0) {
- REPORT("fork error: errno=%d: %s\n", errno, strerror(errno));
- return true;
- }
- else if(p) { // parent
- if(waitpid(p, &status, 0) != p) {
- REPORT("wait error: errno=%d: %s\n", errno, strerror(errno));
- return true;
- }
- if(WIFEXITED(status)) {
- if(!WEXITSTATUS(status)) return false; // ok
- else REPORT("child has exited with return code 0x%x\n", WEXITSTATUS(status));
- } else {
- REPORT("child error 0x%x:%s%s ", status, WIFSIGNALED(status)?" signalled":"",
- WIFSTOPPED(status)?" stopped":"");
- if(WIFSIGNALED(status))
- REPORT("%s%s", sys_siglist[WTERMSIG(status)], WCOREDUMP(status)?" core dumped":"");
- if(WIFSTOPPED(status))
- REPORT("with %d stop-code", WSTOPSIG(status));
- REPORT("\n");
- }
- }
- else { // child
- // reproduces error much often
- execl(self, self, "0", NULL);
- REPORT("exec fails %s: %d: %s\n", self, errno, strerror(errno));
- exit(2);
- }
- return true;
-}
-
-HARNESS_EXPORT
-int main( int argc, char * argv[] ) {
- MinThread = 3000;
- ParseCommandLine( argc, argv );
- if( MinThread <= 0 ) {
- tbb::task_scheduler_init init( 2 ); // even number required for an error
- } else {
- for(int i = 0; i<MinThread; i++) {
- if(exec_test(argv[0])) {
- REPORT("ERROR: execution fails at %d-th iteration!\n", i);
- exit(1);
- }
- }
- REPORT("done\n");
- }
-}
-
-#else /* !__APPLE__ */
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-
-int TestMain () {
- return Harness::Skipped;
-}
-
-#endif /* !__APPLE__ */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-
-#include "../tbb/intrusive_list.h"
-
-#if __TBB_ARENA_PER_MASTER
-
-using tbb::internal::intrusive_list_node;
-
-// Machine word filled with repeated pattern of FC bits
-const uintptr_t NoliMeTangere = ~uintptr_t(0)/0xFF*0xFC;
-
-struct VerificationBase : Harness::NoAfterlife {
- uintptr_t m_Canary;
- VerificationBase () : m_Canary(NoliMeTangere) {}
-};
-
-struct DataItemWithInheritedNodeBase : intrusive_list_node {
- int m_Data;
-public:
- DataItemWithInheritedNodeBase ( int value ) : m_Data(value) {}
-
- int Data() const { return m_Data; }
-};
-
-class DataItemWithInheritedNode : public VerificationBase, public DataItemWithInheritedNodeBase {
- friend class tbb::internal::intrusive_list<DataItemWithInheritedNode>;
-public:
- DataItemWithInheritedNode ( int value ) : DataItemWithInheritedNodeBase(value) {}
-};
-
-struct DataItemWithMemberNodeBase {
- int m_Data;
-public:
- // Cannot be used by member_intrusive_list to form lists of objects derived from DataItemBase
- intrusive_list_node m_BaseNode;
-
- DataItemWithMemberNodeBase ( int value ) : m_Data(value) {}
-
- int Data() const { return m_Data; }
-};
-
-class DataItemWithMemberNodes : public VerificationBase, public DataItemWithMemberNodeBase {
-public:
- intrusive_list_node m_Node;
-
- DataItemWithMemberNodes ( int value ) : DataItemWithMemberNodeBase(value) {}
-};
-
-typedef tbb::internal::intrusive_list<DataItemWithInheritedNode> IntrusiveList1;
-typedef tbb::internal::memptr_intrusive_list<DataItemWithMemberNodes,
- DataItemWithMemberNodeBase, &DataItemWithMemberNodeBase::m_BaseNode> IntrusiveList2;
-typedef tbb::internal::memptr_intrusive_list<DataItemWithMemberNodes,
- DataItemWithMemberNodes, &DataItemWithMemberNodes::m_Node> IntrusiveList3;
-
-const int NumElements = 256 * 1024;
-
-//! Iterates through the list forward and backward checking the validity of values stored by the list nodes
-template<class List, class Iterator>
-void CheckListNodes ( List& il, int valueStep ) {
- int i;
- Iterator it = il.begin();
- for ( i = valueStep - 1; it != il.end(); ++it, i += valueStep ) {
- ASSERT( it->Data() == i, "Unexpected node value while iterating forward" );
- ASSERT( (*it).m_Canary == NoliMeTangere, "Memory corruption" );
- }
- ASSERT( i == NumElements + valueStep - 1, "Wrong number of list elements while iterating forward" );
- it = il.end();
- for ( i = NumElements - 1, it--; it != il.end(); --it, i -= valueStep ) {
- ASSERT( (*it).Data() == i, "Unexpected node value while iterating backward" );
- ASSERT( it->m_Canary == NoliMeTangere, "Memory corruption" );
- }
- ASSERT( i == -1, "Wrong number of list elements while iterating backward" );
-}
-
-template<class List, class Item>
-void TestListOperations () {
- typedef typename List::iterator iterator;
- List il;
- for ( int i = NumElements - 1; i >= 0; --i )
- il.push_front( *new Item(i) );
- CheckListNodes<const List, typename List::const_iterator>( il, 1 );
- iterator it = il.begin();
- for ( ; it != il.end(); ++it ) {
- Item &item = *it;
- it = il.erase( it );
- delete &item;
- }
- CheckListNodes<List, iterator>( il, 2 );
- for ( it = il.begin(); it != il.end(); ++it ) {
- Item &item = *it;
- il.remove( *it++ );
- delete &item;
- }
- CheckListNodes<List, iterator>( il, 4 );
-}
-
-#include "harness_bad_expr.h"
-
-template<class List, class Item>
-void TestListAssertions () {
-#if TRY_BAD_EXPR_ENABLED
- tbb::set_assertion_handler( AssertionFailureHandler );
- List il1, il2;
- Item n1(1), n2(2), n3(3);
- il1.push_front(n1);
- TRY_BAD_EXPR( il2.push_front(n1), "only one intrusive list" );
- TRY_BAD_EXPR( il1.push_front(n1), "only one intrusive list" );
- il2.push_front(n2);
- TRY_BAD_EXPR( il1.remove(n3), "not in the list" );
- tbb::set_assertion_handler( NULL );
-#endif /* TRY_BAD_EXPR_ENABLED */
-}
-#endif /* __TBB_ARENA_PER_MASTER */
-
-int TestMain () {
-#if __TBB_ARENA_PER_MASTER
- TestListOperations<IntrusiveList1, DataItemWithInheritedNode>();
- TestListOperations<IntrusiveList2, DataItemWithMemberNodes>();
- TestListOperations<IntrusiveList3, DataItemWithMemberNodes>();
- TestListAssertions<IntrusiveList1, DataItemWithInheritedNode>();
- TestListAssertions<IntrusiveList2, DataItemWithMemberNodes>();
- TestListAssertions<IntrusiveList3, DataItemWithMemberNodes>();
- return Harness::Done;
-#else
- return Harness::Skipped;
-#endif /* __TBB_ARENA_PER_MASTER */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#if !TBB_USE_THREADING_TOOLS
- #define TBB_USE_THREADING_TOOLS 1
-#endif
-
-#include "harness.h"
-
-#if DO_ITT_NOTIFY
-
-#include "tbb/spin_mutex.h"
-#include "tbb/spin_rw_mutex.h"
-#include "tbb/queuing_rw_mutex.h"
-#include "tbb/queuing_mutex.h"
-#include "tbb/mutex.h"
-#include "tbb/recursive_mutex.h"
-#include "tbb/parallel_for.h"
-#include "tbb/blocked_range.h"
-#include "tbb/task_scheduler_init.h"
-
-
-#include "../tbb/itt_notify.h"
-
-
-template<typename M>
-class WorkEmulator: NoAssign {
- M& m_mutex;
- static volatile size_t s_anchor;
-public:
- void operator()( tbb::blocked_range<size_t>& range ) const {
- for( size_t i=range.begin(); i!=range.end(); ++i ) {
- typename M::scoped_lock lock(m_mutex);
- for ( size_t j = 0; j!=range.end(); ++j )
- s_anchor = (s_anchor - i) / 2 + (s_anchor + j) / 2;
- }
- }
- WorkEmulator( M& mutex ) : m_mutex(mutex) {}
-};
-
-template<typename M>
-volatile size_t WorkEmulator<M>::s_anchor = 0;
-
-
-template<class M>
-void Test( const char * name ) {
- REMARK("%s time = ",name);
- M mtx;
- tbb::profiling::set_name(mtx, name);
-
- const int n = 10000;
- tbb::parallel_for( tbb::blocked_range<size_t>(0,n,n/100), WorkEmulator<M>(mtx) );
-}
-
- #define TEST_MUTEX(type, name) Test<tbb::type>( name )
-
-#endif /* !DO_ITT_NOTIFY */
-
-int TestMain () {
-#if DO_ITT_NOTIFY
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK( "testing with %d workers\n", p );
- tbb::task_scheduler_init init( p );
- TEST_MUTEX( spin_mutex, "Spin Mutex" );
- TEST_MUTEX( queuing_mutex, "Queuing Mutex" );
- TEST_MUTEX( queuing_rw_mutex, "Queuing RW Mutex" );
- TEST_MUTEX( spin_rw_mutex, "Spin RW Mutex" );
- }
- return Harness::Done;
-#else /* !DO_ITT_NOTIFY */
- return Harness::Skipped;
-#endif /* !DO_ITT_NOTIFY */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define NOMINMAX
-#include "tbb/tbb.h"
-#include "tbb/combinable.h"
-#include <cstdio>
-
-#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 <list>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-using namespace std;
-using namespace tbb;
-
-typedef pair<int,int> max_element_t;
-
-void f(int val, int *arr, int start, int stop) {
- for (int i=start; i<=stop; ++i) {
- arr[i] = val;
- }
-}
-
-#include "harness.h"
-
-int Fib(int n) {
- if( n<2 ) {
- return n;
- } else {
- int x=0, y=0;
- task_group g;
-#if __TBB_LAMBDAS_PRESENT
- g.run( [&]{x=Fib(n-1);} ); // spawn a task
- g.run( [&]{y=Fib(n-2);} ); // spawn another task
- g.wait(); // wait for both tasks to complete
-#endif
- return x+y;
- }
-}
-
-#include "harness_report.h"
-#include "harness_assert.h"
-
-int TestMain () {
-#if __TBB_LAMBDAS_PRESENT
- const int N = 1000;
- const int Grainsize = N/1000;
- int a[N];
- ASSERT( MinThread>=1, "Error: Number of threads must be positive.\n");
-
- for(int p=MinThread; p<=MaxThread; ++p) {
- task_scheduler_init init(p);
-
- REMARK("Running lambda expression tests on %d threads...\n", p);
-
- //test parallel_for
- REMARK("Testing parallel_for... ");
- parallel_for(blocked_range<int>(0,N,Grainsize),
- [&] (blocked_range<int>& r) {
- for (int i=r.begin(); i!=r.end(); ++i) a[i] = i;
- });
- ASSERT(a[0]==0 && a[N-1]==N-1, "parallel_for w/lambdas failed.\n");
- REMARK("passed.\n");
-
- //test parallel_reduce
- REMARK("Testing parallel_reduce... ");
- int sum = parallel_reduce(blocked_range<int>(0,N,Grainsize), int(0),
- [&] (blocked_range<int>& r, int current_sum) -> int {
- for (int i=r.begin(); i!=r.end(); ++i)
- current_sum += a[i]*(1000-i);
- return current_sum;
- },
- [] (const int x1, const int x2) {
- return x1+x2;
- } );
-
- max_element_t max_el =
- parallel_reduce(blocked_range<int>(0,N,Grainsize), make_pair(a[0], 0),
- [&] (blocked_range<int>& r, max_element_t current_max)
- -> max_element_t {
- for (int i=r.begin(); i!=r.end(); ++i)
- if (a[i]>current_max.first)
- current_max = make_pair(a[i], i);
- return current_max;
- },
- [] (const max_element_t x1, const max_element_t x2) {
- return (x1.first>x2.first)?x1:x2;
- });
- ASSERT(sum==166666500 && max_el.first==999 && max_el.second==999,
- "parallel_reduce w/lambdas failed.\n");
- REMARK("passed.\n");
-
- //test parallel_do
- REMARK("Testing parallel_do... ");
- list<int> s;
- s.push_back(0);
-
- parallel_do(s.begin(), s.end(),
- [&](int foo, parallel_do_feeder<int>& feeder) {
- if (foo == 42) return;
- else if (foo>42) {
- s.push_back(foo-3);
- feeder.add(foo-3);
- } else {
- s.push_back(foo+5);
- feeder.add(foo+5);
- }
- });
- ASSERT(s.back()==42, "parallel_do w/lambda failed.\n");
- REMARK("passed.\n");
-
- //test parallel_invoke
- REMARK("Testing parallel_invoke... ");
- parallel_invoke([&]{ f(2, a, 0, N/3); },
- [&]{ f(1, a, N/3+1, 2*(N/3)); },
- [&]{ f(0, a, 2*(N/3)+1, N-1); });
- ASSERT(a[0]==2.0 && a[N-1]==0.0, "parallel_invoke w/lambda failed.\n");
- REMARK("passed.\n");
-
- //test tbb_thread
- REMARK("Testing tbb_thread... ");
- tbb_thread::id myId;
- tbb_thread myThread([](int x, int y) {
- ASSERT(x==42 && y==64, "tbb_thread w/lambda failed.\n");
- REMARK("passed.\n");
- }, 42, 64);
- myThread.join();
-
- // test task_group
- REMARK("Testing task_group... ");
- int result;
- result = Fib(32);
- ASSERT(result==2178309, "task_group w/lambda failed.\n");
- REMARK("passed.\n");
-
- // Reset array a to index values
- parallel_for(blocked_range<int>(0,N,Grainsize),
- [&] (blocked_range<int>& r) {
- for (int i=r.begin(); i!=r.end(); ++i) a[i] = i;
- });
- // test parallel_sort
- REMARK("Testing parallel_sort... ");
- int pivot = 42;
-
- // sort nearest by increasing distance from pivot
- parallel_sort(a, a+N,
- [&](int x, int y) { return(abs(pivot-x) < abs(pivot-y)); });
- ASSERT(a[0]==42 && a[N-1]==N-1, "parallel_sort w/lambda failed.\n");
- REMARK("passed.\n");
-
- //test combinable
- REMARK("Testing combinable... ");
- combinable<std::pair<int,int> > minmax_c([&]() { return std::make_pair(a[0], a[0]); } );
-
- parallel_for(blocked_range<int>(0,N),
- [&] (const blocked_range<int> &r) {
- std::pair<int,int>& mmr = minmax_c.local();
- for(int i=r.begin(); i!=r.end(); ++i) {
- if (mmr.first > a[i]) mmr.first = a[i];
- if (mmr.second < a[i]) mmr.second = a[i];
- }
- });
- minmax_c.combine_each([](std::pair<int,int> x) {
- int sum;
- sum = x.first + x.second;
- });
- std::pair<int,int> minmax_result_c;
- minmax_result_c =
- minmax_c.combine([](std::pair<int,int> x, std::pair<int,int> y) {
- return std::make_pair(x.first<y.first?x.first:y.first,
- x.second>y.second?x.second:y.second);
- });
- ASSERT(minmax_result_c.first==0 && minmax_result_c.second==999,
- "combinable w/lambda failed.\n");
- REMARK("passed.\n");
-
- //test enumerable_thread_specific
- REMARK("Testing enumerable_thread_specific... ");
- enumerable_thread_specific< std::pair<int,int> > minmax_ets([&]() { return std::make_pair(a[0], a[0]); } );
-
- parallel_for(blocked_range<int>(0,N),
- [&] (const blocked_range<int> &r) {
- std::pair<int,int>& mmr = minmax_ets.local();
- for(int i=r.begin(); i!=r.end(); ++i) {
- if (mmr.first > a[i]) mmr.first = a[i];
- if (mmr.second < a[i]) mmr.second = a[i];
- }
- });
- minmax_ets.combine_each([](std::pair<int,int> x) {
- int sum;
- sum = x.first + x.second;
- });
- std::pair<int,int> minmax_result_ets;
- minmax_result_ets =
- minmax_ets.combine([](std::pair<int,int> x, std::pair<int,int> y) {
- return std::make_pair(x.first<y.first?x.first:y.first,
- x.second>y.second?x.second:y.second);
- });
- ASSERT(minmax_result_ets.first==0 && minmax_result_ets.second==999,
- "enumerable_thread_specific w/lambda failed.\n");
- REMARK("passed.\n");
- }
- return Harness::Done;
-#else
- return Harness::Skipped;
-#endif /* !__TBB_LAMBDAS_PRESENT */
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-/* Regression test against bug in TBB allocator, manifested when
- dynamic library calls atexit or register dtors of static objects.
- If the allocator is not initialized yet, we can got deadlock,
- because allocator library has static object dtors as well, they
- registred during allocator initialization, and atexit is protected
- by non-recursive mutex in some GLIBCs.
- */
-
-#if _USRDLL
-
-#include <stdlib.h>
-
-#if _WIN32||_WIN64
-// isMallocOverloaded must be defined in DLL to linker not drop the dependence
-// to the DLL.
-extern __declspec(dllexport) bool isMallocOverloaded();
-
-bool isMallocOverloaded()
-{
- return true;
-}
-
-#else
-
-#include <dlfcn.h>
-
-bool isMallocOverloaded()
-{
- return dlsym(RTLD_DEFAULT, "__TBB_malloc_proxy");
-}
-
-#endif
-
-#ifndef _PGO_INSTRUMENT
-void dummyFunction() {}
-
-class Foo {
-public:
- Foo() {
- // add a lot of exit handlers to cause memory allocation
- for (int i=0; i<1024; i++)
- atexit(dummyFunction);
- }
-};
-
-static Foo f;
-#endif
-
-#else // _USRDLL
-#include "harness.h"
-
-#if _WIN32||_WIN64
-extern __declspec(dllimport)
-#endif
-bool isMallocOverloaded();
-
-int TestMain () {
-#ifdef _PGO_INSTRUMENT
- REPORT("Known issue: test_malloc_atexit hangs if compiled with -prof-genx\n");
- return Harness::Skipped;
-#else
- return isMallocOverloaded()? Harness::Done : Harness::Skipped;
-#endif
-}
-
-#endif // _USRDLL
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-const int MByte = 1048576; //1MB
-bool __tbb_test_errno = false;
-
-/* _WIN32_WINNT should be defined at the very beginning,
- because other headers might include <windows.h>
-*/
-
-#if _WIN32 || _WIN64 && !__MINGW64__
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0500
-#include "tbb/machine/windows_api.h"
-#include <stdio.h>
-#include "harness_report.h"
-
-void limitMem( int limit )
-{
- static HANDLE hJob = NULL;
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo;
-
- jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
- jobInfo.ProcessMemoryLimit = limit? limit*MByte : 2*1024LL*MByte;
- if (NULL == hJob) {
- if (NULL == (hJob = CreateJobObject(NULL, NULL))) {
- REPORT("Can't assign create job object: %ld\n", GetLastError());
- exit(1);
- }
- if (0 == AssignProcessToJobObject(hJob, GetCurrentProcess())) {
- REPORT("Can't assign process to job object: %ld\n", GetLastError());
- exit(1);
- }
- }
- if (0 == SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
- &jobInfo, sizeof(jobInfo))) {
- REPORT("Can't set limits: %ld\n", GetLastError());
- exit(1);
- }
-}
-// Do not test errno with static VC runtime
-#else
-#include <sys/resource.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h> // uint64_t on FreeBSD, needed for rlim_t
-#include "harness_report.h"
-
-void limitMem( int limit )
-{
- rlimit rlim;
- rlim.rlim_cur = limit? limit*MByte : (rlim_t)RLIM_INFINITY;
- rlim.rlim_max = (rlim_t)RLIM_INFINITY;
- int ret = setrlimit(RLIMIT_AS,&rlim);
- if (0 != ret) {
- REPORT("Can't set limits: errno %d\n", errno);
- exit(1);
- }
-}
-#endif
-
-#define ASSERT_ERRNO(cond, msg) ASSERT( !__tbb_test_errno || (cond), msg )
-#define CHECK_ERRNO(cond) (__tbb_test_errno && (cond))
-
-#include <time.h>
-#include <errno.h>
-#define __TBB_NO_IMPLICIT_LINKAGE 1
-#include "tbb/scalable_allocator.h"
-#include "tbb/tbb_machine.h"
-
-#define HARNESS_CUSTOM_MAIN 1
-#include "harness.h"
-#include "harness_barrier.h"
-#if __linux__
-#include <stdint.h> // uintptr_t
-#endif
-#if _WIN32 || _WIN64
-#include <malloc.h> // _aligned_(malloc|free|realloc)
-#endif
-
-#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 <vector>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-const size_t COUNT_ELEM_CALLOC = 2;
-const int COUNT_TESTS = 1000;
-const int COUNT_ELEM = 25000;
-const size_t MAX_SIZE = 1000;
-const int COUNTEXPERIMENT = 10000;
-
-const char strError[]="failed";
-const char strOk[]="done";
-
-typedef unsigned int UINT;
-typedef unsigned char UCHAR;
-typedef unsigned long DWORD;
-typedef unsigned char BYTE;
-
-
-typedef void* TestMalloc(size_t size);
-typedef void* TestCalloc(size_t num, size_t size);
-typedef void* TestRealloc(void* memblock, size_t size);
-typedef void TestFree(void* memblock);
-typedef int TestPosixMemalign(void **memptr, size_t alignment, size_t size);
-typedef void* TestAlignedMalloc(size_t size, size_t alignment);
-typedef void* TestAlignedRealloc(void* memblock, size_t size, size_t alignment);
-typedef void TestAlignedFree(void* memblock);
-
-TestMalloc* Tmalloc;
-TestCalloc* Tcalloc;
-TestRealloc* Trealloc;
-TestFree* Tfree;
-TestAlignedFree* Taligned_free;
-// call alignment-related function via pointer and check result's alignment
-int Tposix_memalign(void **memptr, size_t alignment, size_t size);
-void* Taligned_malloc(size_t size, size_t alignment);
-void* Taligned_realloc(void* memblock, size_t size, size_t alignment);
-
-// pointers to alignment-related functions used while testing
-TestPosixMemalign* Rposix_memalign;
-TestAlignedMalloc* Raligned_malloc;
-TestAlignedRealloc* Raligned_realloc;
-
-bool error_occurred = false;
-
-#if __APPLE__
-// Tests that use the variable are skipped on Mac OS* X
-#else
-static bool perProcessLimits = true;
-#endif
-
-const size_t POWERS_OF_2 = 20;
-
-#if __linux__ && __ia64__
-/* Can't use Intel compiler intrinsic due to internal error reported by
- 10.1 compiler */
-pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-int32_t __TBB_machine_fetchadd4__TBB_full_fence (volatile void *ptr, int32_t value)
-{
- pthread_mutex_lock(&counter_mutex);
- int32_t result = *(int32_t*)ptr;
- *(int32_t*)ptr = result + value;
- pthread_mutex_unlock(&counter_mutex);
- return result;
-}
-
-void __TBB_machine_pause(int32_t /*delay*/) {}
-
-#elif (_WIN32||_WIN64) && defined(_M_AMD64) && !__MINGW64__
-
-void __TBB_machine_pause(__int32 /*delay*/ ) {}
-
-#endif
-
-struct MemStruct
-{
- void* Pointer;
- UINT Size;
-
- MemStruct() : Pointer(NULL), Size(0) {}
- MemStruct(void* Pointer, UINT Size) : Pointer(Pointer), Size(Size) {}
-};
-
-class CMemTest: NoAssign
-{
- UINT CountErrors;
- bool FullLog;
- Harness::SpinBarrier *limitBarrier;
- static bool firstTime;
-
-public:
- CMemTest(Harness::SpinBarrier *limitBarrier, bool isVerbose=false) :
- CountErrors(0), limitBarrier(limitBarrier)
- {
- srand((UINT)time(NULL));
- FullLog=isVerbose;
- rand();
- }
- void InvariantDataRealloc(bool aligned); //realloc does not change data
- void NULLReturn(UINT MinSize, UINT MaxSize, int total_threads); // NULL pointer + check errno
- void UniquePointer(); // unique pointer - check with padding
- void AddrArifm(); // unique pointer - check with pointer arithmetic
- bool ShouldReportError();
- void Free_NULL(); //
- void Zerofilling(); // check if arrays are zero-filled
- void TestAlignedParameters();
- void RunAllTests(int total_threads);
- ~CMemTest() {}
-};
-
-class Limit {
- int limit;
-public:
- Limit(int limit) : limit(limit) {}
- void operator() () const {
- limitMem(limit);
- }
-};
-
-int argC;
-char** argV;
-
-struct RoundRobin: NoAssign {
- const long number_of_threads;
- mutable CMemTest test;
-
- RoundRobin( long p, Harness::SpinBarrier *limitBarrier, bool verbose ) :
- number_of_threads(p), test(limitBarrier, verbose) {}
- void operator()( int /*id*/ ) const
- {
- test.RunAllTests(number_of_threads);
- }
-};
-
-bool CMemTest::firstTime = true;
-
-static void setSystemAllocs()
-{
- Tmalloc=malloc;
- Trealloc=realloc;
- Tcalloc=calloc;
- Tfree=free;
-#if (_WIN32 || _WIN64) && !__MINGW64__ && !__MINGW32__
- Raligned_malloc=_aligned_malloc;
- Raligned_realloc=_aligned_realloc;
- Taligned_free=_aligned_free;
- Rposix_memalign=0;
-#elif __APPLE__ || __sun || __MINGW64__ || __MINGW32__ // Max OS X MinGW and Solaris don't have posix_memalign
- Raligned_malloc=0;
- Raligned_realloc=0;
- Taligned_free=0;
- Rposix_memalign=0;
-#else
- Raligned_malloc=0;
- Raligned_realloc=0;
- Taligned_free=0;
- Rposix_memalign=posix_memalign;
-#endif
-}
-
-// check that realloc works as free and as malloc
-void ReallocParam()
-{
- const int ITERS = 1000;
- int i;
- void *bufs[ITERS];
-
- bufs[0] = Trealloc(NULL, 30*MByte);
- ASSERT(bufs[0], "Can't get memory to start the test.");
-
- for (i=1; i<ITERS; i++)
- {
- bufs[i] = Trealloc(NULL, 30*MByte);
- if (NULL == bufs[i])
- break;
- }
- ASSERT(i<ITERS, "Limits should be decreased for the test to work.");
-
- Trealloc(bufs[0], 0);
- /* There is a race for the free space between different threads at
- this point. So, have to run the test sequentially.
- */
- bufs[0] = Trealloc(NULL, 30*MByte);
- ASSERT(bufs[0], NULL);
-
- for (int j=0; j<i; j++)
- Trealloc(bufs[j], 0);
-}
-
-HARNESS_EXPORT
-int main(int argc, char* argv[]) {
- argC=argc;
- argV=argv;
- MaxThread = MinThread = 1;
- Tmalloc=scalable_malloc;
- Trealloc=scalable_realloc;
- Tcalloc=scalable_calloc;
- Tfree=scalable_free;
- Rposix_memalign=scalable_posix_memalign;
- Raligned_malloc=scalable_aligned_malloc;
- Raligned_realloc=scalable_aligned_realloc;
- Taligned_free=scalable_aligned_free;
-
- // check if we were called to test standard behavior
- for (int i=1; i< argc; i++) {
- if (strcmp((char*)*(argv+i),"-s")==0)
- {
- setSystemAllocs();
- argC--;
- break;
- }
- }
-
- ParseCommandLine( argC, argV );
-#if __linux__
- /* According to man pthreads
- "NPTL threads do not share resource limits (fixed in kernel 2.6.10)".
- Use per-threads limits for affected systems.
- */
- if ( LinuxKernelVersion() < 2*1000000 + 6*1000 + 10)
- perProcessLimits = false;
-#endif
- //-------------------------------------
-#if __APPLE__
- /* Skip due to lack of memory limit enforcing under Mac OS X. */
-#else
- limitMem(200);
- ReallocParam();
- limitMem(0);
-#endif
-
-//for linux and dynamic runtime errno is used to check allocator fuctions
-//check if library compiled with /MD(d) and we can use errno
-#if _MSC_VER
-#if defined(_MT) && defined(_DLL) //check errno if test itself compiled with /MD(d) only
- #pragma comment(lib, "version.lib")
- char* version_info_block = NULL;
- int version_info_block_size;
- LPVOID comments_block = NULL;
- UINT comments_block_size;
-#ifdef _DEBUG
-#define __TBBMALLOCDLL "tbbmalloc_debug.dll"
-#else //_DEBUG
-#define __TBBMALLOCDLL "tbbmalloc.dll"
-#endif //_DEBUG
- version_info_block_size = GetFileVersionInfoSize( __TBBMALLOCDLL, (LPDWORD)&version_info_block_size );
- if( version_info_block_size
- && ((version_info_block = (char*)malloc(version_info_block_size)) != NULL)
- && GetFileVersionInfo( __TBBMALLOCDLL, NULL, version_info_block_size, version_info_block )
- && VerQueryValue( version_info_block, "\\StringFileInfo\\000004b0\\Comments", &comments_block, &comments_block_size )
- && strstr( (char*)comments_block, "/MD" )
- ){
- __tbb_test_errno = true;
- }
- if( version_info_block ) free( version_info_block );
-#endif // defined(_MT) && defined(_DLL)
-#else // _MSC_VER
- __tbb_test_errno = true;
-#endif // _MSC_VER
-
- for( int p=MaxThread; p>=MinThread; --p ) {
- REMARK("testing with %d threads\n", p );
- Harness::SpinBarrier *barrier = new Harness::SpinBarrier(p);
- NativeParallelFor( p, RoundRobin(p, barrier, Verbose) );
- delete barrier;
- }
- if( !error_occurred )
- REPORT("done\n");
- return 0;
-}
-
-struct TestStruct
-{
- DWORD field1:2;
- DWORD field2:6;
- double field3;
- UCHAR field4[100];
- TestStruct* field5;
-// std::string field6;
- std::vector<int> field7;
- double field8;
- bool IsZero() {
- int wordSz = sizeof(TestStruct) / sizeof(intptr_t);
- int tailSz = sizeof(TestStruct) % sizeof(intptr_t);
-
- intptr_t *buf =(intptr_t*)this;
- char *bufTail =(char*) (buf+wordSz);
-
- for (int i=0; i<wordSz; i++)
- if (buf[i]) return false;
- for (int i=0; i<tailSz; i++)
- if (bufTail[i]) return false;
- return true;
- }
-};
-
-int Tposix_memalign(void **memptr, size_t alignment, size_t size)
-{
- int ret = Rposix_memalign(memptr, alignment, size);
- if (0 == ret)
- ASSERT(0==((uintptr_t)*memptr & (alignment-1)),
- "allocation result should be aligned");
- return ret;
-}
-void* Taligned_malloc(size_t size, size_t alignment)
-{
- void *ret = Raligned_malloc(size, alignment);
- if (0 != ret)
- ASSERT(0==((uintptr_t)ret & (alignment-1)),
- "allocation result should be aligned");
- return ret;
-}
-void* Taligned_realloc(void* memblock, size_t size, size_t alignment)
-{
- void *ret = Raligned_realloc(memblock, size, alignment);
- if (0 != ret)
- ASSERT(0==((uintptr_t)ret & (alignment-1)),
- "allocation result should be aligned");
- return ret;
-}
-
-inline size_t choose_random_alignment() {
- return sizeof(void*)<<(rand() % POWERS_OF_2);
-}
-
-void CMemTest::InvariantDataRealloc(bool aligned)
-{
- size_t size, sizeMin;
- CountErrors=0;
- if (FullLog) REPORT("\nInvariant data by realloc....");
- UCHAR* pchar;
- sizeMin=size=rand()%MAX_SIZE+10;
- pchar = aligned?
- (UCHAR*)Taligned_realloc(NULL,size,choose_random_alignment())
- : (UCHAR*)Trealloc(NULL,size);
- if (NULL == pchar)
- return;
- for (size_t k=0; k<size; k++)
- pchar[k]=(UCHAR)k%255+1;
- for (int i=0; i<COUNTEXPERIMENT; i++)
- {
- size=rand()%MAX_SIZE+10;
- UCHAR *pcharNew = aligned?
- (UCHAR*)Taligned_realloc(pchar,size, choose_random_alignment())
- : (UCHAR*)Trealloc(pchar,size);
- if (NULL == pcharNew)
- continue;
- pchar = pcharNew;
- sizeMin=size<sizeMin ? size : sizeMin;
- for (size_t k=0; k<sizeMin; k++)
- if (pchar[k] != (UCHAR)k%255+1)
- {
- CountErrors++;
- if (ShouldReportError())
- {
- REPORT("stand '%c', must stand '%c'\n",pchar[k],(UCHAR)k%255+1);
- REPORT("error: data changed (at %llu, SizeMin=%llu)\n",
- (long long unsigned)k,(long long unsigned)sizeMin);
- }
- }
- }
- if (aligned)
- Taligned_realloc(pchar,0,choose_random_alignment());
- else
- Trealloc(pchar,0);
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
- //REPORT("end check\n");
-}
-
-struct PtrSize {
- void *ptr;
- size_t size;
-};
-
-static int cmpAddrs(const void *p1, const void *p2)
-{
- const PtrSize *a = (const PtrSize *)p1;
- const PtrSize *b = (const PtrSize *)p2;
-
- return a->ptr < b->ptr ? -1 : ( a->ptr == b->ptr ? 0 : 1);
-}
-
-void CMemTest::AddrArifm()
-{
- PtrSize *arr = (PtrSize*)Tmalloc(COUNT_ELEM*sizeof(PtrSize));
-
- if (FullLog) REPORT("\nUnique pointer using Address arithmetics\n");
- if (FullLog) REPORT("malloc....");
- ASSERT(arr, NULL);
- for (int i=0; i<COUNT_ELEM; i++)
- {
- arr[i].size=rand()%MAX_SIZE;
- arr[i].ptr=Tmalloc(arr[i].size);
- }
- qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
-
- for (int i=0; i<COUNT_ELEM-1; i++)
- {
- if (NULL!=arr[i].ptr && NULL!=arr[i+1].ptr)
- ASSERT((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
- "intersection detected");
- }
- //----------------------------------------------------------------
- if (FullLog) REPORT("realloc....");
- for (int i=0; i<COUNT_ELEM; i++)
- {
- size_t count=arr[i].size*2;
- void *tmpAddr=Trealloc(arr[i].ptr,count);
- if (NULL!=tmpAddr) {
- arr[i].ptr = tmpAddr;
- arr[i].size = count;
- } else if (count==0) { // becasue realloc(..., 0) works as free
- arr[i].ptr = NULL;
- arr[i].size = 0;
- }
- }
- qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
-
- for (int i=0; i<COUNT_ELEM-1; i++)
- {
- if (NULL!=arr[i].ptr && NULL!=arr[i+1].ptr)
- ASSERT((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
- "intersection detected");
- }
- for (int i=0; i<COUNT_ELEM; i++)
- {
- Tfree(arr[i].ptr);
- }
- //-------------------------------------------
- if (FullLog) REPORT("calloc....");
- for (int i=0; i<COUNT_ELEM; i++)
- {
- arr[i].size=rand()%MAX_SIZE;
- arr[i].ptr=Tcalloc(arr[i].size,1);
- }
- qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
-
- for (int i=0; i<COUNT_ELEM-1; i++)
- {
- if (NULL!=arr[i].ptr && NULL!=arr[i+1].ptr)
- ASSERT((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
- "intersection detected");
- }
- for (int i=0; i<COUNT_ELEM; i++)
- {
- Tfree(arr[i].ptr);
- }
- Tfree(arr);
-}
-
-void CMemTest::Zerofilling()
-{
- TestStruct* TSMas;
- size_t CountElement;
- CountErrors=0;
- if (FullLog) REPORT("\nzeroings elements of array....");
- //test struct
- for (int i=0; i<COUNTEXPERIMENT; i++)
- {
- CountElement=rand()%MAX_SIZE;
- TSMas=(TestStruct*)Tcalloc(CountElement,sizeof(TestStruct));
- if (NULL == TSMas)
- continue;
- for (size_t j=0; j<CountElement; j++)
- {
- if (!(TSMas+j)->IsZero())
- {
- CountErrors++;
- if (ShouldReportError()) REPORT("detect nonzero element at TestStruct\n");
- }
- }
- Tfree(TSMas);
- }
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
-}
-
-#if !__APPLE__
-void CMemTest::NULLReturn(UINT MinSize, UINT MaxSize, int total_threads)
-{
- // find size to guarantee getting NULL for 1024 B allocations
- const int MAXNUM_1024 = (200+50)*1024;
-
- std::vector<MemStruct> PointerList;
- void *tmp;
- CountErrors=0;
- int CountNULL, num_1024;
- if (FullLog) REPORT("\nNULL return & check errno:\n");
- UINT Size;
- Limit limit_200M(200*total_threads), no_limit(0);
- void **buf_1024 = (void**)Tmalloc(MAXNUM_1024*sizeof(void*));
-
- ASSERT(buf_1024, NULL);
- /* We must have space for pointers when memory limit is hit.
- Reserve enough for the worst case.
- */
- PointerList.reserve(200*MByte/MinSize);
-
- /* There is a bug in the specific verion of GLIBC (2.5-12) shipped
- with RHEL5 that leads to erroneous working of the test
- on Intel64 and IPF systems when setrlimit-related part is enabled.
- Switching to GLIBC 2.5-18 from RHEL5.1 resolved the issue.
- */
- if (perProcessLimits)
- limitBarrier->wait(limit_200M);
- else
- limitMem(200);
-
- /* regression test against the bug in allocator when it dereference NULL
- while lack of memory
- */
- for (num_1024=0; num_1024<MAXNUM_1024; num_1024++) {
- buf_1024[num_1024] = Tcalloc(1024, 1);
- if (! buf_1024[num_1024]) {
- ASSERT_ERRNO(errno == ENOMEM, NULL);
- break;
- }
- }
- for (int i=0; i<num_1024; i++)
- Tfree(buf_1024[i]);
- Tfree(buf_1024);
-
- do {
- Size=rand()%(MaxSize-MinSize)+MinSize;
- tmp=Tmalloc(Size);
- if (tmp != NULL)
- {
- memset(tmp, 0, Size);
- PointerList.push_back(MemStruct(tmp, Size));
- }
- } while(tmp != NULL);
- ASSERT_ERRNO(errno == ENOMEM, NULL);
- if (FullLog) REPORT("\n");
-
- // preparation complete, now running tests
- // malloc
- if (FullLog) REPORT("malloc....");
- CountNULL = 0;
- while (CountNULL==0)
- for (int j=0; j<COUNT_TESTS; j++)
- {
- Size=rand()%(MaxSize-MinSize)+MinSize;
- errno = ENOMEM+j+1;
- tmp=Tmalloc(Size);
- if (tmp == NULL)
- {
- CountNULL++;
- if ( CHECK_ERRNO(errno != ENOMEM) ) {
- CountErrors++;
- if (ShouldReportError()) REPORT("NULL returned, error: errno (%d) != ENOMEM\n", errno);
- }
- }
- else
- {
- // Technically, if malloc returns a non-NULL pointer, it is allowed to set errno anyway.
- // However, on most systems it does not set errno.
- bool known_issue = false;
-#if __linux__
- if( CHECK_ERRNO(errno==ENOMEM) ) known_issue = true;
-#endif /* __linux__ */
- if ( CHECK_ERRNO(errno != ENOMEM+j+1) && !known_issue) {
- CountErrors++;
- if (ShouldReportError()) REPORT("error: errno changed to %d though valid pointer was returned\n", errno);
- }
- memset(tmp, 0, Size);
- PointerList.push_back(MemStruct(tmp, Size));
- }
- }
- if (FullLog) REPORT("end malloc\n");
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
-
- CountErrors=0;
- //calloc
- if (FullLog) REPORT("calloc....");
- CountNULL = 0;
- while (CountNULL==0)
- for (int j=0; j<COUNT_TESTS; j++)
- {
- Size=rand()%(MaxSize-MinSize)+MinSize;
- errno = ENOMEM+j+1;
- tmp=Tcalloc(COUNT_ELEM_CALLOC,Size);
- if (tmp == NULL)
- {
- CountNULL++;
- if ( CHECK_ERRNO(errno != ENOMEM) ){
- CountErrors++;
- if (ShouldReportError()) REPORT("NULL returned, error: errno(%d) != ENOMEM\n", errno);
- }
- }
- else
- {
- // Technically, if calloc returns a non-NULL pointer, it is allowed to set errno anyway.
- // However, on most systems it does not set errno.
- bool known_issue = false;
-#if __linux__
- if( CHECK_ERRNO(errno==ENOMEM) ) known_issue = true;
-#endif /* __linux__ */
- if ( CHECK_ERRNO(errno != ENOMEM+j+1) && !known_issue ) {
- CountErrors++;
- if (ShouldReportError()) REPORT("error: errno changed to %d though valid pointer was returned\n", errno);
- }
- PointerList.push_back(MemStruct(tmp, Size));
- }
- }
- if (FullLog) REPORT("end calloc\n");
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
- CountErrors=0;
- if (FullLog) REPORT("realloc....");
- CountNULL = 0;
- if (PointerList.size() > 0)
- while (CountNULL==0)
- for (size_t i=0; i<(size_t)COUNT_TESTS && i<PointerList.size(); i++)
- {
- errno = 0;
- tmp=Trealloc(PointerList[i].Pointer,PointerList[i].Size*2);
- if (PointerList[i].Pointer == tmp) // the same place
- {
- bool known_issue = false;
-#if __linux__
- if( errno==ENOMEM ) known_issue = true;
-#endif /* __linux__ */
- if (errno != 0 && !known_issue) {
- CountErrors++;
- if (ShouldReportError()) REPORT("valid pointer returned, error: errno not kept\n");
- }
- PointerList[i].Size *= 2;
- }
- else if (tmp != PointerList[i].Pointer && tmp != NULL) // another place
- {
- bool known_issue = false;
-#if __linux__
- if( errno==ENOMEM ) known_issue = true;
-#endif /* __linux__ */
- if (errno != 0 && !known_issue) {
- CountErrors++;
- if (ShouldReportError()) REPORT("valid pointer returned, error: errno not kept\n");
- }
- // newly allocated area have to be zeroed
- memset((char*)tmp + PointerList[i].Size, 0, PointerList[i].Size);
- PointerList[i].Pointer = tmp;
- PointerList[i].Size *= 2;
- }
- else if (tmp == NULL)
- {
- CountNULL++;
- if ( CHECK_ERRNO(errno != ENOMEM) )
- {
- CountErrors++;
- if (ShouldReportError()) REPORT("NULL returned, error: errno(%d) != ENOMEM\n", errno);
- }
- // check data integrity
- BYTE *zer=(BYTE*)PointerList[i].Pointer;
- for (UINT k=0; k<PointerList[i].Size; k++)
- if (zer[k] != 0)
- {
- CountErrors++;
- if (ShouldReportError()) REPORT("NULL returned, error: data changed\n");
- }
- }
- }
- if (FullLog) REPORT("realloc end\n");
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
- for (UINT i=0; i<PointerList.size(); i++)
- {
- Tfree(PointerList[i].Pointer);
- }
-
- if (perProcessLimits)
- limitBarrier->wait(no_limit);
- else
- limitMem(0);
-}
-#endif /* #if __APPLE__ */
-
-void CMemTest::UniquePointer()
-{
- CountErrors=0;
- int **MasPointer = (int **)Tmalloc(sizeof(int*)*COUNT_ELEM);
- size_t *MasCountElem = (size_t*)Tmalloc(sizeof(size_t)*COUNT_ELEM);
- if (FullLog) REPORT("\nUnique pointer using 0\n");
- ASSERT(MasCountElem && MasPointer, NULL);
- //
- //-------------------------------------------------------
- //malloc
- for (int i=0; i<COUNT_ELEM; i++)
- {
- MasCountElem[i]=rand()%MAX_SIZE;
- MasPointer[i]=(int*)Tmalloc(MasCountElem[i]*sizeof(int));
- if (NULL == MasPointer[i])
- MasCountElem[i]=0;
- for (UINT j=0; j<MasCountElem[i]; j++)
- *(MasPointer[i]+j)=0;
- }
- if (FullLog) REPORT("malloc....");
- for (UINT i=0; i<COUNT_ELEM-1; i++)
- {
- for (UINT j=0; j<MasCountElem[i]; j++)
- {
- if (*(*(MasPointer+i)+j)!=0)
- {
- CountErrors++;
- if (ShouldReportError()) REPORT("error, detect 1 with 0x%p\n",(*(MasPointer+i)+j));
- }
- *(*(MasPointer+i)+j)+=1;
- }
- }
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
- //----------------------------------------------------------
- //calloc
- for (int i=0; i<COUNT_ELEM; i++)
- Tfree(MasPointer[i]);
- CountErrors=0;
- for (long i=0; i<COUNT_ELEM; i++)
- {
- MasPointer[i]=(int*)Tcalloc(MasCountElem[i]*sizeof(int),2);
- if (NULL == MasPointer[i])
- MasCountElem[i]=0;
- }
- if (FullLog) REPORT("calloc....");
- for (int i=0; i<COUNT_ELEM-1; i++)
- {
- for (UINT j=0; j<*(MasCountElem+i); j++)
- {
- if (*(*(MasPointer+i)+j)!=0)
- {
- CountErrors++;
- if (ShouldReportError()) REPORT("error, detect 1 with 0x%p\n",(*(MasPointer+i)+j));
- }
- *(*(MasPointer+i)+j)+=1;
- }
- }
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
- //---------------------------------------------------------
- //realloc
- CountErrors=0;
- for (int i=0; i<COUNT_ELEM; i++)
- {
- MasCountElem[i]*=2;
- *(MasPointer+i)=
- (int*)Trealloc(*(MasPointer+i),MasCountElem[i]*sizeof(int));
- if (NULL == MasPointer[i])
- MasCountElem[i]=0;
- for (UINT j=0; j<MasCountElem[i]; j++)
- *(*(MasPointer+i)+j)=0;
- }
- if (FullLog) REPORT("realloc....");
- for (int i=0; i<COUNT_ELEM-1; i++)
- {
- for (UINT j=0; j<*(MasCountElem+i); j++)
- {
- if (*(*(MasPointer+i)+j)!=0)
- {
- CountErrors++;
- }
- *(*(MasPointer+i)+j)+=1;
- }
- }
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
- for (int i=0; i<COUNT_ELEM; i++)
- Tfree(MasPointer[i]);
- Tfree(MasCountElem);
- Tfree(MasPointer);
-}
-
-bool CMemTest::ShouldReportError()
-{
- if (FullLog)
- return true;
- else
- if (firstTime) {
- firstTime = false;
- return true;
- } else
- return false;
-}
-
-void CMemTest::Free_NULL()
-{
- CountErrors=0;
- if (FullLog) REPORT("\ncall free with parameter NULL....");
- errno = 0;
- for (int i=0; i<COUNTEXPERIMENT; i++)
- {
- Tfree(NULL);
- if (errno != 0)
- {
- CountErrors++;
- if (ShouldReportError()) REPORT("error is found by a call free with parameter NULL\n");
- }
- }
- if (CountErrors) REPORT("%s\n",strError);
- else if (FullLog) REPORT("%s\n",strOk);
- error_occurred |= ( CountErrors>0 ) ;
-}
-
-void CMemTest::TestAlignedParameters()
-{
- void *memptr;
- int ret;
-
- if (Rposix_memalign) {
- // alignment isn't power of 2
- for (int bad_align=3; bad_align<16; bad_align++)
- if (bad_align&(bad_align-1)) {
- ret = Tposix_memalign(NULL, bad_align, 100);
- ASSERT(EINVAL==ret, NULL);
- }
-
- memptr = &ret;
- ret = Tposix_memalign(&memptr, 5*sizeof(void*), 100);
- ASSERT(memptr == &ret,
- "memptr should not be changed after unsuccesful call");
- ASSERT(EINVAL==ret, NULL);
-
- // alignment is power of 2, but not a multiple of sizeof(void *),
- // we expect that sizeof(void*) > 2
- ret = Tposix_memalign(NULL, 2, 100);
- ASSERT(EINVAL==ret, NULL);
- }
- if (Raligned_malloc) {
- // alignment isn't power of 2
- for (int bad_align=3; bad_align<16; bad_align++)
- if (bad_align&(bad_align-1)) {
- memptr = Taligned_malloc(100, bad_align);
- ASSERT(NULL==memptr, NULL);
- ASSERT_ERRNO(EINVAL==errno, NULL);
- }
-
- // size is zero
- memptr = Taligned_malloc(0, 16);
- ASSERT(NULL==memptr, "size is zero, so must return NULL");
- ASSERT_ERRNO(EINVAL==errno, NULL);
- }
- if (Taligned_free) {
- // NULL pointer is OK to free
- errno = 0;
- Taligned_free(NULL);
- /* As there is no return value for free, strictly speaking we can't
- check errno here. But checked implementations obey the assertion.
- */
- ASSERT_ERRNO(0==errno, NULL);
- }
- if (Raligned_realloc) {
- for (int i=1; i<20; i++) {
- // checks that calls work correctly in presence of non-zero errno
- errno = i;
- void *ptr = Taligned_malloc(i*10, 128);
- ASSERT(NULL!=ptr, NULL);
- ASSERT_ERRNO(0!=errno, NULL);
- // if size is zero and pointer is not NULL, works like free
- memptr = Taligned_realloc(ptr, 0, 64);
- ASSERT(NULL==memptr, NULL);
- ASSERT_ERRNO(0!=errno, NULL);
- }
- // alignment isn't power of 2
- for (int bad_align=3; bad_align<16; bad_align++)
- if (bad_align&(bad_align-1)) {
- void *ptr = &bad_align;
- memptr = Taligned_realloc(&ptr, 100, bad_align);
- ASSERT(NULL==memptr, NULL);
- ASSERT(&bad_align==ptr, NULL);
- ASSERT_ERRNO(EINVAL==errno, NULL);
- }
- }
-}
-
-void CMemTest::RunAllTests(int total_threads)
-{
- Zerofilling();
- Free_NULL();
- InvariantDataRealloc(/*aligned=*/false);
- if (Raligned_realloc)
- InvariantDataRealloc(/*aligned=*/true);
- TestAlignedParameters();
-#if __APPLE__
- REPORT("Known issue: some tests are skipped on Mac OS* X\n");
-#else
- UniquePointer();
- AddrArifm();
-#if !__TBB_MIC_NATIVE
- NULLReturn(1*MByte,100*MByte,total_threads);
-#endif
-#endif
- if (FullLog) REPORT("All tests ended\nclearing memory...");
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/scalable_allocator.h"
-#include "tbb/atomic.h"
-#include "tbb/aligned_space.h"
-#include "../tbb/tbb_assert_impl.h"
-
-#if _WIN64 && defined(_M_AMD64) && !__MINGW64__
-void __TBB_machine_pause(__int32 /*delay*/ ) {}
-#elif __linux__ && __ia64__
-#include <pthread.h>
-
-pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-int32_t __TBB_machine_fetchadd4__TBB_full_fence (volatile void *ptr, int32_t value)
-{
- pthread_mutex_lock(&counter_mutex);
- int32_t result = *(int32_t*)ptr;
- *(int32_t*)ptr = result + value;
- pthread_mutex_unlock(&counter_mutex);
- return result;
-}
-
-void __TBB_machine_pause(int32_t /*delay*/) {}
-#endif
-
-#include "harness.h"
-#include "harness_barrier.h"
-
-tbb::atomic<int> FinishedTasks;
-const int MaxTasks = 16;
-
-/*--------------------------------------------------------------------*/
-// The regression test against a bug triggered when malloc initialization
-// and thread shutdown were called simultaneously, in which case
-// Windows dynamic loader lock and allocator initialization/termination lock
-// were taken in different order.
-
-class TestFunc1 {
- Harness::SpinBarrier* my_barr;
-public:
- TestFunc1 (Harness::SpinBarrier& barr) : my_barr(&barr) {}
- void operator() (bool do_malloc) const {
- my_barr->wait();
- if (do_malloc) scalable_malloc(10);
- ++FinishedTasks;
- }
-};
-
-typedef NativeParallelForTask<bool,TestFunc1> TestTask1;
-
-void Test1 () {
- int NTasks = min(MaxTasks, max(2, MaxThread));
- Harness::SpinBarrier barr(NTasks);
- TestFunc1 tf(barr);
- FinishedTasks = 0;
- tbb::aligned_space<TestTask1,MaxTasks> tasks;
-
- for(int i=0; i<NTasks; ++i) {
- TestTask1* t = tasks.begin()+i;
- new(t) TestTask1(i%2==0, tf);
- t->start();
- }
-
- Harness::Sleep(1000); // wait a second :)
- ASSERT( FinishedTasks==NTasks, "Some threads appear to deadlock" );
-
- for(int i=0; i<NTasks; ++i) {
- TestTask1* t = tasks.begin()+i;
- t->wait_to_finish();
- t->~TestTask1();
- }
-}
-
-/*--------------------------------------------------------------------*/
-// The regression test against a bug when cross-thread deallocation
-// caused livelock at thread shutdown.
-
-void* ptr = NULL;
-
-class TestFunc2a {
- Harness::SpinBarrier* my_barr;
-public:
- TestFunc2a (Harness::SpinBarrier& barr) : my_barr(&barr) {}
- void operator() (int) const {
- ptr = scalable_malloc(8);
- my_barr->wait();
- ++FinishedTasks;
- }
-};
-
-typedef NativeParallelForTask<int,TestFunc2a> TestTask2a;
-
-class TestFunc2b: NoAssign {
- Harness::SpinBarrier* my_barr;
- TestTask2a& my_ward;
-public:
- TestFunc2b (Harness::SpinBarrier& barr, TestTask2a& t) : my_barr(&barr), my_ward(t) {}
- void operator() (int) const {
- tbb::internal::spin_wait_while_eq(ptr, (void*)NULL);
- scalable_free(ptr);
- my_barr->wait();
- my_ward.wait_to_finish();
- ++FinishedTasks;
- }
-};
-void Test2() {
- Harness::SpinBarrier barr(2);
- TestFunc2a func2a(barr);
- TestTask2a t2a(0, func2a);
- TestFunc2b func2b(barr, t2a);
- NativeParallelForTask<int,TestFunc2b> t2b(1, func2b);
- FinishedTasks = 0;
- t2a.start(); t2b.start();
- Harness::Sleep(1000); // wait a second :)
- ASSERT( FinishedTasks==2, "Threads appear to deadlock" );
- t2b.wait_to_finish(); // t2a is monitored by t2b
-}
-
-int TestMain () {
- Test1(); // requires malloc initialization so should be first
- Test2();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-
-#include <cstdlib>
-#if _WIN32 || _WIN64
-#include "tbb/machine/windows_api.h"
-#else
-#include <dlfcn.h>
-#endif
-#include "tbb/tbb_stddef.h"
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-#include "harness_memory.h"
-
-#if TBB_USE_DEBUG
-#define SUFFIX1 "_debug"
-#define SUFFIX2
-#else
-#define SUFFIX1
-#define SUFFIX2 "_debug"
-#endif /* TBB_USE_DEBUG */
-
-#if _WIN32||_WIN64
-#define PREFIX
-#define EXT ".dll"
-#else
-#define PREFIX "lib"
-#if __APPLE__
-#define EXT ".dylib"
-#elif __linux__
-#define EXT __TBB_STRING(.so.TBB_COMPATIBLE_INTERFACE_VERSION)
-#elif __FreeBSD__ || __sun || _AIX
-#define EXT ".so"
-#else
-#error Unknown OS
-#endif
-#endif
-
-// Form the names of the TBB memory allocator binaries.
-#define MALLOCLIB_NAME1 PREFIX "tbbmalloc" SUFFIX1 EXT
-#define MALLOCLIB_NAME2 PREFIX "tbbmalloc" SUFFIX2 EXT
-
-#if _WIN32 || _WIN64
-#define LIBRARY_HANDLE HMODULE
-#define LOAD_LIBRARY(name) LoadLibrary((name))
-#else
-#define LIBRARY_HANDLE void*
-#define LOAD_LIBRARY(name) dlopen((name), RTLD_NOW|RTLD_GLOBAL)
-#endif
-
-struct Run {
- void operator()( int /*id*/ ) const {
- void* (*malloc_ptr)(std::size_t);
- void (*free_ptr)(void*);
-
- const char* actual_name;
- LIBRARY_HANDLE lib = LOAD_LIBRARY(actual_name = MALLOCLIB_NAME1);
- if (!lib) lib = LOAD_LIBRARY(actual_name = MALLOCLIB_NAME2);
- if (!lib) {
- REPORT("Can't load " MALLOCLIB_NAME1 " or " MALLOCLIB_NAME2 "\n");
- exit(1);
- }
-#if _WIN32 || _WIN64
- // casts at both sides are to soothe MinGW compiler
- (void *&)malloc_ptr = (void*)GetProcAddress(lib, "scalable_malloc");
- (void *&)free_ptr = (void*)GetProcAddress(lib, "scalable_free");
-#else
- (void *&)malloc_ptr = dlsym(lib, "scalable_malloc");
- (void *&)free_ptr = dlsym(lib, "scalable_free");
-#endif
- if (!malloc_ptr || !free_ptr) {
- REPORT("Can't find scalable_(malloc|free) in %s \n", actual_name);
- exit(1);
- }
-
- void *p = malloc_ptr(100);
- memset(p, 1, 100);
- free_ptr(p);
-
-#if _WIN32 || _WIN64
- BOOL ret = FreeLibrary(lib);
- ASSERT(ret, "FreeLibrary must be successful");
- ASSERT(GetModuleHandle(actual_name),
- "allocator library must not be unloaded");
-#else
- int ret = dlclose(lib);
- ASSERT(ret == 0, "dlclose must be successful");
- ASSERT(dlsym(RTLD_DEFAULT, "scalable_malloc"),
- "allocator library must not be unloaded");
-#endif
- }
-};
-
-int TestMain () {
- int i;
- std::ptrdiff_t memory_leak;
-
- // warm-up run
- NativeParallelFor( 1, Run() );
- /* 1st call to GetMemoryUsage() allocate some memory,
- but it seems memory consumption stabilized after this.
- */
- GetMemoryUsage();
- std::size_t memory_in_use = GetMemoryUsage();
- ASSERT(memory_in_use == GetMemoryUsage(),
- "Memory consumption should not increase after 1st GetMemoryUsage() call");
-
- // expect that memory consumption stabilized after several runs
- for (i=0; i<3; i++) {
- std::size_t memory_in_use = GetMemoryUsage();
- for (int j=0; j<10; j++)
- NativeParallelFor( 1, Run() );
- memory_leak = GetMemoryUsage() - memory_in_use;
- if (memory_leak == 0) // possibly too strong?
- break;
- }
- if(3==i) {
- // not stabilized, could be leak
- REPORT( "Error: memory leak of up to %ld bytes\n", static_cast<long>(memory_leak));
- exit(1);
- }
-
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-
-#if __linux__
-#define MALLOC_REPLACEMENT_AVAILABLE 1
-#elif _WIN32 && !__MINGW32__ && !__MINGW64__
-#define MALLOC_REPLACEMENT_AVAILABLE 2
-#include "tbb/tbbmalloc_proxy.h"
-#endif
-
-#if MALLOC_REPLACEMENT_AVAILABLE
-
-#if _WIN32 || _WIN64
-// As the test is intentionally build with /EHs-, suppress multiple VS2005's
-// warnings like C4530: C++ exception handler used, but unwind semantics are not enabled
-#if defined(_MSC_VER) && !__INTEL_COMPILER
-/* ICC 10.1 and 11.0 generates code that uses std::_Raise_handler,
- but it's only defined in libcpmt(d), which the test doesn't linked with.
- */
-#define _HAS_EXCEPTIONS 0
-#endif
-// to use strdup and putenv w/o warnings
-#define _CRT_NONSTDC_NO_DEPRECATE 1
-#endif
-#include "harness_report.h"
-#include "harness_assert.h"
-#include <stdlib.h>
-#include <string.h>
-#include <malloc.h>
-#include <stdio.h>
-#include <new>
-
-#if __linux__
-#include <dlfcn.h>
-#include <unistd.h> // for sysconf
-#include <stdint.h> // for uintptr_t
-
-#elif _WIN32
-#include <stddef.h>
-#if __MINGW32__
-#include <unistd.h>
-#else
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int64 uint64_t;
-#endif
-
-#endif /* OS selection */
-
-#if _WIN32
-// On Windows, the tricky way to print "done" is necessary to create
-// dependence on msvcpXX.dll, for sake of a regression test.
-// On Linux, C++ RTL headers are undesirable because of breaking strict ANSI mode.
-#if defined(_MSC_VER) && _MSC_VER >= 1300 && _MSC_VER <= 1310 && !defined(__INTEL_COMPILER)
-/* Fixing compilation error reported by VS2003 for exception class
- when _HAS_EXCEPTIONS is 0:
- bad_cast that inherited from exception is not in std namespace.
-*/
-using namespace std;
-#endif
-#include <string>
-#endif
-
-
-template<typename T>
-static inline T alignDown(T arg, uintptr_t alignment) {
- return T( (uintptr_t)arg & ~(alignment-1));
-}
-template<typename T>
-static inline bool isAligned(T arg, uintptr_t alignment) {
- return 0==((uintptr_t)arg & (alignment-1));
-}
-
-/* Below is part of MemoryAllocator.cpp. */
-
-class BackRefIdx { // composite index to backreference array
-private:
- uint16_t master; // index in BackRefMaster
- uint16_t largeObj:1; // is this object "large"?
- uint16_t offset :15; // offset from beginning of BackRefBlock
-public:
- BackRefIdx() : master((uint16_t)-1) {}
- bool isInvalid() { return master == (uint16_t)-1; }
- bool isLargeObject() const { return largeObj; }
- uint16_t getMaster() const { return master; }
- uint16_t getOffset() const { return offset; }
-
- // only newBackRef can modify BackRefIdx
- static BackRefIdx newBackRef(bool largeObj);
-};
-
-struct LargeMemoryBlock {
- LargeMemoryBlock *next, // ptrs in list of cached blocks
- *prev;
- uintptr_t age; // age of block while in cache
- size_t objectSize; // the size requested by a client
- size_t unalignedSize; // the size requested from getMemory
- bool fromMapMemory;
- BackRefIdx backRefIdx; // cached here, used copy is in LargeObjectHdr
-};
-
-struct LargeObjectHdr {
- LargeMemoryBlock *memoryBlock;
- /* Have to duplicate it here from CachedObjectHdr,
- as backreference must be checked without further pointer dereference.
- Points to LargeObjectHdr. */
- BackRefIdx backRefIdx;
-};
-
-/*
- * Objects of this size and larger are considered large objects.
- */
-const uint32_t minLargeObjectSize = 8065;
-
-/* end of inclusion from MemoryAllocator.cpp */
-
-/* Correct only for large blocks, i.e. not smaller then minLargeObjectSize */
-static bool scalableMallocLargeBlock(void *object, size_t size)
-{
- ASSERT(size >= minLargeObjectSize, NULL);
-#if MALLOC_REPLACEMENT_AVAILABLE == 2
- // Check that _msize works correctly
- ASSERT(_msize(object) >= size, NULL);
-#endif
-
- LargeMemoryBlock *lmb = ((LargeObjectHdr*)object-1)->memoryBlock;
- return uintptr_t(lmb)<uintptr_t(((LargeObjectHdr*)object-1)) && lmb->objectSize==size;
-}
-
-struct BigStruct {
- char f[minLargeObjectSize];
-};
-
-int main(int , char *[]) {
- void *ptr, *ptr1;
-
-#if MALLOC_REPLACEMENT_AVAILABLE == 1
- if (NULL == dlsym(RTLD_DEFAULT, "scalable_malloc")) {
- REPORT("libtbbmalloc not found\nfail\n");
- return 1;
- }
-#endif
-
-/* On Windows, memory block size returned by _msize() is sometimes used
- to calculate the size for an extended block. Substituting _msize,
- scalable_msize initially returned 0 for regions not allocated by the scalable
- allocator, which led to incorrect memory reallocation and subsequent crashes.
- It was found that adding a new environment variable triggers the error.
-*/
- ASSERT(getenv("PATH"), "We assume that PATH is set everywhere.");
- char *pathCopy = strdup(getenv("PATH"));
- const char *newEnvName = "__TBBMALLOC_OVERLOAD_REGRESSION_TEST_FOR_REALLOC_AND_MSIZE";
- char *newEnv = (char*)malloc(3 + strlen(newEnvName));
-
- ASSERT(!getenv(newEnvName), "Environment variable should not be used before.");
- strcpy(newEnv, newEnvName);
- strcat(newEnv, "=1");
- int r = putenv(newEnv);
- ASSERT(!r, NULL);
- char *path = getenv("PATH");
- ASSERT(path && 0==strcmp(path, pathCopy), "Environment was changed erroneously.");
- free(pathCopy);
- free(newEnv);
-
- ptr = malloc(minLargeObjectSize);
- ASSERT(ptr!=NULL && scalableMallocLargeBlock(ptr, minLargeObjectSize), NULL);
- free(ptr);
-
- ptr = calloc(minLargeObjectSize, 2);
- ASSERT(ptr!=NULL && scalableMallocLargeBlock(ptr, minLargeObjectSize*2), NULL);
- ptr1 = realloc(ptr, minLargeObjectSize*10);
- ASSERT(ptr1!=NULL && scalableMallocLargeBlock(ptr1, minLargeObjectSize*10), NULL);
- free(ptr1);
-
-#if MALLOC_REPLACEMENT_AVAILABLE == 1
-
- int ret = posix_memalign(&ptr, 1024, 3*minLargeObjectSize);
- ASSERT(0==ret && ptr!=NULL && scalableMallocLargeBlock(ptr, 3*minLargeObjectSize), NULL);
- free(ptr);
-
- ptr = memalign(128, 4*minLargeObjectSize);
- ASSERT(ptr!=NULL && scalableMallocLargeBlock(ptr, 4*minLargeObjectSize), NULL);
- free(ptr);
-
- ptr = valloc(minLargeObjectSize);
- ASSERT(ptr!=NULL && scalableMallocLargeBlock(ptr, minLargeObjectSize), NULL);
- free(ptr);
-
- long memoryPageSize = sysconf(_SC_PAGESIZE);
- int sz = 1024*minLargeObjectSize;
- ptr = pvalloc(sz);
- ASSERT(ptr!=NULL && // align size up to the page size
- scalableMallocLargeBlock(ptr, ((sz-1) | (memoryPageSize-1)) + 1), NULL);
- free(ptr);
-
- struct mallinfo info = mallinfo();
- // right now mallinfo initialized by zero
- ASSERT(!info.arena && !info.ordblks && !info.smblks && !info.hblks
- && !info.hblkhd && !info.usmblks && !info.fsmblks
- && !info.uordblks && !info.fordblks && !info.keepcost, NULL);
-
-#elif MALLOC_REPLACEMENT_AVAILABLE == 2
-
- ptr = _aligned_malloc(minLargeObjectSize,16);
- ASSERT(ptr!=NULL && scalableMallocLargeBlock(ptr, minLargeObjectSize), NULL);
-
- ptr1 = _aligned_realloc(ptr, minLargeObjectSize*10,16);
- ASSERT(ptr1!=NULL && scalableMallocLargeBlock(ptr1, minLargeObjectSize*10), NULL);
- _aligned_free(ptr1);
-
-#endif
-
- BigStruct *f = new BigStruct;
- ASSERT(f!=NULL && scalableMallocLargeBlock(f, sizeof(BigStruct)), NULL);
- delete f;
-
- f = new BigStruct[10];
- ASSERT(f!=NULL && scalableMallocLargeBlock(f, 10*sizeof(BigStruct)), NULL);
- delete []f;
-
- f = new(std::nothrow) BigStruct;
- ASSERT(f!=NULL && scalableMallocLargeBlock(f, sizeof(BigStruct)), NULL);
- delete f;
-
- f = new(std::nothrow) BigStruct[2];
- ASSERT(f!=NULL && scalableMallocLargeBlock(f, 2*sizeof(BigStruct)), NULL);
- delete []f;
-
-#if _WIN32
- std::string stdstring = "done";
- const char* s = stdstring.c_str();
-#else
- const char* s = "done";
-#endif
- REPORT("%s\n", s);
- return 0;
-}
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#define HARNESS_CUSTOM_MAIN 1
-#include "harness.h"
-
-#else /* !MALLOC_REPLACEMENT_AVAILABLE */
-#include <stdio.h>
-
-int main(int , char *[]) {
- printf("skip\n");
- return 0;
-}
-#endif /* !MALLOC_REPLACEMENT_AVAILABLE */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#ifdef __cplusplus
-#error For testing purpose, this file should be compiled with a C compiler, not C++
-#endif /*__cplusplus */
-
-#include "tbb/scalable_allocator.h"
-#include <stdio.h>
-#include <assert.h>
-
-/*
- * The test is to check if the scalable_allocator.h and its functions
- * can be used from pure C programs; also some regression checks are done
- */
-
-int main(void) {
- size_t i, j;
- void *p1, *p2;
- for( i=0; i<=1<<16; ++i) {
- p1 = scalable_malloc(i);
- if( !p1 )
- printf("Warning: there should be memory but scalable_malloc returned NULL\n");
- scalable_free(p1);
- }
- p1 = p2 = NULL;
- for( i=1024*1024; ; i/=2 )
- {
- scalable_free(p1);
- p1 = scalable_realloc(p2, i);
- p2 = scalable_calloc(i, 32);
- if (p2) {
- if (i<sizeof(size_t)) {
- for (j=0; j<i; j++)
- assert(0==*((char*)p2+j));
- } else {
- for (j=0; j<i; j+=sizeof(size_t))
- assert(0==*((size_t*)p2+j));
- }
- }
- scalable_free(p2);
- p2 = scalable_malloc(i);
- if (i==0) break;
- }
- for( i=1; i<1024*1024; i*=2 )
- {
- scalable_free(p1);
- p1 = scalable_realloc(p2, i);
- p2 = scalable_malloc(i);
- }
- scalable_free(p1);
- scalable_free(p2);
- printf("done\n");
- return 0;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-
-#include <stdio.h>
-#include "tbb/scalable_allocator.h"
-
-class minimalAllocFree {
-public:
- void operator()(int size) const {
- tbb::scalable_allocator<char> a;
- char* str = a.allocate( size );
- a.deallocate( str, size );
- }
-};
-
-#include "harness.h"
-
-template<typename Body, typename Arg>
-void RunThread(const Body& body, const Arg& arg) {
- NativeParallelForTask<Arg,Body> job(arg, body);
- job.start();
- job.wait_to_finish();
-}
-
-/*--------------------------------------------------------------------*/
-// The regression test against bug #1518 where thread boot strap allocations "leaked"
-
-#include "harness_memory.h"
-
-bool TestBootstrapLeak() {
- /* In the bug 1518, each thread leaked ~384 bytes.
- Initially, scalable allocator maps 1MB. Thus it is necessary to take out most of this space.
- 1MB is chunked into 16K blocks; of those, one block is for thread boot strap, and one more
- should be reserved for the test body. 62 blocks left, each can serve 15 objects of 1024 bytes.
- */
- const int alloc_size = 1024;
- const int take_out_count = 15*62;
-
- tbb::scalable_allocator<char> a;
- char* array[take_out_count];
- for( int i=0; i<take_out_count; ++i )
- array[i] = a.allocate( alloc_size );
-
- RunThread( minimalAllocFree(), alloc_size ); // for threading library to take some memory
- size_t memory_in_use = GetMemoryUsage();
- // Wait for memory usage data to "stabilize". The test number (1000) has nothing underneath.
- for( int i=0; i<1000; i++) {
- if( GetMemoryUsage()!=memory_in_use ) {
- memory_in_use = GetMemoryUsage();
- i = -1;
- }
- }
-
- ptrdiff_t memory_leak = 0;
- // Notice that 16K boot strap memory block is enough to serve 42 threads.
- const int num_thread_runs = 200;
- for (int run=0; run<3; run++) {
- memory_in_use = GetMemoryUsage();
- for( int i=0; i<num_thread_runs; ++i )
- RunThread( minimalAllocFree(), alloc_size );
-
- memory_leak = GetMemoryUsage() - memory_in_use;
- if (!memory_leak)
- break;
- }
- if( memory_leak>0 ) { // possibly too strong?
- REPORT( "Error: memory leak of up to %ld bytes\n", static_cast<long>(memory_leak));
- }
-
- for( int i=0; i<take_out_count; ++i )
- a.deallocate( array[i], alloc_size );
-
- return memory_leak<=0;
-}
-
-/*--------------------------------------------------------------------*/
-// The regression test against a bug with incompatible semantics of msize and realloc
-
-bool TestReallocMsize(size_t startSz) {
- bool passed = true;
-
- char *buf = (char*)scalable_malloc(startSz);
- ASSERT(buf, "");
- size_t realSz = scalable_msize(buf);
- ASSERT(realSz>=startSz, "scalable_msize must be not less then allocated size");
- memset(buf, 'a', realSz-1);
- buf[realSz-1] = 0;
- char *buf1 = (char*)scalable_realloc(buf, 2*realSz);
- ASSERT(buf1, "");
- ASSERT(scalable_msize(buf1)>=2*realSz,
- "scalable_msize must be not less then allocated size");
- buf1[2*realSz-1] = 0;
- if ( strspn(buf1, "a") < realSz-1 ) {
- REPORT( "Error: data broken for %d Bytes object.\n", startSz);
- passed = false;
- }
- scalable_free(buf1);
-
- return passed;
-}
-
-/*--------------------------------------------------------------------*/
-// The main test function
-
-int TestMain () {
- bool passed = true;
- // Check whether memory usage data can be obtained; if not, skip test_bootstrap_leak.
- if( GetMemoryUsage() )
- passed &= TestBootstrapLeak();
-
- // TestReallocMsize runs for each power of 2 and each Fibonacci number below 64K
- for (size_t a=1, b=1, sum=1; sum<=64*1024; ) {
- passed &= TestReallocMsize(sum);
- a = b;
- b = sum;
- sum = a+b;
- }
- for (size_t a=2; a<=64*1024; a*=2)
- passed &= TestReallocMsize(a);
-
- ASSERT( passed, "Test failed" );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/scalable_allocator.h"
-#include "harness.h"
-#include "harness_barrier.h"
-
-// To not depends on ITT support stuff
-#ifdef DO_ITT_NOTIFY
-#undef DO_ITT_NOTIFY
-#endif
-
-#define protected public
-#define private public
-#include "../tbbmalloc/frontend.cpp"
-#undef protected
-#undef private
-#include "../tbbmalloc/backend.cpp"
-#include "../tbbmalloc/backref.cpp"
-#include "../tbbmalloc/large_objects.cpp"
-#include "../tbbmalloc/tbbmalloc.cpp"
-
-const int LARGE_MEM_SIZES_NUM = 10;
-const size_t MByte = 1024*1024;
-
-class AllocInfo {
- int *p;
- int val;
- int size;
-public:
- AllocInfo() : p(NULL), val(0), size(0) {}
- explicit AllocInfo(int size) : p((int*)scalable_malloc(size*sizeof(int))),
- val(rand()), size(size) {
- ASSERT(p, NULL);
- for (int k=0; k<size; k++)
- p[k] = val;
- }
- void check() const {
- for (int k=0; k<size; k++)
- ASSERT(p[k] == val, NULL);
- }
- void clear() {
- scalable_free(p);
- }
-};
-
-class TestLargeObjCache: NoAssign {
- static Harness::SpinBarrier barrier;
-public:
- static int largeMemSizes[LARGE_MEM_SIZES_NUM];
-
- static void initBarrier(unsigned thrds) { barrier.initialize(thrds); }
-
- TestLargeObjCache( ) {}
-
- void operator()( int /*mynum*/ ) const {
- AllocInfo allocs[LARGE_MEM_SIZES_NUM];
-
- // push to maximal cache limit
- for (int i=0; i<2; i++) {
- const int sizes[] = { MByte/sizeof(int),
- (MByte-2*largeBlockCacheStep)/sizeof(int) };
- for (int q=0; q<2; q++) {
- size_t curr = 0;
- for (int j=0; j<LARGE_MEM_SIZES_NUM; j++, curr++)
- new (allocs+curr) AllocInfo(sizes[q]);
-
- for (size_t j=0; j<curr; j++) {
- allocs[j].check();
- allocs[j].clear();
- }
- }
- }
-
- barrier.wait();
-
- // check caching correctness
- for (int i=0; i<1000; i++) {
- size_t curr = 0;
- for (int j=0; j<LARGE_MEM_SIZES_NUM-1; j++, curr++)
- new (allocs+curr) AllocInfo(largeMemSizes[j]);
-
- new (allocs+curr)
- AllocInfo((int)(4*minLargeObjectSize +
- 2*minLargeObjectSize*(1.*rand()/RAND_MAX)));
- curr++;
-
- for (size_t j=0; j<curr; j++) {
- allocs[j].check();
- allocs[j].clear();
- }
- }
- }
-};
-
-Harness::SpinBarrier TestLargeObjCache::barrier;
-int TestLargeObjCache::largeMemSizes[LARGE_MEM_SIZES_NUM];
-
-#if MALLOC_CHECK_RECURSION
-
-class TestStartupAlloc: NoAssign {
- static Harness::SpinBarrier init_barrier;
- struct TestBlock {
- void *ptr;
- size_t sz;
- };
- static const int ITERS = 100;
-public:
- TestStartupAlloc() {}
- static void initBarrier(unsigned thrds) { init_barrier.initialize(thrds); }
- void operator()(int) const {
- TestBlock blocks1[ITERS], blocks2[ITERS];
-
- init_barrier.wait();
-
- for (int i=0; i<ITERS; i++) {
- blocks1[i].sz = rand() % minLargeObjectSize;
- blocks1[i].ptr = StartupBlock::allocate(blocks1[i].sz);
- ASSERT(blocks1[i].ptr && StartupBlock::msize(blocks1[i].ptr)>=blocks1[i].sz
- && 0==(uintptr_t)blocks1[i].ptr % sizeof(void*), NULL);
- memset(blocks1[i].ptr, i, blocks1[i].sz);
- }
- for (int i=0; i<ITERS; i++) {
- blocks2[i].sz = rand() % minLargeObjectSize;
- blocks2[i].ptr = StartupBlock::allocate(blocks2[i].sz);
- ASSERT(blocks2[i].ptr && StartupBlock::msize(blocks2[i].ptr)>=blocks2[i].sz
- && 0==(uintptr_t)blocks2[i].ptr % sizeof(void*), NULL);
- memset(blocks2[i].ptr, i, blocks2[i].sz);
-
- for (size_t j=0; j<blocks1[i].sz; j++)
- ASSERT(*((char*)blocks1[i].ptr+j) == i, NULL);
- Block *block = (Block *)alignDown(blocks1[i].ptr, blockSize);
- ((StartupBlock *)block)->free(blocks1[i].ptr);
- }
- for (int i=ITERS-1; i>=0; i--) {
- for (size_t j=0; j<blocks2[i].sz; j++)
- ASSERT(*((char*)blocks2[i].ptr+j) == i, NULL);
- Block *block = (Block *)alignDown(blocks2[i].ptr, blockSize);
- ((StartupBlock *)block)->free(blocks2[i].ptr);
- }
- }
-};
-
-Harness::SpinBarrier TestStartupAlloc::init_barrier;
-
-#endif /* MALLOC_CHECK_RECURSION */
-
-class BackRefWork: NoAssign {
- struct TestBlock {
- intptr_t data;
- BackRefIdx idx;
- };
- static const int ITERS = 2*BR_MAX_CNT+2;
-public:
- BackRefWork() {}
- void operator()(int) const {
- TestBlock blocks[ITERS];
-
- for (int i=0; i<ITERS; i++) {
- blocks[i].idx = BackRefIdx::newBackRef(/*largeObj=*/false);
- setBackRef(blocks[i].idx, &blocks[i].data);
- }
- for (int i=0; i<ITERS; i++)
- ASSERT((Block*)&blocks[i].data == getBackRef(blocks[i].idx), NULL);
- for (int i=ITERS-1; i>=0; i--)
- removeBackRef(blocks[i].idx);
- }
-};
-
-class FreeBlockPoolHit: NoAssign {
- // to trigger possible leak for both cleanup on pool overflow
- // and on thread termination
- static const int ITERS = 2*FreeBlockPool::POOL_HIGH_MARK;
-public:
- FreeBlockPoolHit() {}
- void operator()(int) const {
- void *objs[ITERS];
-
- for (int i=0; i<ITERS; i++)
- objs[i] = scalable_malloc(minLargeObjectSize-1);
- for (int i=0; i<ITERS; i++)
- scalable_free(objs[i]);
-
-#ifdef USE_WINTHREAD
- // under Windows DllMain used to call mallocThreadShutdownNotification,
- // as we don't use it have to call the callback manually
- mallocThreadShutdownNotification(NULL);
-#endif
- }
-};
-
-static size_t allocatedBackRefCount()
-{
- size_t cnt = 0;
- for (int i=0; i<=backRefMaster->lastUsed; i++)
- cnt += backRefMaster->backRefBl[i]->allocatedCount;
- return cnt;
-}
-
-void TestBackRef() {
- size_t beforeNumBackRef, afterNumBackRef;
-
- beforeNumBackRef = allocatedBackRefCount();
- for( int p=MaxThread; p>=MinThread; --p )
- NativeParallelFor( p, BackRefWork() );
- afterNumBackRef = allocatedBackRefCount();
- ASSERT(beforeNumBackRef==afterNumBackRef, "backreference leak detected");
-
- // lastUsed marks peak resource consumption. As we allocate below the mark,
- // it must not move up, otherwise there is a resource leak.
- int sustLastUsed = backRefMaster->lastUsed;
- NativeParallelFor( 1, BackRefWork() );
- ASSERT(sustLastUsed == backRefMaster->lastUsed, "backreference leak detected");
-
- // check leak of back references while per-thread small object pool is in use
- // warm up need to cover bootStrapMalloc call
- NativeParallelFor( 1, FreeBlockPoolHit() );
- beforeNumBackRef = allocatedBackRefCount();
- NativeParallelFor( 1, FreeBlockPoolHit() );
- afterNumBackRef = allocatedBackRefCount();
- ASSERT(beforeNumBackRef==afterNumBackRef, "backreference leak detected");
-}
-
-void TestObjectRecognition() {
- size_t headersSize = sizeof(LargeMemoryBlock)+sizeof(LargeObjectHdr);
- unsigned falseObjectSize = 113; // unsigned is the type expected by getObjectSize
- size_t obtainedSize;
- Block *auxBackRef;
-
- ASSERT(sizeof(BackRefIdx)==4, "Unexpected size of BackRefIdx");
- ASSERT(getObjectSize(falseObjectSize)!=falseObjectSize, "Error in test: bad choice for false object size");
-
- void* mem = scalable_malloc(2*blockSize);
- Block* falseBlock = (Block*)alignUp((uintptr_t)mem, blockSize);
- falseBlock->objectSize = falseObjectSize;
- char* falseSO = (char*)falseBlock + falseObjectSize*7;
- ASSERT(alignDown(falseSO, blockSize)==(void*)falseBlock, "Error in test: false object offset is too big");
-
- void* bufferLOH = scalable_malloc(2*blockSize + headersSize);
- LargeObjectHdr* falseLO =
- (LargeObjectHdr*)alignUp((uintptr_t)bufferLOH + headersSize, blockSize);
- LargeObjectHdr* headerLO = (LargeObjectHdr*)falseLO-1;
- headerLO->memoryBlock = (LargeMemoryBlock*)bufferLOH;
- headerLO->memoryBlock->unalignedSize = 2*blockSize + headersSize;
- headerLO->memoryBlock->objectSize = blockSize + headersSize;
- headerLO->backRefIdx = BackRefIdx::newBackRef(/*largeObj=*/true);
- setBackRef(headerLO->backRefIdx, headerLO);
- ASSERT(scalable_msize(falseLO) == blockSize + headersSize,
- "Error in test: LOH falsification failed");
- removeBackRef(headerLO->backRefIdx);
-
- const int NUM_OF_IDX = BR_MAX_CNT+2;
- BackRefIdx idxs[NUM_OF_IDX];
- for (int cnt=0; cnt<2; cnt++) {
- for (int master = -10; master<10; master++) {
- falseBlock->backRefIdx.master = (uint16_t)master;
- headerLO->backRefIdx.master = (uint16_t)master;
-
- for (int bl = -10; bl<BR_MAX_CNT+10; bl++) {
- falseBlock->backRefIdx.offset = (uint16_t)bl;
- headerLO->backRefIdx.offset = (uint16_t)bl;
-
- for (int largeObj = 0; largeObj<2; largeObj++) {
- falseBlock->backRefIdx.largeObj = largeObj;
- headerLO->backRefIdx.largeObj = largeObj;
-
- obtainedSize = safer_scalable_msize(falseSO, NULL);
- ASSERT(obtainedSize==0, "Incorrect pointer accepted");
- obtainedSize = safer_scalable_msize(falseLO, NULL);
- ASSERT(obtainedSize==0, "Incorrect pointer accepted");
- }
- }
- }
- if (cnt == 1) {
- for (int i=0; i<NUM_OF_IDX; i++)
- removeBackRef(idxs[i]);
- break;
- }
- for (int i=0; i<NUM_OF_IDX; i++) {
- idxs[i] = BackRefIdx::newBackRef(/*largeObj=*/false);
- setBackRef(idxs[i], NULL);
- }
- }
- char *smallPtr = (char*)scalable_malloc(falseObjectSize);
- obtainedSize = safer_scalable_msize(smallPtr, NULL);
- ASSERT(obtainedSize==getObjectSize(falseObjectSize), "Correct pointer not accepted?");
- scalable_free(smallPtr);
-
- obtainedSize = safer_scalable_msize(mem, NULL);
- ASSERT(obtainedSize>=2*blockSize, "Correct pointer not accepted?");
- scalable_free(mem);
- scalable_free(bufferLOH);
-}
-
-
-int TestMain () {
- // backreference requires that initialization was done
- if(!isMallocInitialized()) doInitialization();
- // to succeed, leak detection must be the 1st memory-intensive test
- TestBackRef();
-
-#if MALLOC_CHECK_RECURSION
- for( int p=MaxThread; p>=MinThread; --p ) {
- TestStartupAlloc::initBarrier( p );
- NativeParallelFor( p, TestStartupAlloc() );
- ASSERT(!firstStartupBlock, "Startup heap memory leak detected");
- }
-#endif
-
- for (int i=0; i<LARGE_MEM_SIZES_NUM; i++)
- TestLargeObjCache::largeMemSizes[i] =
- (int)(minLargeObjectSize + 2*minLargeObjectSize*(1.*rand()/RAND_MAX));
-
- for( int p=MaxThread; p>=MinThread; --p ) {
- TestLargeObjCache::initBarrier( p );
- NativeParallelFor( p, TestLargeObjCache() );
- }
-
- TestObjectRecognition();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-
-#if _WIN32 || _WIN64
-#include "tbb/machine/windows_api.h"
-#else
-#include <dlfcn.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "tbb/tbb_config.h"
-
-#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 <stdexcept>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#if TBB_USE_EXCEPTIONS
- #include "harness_report.h"
-#endif
-
-#ifdef _USRDLL
-#include "tbb/task_scheduler_init.h"
-
-class CModel {
-public:
- CModel(void) {};
- static tbb::task_scheduler_init tbb_init;
-
- void init_and_terminate( int );
-};
-
-tbb::task_scheduler_init CModel::tbb_init(1);
-
-//! Test that task::initialize and task::terminate work when doing nothing else.
-/** maxthread is treated as the "maximum" number of worker threads. */
-void CModel::init_and_terminate( int maxthread ) {
- for( int i=0; i<200; ++i ) {
- switch( i&3 ) {
- default: {
- tbb::task_scheduler_init init( rand() % maxthread + 1 );
- break;
- }
- case 0: {
- tbb::task_scheduler_init init;
- break;
- }
- case 1: {
- tbb::task_scheduler_init init( tbb::task_scheduler_init::automatic );
- break;
- }
- case 2: {
- tbb::task_scheduler_init init( tbb::task_scheduler_init::deferred );
- init.initialize( rand() % maxthread + 1 );
- init.terminate();
- break;
- }
- }
- }
-}
-
-extern "C"
-#if _WIN32 || _WIN64
-__declspec(dllexport)
-#endif
-void plugin_call(int maxthread)
-{
- srand(2);
- __TBB_TRY {
- CModel model;
- model.init_and_terminate(maxthread);
- } __TBB_CATCH( std::runtime_error& error ) {
-#if TBB_USE_EXCEPTIONS
- REPORT("ERROR: %s\n", error.what());
-#endif /* TBB_USE_EXCEPTIONS */
- }
-}
-
-#else /* _USRDLL undefined */
-
-#define HARNESS_NO_ASSERT 1
-#include "harness.h"
-
-extern "C" void plugin_call(int);
-
-void report_error_in(const char* function_name)
-{
-#if _WIN32 || _WIN64
- char* message;
- int code = GetLastError();
-
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, code,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char*)&message, 0, NULL );
-#else
- char* message = (char*)dlerror();
- int code = 0;
-#endif
- REPORT( "%s failed with error %d: %s\n", function_name, code, message);
-
-#if _WIN32 || _WIN64
- LocalFree(message);
-#endif
-}
-
-int use_lot_of_tls() {
- int count = 0;
-#if _WIN32 || _WIN64
- DWORD last_handles[10];
- DWORD result;
- result = TlsAlloc();
- while( result!=TLS_OUT_OF_INDEXES ) {
- last_handles[++count%10] = result;
- result = TlsAlloc();
- }
- for( int i=0; i<10; ++i )
- TlsFree(last_handles[i]);
-#else
- pthread_key_t last_handles[10];
- pthread_key_t result;
- int setspecific_dummy=10;
- while( pthread_key_create(&result, NULL)==0
- && count < 4096 ) // Sun Solaris doesn't have any built-in limit, so we set something big enough
- {
- last_handles[++count%10] = result;
- pthread_setspecific(result,&setspecific_dummy);
- }
- REMARK("Created %d keys\n", count);
- for( int i=0; i<10; ++i )
- pthread_key_delete(last_handles[i]);
-#endif
- return count-10;
-}
-
-typedef void (*PLUGIN_CALL)(int);
-
-int TestMain () {
-#if !RML_USE_WCRM
- PLUGIN_CALL my_plugin_call;
-
- int tls_key_count = use_lot_of_tls();
- REMARK("%d thread local objects allocated in advance\n", tls_key_count);
-
-#if _WIN32 || _WIN64
- HMODULE hLib;
-#if __TBB_ARENA_PER_MASTER
- hLib = LoadLibrary("irml.dll");
- if ( !hLib )
- hLib = LoadLibrary("irml_debug.dll");
- if ( !hLib )
- return Harness::Skipped; // No shared RML, skip the test
- FreeLibrary(hLib);
-#endif /* __TBB_ARENA_PER_MASTER */
-#else /* !WIN */
-#if __APPLE__
- #define LIBRARY_NAME(base) base".dylib"
-#else
- #define LIBRARY_NAME(base) base".so"
-#endif
- void* hLib;
-#if __TBB_ARENA_PER_MASTER
-#if __linux__
- #define RML_LIBRARY_NAME(base) LIBRARY_NAME(base) ".1"
-#else
- #define RML_LIBRARY_NAME(base) LIBRARY_NAME(base)
-#endif
- hLib = dlopen(RML_LIBRARY_NAME("libirml"), RTLD_LAZY);
- if ( !hLib )
- hLib = dlopen(RML_LIBRARY_NAME("libirml_debug"), RTLD_LAZY);
- if ( !hLib )
- return Harness::Skipped;
- dlclose(hLib);
-#endif /* __TBB_ARENA_PER_MASTER */
-#endif /* OS */
- for( int i=1; i<100; ++i ) {
- REMARK("Iteration %d, loading plugin library...\n", i);
-#if _WIN32 || _WIN64
- hLib = LoadLibrary("test_model_plugin_dll.dll");
- if ( !hLib ) {
-#if !__TBB_NO_IMPLICIT_LINKAGE
- report_error_in("LoadLibrary");
- return -1;
-#else
- return Harness::Skipped;
-#endif
- }
- my_plugin_call = (PLUGIN_CALL) GetProcAddress(hLib, "plugin_call");
- if (my_plugin_call==NULL) {
- report_error_in("GetProcAddress");
- return -1;
- }
-#else /* !WIN */
- hLib = dlopen( LIBRARY_NAME("test_model_plugin_dll"), RTLD_LAZY );
- if ( !hLib ) {
-#if !__TBB_NO_IMPLICIT_LINKAGE
- report_error_in("dlopen");
- return -1;
-#else
- return Harness::Skipped;
-#endif
- }
- my_plugin_call = PLUGIN_CALL (dlsym(hLib, "plugin_call"));
- if (my_plugin_call==NULL) {
- report_error_in("dlsym");
- return -1;
- }
-#endif /* !WIN */
-
- REMARK("Calling plugin method...\n");
- my_plugin_call(MaxThread);
-
- REMARK("Unloading plugin library...\n");
-#if _WIN32 || _WIN64
- FreeLibrary(hLib);
-#else
- dlclose(hLib);
-#endif
- } // end for(1,100)
-
- return Harness::Done;
-#else
- return Harness::Skipped;
-#endif /* !RML_USE_WCRM */
-}
-
-#endif//_USRDLL
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-//------------------------------------------------------------------------
-// Test TBB mutexes when used with parallel_for.h
-//
-// Usage: test_Mutex.exe [-v] nthread
-//
-// The -v option causes timing information to be printed.
-//
-// Compile with _OPENMP and -openmp
-//------------------------------------------------------------------------
-#include "tbb/spin_mutex.h"
-#include "tbb/critical_section.h"
-#include "tbb/spin_rw_mutex.h"
-#include "tbb/queuing_rw_mutex.h"
-#include "tbb/queuing_mutex.h"
-#include "tbb/mutex.h"
-#include "tbb/recursive_mutex.h"
-#include "tbb/null_mutex.h"
-#include "tbb/null_rw_mutex.h"
-#include "tbb/parallel_for.h"
-#include "tbb/blocked_range.h"
-#include "tbb/tick_count.h"
-#include "tbb/atomic.h"
-#include "harness.h"
-#include <cstdlib>
-#include <cstdio>
-#if _OPENMP
-#include "test/OpenMP_Mutex.h"
-#endif /* _OPENMP */
-#include "tbb/tbb_profiling.h"
-
-#ifndef TBB_TEST_LOW_WORKLOAD
- #define TBB_TEST_LOW_WORKLOAD TBB_USE_THREADING_TOOLS
-#endif
-
-// This test deliberately avoids a "using tbb" statement,
-// so that the error of putting types in the wrong namespace will be caught.
-
-template<typename M>
-struct Counter {
- typedef M mutex_type;
- M mutex;
- volatile long value;
-};
-
-//! Function object for use with parallel_for.h.
-template<typename C>
-struct AddOne: NoAssign {
- C& counter;
- /** Increments counter once for each iteration in the iteration space. */
- void operator()( tbb::blocked_range<size_t>& range ) const {
- for( size_t i=range.begin(); i!=range.end(); ++i ) {
- if( i&1 ) {
- // Try implicit acquire and explicit release
- typename C::mutex_type::scoped_lock lock(counter.mutex);
- counter.value = counter.value+1;
- lock.release();
- } else {
- // Try explicit acquire and implicit release
- typename C::mutex_type::scoped_lock lock;
- lock.acquire(counter.mutex);
- counter.value = counter.value+1;
- }
- }
- }
- AddOne( C& counter_ ) : counter(counter_) {}
-};
-
-//! Adaptor for using ISO C++0x style mutex as a TBB-style mutex.
-template<typename M>
-class TBB_MutexFromISO_Mutex {
- M my_iso_mutex;
-public:
- typedef TBB_MutexFromISO_Mutex mutex_type;
-
- class scoped_lock;
- friend class scoped_lock;
-
- class scoped_lock {
- mutex_type* my_mutex;
- public:
- scoped_lock() : my_mutex(NULL) {}
- scoped_lock( mutex_type& m ) : my_mutex(NULL) {
- acquire(m);
- }
- scoped_lock( mutex_type& m, bool is_writer ) : my_mutex(NULL) {
- acquire(m,is_writer);
- }
- void acquire( mutex_type& m ) {
- m.my_iso_mutex.lock();
- my_mutex = &m;
- }
- bool try_acquire( mutex_type& m ) {
- if( m.my_iso_mutex.try_lock() ) {
- my_mutex = &m;
- return true;
- } else {
- return false;
- }
- }
- void release() {
- my_mutex->my_iso_mutex.unlock();
- my_mutex = NULL;
- }
-
- // Methods for reader-writer mutex
- // These methods can be instantiated only if M supports lock_read() and try_lock_read().
-
- void acquire( mutex_type& m, bool is_writer ) {
- if( is_writer ) m.my_iso_mutex.lock();
- else m.my_iso_mutex.lock_read();
- my_mutex = &m;
- }
- bool try_acquire( mutex_type& m, bool is_writer ) {
- if( is_writer ? m.my_iso_mutex.try_lock() : m.my_iso_mutex.try_lock_read() ) {
- my_mutex = &m;
- return true;
- } else {
- return false;
- }
- }
- bool upgrade_to_writer() {
- my_mutex->my_iso_mutex.unlock();
- my_mutex->my_iso_mutex.lock();
- return false;
- }
- bool downgrade_to_reader() {
- my_mutex->my_iso_mutex.unlock();
- my_mutex->my_iso_mutex.lock_read();
- return false;
- }
- ~scoped_lock() {
- if( my_mutex )
- release();
- }
- };
-
- static const bool is_recursive_mutex = M::is_recursive_mutex;
- static const bool is_rw_mutex = M::is_rw_mutex;
-};
-
-namespace tbb {
- namespace profiling {
- template<typename M>
- void set_name( const TBB_MutexFromISO_Mutex<M>&, const char* ) {}
- }
-}
-
-//! Generic test of a TBB mutex type M.
-/** Does not test features specific to reader-writer locks. */
-template<typename M>
-void Test( const char * name ) {
- REMARK("%s time = ",name);
- Counter<M> counter;
- counter.value = 0;
- tbb::profiling::set_name(counter.mutex, name);
-#if TBB_TEST_LOW_WORKLOAD
- const int n = 10000;
-#else
- const int n = 100000;
-#endif /* TBB_TEST_LOW_WORKLOAD */
- tbb::tick_count t0 = tbb::tick_count::now();
- tbb::parallel_for(tbb::blocked_range<size_t>(0,n,n/10),AddOne<Counter<M> >(counter));
- tbb::tick_count t1 = tbb::tick_count::now();
- REMARK("%g usec\n",(t1-t0).seconds());
- if( counter.value!=n )
- REPORT("ERROR for %s: counter.value=%ld\n",name,counter.value);
-}
-
-template<typename M, size_t N>
-struct Invariant {
- typedef M mutex_type;
- M mutex;
- const char* mutex_name;
- volatile long value[N];
- volatile long single_value;
- Invariant( const char* mutex_name_ ) :
- mutex_name(mutex_name_)
- {
- single_value = 0;
- for( size_t k=0; k<N; ++k )
- value[k] = 0;
- tbb::profiling::set_name(mutex, mutex_name_);
- }
- void update() {
- for( size_t k=0; k<N; ++k )
- ++value[k];
- }
- bool value_is( long expected_value ) const {
- long tmp;
- for( size_t k=0; k<N; ++k )
- if( (tmp=value[k])!=expected_value ) {
- REPORT("ERROR: %ld!=%ld\n", tmp, expected_value);
- return false;
- }
- return true;
- }
- bool is_okay() {
- return value_is( value[0] );
- }
-};
-
-//! Function object for use with parallel_for.h.
-template<typename I>
-struct TwiddleInvariant: NoAssign {
- I& invariant;
- /** Increments counter once for each iteration in the iteration space. */
- void operator()( tbb::blocked_range<size_t>& range ) const {
- for( size_t i=range.begin(); i!=range.end(); ++i ) {
- //! Every 8th access is a write access
- bool write = (i%8)==7;
- bool okay = true;
- bool lock_kept = true;
- if( (i/8)&1 ) {
- // Try implicit acquire and explicit release
- typename I::mutex_type::scoped_lock lock(invariant.mutex,write);
- if( write ) {
- long my_value = invariant.value[0];
- invariant.update();
- if( i%16==7 ) {
- lock_kept = lock.downgrade_to_reader();
- if( !lock_kept )
- my_value = invariant.value[0] - 1;
- okay = invariant.value_is(my_value+1);
- }
- } else {
- okay = invariant.is_okay();
- if( i%8==3 ) {
- long my_value = invariant.value[0];
- lock_kept = lock.upgrade_to_writer();
- if( !lock_kept )
- my_value = invariant.value[0];
- invariant.update();
- okay = invariant.value_is(my_value+1);
- }
- }
- lock.release();
- } else {
- // Try explicit acquire and implicit release
- typename I::mutex_type::scoped_lock lock;
- lock.acquire(invariant.mutex,write);
- if( write ) {
- long my_value = invariant.value[0];
- invariant.update();
- if( i%16==7 ) {
- lock_kept = lock.downgrade_to_reader();
- if( !lock_kept )
- my_value = invariant.value[0] - 1;
- okay = invariant.value_is(my_value+1);
- }
- } else {
- okay = invariant.is_okay();
- if( i%8==3 ) {
- long my_value = invariant.value[0];
- lock_kept = lock.upgrade_to_writer();
- if( !lock_kept )
- my_value = invariant.value[0];
- invariant.update();
- okay = invariant.value_is(my_value+1);
- }
- }
- }
- if( !okay ) {
- REPORT( "ERROR for %s at %ld: %s %s %s %s\n",invariant.mutex_name, long(i),
- write?"write,":"read,", write?(i%16==7?"downgrade,":""):(i%8==3?"upgrade,":""),
- lock_kept?"lock kept,":"lock not kept,", (i/8)&1?"imp/exp":"exp/imp" );
- }
- }
- }
- TwiddleInvariant( I& invariant_ ) : invariant(invariant_) {}
-};
-
-/** This test is generic so that we can test any other kinds of ReaderWriter locks we write later. */
-template<typename M>
-void TestReaderWriterLock( const char * mutex_name ) {
- REMARK( "%s readers & writers time = ", mutex_name );
- Invariant<M,8> invariant(mutex_name);
-#if TBB_TEST_LOW_WORKLOAD
- const size_t n = 10000;
-#else
- const size_t n = 500000;
-#endif /* TBB_TEST_LOW_WORKLOAD */
- tbb::tick_count t0 = tbb::tick_count::now();
- tbb::parallel_for(tbb::blocked_range<size_t>(0,n,n/100),TwiddleInvariant<Invariant<M,8> >(invariant));
- tbb::tick_count t1 = tbb::tick_count::now();
- // There is either a writer or a reader upgraded to a writer for each 4th iteration
- long expected_value = n/4;
- if( !invariant.value_is(expected_value) )
- REPORT("ERROR for %s: final invariant value is wrong\n",mutex_name);
- REMARK( "%g usec\n", (t1-t0).seconds() );
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Suppress "conditional expression is constant" warning.
- #pragma warning( push )
- #pragma warning( disable: 4127 )
-#endif
-
-/** Test try_acquire_reader functionality of a non-reenterable reader-writer mutex */
-template<typename M>
-void TestTryAcquireReader_OneThread( const char * mutex_name ) {
- M tested_mutex;
- typename M::scoped_lock lock1;
- if( M::is_rw_mutex ) {
- if( lock1.try_acquire(tested_mutex, false) )
- lock1.release();
- else
- REPORT("ERROR for %s: try_acquire failed though it should not\n", mutex_name);
- {
- typename M::scoped_lock lock2(tested_mutex, false);
- if( lock1.try_acquire(tested_mutex) )
- REPORT("ERROR for %s: try_acquire succeeded though it should not\n", mutex_name);
- lock2.release();
- lock2.acquire(tested_mutex, true);
- if( lock1.try_acquire(tested_mutex, false) )
- REPORT("ERROR for %s: try_acquire succeeded though it should not\n", mutex_name);
- }
- if( lock1.try_acquire(tested_mutex, false) )
- lock1.release();
- else
- REPORT("ERROR for %s: try_acquire failed though it should not\n", mutex_name);
- }
-}
-
-/** Test try_acquire functionality of a non-reenterable mutex */
-template<typename M>
-void TestTryAcquire_OneThread( const char * mutex_name ) {
- M tested_mutex;
- typename M::scoped_lock lock1;
- if( lock1.try_acquire(tested_mutex) )
- lock1.release();
- else
- REPORT("ERROR for %s: try_acquire failed though it should not\n", mutex_name);
- {
- if( M::is_recursive_mutex ) {
- typename M::scoped_lock lock2(tested_mutex);
- if( lock1.try_acquire(tested_mutex) )
- lock1.release();
- else
- REPORT("ERROR for %s: try_acquire on recursive lock failed though it should not\n", mutex_name);
- //windows.. -- both are recursive
- } else {
- typename M::scoped_lock lock2(tested_mutex);
- if( lock1.try_acquire(tested_mutex) )
- REPORT("ERROR for %s: try_acquire succeeded though it should not\n", mutex_name);
- }
- }
- if( lock1.try_acquire(tested_mutex) )
- lock1.release();
- else
- REPORT("ERROR for %s: try_acquire failed though it should not\n", mutex_name);
-}
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-const int RecurN = 4;
-int RecurArray[ RecurN ];
-tbb::recursive_mutex RecurMutex[ RecurN ];
-
-struct RecursiveAcquisition {
- /** x = number being decoded in base N
- max_lock = index of highest lock acquired so far
- mask = bit mask; ith bit set if lock i has been acquired. */
- void Body( size_t x, int max_lock=-1, unsigned int mask=0 ) const
- {
- int i = (int) (x % RecurN);
- bool first = (mask&1U<<i)==0;
- if( first ) {
- // first time to acquire lock
- if( i<max_lock )
- // out of order acquisition might lead to deadlock, so stop
- return;
- max_lock = i;
- }
-
- if( (i&1)!=0 ) {
- // acquire lock on location RecurArray[i] using explict acquire
- tbb::recursive_mutex::scoped_lock r_lock;
- r_lock.acquire( RecurMutex[i] );
- int a = RecurArray[i];
- ASSERT( (a==0)==first, "should be either a==0 if it is the first time to acquire the lock or a!=0 otherwise" );
- ++RecurArray[i];
- if( x )
- Body( x/RecurN, max_lock, mask|1U<<i );
- --RecurArray[i];
- ASSERT( a==RecurArray[i], "a is not equal to RecurArray[i]" );
-
- // release lock on location RecurArray[i] using explicit release; otherwise, use implicit one
- if( (i&2)!=0 ) r_lock.release();
- } else {
- // acquire lock on location RecurArray[i] using implicit acquire
- tbb::recursive_mutex::scoped_lock r_lock( RecurMutex[i] );
- int a = RecurArray[i];
-
- ASSERT( (a==0)==first, "should be either a==0 if it is the first time to acquire the lock or a!=0 otherwise" );
-
- ++RecurArray[i];
- if( x )
- Body( x/RecurN, max_lock, mask|1U<<i );
- --RecurArray[i];
-
- ASSERT( a==RecurArray[i], "a is not equal to RecurArray[i]" );
-
- // release lock on location RecurArray[i] using explicit release; otherwise, use implicit one
- if( (i&2)!=0 ) r_lock.release();
- }
- }
-
- void operator()( const tbb::blocked_range<size_t> &r ) const
- {
- for( size_t x=r.begin(); x<r.end(); x++ ) {
- Body( x );
- }
- }
-};
-
-/** This test is generic so that we may test other kinds of recursive mutexes.*/
-template<typename M>
-void TestRecursiveMutex( const char * mutex_name )
-{
- for ( int i = 0; i < RecurN; ++i ) {
- tbb::profiling::set_name(RecurMutex[i], mutex_name);
- }
- tbb::tick_count t0 = tbb::tick_count::now();
- tbb::parallel_for(tbb::blocked_range<size_t>(0,10000,500), RecursiveAcquisition());
- tbb::tick_count t1 = tbb::tick_count::now();
- REMARK( "%s recursive mutex time = %g usec\n", mutex_name, (t1-t0).seconds() );
-}
-
-template<typename C>
-struct NullRecursive: NoAssign {
- void recurse_till( size_t i, size_t till ) const {
- if( i==till ) {
- counter.value = counter.value+1;
- return;
- }
- if( i&1 ) {
- typename C::mutex_type::scoped_lock lock2(counter.mutex);
- recurse_till( i+1, till );
- lock2.release();
- } else {
- typename C::mutex_type::scoped_lock lock2;
- lock2.acquire(counter.mutex);
- recurse_till( i+1, till );
- }
- }
-
- void operator()( tbb::blocked_range<size_t>& range ) const {
- typename C::mutex_type::scoped_lock lock(counter.mutex);
- recurse_till( range.begin(), range.end() );
- }
- NullRecursive( C& counter_ ) : counter(counter_) {
- ASSERT( C::mutex_type::is_recursive_mutex, "Null mutex should be a recursive mutex." );
- }
- C& counter;
-};
-
-template<typename M>
-struct NullUpgradeDowngrade: NoAssign {
- void operator()( tbb::blocked_range<size_t>& range ) const {
- typename M::scoped_lock lock2;
- for( size_t i=range.begin(); i!=range.end(); ++i ) {
- if( i&1 ) {
- typename M::scoped_lock lock1(my_mutex, true) ;
- if( lock1.downgrade_to_reader()==false )
- REPORT("ERROR for %s: downgrade should always succeed\n", name);
- } else {
- lock2.acquire( my_mutex, false );
- if( lock2.upgrade_to_writer()==false )
- REPORT("ERROR for %s: upgrade should always succeed\n", name);
- lock2.release();
- }
- }
- }
-
- NullUpgradeDowngrade( M& m_, const char* n_ ) : my_mutex(m_), name(n_) {}
- M& my_mutex;
- const char* name;
-} ;
-
-template<typename M>
-void TestNullMutex( const char * name ) {
- Counter<M> counter;
- counter.value = 0;
- const int n = 100;
- REMARK("%s ",name);
- {
- tbb::parallel_for(tbb::blocked_range<size_t>(0,n,10),AddOne<Counter<M> >(counter));
- }
- counter.value = 0;
- {
- tbb::parallel_for(tbb::blocked_range<size_t>(0,n,10),NullRecursive<Counter<M> >(counter));
- }
-
-}
-
-template<typename M>
-void TestNullRWMutex( const char * name ) {
- REMARK("%s ",name);
- const int n = 100;
- M m;
- tbb::parallel_for(tbb::blocked_range<size_t>(0,n,10),NullUpgradeDowngrade<M>(m, name));
-}
-
-//! Test ISO C++0x compatibility portion of TBB mutex
-template<typename M>
-void TestISO( const char * name ) {
- typedef TBB_MutexFromISO_Mutex<M> tbb_from_iso;
- Test<tbb_from_iso>( name );
-}
-
-//! Test ISO C++0x try_lock functionality of a non-reenterable mutex */
-template<typename M>
-void TestTryAcquire_OneThreadISO( const char * name ) {
- typedef TBB_MutexFromISO_Mutex<M> tbb_from_iso;
- TestTryAcquire_OneThread<tbb_from_iso>( name );
-}
-
-//! Test ISO-like C++0x compatibility portion of TBB reader-writer mutex
-template<typename M>
-void TestReaderWriterLockISO( const char * name ) {
- typedef TBB_MutexFromISO_Mutex<M> tbb_from_iso;
- TestReaderWriterLock<tbb_from_iso>( name );
- TestTryAcquireReader_OneThread<tbb_from_iso>( name );
-}
-
-//! Test ISO C++0x compatibility portion of TBB recursive mutex
-template<typename M>
-void TestRecursiveMutexISO( const char * name ) {
- typedef TBB_MutexFromISO_Mutex<M> tbb_from_iso;
- TestRecursiveMutex<tbb_from_iso>(name);
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init( p );
- REMARK( "testing with %d workers\n", static_cast<int>(p) );
-#if TBB_TEST_LOW_WORKLOAD
- // The amount of work is decreased in this mode to bring the length
- // of the runs under tools into the tolerable limits.
- const int n = 1;
-#else
- const int n = 3;
-#endif
- // Run each test several times.
- for( int i=0; i<n; ++i ) {
- TestNullMutex<tbb::null_mutex>( "Null Mutex" );
- TestNullMutex<tbb::null_rw_mutex>( "Null RW Mutex" );
- TestNullRWMutex<tbb::null_rw_mutex>( "Null RW Mutex" );
- Test<tbb::spin_mutex>( "Spin Mutex" );
-#if _OPENMP
- Test<OpenMP_Mutex>( "OpenMP_Mutex" );
-#endif /* _OPENMP */
- Test<tbb::queuing_mutex>( "Queuing Mutex" );
- Test<tbb::mutex>( "Wrapper Mutex" );
- Test<tbb::recursive_mutex>( "Recursive Mutex" );
- Test<tbb::queuing_rw_mutex>( "Queuing RW Mutex" );
- Test<tbb::spin_rw_mutex>( "Spin RW Mutex" );
-
- TestTryAcquire_OneThread<tbb::spin_mutex>("Spin Mutex");
- TestTryAcquire_OneThread<tbb::queuing_mutex>("Queuing Mutex");
-#if USE_PTHREAD
- // under ifdef because on Windows tbb::mutex is reenterable and the test will fail
- TestTryAcquire_OneThread<tbb::mutex>("Wrapper Mutex");
-#endif /* USE_PTHREAD */
- TestTryAcquire_OneThread<tbb::recursive_mutex>( "Recursive Mutex" );
- TestTryAcquire_OneThread<tbb::spin_rw_mutex>("Spin RW Mutex"); // only tests try_acquire for writers
- TestTryAcquire_OneThread<tbb::queuing_rw_mutex>("Queuing RW Mutex"); // only tests try_acquire for writers
- TestTryAcquireReader_OneThread<tbb::spin_rw_mutex>("Spin RW Mutex");
- TestTryAcquireReader_OneThread<tbb::queuing_rw_mutex>("Queuing RW Mutex");
-
- TestReaderWriterLock<tbb::queuing_rw_mutex>( "Queuing RW Mutex" );
- TestReaderWriterLock<tbb::spin_rw_mutex>( "Spin RW Mutex" );
-
- TestRecursiveMutex<tbb::recursive_mutex>( "Recursive Mutex" );
-
- // Test ISO C++0x interface
- TestISO<tbb::spin_mutex>( "ISO Spin Mutex" );
- TestISO<tbb::mutex>( "ISO Mutex" );
- TestISO<tbb::spin_rw_mutex>( "ISO Spin RW Mutex" );
- TestISO<tbb::recursive_mutex>( "ISO Recursive Mutex" );
- TestISO<tbb::critical_section>( "ISO Critical Section" );
- TestTryAcquire_OneThreadISO<tbb::spin_mutex>( "ISO Spin Mutex" );
-#if USE_PTHREAD
- // under ifdef because on Windows tbb::mutex is reenterable and the test will fail
- TestTryAcquire_OneThreadISO<tbb::mutex>( "ISO Mutex" );
-#endif /* USE_PTHREAD */
- TestTryAcquire_OneThreadISO<tbb::spin_rw_mutex>( "ISO Spin RW Mutex" );
- TestTryAcquire_OneThreadISO<tbb::recursive_mutex>( "ISO Recursive Mutex" );
- TestTryAcquire_OneThreadISO<tbb::critical_section>( "ISO Critical Section" );
- TestReaderWriterLockISO<tbb::spin_rw_mutex>( "ISO Spin RW Mutex" );
- TestRecursiveMutexISO<tbb::recursive_mutex>( "ISO Recursive Mutex" );
- }
- REMARK( "calling destructor for task_scheduler_init\n" );
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/spin_mutex.h"
-#include "tbb/queuing_mutex.h"
-#include "tbb/queuing_rw_mutex.h"
-#include "tbb/spin_rw_mutex.h"
-#include "tbb/tick_count.h"
-#include "tbb/atomic.h"
-
-#include "harness.h"
-
-// This test deliberately avoids a "using tbb" statement,
-// so that the error of putting types in the wrong namespace will be caught.
-
-template<typename M>
-struct Counter {
- typedef M mutex_type;
- M mutex;
- volatile long value;
- void flog_once( size_t mode );
-};
-
-template<typename M>
-void Counter<M>::flog_once(size_t mode)
-/** Increments counter once for each iteration in the iteration space. */
-{
- if( mode&1 ) {
- // Try implicit acquire and explicit release
- typename mutex_type::scoped_lock lock(mutex);
- value = value+1;
- lock.release();
- } else {
- // Try explicit acquire and implicit release
- typename mutex_type::scoped_lock lock;
- lock.acquire(mutex);
- value = value+1;
- }
-}
-
-template<typename M, long N>
-struct Invariant {
- typedef M mutex_type;
- M mutex;
- const char* mutex_name;
- volatile long value[N];
- volatile long single_value;
- Invariant( const char* mutex_name_ ) :
- mutex_name(mutex_name_)
- {
- single_value = 0;
- for( long k=0; k<N; ++k )
- value[k] = 0;
- }
- void update() {
- for( long k=0; k<N; ++k )
- ++value[k];
- }
- bool value_is( long expected_value ) const {
- long tmp;
- for( long k=0; k<N; ++k )
- if( (tmp=value[k])!=expected_value ) {
- REPORT("ERROR: %ld!=%ld\n", tmp, expected_value);
- return false;
- }
- return true;
- }
- bool is_okay() {
- return value_is( value[0] );
- }
- void flog_once( size_t mode );
-};
-
-template<typename M, long N>
-void Invariant<M,N>::flog_once( size_t mode )
-{
- //! Every 8th access is a write access
- bool write = (mode%8)==7;
- bool okay = true;
- bool lock_kept = true;
- if( (mode/8)&1 ) {
- // Try implicit acquire and explicit release
- typename mutex_type::scoped_lock lock(mutex,write);
- if( write ) {
- long my_value = value[0];
- update();
- if( mode%16==7 ) {
- lock_kept = lock.downgrade_to_reader();
- if( !lock_kept )
- my_value = value[0] - 1;
- okay = value_is(my_value+1);
- }
- } else {
- okay = is_okay();
- if( mode%8==3 ) {
- long my_value = value[0];
- lock_kept = lock.upgrade_to_writer();
- if( !lock_kept )
- my_value = value[0];
- update();
- okay = value_is(my_value+1);
- }
- }
- lock.release();
- } else {
- // Try explicit acquire and implicit release
- typename mutex_type::scoped_lock lock;
- lock.acquire(mutex,write);
- if( write ) {
- long my_value = value[0];
- update();
- if( mode%16==7 ) {
- lock_kept = lock.downgrade_to_reader();
- if( !lock_kept )
- my_value = value[0] - 1;
- okay = value_is(my_value+1);
- }
- } else {
- okay = is_okay();
- if( mode%8==3 ) {
- long my_value = value[0];
- lock_kept = lock.upgrade_to_writer();
- if( !lock_kept )
- my_value = value[0];
- update();
- okay = value_is(my_value+1);
- }
- }
- }
- if( !okay ) {
- REPORT( "ERROR for %s at %ld: %s %s %s %s\n",mutex_name, long(mode),
- write?"write,":"read,", write?(mode%16==7?"downgrade,":""):(mode%8==3?"upgrade,":""),
- lock_kept?"lock kept,":"lock not kept,", (mode/8)&1?"imp/exp":"exp/imp" );
- }
-}
-
-static tbb::atomic<size_t> Order;
-
-template<typename State, long TestSize>
-struct Work: NoAssign {
- static const size_t chunk = 100;
- State& state;
- Work( State& state_ ) : state(state_) {}
- void operator()( int ) const {
- size_t step;
- while( (step=Order.fetch_and_add<tbb::acquire>(chunk))<TestSize )
- for( size_t i=0; i<chunk && step<TestSize; ++i, ++step )
- state.flog_once(step);
- }
-};
-
-//! Generic test of a TBB Mutex type M.
-/** Does not test features specific to reader-writer locks. */
-template<typename M>
-void Test( const char * name, int nthread ) {
- REMARK("testing %s\n",name);
- Counter<M> counter;
- counter.value = 0;
- Order = 0;
- const long test_size = 100000;
- tbb::tick_count t0 = tbb::tick_count::now();
- NativeParallelFor( nthread, Work<Counter<M>, test_size>(counter) );
- tbb::tick_count t1 = tbb::tick_count::now();
-
- REMARK("%s time = %g usec\n",name, (t1-t0).seconds() );
- if( counter.value!=test_size )
- REPORT("ERROR for %s: counter.value=%ld != %ld=test_size\n",name,counter.value,test_size);
-}
-
-
-//! Generic test of TBB ReaderWriterMutex type M
-template<typename M>
-void TestReaderWriter( const char * mutex_name, int nthread ) {
- REMARK("testing %s\n",mutex_name);
- Invariant<M,8> invariant(mutex_name);
- Order = 0;
- static const long test_size = 1000000;
- tbb::tick_count t0 = tbb::tick_count::now();
- NativeParallelFor( nthread, Work<Invariant<M,8>, test_size>(invariant) );
- tbb::tick_count t1 = tbb::tick_count::now();
- // There is either a writer or a reader upgraded to a writer for each 4th iteration
- long expected_value = test_size/4;
- if( !invariant.value_is(expected_value) )
- REPORT("ERROR for %s: final invariant value is wrong\n",mutex_name);
- REMARK("%s readers & writers time = %g usec\n",mutex_name,(t1-t0).seconds());
-}
-
-int TestMain () {
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK( "testing with %d threads\n", p );
- Test<tbb::spin_mutex>( "spin_mutex", p );
- Test<tbb::queuing_mutex>( "queuing_mutex", p );
- Test<tbb::queuing_rw_mutex>( "queuing_rw_mutex", p );
- Test<tbb::spin_rw_mutex>( "spin_rw_mutex", p );
- TestReaderWriter<tbb::queuing_rw_mutex>( "queuing_rw_mutex", p );
- TestReaderWriter<tbb::spin_rw_mutex>( "spin_rw_mutex", p );
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test mixing OpenMP and TBB
-
-/* SCR #471
- Bellow is workaround to compile test within enviroment of Intel Compiler
- but by Microsoft Compiler. So, there is wrong "omp.h" file included and
- manifest section is missed from .exe file - restoring here.
-
- As of Visual Studio 2010, crtassem.h is no longer shipped.
- */
-#if !defined(__INTEL_COMPILER) && _MSC_VER >= 1400 && _MSC_VER < 1600
- #include <crtassem.h>
- #if !defined(_OPENMP)
- #define _OPENMP
- #if defined(_DEBUG)
- #pragma comment(lib, "vcompd")
- #else // _DEBUG
- #pragma comment(lib, "vcomp")
- #endif // _DEBUG
- #endif // _OPENMP
-
- #if defined(_DEBUG)
- #if defined(_M_IX86)
- #pragma comment(linker,"/manifestdependency:\"type='win32' " \
- "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \
- "version='" _CRT_ASSEMBLY_VERSION "' " \
- "processorArchitecture='x86' " \
- "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")
- #elif defined(_M_AMD64)
- #pragma comment(linker,"/manifestdependency:\"type='win32' " \
- "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \
- "version='" _CRT_ASSEMBLY_VERSION "' " \
- "processorArchitecture='amd64' " \
- "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")
- #elif defined(_M_IA64)
- #pragma comment(linker,"/manifestdependency:\"type='win32' " \
- "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \
- "version='" _CRT_ASSEMBLY_VERSION "' " \
- "processorArchitecture='ia64' " \
- "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")
- #endif
- #else // _DEBUG
- #if defined(_M_IX86)
- #pragma comment(linker,"/manifestdependency:\"type='win32' " \
- "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \
- "version='" _CRT_ASSEMBLY_VERSION "' " \
- "processorArchitecture='x86' " \
- "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")
- #elif defined(_M_AMD64)
- #pragma comment(linker,"/manifestdependency:\"type='win32' " \
- "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \
- "version='" _CRT_ASSEMBLY_VERSION "' " \
- "processorArchitecture='amd64' " \
- "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")
- #elif defined(_M_IA64)
- #pragma comment(linker,"/manifestdependency:\"type='win32' " \
- "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \
- "version='" _CRT_ASSEMBLY_VERSION "' " \
- "processorArchitecture='ia64' " \
- "publicKeyToken='" _VC_ASSEMBLY_PUBLICKEYTOKEN "'\"")
- #endif
- #endif // _DEBUG
- #define _OPENMP_NOFORCE_MANIFEST
-#endif
-
-#include <omp.h>
-
-
-typedef short T;
-
-void SerialConvolve( T c[], const T a[], int m, const T b[], int n ) {
- for( int i=0; i<m+n-1; ++i ) {
- int start = i<n ? 0 : i-n+1;
- int finish = i<m ? i+1 : m;
- T sum = 0;
- for( int j=start; j<finish; ++j )
- sum += a[j]*b[i-j];
- c[i] = sum;
- }
-}
-
-#include "tbb/blocked_range.h"
-#include "tbb/parallel_for.h"
-#include "tbb/parallel_reduce.h"
-#include "tbb/task_scheduler_init.h"
-#include "harness.h"
-
-using namespace tbb;
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Suppress overzealous warning about short+=short
- #pragma warning( push )
- #pragma warning( disable: 4244 )
-#endif
-
-class InnerBody: NoAssign {
- const T* my_a;
- const T* my_b;
- const int i;
-public:
- T sum;
- InnerBody( T /*c*/[], const T a[], const T b[], int i ) :
- my_a(a), my_b(b), sum(0), i(i)
- {}
- InnerBody( InnerBody& x, split ) :
- my_a(x.my_a), my_b(x.my_b), sum(0), i(x.i)
- {
- }
- void join( InnerBody& x ) {sum += x.sum;}
- void operator()( const blocked_range<int>& range ) {
- for( int j=range.begin(); j!=range.end(); ++j )
- sum += my_a[j]*my_b[i-j];
- }
-};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-//! Test OpenMMP loop around TBB loop
-void OpenMP_TBB_Convolve( T c[], const T a[], int m, const T b[], int n ) {
- REMARK("testing OpenMP loop around TBB loop\n");
-#pragma omp parallel
- {
- task_scheduler_init init;
-#pragma omp for
- for( int i=0; i<m+n-1; ++i ) {
- int start = i<n ? 0 : i-n+1;
- int finish = i<m ? i+1 : m;
- InnerBody body(c,a,b,i);
- parallel_reduce( blocked_range<int>(start,finish,10), body );
- c[i] = body.sum;
- }
- }
-}
-
-class OuterBody: NoAssign {
- const T* my_a;
- const T* my_b;
- T* my_c;
- const int m;
- const int n;
-public:
- T sum;
- OuterBody( T c[], const T a[], int m_, const T b[], int n_ ) :
- my_c(c), my_a(a), my_b(b), m(m_), n(n_)
- {}
- void operator()( const blocked_range<int>& range ) const {
- for( int i=range.begin(); i!=range.end(); ++i ) {
- int start = i<n ? 0 : i-n+1;
- int finish = i<m ? i+1 : m;
- T sum = 0;
-#pragma omp parallel for reduction(+:sum)
- for( int j=start; j<finish; ++j )
- sum += my_a[j]*my_b[i-j];
- my_c[i] = sum;
- }
- }
-};
-
-//! Test TBB loop around OpenMP loop
-void TBB_OpenMP_Convolve( T c[], const T a[], int m, const T b[], int n ) {
- REMARK("testing TBB loop around OpenMP loop\n");
- parallel_for( blocked_range<int>(0,m+n-1,10), OuterBody( c, a, m, b, n ) );
-}
-
-#include <stdio.h>
-
-const int M = 17*17;
-const int N = 13*13;
-
-int TestMain () {
- MinThread = 1;
- for( int p=MinThread; p<=MaxThread; ++p ) {
- T a[M];
- T b[N];
- for( int m=1; m<=M; m*=17 ) {
- for( int n=1; n<=M; n*=13 ) {
- for( int i=0; i<m; ++i ) a[i] = T(1+i/5);
- for( int i=0; i<n; ++i ) b[i] = T(1+i/7);
- T expected[M+N];
- SerialConvolve( expected, a, m, b, n );
- task_scheduler_init init(p);
- T actual[M+N];
- for( int k = 0; k<2; ++k ) {
- memset( actual, -1, sizeof(actual) );
- switch(k) {
- case 0:
- TBB_OpenMP_Convolve( actual, a, m, b, n );
- break;
- case 1:
- OpenMP_TBB_Convolve( actual, a, m, b, n );
- break;
- }
- for( int i=0; i<m+n-1; ++i ) {
- ASSERT( actual[i]==expected[i], NULL );
- }
- }
- }
- }
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/parallel_do.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/atomic.h"
-#include "harness.h"
-#include "harness_cpu.h"
-
-#if defined(_MSC_VER) && defined(_Wp64)
- // Workaround for overzealous compiler warnings in /Wp64 mode
- #pragma warning (disable: 4267)
-#endif /* _MSC_VER && _Wp64 */
-
-#define N_DEPTHS 20
-
-static tbb::atomic<int> g_values_counter;
-
-class value_t {
- size_t x;
- value_t& operator= ( const value_t& );
-public:
- value_t ( size_t xx ) : x(xx) { ++g_values_counter; }
- value_t ( const value_t& v ) : x(v.value()) { ++g_values_counter; }
- ~value_t () { --g_values_counter; }
- size_t value() const volatile { return x; }
-};
-
-#include "harness_iterator.h"
-
-static size_t g_tasks_expected = 0;
-static tbb::atomic<size_t> g_tasks_observed;
-
-size_t FindNumOfTasks ( size_t max_depth ) {
- if( max_depth == 0 )
- return 1;
- return max_depth * FindNumOfTasks( max_depth - 1 ) + 1;
-}
-
-//! Simplest form of the parallel_do functor object.
-class FakeTaskGeneratorBody {
-public:
- //! The simplest form of the function call operator
- /** It does not allow adding new tasks during its execution. **/
- void operator() ( value_t depth ) const {
- g_tasks_observed += FindNumOfTasks(depth.value());
- }
-};
-
-/** Work item is passed by reference here. **/
-class FakeTaskGeneratorBody_RefVersion {
-public:
- void operator() ( value_t& depth ) const {
- g_tasks_observed += FindNumOfTasks(depth.value());
- }
-};
-
-/** Work item is passed by reference to const here. **/
-class FakeTaskGeneratorBody_ConstRefVersion {
-public:
- void operator() ( const value_t& depth ) const {
- g_tasks_observed += FindNumOfTasks(depth.value());
- }
-};
-
-/** Work item is passed by reference to volatile here. **/
-class FakeTaskGeneratorBody_VolatileRefVersion {
-public:
- void operator() ( volatile value_t& depth, tbb::parallel_do_feeder<value_t>& ) const {
- g_tasks_observed += FindNumOfTasks(depth.value());
- }
-};
-
-void do_work ( const value_t& depth, tbb::parallel_do_feeder<value_t>& feeder ) {
- ++g_tasks_observed;
- size_t d=depth.value();
- --d;
- for( size_t i = 0; i < depth.value(); ++i)
- feeder.add(value_t(d));
-}
-
-//! Standard form of the parallel_do functor object.
-/** Allows adding new work items on the fly. **/
-class TaskGeneratorBody
-{
-public:
- //! This form of the function call operator can be used when the body needs to add more work during the processing
- void operator() ( value_t depth, tbb::parallel_do_feeder<value_t>& feeder ) const {
- do_work(depth, feeder);
- }
-private:
- // Assert that parallel_do does not ever access body constructors
- TaskGeneratorBody () {}
- TaskGeneratorBody ( const TaskGeneratorBody& );
- // TestBody() needs access to the default constructor
- template<class Body, class Iterator> friend void TestBody( size_t );
-}; // class TaskGeneratorBody
-
-/** Work item is passed by reference here. **/
-class TaskGeneratorBody_RefVersion
-{
-public:
- void operator() ( value_t& depth, tbb::parallel_do_feeder<value_t>& feeder ) const {
- do_work(depth, feeder);
- }
-}; // class TaskGeneratorBody
-
-/** Work item is passed as const here. Compilers must ignore the const qualifier. **/
-class TaskGeneratorBody_ConstVersion
-{
-public:
- void operator() ( const value_t depth, tbb::parallel_do_feeder<value_t>& feeder ) const {
- do_work(depth, feeder);
- }
-}; // class TaskGeneratorBody
-
-/** Work item is passed by reference to const here. **/
-class TaskGeneratorBody_ConstRefVersion
-{
-public:
- void operator() ( const value_t& depth, tbb::parallel_do_feeder<value_t>& feeder ) const {
- do_work(depth, feeder);
- }
-}; // class TaskGeneratorBody
-
-/** Work item is passed by reference to volatile here. **/
-class TaskGeneratorBody_VolatileRefVersion
-{
-public:
- void operator() ( volatile value_t& depth, tbb::parallel_do_feeder<value_t>& feeder ) const {
- do_work(const_cast<value_t&>(depth), feeder);
- }
-}; // class TaskGeneratorBody
-
-/** Work item is passed by reference to volatile here. **/
-class TaskGeneratorBody_ConstVolatileRefVersion
-{
-public:
- void operator() ( const volatile value_t& depth, tbb::parallel_do_feeder<value_t>& feeder ) const {
- do_work(const_cast<value_t&>(depth), feeder);
- }
-}; // class TaskGeneratorBody
-
-
-static value_t g_depths[N_DEPTHS] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2};
-
-template<class Body, class Iterator>
-void TestBody ( size_t depth ) {
- typedef typename std::iterator_traits<Iterator>::value_type value_type;
- value_type a_depths[N_DEPTHS] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2};
- Body body;
- Iterator begin(a_depths);
- Iterator end(a_depths + depth);
- g_tasks_observed = 0;
- tbb::parallel_do(begin, end, body);
- ASSERT (g_tasks_observed == g_tasks_expected, NULL);
-}
-
-template<class Iterator>
-void TestIterator_RvalueOnly ( int /*nthread*/, size_t depth ) {
- g_values_counter = 0;
- TestBody<FakeTaskGeneratorBody, Iterator> (depth);
- TestBody<FakeTaskGeneratorBody_ConstRefVersion, Iterator> (depth);
- TestBody<TaskGeneratorBody, Iterator> (depth);
- TestBody<TaskGeneratorBody_ConstVersion, Iterator> (depth);
- TestBody<TaskGeneratorBody_ConstRefVersion, Iterator> (depth);
-}
-
-template<class Iterator>
-void TestIterator ( int nthread, size_t depth ) {
- TestIterator_RvalueOnly<Iterator>(nthread, depth);
- TestBody<FakeTaskGeneratorBody_RefVersion, Iterator> (depth);
- TestBody<FakeTaskGeneratorBody_VolatileRefVersion, Iterator> (depth);
- TestBody<TaskGeneratorBody_RefVersion, Iterator> (depth);
- TestBody<TaskGeneratorBody_VolatileRefVersion, Iterator> (depth);
- TestBody<TaskGeneratorBody_ConstVolatileRefVersion, Iterator> (depth);
-}
-
-void Run( int nthread ) {
- for( size_t depth = 0; depth <= N_DEPTHS; ++depth ) {
- g_tasks_expected = 0;
- for ( size_t i=0; i < depth; ++i )
- g_tasks_expected += FindNumOfTasks( g_depths[i].value() );
- // Test for iterators over values convertible to work item type
- TestIterator_RvalueOnly<size_t*>(nthread, depth);
- // Test for random access iterators
- TestIterator<value_t*>(nthread, depth);
- // Test for input iterators
- TestIterator<Harness::InputIterator<value_t> >(nthread, depth);
- // Test for forward iterators
- TestIterator<Harness::ForwardIterator<value_t> >(nthread, depth);
- }
-}
-
-int TestMain () {
- if( MinThread<1 ) {
- REPORT("number of threads must be positive\n");
- exit(1);
- }
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init( p );
- Run(p);
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
- }
- // This check must be performed after the scheduler terminated because only in this
- // case there is a guarantee that the workers already destroyed their last tasks.
- ASSERT( g_values_counter == 0, "Value objects were leaked" );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test for function template parallel_for.h
-
-#if _MSC_VER
-#pragma warning (push)
-#if !defined(__INTEL_COMPILER)
- // Suppress pointless "unreachable code" warning.
- #pragma warning (disable: 4702)
-#endif
-#if defined(_Wp64)
- // Workaround for overzealous compiler warnings in /Wp64 mode
- #pragma warning (disable: 4267)
-#endif
-#endif //#if _MSC_VER
-
-#include "tbb/parallel_for.h"
-#include "tbb/atomic.h"
-#include "harness_assert.h"
-#include "harness.h"
-
-static tbb::atomic<int> FooBodyCount;
-
-//! An range object whose only public members are those required by the Range concept.
-template<size_t Pad>
-class FooRange {
- //! Start of range
- int start;
-
- //! Size of range
- int size;
- FooRange( int start_, int size_ ) : start(start_), size(size_) {
- zero_fill<char>(pad, Pad);
- pad[Pad-1] = 'x';
- }
- template<size_t Pad_> friend void Flog( int nthread );
- template<size_t Pad_> friend class FooBody;
- void operator&();
-
- char pad[Pad];
-public:
- bool empty() const {return size==0;}
- bool is_divisible() const {return size>1;}
- FooRange( FooRange& original, tbb::split ) : size(original.size/2) {
- original.size -= size;
- start = original.start+original.size;
- ASSERT( original.pad[Pad-1]=='x', NULL );
- pad[Pad-1] = 'x';
- }
-};
-
-//! An range object whose only public members are those required by the parallel_for.h body concept.
-template<size_t Pad>
-class FooBody {
- static const int LIVE = 0x1234;
- tbb::atomic<int>* array;
- int state;
- friend class FooRange<Pad>;
- template<size_t Pad_> friend void Flog( int nthread );
- FooBody( tbb::atomic<int>* array_ ) : array(array_), state(LIVE) {}
-public:
- ~FooBody() {
- --FooBodyCount;
- for( size_t i=0; i<sizeof(*this); ++i )
- reinterpret_cast<char*>(this)[i] = -1;
- }
- //! Copy constructor
- FooBody( const FooBody& other ) : array(other.array), state(other.state) {
- ++FooBodyCount;
- ASSERT( state==LIVE, NULL );
- }
- void operator()( FooRange<Pad>& r ) const {
- for( int k=0; k<r.size; ++k )
- array[r.start+k]++;
- }
-};
-
-#include "tbb/tick_count.h"
-
-static const int N = 1000;
-static tbb::atomic<int> Array[N];
-
-template<size_t Pad>
-void Flog( int nthread ) {
- tbb::tick_count T0 = tbb::tick_count::now();
- for( int i=0; i<N; ++i ) {
- for ( int mode = 0; mode < 4; ++mode)
- {
- FooRange<Pad> r( 0, i );
- const FooRange<Pad> rc = r;
- FooBody<Pad> f( Array );
- const FooBody<Pad> fc = f;
- memset( Array, 0, sizeof(Array) );
- FooBodyCount = 1;
- switch (mode) {
- case 0:
- tbb::parallel_for( rc, fc );
- break;
- case 1:
- tbb::parallel_for( rc, fc, tbb::simple_partitioner() );
- break;
- case 2:
- tbb::parallel_for( rc, fc, tbb::auto_partitioner() );
- break;
- case 3: {
- static tbb::affinity_partitioner affinity;
- tbb::parallel_for( rc, fc, affinity );
- }
- break;
- }
- for( int j=0; j<i; ++j )
- ASSERT( Array[j]==1, NULL );
- for( int j=i; j<N; ++j )
- ASSERT( Array[j]==0, NULL );
- // Destruction of bodies might take a while, but there should be at most one body per thread
- // at this point.
- while( FooBodyCount>1 && FooBodyCount<=nthread )
- __TBB_Yield();
- ASSERT( FooBodyCount==1, NULL );
- }
- }
- tbb::tick_count T1 = tbb::tick_count::now();
- REMARK("time=%g\tnthread=%d\tpad=%d\n",(T1-T0).seconds(),nthread,int(Pad));
-}
-
-// Testing parallel_for with step support
-const size_t PFOR_BUFFER_TEST_SIZE = 1024;
-// test_buffer has some extra items beyound right bound
-const size_t PFOR_BUFFER_ACTUAL_SIZE = PFOR_BUFFER_TEST_SIZE + 1024;
-size_t pfor_buffer[PFOR_BUFFER_ACTUAL_SIZE];
-
-template<typename T>
-class TestFunctor{
-public:
- void operator ()(T index) const {
- pfor_buffer[index]++;
- }
-};
-
-#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 <stdexcept> // std::invalid_argument
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-template <typename T>
-void TestParallelForWithStepSupport()
-{
- const T pfor_buffer_test_size = static_cast<T>(PFOR_BUFFER_TEST_SIZE);
- const T pfor_buffer_actual_size = static_cast<T>(PFOR_BUFFER_ACTUAL_SIZE);
- // Testing parallel_for with different step values
- for (T begin = 0; begin < pfor_buffer_test_size - 1; begin += pfor_buffer_test_size / 10 + 1) {
- T step;
- for (step = 1; step < pfor_buffer_test_size; step++) {
- memset(pfor_buffer, 0, pfor_buffer_actual_size * sizeof(size_t));
- if (step == 1){
- tbb::parallel_for(begin, pfor_buffer_test_size, TestFunctor<T>());
- } else {
- tbb::parallel_for(begin, pfor_buffer_test_size, step, TestFunctor<T>());
- }
- // Verifying that parallel_for processed all items it should
- for (T i = begin; i < pfor_buffer_test_size; i = i + step) {
- ASSERT(pfor_buffer[i] == 1, "parallel_for didn't process all required elements");
- pfor_buffer[i] = 0;
- }
- // Verifying that no extra items were processed and right bound of array wasn't crossed
- for (T i = 0; i < pfor_buffer_actual_size; i++) {
- ASSERT(pfor_buffer[i] == 0, "parallel_for processed an extra element");
- }
- }
- }
-
- // Testing some corner cases
- tbb::parallel_for(static_cast<T>(2), static_cast<T>(1), static_cast<T>(1), TestFunctor<T>());
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- try{
- tbb::parallel_for(static_cast<T>(1), static_cast<T>(100), static_cast<T>(0), TestFunctor<T>()); // should cause std::invalid_argument
- }catch(std::invalid_argument){
- return;
- }
- catch ( ... ) {
- ASSERT ( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unrecognized exception. std::invalid_argument is expected" );
- }
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
-}
-
-// Exception support test
-#define HARNESS_EH_SIMPLE_MODE 1
-#include "tbb/tbb_exception.h"
-#include "harness_eh.h"
-
-#if TBB_USE_EXCEPTIONS
-class test_functor_with_exception {
-public:
- void operator ()(size_t) const { ThrowTestException(); }
-};
-
-void TestExceptionsSupport() {
- REMARK (__FUNCTION__);
- { // Tests version with a step provided
- ResetEhGlobals();
- TRY();
- tbb::parallel_for((size_t)0, (size_t)PFOR_BUFFER_TEST_SIZE, (size_t)1, test_functor_with_exception());
- CATCH_AND_ASSERT();
- }
- { // Tests version without a step
- ResetEhGlobals();
- TRY();
- tbb::parallel_for((size_t)0, (size_t)PFOR_BUFFER_TEST_SIZE, test_functor_with_exception());
- CATCH_AND_ASSERT();
- }
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-// Cancellation support test
-class functor_to_cancel {
-public:
- void operator()(size_t) const {
- ++g_CurExecuted;
- CancellatorTask::WaitUntilReady();
- }
-};
-
-size_t g_worker_task_step = 0;
-
-class my_worker_pfor_step_task : public tbb::task
-{
- tbb::task_group_context &my_ctx;
-
- tbb::task* execute () {
- if (g_worker_task_step == 0){
- tbb::parallel_for((size_t)0, (size_t)PFOR_BUFFER_TEST_SIZE, functor_to_cancel(), my_ctx);
- }else{
- tbb::parallel_for((size_t)0, (size_t)PFOR_BUFFER_TEST_SIZE, g_worker_task_step, functor_to_cancel(), my_ctx);
- }
- return NULL;
- }
-public:
- my_worker_pfor_step_task ( tbb::task_group_context &context_) : my_ctx(context_) { }
-};
-
-void TestCancellation()
-{
- // tests version without a step
- g_worker_task_step = 0;
- ResetEhGlobals();
- RunCancellationTest<my_worker_pfor_step_task, CancellatorTask>();
-
- // tests version with step
- g_worker_task_step = 1;
- ResetEhGlobals();
- RunCancellationTest<my_worker_pfor_step_task, CancellatorTask>();
-}
-
-#include "harness_m128.h"
-
-#if HAVE_m128 && !__TBB_SSE_STACK_ALIGNMENT_BROKEN
-struct SSE_Functor {
- ClassWithSSE* Src, * Dst;
- SSE_Functor( ClassWithSSE* src, ClassWithSSE* dst ) : Src(src), Dst(dst) {}
-
- void operator()( tbb::blocked_range<int>& r ) const {
- for( int i=r.begin(); i!=r.end(); ++i )
- Dst[i] = Src[i];
- }
-};
-
-//! Test that parallel_for works with stack-allocated __m128
-void TestSSE() {
- ClassWithSSE Array1[N], Array2[N];
- for( int i=0; i<N; ++i )
- Array1[i] = ClassWithSSE(i);
- tbb::parallel_for( tbb::blocked_range<int>(0,N), SSE_Functor(Array1, Array2) );
- for( int i=0; i<N; ++i )
- ASSERT( Array2[i]==ClassWithSSE(i), NULL ) ;
-}
-#endif /* HAVE_m128 */
-
-#include <cstdio>
-#include "tbb/task_scheduler_init.h"
-#include "harness_cpu.h"
-
-int TestMain () {
- if( MinThread<1 ) {
- REPORT("number of threads must be positive\n");
- exit(1);
- }
- for( int p=MinThread; p<=MaxThread; ++p ) {
- if( p>0 ) {
- tbb::task_scheduler_init init( p );
- Flog<1>(p);
- Flog<10>(p);
- Flog<100>(p);
- Flog<1000>(p);
- Flog<10000>(p);
-
- // Testing with different integer types
- TestParallelForWithStepSupport<short>();
- TestParallelForWithStepSupport<unsigned short>();
- TestParallelForWithStepSupport<int>();
- TestParallelForWithStepSupport<unsigned int>();
- TestParallelForWithStepSupport<long>();
- TestParallelForWithStepSupport<unsigned long>();
- TestParallelForWithStepSupport<long long>();
- TestParallelForWithStepSupport<unsigned long long>();
- TestParallelForWithStepSupport<size_t>();
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- TestExceptionsSupport();
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- if (p>1) TestCancellation();
-#if HAVE_m128 && !__TBB_SSE_STACK_ALIGNMENT_BROKEN
- TestSSE();
-#endif /* HAVE_m128 */
-
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
- }
- }
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception handling tests are skipped.\n");
-#endif
-#if HAVE_m128 && __TBB_SSE_STACK_ALIGNMENT_BROKEN
- REPORT("Known issue: stack alignment for SSE not tested.\n");
-#endif
- return Harness::Done;
-}
-
-#if _MSC_VER
-#pragma warning (pop)
-#endif
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
-#pragma warning(disable: 4180) // "qualifier applied to function type has no meaning; ignored"
-#endif
-
-#include "tbb/parallel_for_each.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/atomic.h"
-#include "harness.h"
-#include "harness_iterator.h"
-
-// Some old compilers can't deduce template paremeter type for parallel_for_each
-// if the function name is passed without explicit cast to function pointer.
-typedef void (*TestFunctionType)(size_t);
-
-tbb::atomic<size_t> sum;
-
-// This function is called via parallel_for_each
-void TestFunction (size_t value) {
- sum += (unsigned int)value;
-}
-
-const size_t NUMBER_OF_ELEMENTS = 1000;
-
-// Tests tbb::parallel_for_each functionality
-template <typename Iterator>
-void RunPForEachTests()
-{
- size_t test_vector[NUMBER_OF_ELEMENTS + 1];
-
- sum = 0;
- size_t test_sum = 0;
-
- for (size_t i =0; i < NUMBER_OF_ELEMENTS; i++) {
- test_vector[i] = i;
- test_sum += i;
- }
- test_vector[NUMBER_OF_ELEMENTS] = 1000000; // parallel_for_each shouldn't touch this element
-
- Iterator begin(&test_vector[0]);
- Iterator end(&test_vector[NUMBER_OF_ELEMENTS]);
-
- tbb::parallel_for_each(begin, end, (TestFunctionType)TestFunction);
- ASSERT(sum == test_sum, "Not all items of test vector were processed by parallel_for_each");
- ASSERT(test_vector[NUMBER_OF_ELEMENTS] == 1000000, "parallel_for_each processed an extra element");
-}
-
-typedef void (*TestMutatorType)(size_t&);
-
-void TestMutator(size_t& value) {
- ASSERT(value==0,NULL);
- ++sum;
- ++value;
-}
-
-//! Test that tbb::parallel_for_each works for mutable iterators.
-template <typename Iterator>
-void RunMutablePForEachTests() {
- size_t test_vector[NUMBER_OF_ELEMENTS];
- for( size_t i=0; i<NUMBER_OF_ELEMENTS; ++i )
- test_vector[i] = 0;
- sum = 0;
- tbb::parallel_for_each( Iterator(test_vector), Iterator(test_vector+NUMBER_OF_ELEMENTS), (TestMutatorType)TestMutator );
- ASSERT( sum==NUMBER_OF_ELEMENTS, "parallel_for_each called function wrong number of times" );
- for( size_t i=0; i<NUMBER_OF_ELEMENTS; ++i )
- ASSERT( test_vector[i]==1, "parallel_for_each did not process each element exactly once" );
-}
-
-#define HARNESS_EH_SIMPLE_MODE 1
-#include "tbb/tbb_exception.h"
-#include "harness_eh.h"
-
-#if TBB_USE_EXCEPTIONS
-void test_function_with_exception(size_t) {
- ThrowTestException();
-}
-
-template <typename Iterator>
-void TestExceptionsSupport()
-{
- REMARK (__FUNCTION__);
- size_t test_vector[NUMBER_OF_ELEMENTS + 1];
-
- for (size_t i = 0; i < NUMBER_OF_ELEMENTS; i++) {
- test_vector[i] = i;
- }
-
- Iterator begin(&test_vector[0]);
- Iterator end(&test_vector[NUMBER_OF_ELEMENTS]);
-
- TRY();
- tbb::parallel_for_each(begin, end, (TestFunctionType)test_function_with_exception);
- CATCH_AND_ASSERT();
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-// Cancelation support test
-void function_to_cancel(size_t ) {
- ++g_CurExecuted;
- CancellatorTask::WaitUntilReady();
-}
-
-template <typename Iterator>
-class my_worker_pforeach_task : public tbb::task
-{
- tbb::task_group_context &my_ctx;
-
- tbb::task* execute () {
- size_t test_vector[NUMBER_OF_ELEMENTS + 1];
- for (size_t i = 0; i < NUMBER_OF_ELEMENTS; i++) {
- test_vector[i] = i;
- }
- Iterator begin(&test_vector[0]);
- Iterator end(&test_vector[NUMBER_OF_ELEMENTS]);
-
- tbb::parallel_for_each(begin, end, (TestFunctionType)function_to_cancel);
-
- return NULL;
- }
-public:
- my_worker_pforeach_task ( tbb::task_group_context &ctx) : my_ctx(ctx) { }
-};
-
-template <typename Iterator>
-void TestCancellation()
-{
- REMARK (__FUNCTION__);
- ResetEhGlobals();
- RunCancellationTest<my_worker_pforeach_task<Iterator>, CancellatorTask>();
-}
-
-#include "harness_cpu.h"
-
-int TestMain () {
- if( MinThread<1 ) {
- REPORT("number of threads must be positive\n");
- exit(1);
- }
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init( p );
- RunPForEachTests<Harness::RandomIterator<size_t> >();
- RunPForEachTests<Harness::InputIterator<size_t> >();
- RunPForEachTests<Harness::ForwardIterator<size_t> >();
- RunMutablePForEachTests<Harness::RandomIterator<size_t> >();
- RunMutablePForEachTests<Harness::ForwardIterator<size_t> >();
-
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- TestExceptionsSupport<Harness::RandomIterator<size_t> >();
- TestExceptionsSupport<Harness::InputIterator<size_t> >();
- TestExceptionsSupport<Harness::ForwardIterator<size_t> >();
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- if (p > 1) {
- TestCancellation<Harness::RandomIterator<size_t> >();
- TestCancellation<Harness::InputIterator<size_t> >();
- TestCancellation<Harness::ForwardIterator<size_t> >();
- }
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
- }
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception handling tests are skipped.\n");
-#endif
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
-#pragma warning(disable: 4180) // "qualifier applied to function type has no meaning; ignored"
-#endif
-
-#include "tbb/parallel_invoke.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/atomic.h"
-#include "tbb/tbb_exception.h"
-#include "harness.h"
-
-#if !defined(__INTEL_COMPILER)
-#if defined(_MSC_VER) && _MSC_VER <= 1400 || __GNUC__==3 && __GNUC_MINOR__<=3 || __SUNPRO_CC
- #define __TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN 1
-#endif
-#endif
-
-static const size_t MAX_NUMBER_OF_PINVOKE_ARGS = 10;
-tbb::atomic<size_t> function_counter;
-
-// Some macros to make the test easier to read
-
-// 10 functions test0 ... test9 are defined
-// pointer to each function is also defined
-
-#define TEST_FUNCTION(value) void test##value () \
-{ \
- ASSERT(!(function_counter & (1 << value)), "Test function has already been called"); \
- function_counter += 1 << value; \
-} \
-void (*test_pointer##value)(void) = test##value;
-
-TEST_FUNCTION(0)
-TEST_FUNCTION(1)
-TEST_FUNCTION(2)
-TEST_FUNCTION(3)
-TEST_FUNCTION(4)
-TEST_FUNCTION(5)
-TEST_FUNCTION(6)
-TEST_FUNCTION(7)
-TEST_FUNCTION(8)
-TEST_FUNCTION(9)
-
-// The same with functors
-#define TEST_FUNCTOR(value) class test_functor##value \
-{ \
-public: \
- void operator() () const { \
- function_counter += 1 << value; \
- } \
-} functor##value;
-
-TEST_FUNCTOR(0)
-TEST_FUNCTOR(1)
-TEST_FUNCTOR(2)
-TEST_FUNCTOR(3)
-TEST_FUNCTOR(4)
-TEST_FUNCTOR(5)
-TEST_FUNCTOR(6)
-TEST_FUNCTOR(7)
-TEST_FUNCTOR(8)
-TEST_FUNCTOR(9)
-
-#define INIT_TEST function_counter = 0;
-
-#define VALIDATE_INVOKE_RUN(number_of_args, test_type) \
- 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);
-
-// Calls parallel_invoke for different number of arguments
-// It can be called with and without user context
-template <typename F0, typename F1, typename F2, typename F3, typename F4, typename F5,
- typename F6, typename F7, typename F8, typename F9>
-void call_parallel_invoke( size_t n, F0& f0, F1& f1, F2& f2, F3& f3, F4 &f4, F5 &f5,
- F6& f6, F7 &f7, F8 &f8, F9 &f9, tbb::task_group_context* context) {
- switch(n) {
- default:
- ASSERT(false, "number of arguments must be between 2 and 10");
- case 2:
- if (context)
- tbb::parallel_invoke (f0, f1, *context);
- else
- tbb::parallel_invoke (f0, f1);
- break;
- case 3:
- if (context)
- tbb::parallel_invoke (f0, f1, f2, *context);
- else
- tbb::parallel_invoke (f0, f1, f2);
- break;
- case 4:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3);
- break;
- case 5:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, f4, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3, f4);
- break;
- case 6:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5);
- break;
- case 7:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6);
- break;
- case 8:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7);
- break;
- case 9:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8);
- break;
- case 10:
- if(context)
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, *context);
- else
- tbb::parallel_invoke (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9);
- break;
- }
-}
-
-#if !__TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN
-template<typename function> void aux_invoke(const function& f) {
- f();
-}
-
-bool function_by_constref_in_template_codegen_broken() {
- function_counter = 0;
- aux_invoke(test1);
- return function_counter==0;
-}
-#endif /* !__TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN */
-
-void test_parallel_invoke()
-{
- REMARK (__FUNCTION__);
- // Testing with pointers to functions
- for (int n = 2; n <=10; n++)
- {
- INIT_TEST;
- call_parallel_invoke(n, test_pointer0, test_pointer1, test_pointer2, test_pointer3, test_pointer4,
- test_pointer5, test_pointer6, test_pointer7, test_pointer8, test_pointer9, NULL);
- VALIDATE_INVOKE_RUN(n, "pointers to function");
- }
-
- // Testing parallel_invoke with functors
- for (int n = 2; n <=10; n++)
- {
- INIT_TEST;
- call_parallel_invoke(n, functor0, functor1, functor2, functor3, functor4,
- functor5, functor6, functor7, functor8, functor9, NULL);
- VALIDATE_INVOKE_RUN(n, "functors");
- }
-
-#if __TBB_FUNCTION_BY_CONSTREF_IN_TEMPLATE_BROKEN
- // some old compilers can't cope with passing function name into parallel_invoke
-#else
- // and some compile but generate broken code that does not call the function
- if (function_by_constref_in_template_codegen_broken())
- return;
-
- // Testing parallel_invoke with functions
- for (int n = 2; n <=10; n++)
- {
- INIT_TEST;
- call_parallel_invoke(n, test0, test1, test2, test3, test4, test5, test6, test7, test8, test9, NULL);
- VALIDATE_INVOKE_RUN(n, "functions");
- }
-#endif
-}
-
-// Exception handling support test
-
-#define HARNESS_EH_SIMPLE_MODE 1
-#include "harness_eh.h"
-
-#if TBB_USE_EXCEPTIONS
-volatile size_t exception_mask; // each bit represents whether the function should throw exception or not
-
-// throws exception if corresponding exception_mask bit is set
-#define TEST_FUNCTOR_WITH_THROW(value) \
-struct throwing_functor##value { \
- void operator() () const { \
- if (exception_mask & (1 << value)) \
- ThrowTestException(); \
- } \
-} test_with_throw##value;
-
-TEST_FUNCTOR_WITH_THROW(0)
-TEST_FUNCTOR_WITH_THROW(1)
-TEST_FUNCTOR_WITH_THROW(2)
-TEST_FUNCTOR_WITH_THROW(3)
-TEST_FUNCTOR_WITH_THROW(4)
-TEST_FUNCTOR_WITH_THROW(5)
-TEST_FUNCTOR_WITH_THROW(6)
-TEST_FUNCTOR_WITH_THROW(7)
-TEST_FUNCTOR_WITH_THROW(8)
-TEST_FUNCTOR_WITH_THROW(9)
-
-void TestExceptionHandling()
-{
- REMARK (__FUNCTION__);
- for( size_t n = 2; n <= 10; ++n ) {
- for( exception_mask = 1; exception_mask < (size_t) (1 << n); ++exception_mask ) {
- ResetEhGlobals();
- TRY();
- REMARK("Calling parallel_invoke, number of functions = %d, exception_mask = %d\n", n, exception_mask);
- call_parallel_invoke(n, test_with_throw0, test_with_throw1, test_with_throw2, test_with_throw3,
- test_with_throw4, test_with_throw5, test_with_throw6, test_with_throw7, test_with_throw8, test_with_throw9, NULL);
- CATCH_AND_ASSERT();
- }
- }
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-// Cancelation support test
-void function_to_cancel() {
- ++g_CurExecuted;
- CancellatorTask::WaitUntilReady();
-}
-
-// The function is used to test cancellation
-void simple_test_nothrow (){
- ++g_CurExecuted;
-}
-
-size_t g_numFunctions,
- g_functionToCancel;
-
-class ParInvokeLauncherTask : public tbb::task
-{
- tbb::task_group_context &my_ctx;
- void(*func_array[10])(void);
-
- tbb::task* execute () {
- func_array[g_functionToCancel] = &function_to_cancel;
- call_parallel_invoke(g_numFunctions, func_array[0], func_array[1], func_array[2], func_array[3],
- func_array[4], func_array[5], func_array[6], func_array[7], func_array[8], func_array[9], &my_ctx);
- return NULL;
- }
-public:
- ParInvokeLauncherTask ( tbb::task_group_context& ctx ) : my_ctx(ctx) {
- for (int i = 0; i <=9; ++i)
- func_array[i] = &simple_test_nothrow;
- }
-};
-
-void TestCancellation ()
-{
- REMARK (__FUNCTION__);
- for ( int n = 2; n <= 10; ++n ) {
- for ( int m = 0; m <= n - 1; ++m ) {
- g_numFunctions = n;
- g_functionToCancel = m;
- ResetEhGlobals();
- RunCancellationTest<ParInvokeLauncherTask, CancellatorTask>();
- }
- }
-}
-
-//------------------------------------------------------------------------
-// Entry point
-//------------------------------------------------------------------------
-
-#include "harness_cpu.h"
-
-int TestMain () {
- MinThread = min(MinThread, MaxThread);
- ASSERT (MinThread>=1, "Minimal number of threads must be 1 or more");
- for ( int p = MinThread; p <= MaxThread; ++p ) {
- tbb::task_scheduler_init init(p);
- test_parallel_invoke();
- if (p > 1) {
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception handling tests are skipped.\n");
-#elif TBB_USE_EXCEPTIONS
- TestExceptionHandling();
-#endif /* TBB_USE_EXCEPTIONS */
- TestCancellation();
- }
- TestCPUUserTime(p);
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Before including pipeline.h, set up the variable to count heap allocated
-// filter_node objects, and make it known for the header.
-int filter_node_count = 0;
-#define __TBB_TEST_FILTER_NODE_COUNT filter_node_count
-#include "tbb/pipeline.h"
-
-#include "tbb/atomic.h"
-#include "harness.h"
-
-const int n_tokens = 8;
-const int max_counter = 16;
-static tbb::atomic<int> output_counter;
-static tbb::atomic<int> input_counter;
-static tbb::atomic<int> check_type_counter;
-
-class check_type : Harness::NoAfterlife {
- unsigned int id;
- bool am_ready;
-public:
- check_type( ) : id(0), am_ready(false) {
- ++check_type_counter;
- }
-
- check_type(const check_type& other) : Harness::NoAfterlife(other) {
- other.AssertLive();
- AssertLive();
- id = other.id;
- am_ready = other.am_ready;
- ++check_type_counter;
- }
-
- ~check_type() {
- AssertLive();
- --check_type_counter;
- }
- unsigned int my_id() { AssertLive(); return id; }
- bool is_ready() { AssertLive(); return am_ready; }
- void function() {
- AssertLive();
- if( id == 0 ) {
- id = 1;
- am_ready = true;
- }
- }
-};
-
-// Filters must be copy-constructible, and be const-qualifiable.
-template<typename U>
-class input_filter : Harness::NoAfterlife {
-public:
- U operator()( tbb::flow_control& control ) const {
- AssertLive();
- if( --input_counter < 0 ) {
- control.stop();
- }
- return U(); // default constructed
- }
-
-};
-
-template<>
-class input_filter<void> : Harness::NoAfterlife {
-public:
- void operator()( tbb::flow_control& control ) const {
- AssertLive();
- if( --input_counter < 0 ) {
- control.stop();
- }
- }
-
-};
-
-
-template<>
-class input_filter<check_type> : Harness::NoAfterlife {
-public:
- check_type operator()( tbb::flow_control& control ) const {
- AssertLive();
- if( --input_counter < 0 ) {
- control.stop();
- }
- return check_type( ); // default constructed
- }
-};
-
-template<typename T, typename U>
-class middle_filter : Harness::NoAfterlife {
-public:
- U operator()(T /*my_storage*/) const {
- AssertLive();
- return U();
- }
-};
-
-template<>
-class middle_filter<check_type, check_type> : Harness::NoAfterlife {
-public:
- check_type& operator()( check_type &c) const {
- AssertLive();
- ASSERT(!c.my_id(), "bad id value");
- ASSERT(!c.is_ready(), "Already ready" );
- c.function();
- return c;
- }
-
-};
-
-template<typename T>
-class output_filter : Harness::NoAfterlife {
-public:
- void operator()(T) const {
- AssertLive();
- output_counter++;
- }
-};
-
-template<>
-class output_filter<check_type> : Harness::NoAfterlife {
-public:
- void operator()(check_type &c) const {
- AssertLive();
- ASSERT(c.my_id(), "unset id value");
- ASSERT(c.is_ready(), "not yet ready");
- output_counter++;
- }
-};
-
-void resetCounters() {
- output_counter = 0;
- input_counter = max_counter;
-}
-
-void checkCounters() {
- ASSERT(output_counter == max_counter, "not all tokens were passed through pipeline");
-}
-
-static const tbb::filter::mode filter_table[] = { tbb::filter::parallel, tbb::filter::serial_in_order, tbb::filter::serial_out_of_order};
-const unsigned number_of_filter_types = sizeof(filter_table)/sizeof(filter_table[0]);
-
-typedef tbb::filter_t<void, void> filter_chain;
-typedef tbb::filter::mode mode_array;
-
-// The filters are passed by value, which forces a temporary copy to be created. This is
-// to reproduce the bug where a filter_chain uses refs to filters, which after a call
-// would be references to destructed temporaries.
-template<typename type1, typename type2>
-void fill_chain( filter_chain &my_chain, mode_array *filter_type, input_filter<type1> i_filter,
- middle_filter<type1, type2> m_filter, output_filter<type2> o_filter ) {
- my_chain = tbb::make_filter<void, type1>(filter_type[0], i_filter) &
- tbb::make_filter<type1, type2>(filter_type[1], m_filter) &
- tbb::make_filter<type2, void>(filter_type[2], o_filter);
-}
-
-void run_function_spec() {
- ASSERT(!filter_node_count, NULL);
- REMARK("Testing < void, void > (single filter in pipeline)");
-#if __TBB_LAMBDAS_PRESENT
- REMARK( " ( + lambdas)");
-#endif
- REMARK("\n");
- input_filter<void> i_filter;
- // Test pipeline that contains only one filter
- for( unsigned i = 0; i<number_of_filter_types; i++) {
- tbb::filter_t<void, void> one_filter( filter_table[i], i_filter );
- ASSERT(filter_node_count==1, "some filter nodes left after previous iteration?");
- resetCounters();
- tbb::parallel_pipeline( n_tokens, one_filter );
- // no need to check counters
-#if __TBB_LAMBDAS_PRESENT
- tbb::atomic<int> counter;
- counter = max_counter;
- // Construct filter using lambda-syntax when parallel_pipeline() is being run;
- tbb::parallel_pipeline( n_tokens,
- tbb::make_filter<void, void>(filter_table[i], [&counter]( tbb::flow_control& control ) {
- if( counter-- == 0 )
- control.stop();
- }
- )
- );
-#endif
- }
- ASSERT(!filter_node_count, "filter_node objects leaked");
-}
-
-template<typename type1, typename type2>
-void run_function(const char *l1, const char *l2) {
- ASSERT(!filter_node_count, NULL);
- REMARK("Testing < %s, %s >", l1, l2 );
-#if __TBB_LAMBDAS_PRESENT
- REMARK( " ( + lambdas)");
-#endif
- REMARK("\n");
-
- const size_t number_of_filters = 3;
-
- input_filter<type1> i_filter;
- middle_filter<type1, type2> m_filter;
- output_filter<type2> o_filter;
-
- unsigned limit = 1;
- // Test pipeline that contains number_of_filters filters
- for( unsigned i=0; i<number_of_filters; ++i)
- limit *= number_of_filter_types;
- // Iterate over possible filter sequences
- for( unsigned numeral=0; numeral<limit; ++numeral ) {
- unsigned temp = numeral;
- tbb::filter::mode filter_type[number_of_filter_types];
- for( unsigned i=0; i<number_of_filters; ++i, temp/=number_of_filter_types )
- filter_type[i] = filter_table[temp%number_of_filter_types];
-
- tbb::filter_t<void, type1> filter1( filter_type[0], i_filter );
- tbb::filter_t<type1, type2> filter2( filter_type[1], m_filter );
- tbb::filter_t<type2, void> filter3( filter_type[2], o_filter );
- ASSERT(filter_node_count==3, "some filter nodes left after previous iteration?");
- resetCounters();
- // Create filters sequence when parallel_pipeline() is being run
- tbb::parallel_pipeline( n_tokens, filter1 & filter2 & filter3 );
- checkCounters();
-
- // Create filters sequence partially outside parallel_pipeline() and also when parallel_pipeline() is being run
- tbb::filter_t<void, type2> filter12;
- filter12 = filter1 & filter2;
- resetCounters();
- tbb::parallel_pipeline( n_tokens, filter12 & filter3 );
- checkCounters();
-
- tbb::filter_t<void, void> filter123 = filter12 & filter3;
- // Run pipeline twice with the same filter sequence
- for( unsigned i = 0; i<2; i++ ) {
- resetCounters();
- tbb::parallel_pipeline( n_tokens, filter123 );
- checkCounters();
- }
-
- // Now copy-construct another filter_t instance, and use it to run pipeline
- {
- tbb::filter_t<void, void> copy123( filter123 );
- resetCounters();
- tbb::parallel_pipeline( n_tokens, copy123 );
- checkCounters();
- }
-
- // Construct filters and create the sequence when parallel_pipeline() is being run
- resetCounters();
- tbb::parallel_pipeline( n_tokens,
- tbb::make_filter<void, type1>(filter_type[0], i_filter) &
- tbb::make_filter<type1, type2>(filter_type[1], m_filter) &
- tbb::make_filter<type2, void>(filter_type[2], o_filter) );
- checkCounters();
-
- // Construct filters, make a copy, destroy the original filters, and run with the copy
- int cnt = filter_node_count;
- {
- tbb::filter_t<void, void>* p123 = new tbb::filter_t<void,void> (
- tbb::make_filter<void, type1>(filter_type[0], i_filter) &
- tbb::make_filter<type1, type2>(filter_type[1], m_filter) &
- tbb::make_filter<type2, void>(filter_type[2], o_filter) );
- ASSERT(filter_node_count==cnt+5, "filter node accounting error?");
- tbb::filter_t<void, void> copy123( *p123 );
- delete p123;
- ASSERT(filter_node_count==cnt+5, "filter nodes deleted prematurely?");
- resetCounters();
- tbb::parallel_pipeline( n_tokens, copy123 );
- checkCounters();
- }
-
- // construct a filter with temporaries
- {
- tbb::filter_t<void, void> my_filter;
- fill_chain<type1,type2>( my_filter, filter_type, i_filter, m_filter, o_filter );
- resetCounters();
- tbb::parallel_pipeline( n_tokens, my_filter );
- checkCounters();
- }
- ASSERT(filter_node_count==cnt, "scope ended but filter nodes not deleted?");
-
-#if __TBB_LAMBDAS_PRESENT
- tbb::atomic<int> counter;
- counter = max_counter;
- // Construct filters using lambda-syntax and create the sequence when parallel_pipeline() is being run;
- resetCounters(); // only need the output_counter reset.
- tbb::parallel_pipeline( n_tokens,
- tbb::make_filter<void, type1>(filter_type[0], [&counter]( tbb::flow_control& control ) -> type1 {
- if( --counter < 0 )
- control.stop();
- return type1(); }
- ) &
- tbb::make_filter<type1, type2>(filter_type[1], []( type1 /*my_storage*/ ) -> type2 {
- return type2(); }
- ) &
- tbb::make_filter<type2, void>(filter_type[2], [] ( type2 ) -> void {
- output_counter++; }
- )
- );
- checkCounters();
-#endif
- }
- ASSERT(!filter_node_count, "filter_node objects leaked");
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain() {
- // Test with varying number of threads.
- for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) {
- // Initialize TBB task scheduler
- REMARK("\nTesting with nthread=%d\n", nthread);
- tbb::task_scheduler_init init(nthread);
-
- // Run test several times with different types
- run_function_spec();
- run_function<size_t,int>("size_t", "int");
- run_function<int,double>("int", "double");
- check_type_counter = 0;
- run_function<check_type,size_t>("check_type", "size_t");
- ASSERT(!check_type_counter, "Error in check_type creation/destruction");
- // check_type as the second type in the pipeline only works if check_type
- // is also the first type. The middle_filter specialization for <check_type, check_type>
- // changes the state of the check_type items, and this is checked in the output_filter
- // specialization.
- run_function<check_type, check_type>("check_type", "check_type");
- ASSERT(!check_type_counter, "Error in check_type creation/destruction");
- }
- return Harness::Done;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/parallel_reduce.h"
-#include "tbb/atomic.h"
-#include "harness_assert.h"
-
-using namespace std;
-
-static tbb::atomic<long> ForkCount;
-static tbb::atomic<long> FooBodyCount;
-
-//! Class with public interface that is exactly minimal requirements for Range concept
-class MinimalRange {
- size_t begin, end;
- friend class FooBody;
- explicit MinimalRange( size_t i ) : begin(0), end(i) {}
- friend void Flog( int nthread, bool inteference );
-public:
- MinimalRange( MinimalRange& r, tbb::split ) : end(r.end) {
- begin = r.end = (r.begin+r.end)/2;
- }
- bool is_divisible() const {return end-begin>=2;}
- bool empty() const {return begin==end;}
-};
-
-//! Class with public interface that is exactly minimal requirements for Body of a parallel_reduce
-class FooBody {
-private:
- FooBody( const FooBody& ); // Deny access
- void operator=( const FooBody& ); // Deny access
- friend void Flog( int nthread, bool interference );
- //! Parent that created this body via split operation. NULL if original body.
- FooBody* parent;
- //! Total number of index values processed by body and its children.
- size_t sum;
- //! Number of join operations done so far on this body and its children.
- long join_count;
- //! Range that has been processed so far by this body and its children.
- size_t begin, end;
- //! True if body has not yet been processed at least once by operator().
- bool is_new;
- //! 1 if body was created by split; 0 if original body.
- int forked;
- FooBody() {++FooBodyCount;}
-public:
- ~FooBody() {
- forked = 0xDEADBEEF;
- sum=0xDEADBEEF;
- join_count=0xDEADBEEF;
- --FooBodyCount;
- }
- FooBody( FooBody& other, tbb::split ) {
- ++FooBodyCount;
- ++ForkCount;
- sum = 0;
- parent = &other;
- join_count = 0;
- is_new = true;
- forked = 1;
- }
- void join( FooBody& s ) {
- ASSERT( s.forked==1, NULL );
- ASSERT( this!=&s, NULL );
- ASSERT( this==s.parent, NULL );
- ASSERT( end==s.begin, NULL );
- end = s.end;
- sum += s.sum;
- join_count += s.join_count + 1;
- s.forked = 2;
- }
- void operator()( const MinimalRange& r ) {
- for( size_t k=r.begin; k<r.end; ++k )
- ++sum;
- if( is_new ) {
- is_new = false;
- begin = r.begin;
- } else
- ASSERT( end==r.begin, NULL );
- end = r.end;
- }
-};
-
-#include <cstdio>
-#include "harness.h"
-#include "tbb/tick_count.h"
-
-void Flog( int nthread, bool interference=false ) {
- for (int mode = 0; mode < 4; mode++) {
- tbb::tick_count T0 = tbb::tick_count::now();
- long join_count = 0;
- tbb::affinity_partitioner ap;
- for( size_t i=0; i<=1000; ++i ) {
- FooBody f;
- f.sum = 0;
- f.parent = NULL;
- f.join_count = 0;
- f.is_new = true;
- f.forked = 0;
- f.begin = ~size_t(0);
- f.end = ~size_t(0);
- ASSERT( FooBodyCount==1, NULL );
- switch (mode) {
- case 0:
- tbb::parallel_reduce( MinimalRange(i), f );
- break;
- case 1:
- tbb::parallel_reduce( MinimalRange(i), f, tbb::simple_partitioner() );
- break;
- case 2:
- tbb::parallel_reduce( MinimalRange(i), f, tbb::auto_partitioner() );
- break;
- case 3:
- tbb::parallel_reduce( MinimalRange(i), f, ap );
- break;
- }
- join_count += f.join_count;
- ASSERT( FooBodyCount==1, NULL );
- ASSERT( f.sum==i, NULL );
- ASSERT( f.begin==(i==0 ? ~size_t(0) : 0), NULL );
- ASSERT( f.end==(i==0 ? ~size_t(0) : i), NULL );
- }
- tbb::tick_count T1 = tbb::tick_count::now();
- REMARK("time=%g join_count=%ld ForkCount=%ld nthread=%d%s\n",
- (T1-T0).seconds(),join_count,long(ForkCount), nthread, interference ? " with interference)":"");
- }
-}
-
-class DeepThief: public tbb::task {
- /*override*/tbb::task* execute() {
- if( !is_stolen_task() )
- spawn(*child);
- wait_for_all();
- return NULL;
- }
- task* child;
- friend void FlogWithInterference(int);
-public:
- DeepThief() : child() {}
-};
-
-//! Test for problem in TBB 2.1 parallel_reduce where middle of a range is stolen.
-/** Warning: this test is a somewhat abusive use of TBB somewhat because
- it requires two or more threads to avoid deadlock. */
-void FlogWithInterference( int nthread ) {
- ASSERT( nthread>=2, "requires too or more threads" );
-
- // Build linear chain of tasks.
- // The purpose is to drive up "task depth" in TBB 2.1.
- // An alternative would be to use add_to_depth, but that method is deprecated in TBB 2.2,
- // and this way we generalize to catching problems with implicit depth calculations.
- tbb::task* root = new( tbb::task::allocate_root() ) tbb::empty_task;
- root->set_ref_count(2);
- tbb::task* t = root;
- for( int i=0; i<3; ++i ) {
- t = new( t->allocate_child() ) tbb::empty_task;
- t->set_ref_count(1);
- }
-
- // Append a DeepThief to the chain.
- DeepThief* deep_thief = new( t->allocate_child() ) DeepThief;
- deep_thief->set_ref_count(2);
-
- // Append a leaf to the chain.
- tbb::task* leaf = new( deep_thief->allocate_child() ) tbb::empty_task;
- deep_thief->child = leaf;
-
- root->spawn(*deep_thief);
-
- Flog(nthread,true);
-
- if( root->ref_count()==2 ) {
- // Spawn leaf, which when it finishes, cause the DeepThief and rest of the chain to finish.
- root->spawn( *leaf );
- }
- // Wait for all tasks in the chain from root to leaf to finish.
- root->wait_for_all();
- root->destroy( *root );
-}
-
-#include "tbb/blocked_range.h"
-
-#if _MSC_VER
- typedef tbb::internal::uint64_t ValueType;
-#else
- typedef uint64_t ValueType;
-#endif
-
-struct Sum {
- template<typename T>
- T operator() ( const T& v1, const T& v2 ) const {
- return v1 + v2;
- }
-};
-
-struct Accumulator {
- ValueType operator() ( const tbb::blocked_range<ValueType*>& r, ValueType value ) const {
- for ( ValueType* pv = r.begin(); pv != r.end(); ++pv )
- value += *pv;
- return value;
- }
-};
-
-void ParallelSum () {
- const ValueType I = 0,
- N = 1000000,
- R = N * (N + 1) / 2;
- ValueType *array = new ValueType[N + 1];
- for ( ValueType i = 0; i < N; ++i )
- array[i] = i + 1;
- tbb::blocked_range<ValueType*> range(array, array + N);
- ValueType r1 = tbb::parallel_reduce( range, I, Accumulator(), Sum() );
- ASSERT( r1 == R, NULL );
-#if __TBB_LAMBDAS_PRESENT
- ValueType r2 = tbb::parallel_reduce( range, I,
- [](const tbb::blocked_range<ValueType*>& r, ValueType value) -> ValueType {
- for ( ValueType* pv = r.begin(); pv != r.end(); ++pv )
- value += *pv;
- return value;
- },
- Sum()
- );
- ASSERT( r2 == R, NULL );
-#endif /* LAMBDAS */
- delete array;
-}
-
-#include "tbb/task_scheduler_init.h"
-#include "harness_cpu.h"
-
-int TestMain () {
- if( MinThread<0 ) {
- REPORT("Usage: nthread must be positive\n");
- exit(1);
- }
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init( p );
- Flog(p);
- if( p>=2 )
- FlogWithInterference(p);
- ParallelSum();
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/parallel_scan.h"
-#include "tbb/blocked_range.h"
-#include "harness_assert.h"
-
-typedef tbb::blocked_range<long> Range;
-
-static volatile bool ScanIsRunning = false;
-
-//! Sum of 0..i with wrap around on overflow.
-inline int TriangularSum( int i ) {
- return i&1 ? ((i>>1)+1)*i : (i>>1)*(i+1);
-}
-
-//! Verify that sum is sum of integers in closed interval [start_index..finish_index].
-/** line should be the source line of the caller */
-static void VerifySum( long start_index, long finish_index, int sum, int line );
-
-const int MAXN = 2000;
-
-enum AddendFlag {
- UNUSED=0,
- USED_NONFINAL=1,
- USED_FINAL=2
-};
-
-//! Array recording how each addend was used.
-/** 'unsigned char' instead of AddendFlag for sake of compactness. */
-static unsigned char AddendHistory[MAXN];
-
-//! Set to 1 for debugging output
-#define PRINT_DEBUG 0
-
-#include "tbb/atomic.h"
-#if PRINT_DEBUG
-#include <stdio.h>
-tbb::atomic<long> NextBodyId;
-#endif /* PRINT_DEBUG */
-
-struct BodyId {
-#if PRINT_DEBUG
- const int id;
- BodyId() : id(NextBodyId++) {}
-#endif /* PRINT_DEBUG */
-};
-
-tbb::atomic<long> NumberOfLiveAccumulator;
-
-static void Snooze( bool scan_should_be_running ) {
- ASSERT( ScanIsRunning==scan_should_be_running, NULL );
-}
-
-template<typename T>
-class Accumulator: BodyId {
- T my_total;
- const T* my_array;
- T* my_sum;
- Range my_range;
- //! Equals this while object is fully constructed, NULL otherwise.
- /** Used to detect premature destruction and accidental bitwise copy. */
- Accumulator* self;
- Accumulator( const T array[], T sum[] ) :
- my_total(), my_array(array), my_sum(sum), my_range(-1,-1,1)
- {
- ++NumberOfLiveAccumulator;
- // Set self as last action of constructor, to indicate that object is fully constructed.
- self = this;
- }
- friend void TestAccumulator( int mode, int nthread );
-public:
-#if PRINT_DEBUG
- void print() const {
- REPORT("%d [%ld..%ld)\n", id,my_range.begin(),my_range.end() );
- }
-#endif /* PRINT_DEBUG */
- ~Accumulator() {
-#if PRINT_DEBUG
- REPORT("%d [%ld..%ld) destroyed\n",id,my_range.begin(),my_range.end() );
-#endif /* PRINT_DEBUG */
- // Clear self as first action of destructor, to indicate that object is not fully constructed.
- self = 0;
- --NumberOfLiveAccumulator;
- }
- Accumulator( Accumulator& a, tbb::split ) :
- my_total(0), my_array(a.my_array), my_sum(a.my_sum), my_range(-1,-1,1)
- {
- ++NumberOfLiveAccumulator;
-#if PRINT_DEBUG
- REPORT("%d forked from %d\n",id,a.id);
-#endif /* PRINT_DEBUG */
- Snooze(true);
- // Set self as last action of constructor, to indicate that object is fully constructed.
- self = this;
- }
- template<typename Tag>
- void operator()( const Range& r, Tag /*tag*/ ) {
- Snooze(true);
-#if PRINT_DEBUG
- if( my_range.empty() )
- REPORT("%d computing %s [%ld..%ld)\n",id,Tag::is_final_scan()?"final":"lookahead",r.begin(),r.end() );
- else
- REPORT("%d computing %s [%ld..%ld) [%ld..%ld)\n",id,Tag::is_final_scan()?"final":"lookahead",my_range.begin(),my_range.end(),r.begin(),r.end());
-#endif /* PRINT_DEBUG */
- ASSERT( !Tag::is_final_scan() || (my_range.begin()==0 && my_range.end()==r.begin()) || (my_range.empty() && r.begin()==0), NULL );
- for( long i=r.begin(); i<r.end(); ++i ) {
- my_total += my_array[i];
- if( Tag::is_final_scan() ) {
- ASSERT( AddendHistory[i]<USED_FINAL, "addend used 'finally' twice?" );
- AddendHistory[i] |= USED_FINAL;
- my_sum[i] = my_total;
- VerifySum( 0L, i, int(my_sum[i]), __LINE__ );
- } else {
- ASSERT( AddendHistory[i]==UNUSED, "addend used too many times" );
- AddendHistory[i] |= USED_NONFINAL;
- }
- }
- if( my_range.empty() )
- my_range = r;
- else
- my_range = Range(my_range.begin(), r.end(), 1 );
- Snooze(true);
- ASSERT( self==this, "this Accumulator corrupted or prematurely destroyed" );
- }
- void reverse_join( const Accumulator& left ) {
-#if PRINT_DEBUG
- REPORT("reverse join %d [%ld..%ld) %d [%ld..%ld)\n",
- left.id,left.my_range.begin(),left.my_range.end(),
- id,my_range.begin(),my_range.end());
-#endif /* PRINT_DEBUG */
- Snooze(true);
- ASSERT( ScanIsRunning, NULL );
- ASSERT( left.my_range.end()==my_range.begin(), NULL );
- my_total += left.my_total;
- my_range = Range( left.my_range.begin(), my_range.end(), 1 );
- ASSERT( ScanIsRunning, NULL );
- Snooze(true);
- ASSERT( ScanIsRunning, NULL );
- ASSERT( self==this, NULL );
- ASSERT( left.self==&left, NULL );
- }
- void assign( const Accumulator& other ) {
- my_total = other.my_total;
- my_range = other.my_range;
- ASSERT( self==this, NULL );
- ASSERT( other.self==&other, "other Accumulator corrupted or prematurely destroyed" );
- }
-};
-
-#include "tbb/tick_count.h"
-#include "harness.h"
-
-static void VerifySum( long start_index, long finish_index, int sum, int line ) {
- int expected = TriangularSum( finish_index ) - TriangularSum( start_index );
- if( expected!=sum ) {
- REPORT( "line %d: sum[%ld..%ld] should be = %d, but was computed as %d\n",
- line, start_index, finish_index, expected, sum );
- abort();
- }
-}
-
-void TestAccumulator( int mode, int nthread ) {
- typedef int T;
- T* addend = new T[MAXN];
- T* sum = new T[MAXN];
- for( long n=0; n<=MAXN; ++n ) {
- for( long i=0; i<MAXN; ++i ) {
- addend[i] = -1;
- sum[i] = -2;
- AddendHistory[i] = UNUSED;
- }
- for( long i=0; i<n; ++i )
- addend[i] = i;
- Accumulator<T> acc( addend, sum );
- tbb::tick_count t0 = tbb::tick_count::now();
-#if PRINT_DEBUG
- REPORT("--------- mode=%d range=[0..%ld)\n",mode,n);
-#endif /* PRINT_DEBUG */
- ScanIsRunning = true;
-
- switch (mode) {
- case 0:
- tbb::parallel_scan( Range( 0, n, 1 ), acc );
- break;
- case 1:
- tbb::parallel_scan( Range( 0, n, 1 ), acc, tbb::simple_partitioner() );
- break;
- case 2:
- tbb::parallel_scan( Range( 0, n, 1 ), acc, tbb::auto_partitioner() );
- break;
- }
-
- ScanIsRunning = false;
-#if PRINT_DEBUG
- REPORT("=========\n");
-#endif /* PRINT_DEBUG */
- Snooze(false);
- tbb::tick_count t1 = tbb::tick_count::now();
- long used_once_count = 0;
- for( long i=0; i<n; ++i )
- if( !(AddendHistory[i]&USED_FINAL) ) {
- REPORT("failed to use addend[%ld] %s\n",i,AddendHistory[i]&USED_NONFINAL?"(but used nonfinal)":"");
- }
- for( long i=0; i<n; ++i ) {
- VerifySum( 0, i, sum[i], __LINE__ );
- used_once_count += AddendHistory[i]==USED_FINAL;
- }
- if( n )
- ASSERT( acc.my_total==sum[n-1], NULL );
- else
- ASSERT( acc.my_total==0, NULL );
- REMARK("time [n=%ld] = %g\tused_once%% = %g\tnthread=%d\n",n,(t1-t0).seconds(), n==0 ? 0 : 100.0*used_once_count/n,nthread);
- }
- delete[] addend;
- delete[] sum;
-}
-
-static void TestScanTags() {
- ASSERT( tbb::pre_scan_tag::is_final_scan()==false, NULL );
- ASSERT( tbb::final_scan_tag::is_final_scan()==true, NULL );
-}
-
-#include "tbb/task_scheduler_init.h"
-#include "harness_cpu.h"
-
-int TestMain () {
- TestScanTags();
- for( int p=MinThread; p<=MaxThread; ++p ) {
- for (int mode = 0; mode < 3; mode++) {
- tbb::task_scheduler_init init(p);
- NumberOfLiveAccumulator = 0;
- TestAccumulator(mode, p);
-
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
-
- // Checking has to be done late, because when parallel_scan makes copies of
- // the user's "Body", the copies might be destroyed slightly after parallel_scan
- // returns.
- ASSERT( NumberOfLiveAccumulator==0, NULL );
- }
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/parallel_sort.h"
-#include "tbb/task_scheduler_init.h"
-#include "tbb/concurrent_vector.h"
-#include "harness.h"
-#include <math.h>
-#include <exception>
-
-#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 <algorithm>
-#include <iterator>
-#include <functional>
-#include <string>
-#include <cstring>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-/** Has tightly controlled interface so that we can verify
- that parallel_sort uses only the required interface. */
-class Minimal {
- int val;
-public:
- Minimal() {}
- void set_val(int i) { val = i; }
- static bool CompareWith (const Minimal &a, const Minimal &b) {
- return (a.val < b.val);
- }
- static bool AreEqual( Minimal &a, Minimal &b) {
- return a.val == b.val;
- }
-};
-
-//! Defines a comparison function object for Minimal
-class MinimalCompare {
-public:
- bool operator() (const Minimal &a, const Minimal &b) const {
- return Minimal::CompareWith(a,b);
- }
-};
-
-//! The default validate; but it uses operator== which is not required
-template<typename RandomAccessIterator>
-bool Validate(RandomAccessIterator a, RandomAccessIterator b, size_t n) {
- for (size_t i = 0; i < n; i++) {
- ASSERT( a[i] == b[i], NULL );
- }
- return true;
-}
-
-//! A Validate specialized to string for debugging-only
-template<>
-bool Validate<std::string *>(std::string * a, std::string * b, size_t n) {
- for (size_t i = 0; i < n; i++) {
- if ( Verbose && a[i] != b[i]) {
- for (size_t j = 0; j < n; j++) {
- REPORT("a[%llu] == %s and b[%llu] == %s\n", static_cast<unsigned long long>(j), a[j].c_str(), static_cast<unsigned long long>(j), b[j].c_str());
- }
- }
- ASSERT( a[i] == b[i], NULL );
- }
- return true;
-}
-
-//! A Validate specialized to Minimal since it does not define an operator==
-template<>
-bool Validate<Minimal *>(Minimal *a, Minimal *b, size_t n) {
- for (size_t i = 0; i < n; i++) {
- ASSERT( Minimal::AreEqual(a[i],b[i]), NULL );
- }
- return true;
-}
-
-//! A Validate specialized to concurrent_vector<Minimal> since it does not define an operator==
-template<>
-bool Validate<tbb::concurrent_vector<Minimal>::iterator>(tbb::concurrent_vector<Minimal>::iterator a,
- tbb::concurrent_vector<Minimal>::iterator b, size_t n) {
- for (size_t i = 0; i < n; i++) {
- ASSERT( Minimal::AreEqual(a[i],b[i]), NULL );
- }
- return true;
-}
-
-//! used in Verbose mode for identifying which data set is being used
-static std::string test_type;
-
-//! The default initialization routine.
-/*! This routine assumes that you can assign to the elements from a float.
- It assumes that iter and sorted_list have already been allocated. It fills
- them according to the current data set (tracked by a local static variable).
- Returns true if a valid test has been setup, or false if there is no test to
- perform.
-*/
-
-template < typename RandomAccessIterator, typename Compare >
-bool init_iter(RandomAccessIterator iter, RandomAccessIterator sorted_list, size_t n, const Compare &compare, bool reset) {
- static char test_case = 0;
- const char num_cases = 3;
-
- if (reset) test_case = 0;
-
- if (test_case < num_cases) {
- // switch on the current test case, filling the iter and sorted_list appropriately
- switch(test_case) {
- case 0:
- /* use sin to generate the values */
- test_type = "sin";
- for (size_t i = 0; i < n; i++)
- iter[i] = sorted_list[i] = static_cast<typename std::iterator_traits< RandomAccessIterator >::value_type>(sin(float(i)));
- break;
- case 1:
- /* presorted list */
- test_type = "pre-sorted";
- for (size_t i = 0; i < n; i++)
- iter[i] = sorted_list[i] = static_cast<typename std::iterator_traits< RandomAccessIterator >::value_type>(i);
- break;
- case 2:
- /* reverse-sorted list */
- test_type = "reverse-sorted";
- for (size_t i = 0; i < n; i++)
- iter[i] = sorted_list[i] = static_cast<typename std::iterator_traits< RandomAccessIterator >::value_type>(n - i);
- break;
- }
-
- // pre-sort sorted_list for later validity testing
- std::sort(sorted_list, sorted_list + n, compare);
- test_case++;
- return true;
- }
- return false;
-}
-
-template < typename T, typename Compare >
-bool init_iter(T * iter, T * sorted_list, size_t n, const Compare &compare, bool reset) {
- static char test_case = 0;
- const char num_cases = 3;
-
- if (reset) test_case = 0;
-
- if (test_case < num_cases) {
- // switch on the current test case, filling the iter and sorted_list appropriately
- switch(test_case) {
- case 0:
- /* use sin to generate the values */
- test_type = "sin";
- for (size_t i = 0; i < n; i++)
- iter[i] = sorted_list[i] = T(sin(float(i)));
- break;
- case 1:
- /* presorted list */
- test_type = "pre-sorted";
- for (size_t i = 0; i < n; i++)
- iter[i] = sorted_list[i] = T(i);
- break;
- case 2:
- /* reverse-sorted list */
- test_type = "reverse-sorted";
- for (size_t i = 0; i < n; i++)
- iter[i] = sorted_list[i] = T(n - i);
- break;
- }
-
- // pre-sort sorted_list for later validity testing
- std::sort(sorted_list, sorted_list + n, compare);
- test_case++;
- return true;
- }
- return false;
-}
-
-
-//! The initialization routine specialized to the class Minimal
-/*! Minimal cannot have floats assigned to it. This function uses the set_val method
-*/
-
-template < >
-bool init_iter(Minimal* iter, Minimal * sorted_list, size_t n, const MinimalCompare &compare, bool reset) {
- static char test_case = 0;
- const char num_cases = 3;
-
- if (reset) test_case = 0;
-
- if (test_case < num_cases) {
- switch(test_case) {
- case 0:
- /* use sin to generate the values */
- test_type = "sin";
- for (size_t i = 0; i < n; i++) {
- iter[i].set_val( int( sin( float(i) ) * 1000.f) );
- sorted_list[i].set_val( int ( sin( float(i) ) * 1000.f) );
- }
- break;
- case 1:
- /* presorted list */
- test_type = "pre-sorted";
- for (size_t i = 0; i < n; i++) {
- iter[i].set_val( int(i) );
- sorted_list[i].set_val( int(i) );
- }
- break;
- case 2:
- /* reverse-sorted list */
- test_type = "reverse-sorted";
- for (size_t i = 0; i < n; i++) {
- iter[i].set_val( int(n-i) );
- sorted_list[i].set_val( int(n-i) );
- }
- break;
- }
- std::sort(sorted_list, sorted_list + n, compare);
- test_case++;
- return true;
- }
- return false;
-}
-
-//! The initialization routine specialized to the class concurrent_vector<Minimal>
-/*! Minimal cannot have floats assigned to it. This function uses the set_val method
-*/
-
-template < >
-bool init_iter(tbb::concurrent_vector<Minimal>::iterator iter, tbb::concurrent_vector<Minimal>::iterator sorted_list,
- size_t n, const MinimalCompare &compare, bool reset) {
- static char test_case = 0;
- const char num_cases = 3;
-
- if (reset) test_case = 0;
-
- if (test_case < num_cases) {
- switch(test_case) {
- case 0:
- /* use sin to generate the values */
- test_type = "sin";
- for (size_t i = 0; i < n; i++) {
- iter[i].set_val( int( sin( float(i) ) * 1000.f) );
- sorted_list[i].set_val( int ( sin( float(i) ) * 1000.f) );
- }
- break;
- case 1:
- /* presorted list */
- test_type = "pre-sorted";
- for (size_t i = 0; i < n; i++) {
- iter[i].set_val( int(i) );
- sorted_list[i].set_val( int(i) );
- }
- break;
- case 2:
- /* reverse-sorted list */
- test_type = "reverse-sorted";
- for (size_t i = 0; i < n; i++) {
- iter[i].set_val( int(n-i) );
- sorted_list[i].set_val( int(n-i) );
- }
- break;
- }
- std::sort(sorted_list, sorted_list + n, compare);
- test_case++;
- return true;
- }
- return false;
-}
-
-//! The initialization routine specialized to the class string
-/*! strings are created from floats.
-*/
-
-template<>
-bool init_iter(std::string *iter, std::string *sorted_list, size_t n, const std::less<std::string> &compare, bool reset) {
- static char test_case = 0;
- const char num_cases = 1;
-
- if (reset) test_case = 0;
-
- if (test_case < num_cases) {
- switch(test_case) {
- case 0:
- /* use sin to generate the values */
- test_type = "sin";
- for (size_t i = 0; i < n; i++) {
- char buffer[20];
-#if __STDC_SECURE_LIB__>=200411 && !__MINGW64__
- sprintf_s(buffer, sizeof(buffer), "%f", float(sin(float(i))));
-#else
- sprintf(buffer, "%f", float(sin(float(i))));
-#endif /* _MSC_VER>=1400 */
- sorted_list[i] = iter[i] = std::string(buffer);
- }
- break;
- }
- std::sort(sorted_list, sorted_list + n, compare);
- test_case++;
- return true;
- }
- return false;
-}
-
-//! The current number of threads in use (for Verbose only)
-static size_t current_p;
-
-//! The current data type being sorted (for Verbose only)
-static std::string current_type;
-
-//! The default test routine.
-/*! Tests all data set sizes from 0 to N, all grainsizes from 0 to G=10, and selects from
- all possible interfaces to parallel_sort depending on whether a scratch space and
- compare have been provided.
-*/
-template<typename RandomAccessIterator, typename Compare>
-bool parallel_sortTest(size_t n, RandomAccessIterator iter, RandomAccessIterator sorted_list, const Compare *comp) {
- bool passed = true;
-
- Compare local_comp;
-
- init_iter(iter, sorted_list, n, local_comp, true);
- do {
- REMARK("%s %s p=%llu n=%llu :",current_type.c_str(), test_type.c_str(),
- static_cast<unsigned long long>(current_p), static_cast<unsigned long long>(n));
- if (comp != NULL) {
- tbb::parallel_sort(iter, iter + n, local_comp );
- } else {
- tbb::parallel_sort(iter, iter + n );
- }
- if (!Validate(iter, sorted_list, n))
- passed = false;
- REMARK("passed\n");
- } while (init_iter(iter, sorted_list, n, local_comp, false));
- return passed;
-}
-
-//! The test routine specialize to Minimal, since it does not have a less defined for it
-template<>
-bool parallel_sortTest(size_t n, Minimal * iter, Minimal * sorted_list, const MinimalCompare *compare) {
- bool passed = true;
-
- if (compare == NULL) return passed;
-
- init_iter(iter, sorted_list, n, *compare, true);
- do {
- REMARK("%s %s p=%llu n=%llu :",current_type.c_str(), test_type.c_str(),
- static_cast<unsigned long long>(current_p), static_cast<unsigned long long>(n));
-
- tbb::parallel_sort(iter, iter + n, *compare );
-
- if (!Validate(iter, sorted_list, n))
- passed = false;
- REMARK("passed\n");
- } while (init_iter(iter, sorted_list, n, *compare, false));
- return passed;
-}
-
-//! The test routine specialize to concurrent_vector of Minimal, since it does not have a less defined for it
-template<>
-bool parallel_sortTest(size_t n, tbb::concurrent_vector<Minimal>::iterator iter,
- tbb::concurrent_vector<Minimal>::iterator sorted_list, const MinimalCompare *compare) {
- bool passed = true;
-
- if (compare == NULL) return passed;
-
- init_iter(iter, sorted_list, n, *compare, true);
- do {
- REMARK("%s %s p=%llu n=%llu :",current_type.c_str(), test_type.c_str(),
- static_cast<unsigned long long>(current_p), static_cast<unsigned long long>(n));
-
- tbb::parallel_sort(iter, iter + n, *compare );
-
- if (!Validate(iter, sorted_list, n))
- passed = false;
- REMARK("passed\n");
- } while (init_iter(iter, sorted_list, n, *compare, false));
- return passed;
-}
-
-//! The main driver for the tests.
-/*! Minimal, float and string types are used. All interfaces to parallel_sort that are usable
- by each type are tested.
-*/
-void Flog() {
- // For each type create:
- // the list to be sorted by parallel_sort (array)
- // the list to be sort by STL sort (array_2)
- // and a less function object
-
- const size_t N = 50000;
-
- Minimal *minimal_array = new Minimal[N];
- Minimal *minimal_array_2 = new Minimal[N];
- MinimalCompare minimal_less;
-
- float *float_array = new float[N];
- float *float_array_2 = new float[N];
- std::less<float> float_less;
-
- tbb::concurrent_vector<float> float_cv1;
- tbb::concurrent_vector<float> float_cv2;
- float_cv1.grow_to_at_least(N);
- float_cv2.grow_to_at_least(N);
-
- std::string *string_array = new std::string[N];
- std::string *string_array_2 = new std::string[N];
- std::less<std::string> string_less;
-
- tbb::concurrent_vector<Minimal> minimal_cv1;
- tbb::concurrent_vector<Minimal> minimal_cv2;
- minimal_cv1.grow_to_at_least(N);
- minimal_cv2.grow_to_at_least(N);
-
-
- // run the appropriate tests for each type
-
- current_type = "Minimal(less)";
- parallel_sortTest(0, minimal_array, minimal_array_2, &minimal_less);
- parallel_sortTest(1, minimal_array, minimal_array_2, &minimal_less);
- parallel_sortTest(10, minimal_array, minimal_array_2, &minimal_less);
- parallel_sortTest(9999, minimal_array, minimal_array_2, &minimal_less);
- parallel_sortTest(50000, minimal_array, minimal_array_2, &minimal_less);
-
- current_type = "float (no less)";
- parallel_sortTest(0, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
- parallel_sortTest(1, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
- parallel_sortTest(10, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
- parallel_sortTest(9999, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
- parallel_sortTest(50000, float_array, float_array_2, static_cast<std::less<float> *>(NULL));
-
- current_type = "float (less)";
- parallel_sortTest(0, float_array, float_array_2, &float_less);
- parallel_sortTest(1, float_array, float_array_2, &float_less);
- parallel_sortTest(10, float_array, float_array_2, &float_less);
- parallel_sortTest(9999, float_array, float_array_2, &float_less);
- parallel_sortTest(50000, float_array, float_array_2, &float_less);
-
- current_type = "concurrent_vector<float> (no less)";
- parallel_sortTest(0, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
- parallel_sortTest(1, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
- parallel_sortTest(10, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
- parallel_sortTest(9999, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
- parallel_sortTest(50000, float_cv1.begin(), float_cv2.begin(), static_cast<std::less<float> *>(NULL));
-
- current_type = "concurrent_vector<float> (less)";
- parallel_sortTest(0, float_cv1.begin(), float_cv2.begin(), &float_less);
- parallel_sortTest(1, float_cv1.begin(), float_cv2.begin(), &float_less);
- parallel_sortTest(10, float_cv1.begin(), float_cv2.begin(), &float_less);
- parallel_sortTest(9999, float_cv1.begin(), float_cv2.begin(), &float_less);
- parallel_sortTest(50000, float_cv1.begin(), float_cv2.begin(), &float_less);
-
- current_type = "string (no less)";
- parallel_sortTest(0, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
- parallel_sortTest(1, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
- parallel_sortTest(10, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
- parallel_sortTest(9999, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
- parallel_sortTest(50000, string_array, string_array_2, static_cast<std::less<std::string> *>(NULL));
-
- current_type = "string (less)";
- parallel_sortTest(0, string_array, string_array_2, &string_less);
- parallel_sortTest(1, string_array, string_array_2, &string_less);
- parallel_sortTest(10, string_array, string_array_2, &string_less);
- parallel_sortTest(9999, string_array, string_array_2, &string_less);
- parallel_sortTest(50000, string_array, string_array_2, &string_less);
-
- current_type = "concurrent_vector<Minimal> (less)";
- parallel_sortTest(0, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
- parallel_sortTest(1, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
- parallel_sortTest(10, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
- parallel_sortTest(9999, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
- parallel_sortTest(50000, minimal_cv1.begin(), minimal_cv2.begin(), &minimal_less);
-
- delete [] minimal_array;
- delete [] minimal_array_2;
-
- delete [] float_array;
- delete [] float_array_2;
-
- delete [] string_array;
- delete [] string_array_2;
-}
-
-#include <cstdio>
-#include "harness_cpu.h"
-
-int TestMain () {
- if( MinThread<1 ) {
- REPORT("Usage: number of threads must be positive\n");
- exit(1);
- }
- for( int p=MinThread; p<=MaxThread; ++p ) {
- if( p>0 ) {
- tbb::task_scheduler_init init( p );
- current_p = p;
- Flog();
-
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
- }
- }
- return Harness::Done;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/parallel_while.h"
-#include "harness.h"
-
-const int N = 200;
-
-typedef int Element;
-
-//! Representation of an array index with only those signatures required by parallel_while.
-class MinimalArgumentType {
- void operator=( const MinimalArgumentType& );
- long my_value;
- enum {
- DEAD=0xDEAD,
- LIVE=0x2718,
- INITIALIZED=0x3141
- } my_state;
-public:
- ~MinimalArgumentType() {
- ASSERT( my_state==LIVE||my_state==INITIALIZED, NULL );
- my_state = DEAD;
- }
- MinimalArgumentType() {
- my_state = LIVE;
- }
- void set_value( long i ) {
- ASSERT( my_state==LIVE||my_state==INITIALIZED, NULL );
- my_value = i;
- my_state = INITIALIZED;
- }
- long get_value() const {
- ASSERT( my_state==INITIALIZED, NULL );
- return my_value;
- }
-};
-
-class IntegerStream {
- long my_limit;
- long my_index;
-public:
- IntegerStream( long n ) : my_limit(n), my_index(0) {}
- bool pop_if_present( MinimalArgumentType& v ) {
- if( my_index>=my_limit )
- return false;
- v.set_value( my_index );
- my_index+=2;
- return true;
- }
-};
-
-class MatrixMultiplyBody: NoAssign {
- Element (*a)[N];
- Element (*b)[N];
- Element (*c)[N];
- const int n;
- tbb::parallel_while<MatrixMultiplyBody>& my_while;
-public:
- typedef MinimalArgumentType argument_type;
- void operator()( argument_type i_arg ) const {
- long i = i_arg.get_value();
- if( (i&1)==0 && i+1<N ) {
- MinimalArgumentType value;
- value.set_value(i+1);
- my_while.add( value );
- }
- for( int j=0; j<n; ++j )
- c[i][j] = 0;
- for( int k=0; k<n; ++k ) {
- Element aik = a[i][k];
- for( int j=0; j<n; ++j )
- c[i][j] += aik*b[k][j];
- }
- }
- MatrixMultiplyBody( tbb::parallel_while<MatrixMultiplyBody>& w, Element c_[N][N], Element a_[N][N], Element b_[N][N], int n_ ) :
- a(a_), b(b_), c(c_), n(n_), my_while(w)
- {}
-};
-
-void WhileMatrixMultiply( Element c[N][N], Element a[N][N], Element b[N][N], int n ) {
- IntegerStream stream( N );
- tbb::parallel_while<MatrixMultiplyBody> w;
- MatrixMultiplyBody body(w,c,a,b,n);
- w.run( stream, body );
-}
-
-#include "tbb/tick_count.h"
-#include <cstdlib>
-#include <cstdio>
-using namespace std;
-
-static long Iterations = 5;
-
-static void SerialMatrixMultiply( Element c[N][N], Element a[N][N], Element b[N][N], int n ) {
- for( int i=0; i<n; ++i ) {
- for( int j=0; j<n; ++j )
- c[i][j] = 0;
- for( int k=0; k<n; ++k ) {
- Element aik = a[i][k];
- for( int j=0; j<n; ++j )
- c[i][j] += aik*b[k][j];
- }
- }
-}
-
-static void InitializeMatrix( Element x[N][N], int n, int salt ) {
- for( int i=0; i<n; ++i )
- for( int j=0; j<n; ++j )
- x[i][j] = (i*n+j)^salt;
-}
-
-static Element A[N][N], B[N][N], C[N][N], D[N][N];
-
-static void Run( int nthread, int n ) {
- /* Initialize matrices */
- InitializeMatrix(A,n,5);
- InitializeMatrix(B,n,10);
- InitializeMatrix(C,n,0);
- InitializeMatrix(D,n,15);
-
- tbb::tick_count t0 = tbb::tick_count::now();
- for( long i=0; i<Iterations; ++i ) {
- WhileMatrixMultiply( C, A, B, n );
- }
- tbb::tick_count t1 = tbb::tick_count::now();
- SerialMatrixMultiply( D, A, B, n );
-
- // Check result
- for( int i=0; i<n; ++i )
- for( int j=0; j<n; ++j )
- ASSERT( C[i][j]==D[i][j], NULL );
- REMARK("time=%g\tnthread=%d\tn=%d\n",(t1-t0).seconds(),nthread,n);
-}
-
-#include "tbb/task_scheduler_init.h"
-#include "harness_cpu.h"
-
-int TestMain () {
- if( MinThread<1 ) {
- REPORT("number of threads must be positive\n");
- exit(1);
- }
- for( int p=MinThread; p<=MaxThread; ++p ) {
- tbb::task_scheduler_init init( p );
- for( int n=N/4; n<=N; n+=N/4 )
- Run(p,n);
-
- // Test that all workers sleep when no work
- TestCPUUserTime(p);
- }
- return Harness::Done;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/tbb_stddef.h"
-#include "tbb/pipeline.h"
-#include "tbb/spin_mutex.h"
-#include "tbb/atomic.h"
-#include <cstdlib>
-#include <cstdio>
-#include "harness.h"
-
-// In the test, variables related to token counting are declared
-// as unsigned long to match definition of tbb::internal::Token.
-
-struct Buffer {
- //! Indicates that the buffer is not used.
- static const unsigned long unused = ~0ul;
- unsigned long id;
- //! True if Buffer is in use.
- bool is_busy;
- unsigned long sequence_number;
- Buffer() : id(unused), is_busy(false), sequence_number(unused) {}
-};
-
-class waiting_probe {
- size_t check_counter;
-public:
- waiting_probe() : check_counter(0) {}
- bool required( ) {
- ++check_counter;
- return !((check_counter+1)&size_t(0x7FFF));
- }
- void probe( ); // defined below
-};
-
-static const unsigned MaxStreamSize = 8000;
-static const unsigned MaxStreamItemsPerThread = 1000;
-//! Maximum number of filters allowed
-static const unsigned MaxFilters = 5;
-static unsigned StreamSize;
-static const unsigned MaxBuffer = 8;
-static bool Done[MaxFilters][MaxStreamSize];
-static waiting_probe WaitTest;
-static unsigned out_of_order_count;
-
-#include "harness_concurrency_tracker.h"
-
-class BaseFilter: public tbb::filter {
- bool* const my_done;
- const bool my_is_last;
- bool my_is_running;
-public:
- tbb::atomic<tbb::internal::Token> current_token;
- BaseFilter( tbb::filter::mode type, bool done[], bool is_last ) :
- filter(type),
- my_done(done),
- my_is_last(is_last),
- my_is_running(false),
- current_token()
- {}
- virtual Buffer* get_buffer( void* item ) {
- current_token++;
- return static_cast<Buffer*>(item);
- }
- /*override*/void* operator()( void* item ) {
- Harness::ConcurrencyTracker ct;
- if( is_serial() )
- ASSERT( !my_is_running, "premature entry to serial stage" );
- my_is_running = true;
- Buffer* b = get_buffer(item);
- if( b ) {
- if( is_ordered() ) {
- if( b->sequence_number == Buffer::unused )
- b->sequence_number = current_token-1;
- else
- ASSERT( b->sequence_number==current_token-1, "item arrived out of order" );
- } else if( is_serial() ) {
- if( b->sequence_number != current_token-1 && b->sequence_number != Buffer::unused )
- out_of_order_count++;
- }
- ASSERT( b->id < StreamSize, NULL );
- ASSERT( !my_done[b->id], "duplicate processing of token?" );
- ASSERT( b->is_busy, NULL );
- my_done[b->id] = true;
- if( my_is_last ) {
- b->id = Buffer::unused;
- b->sequence_number = Buffer::unused;
- __TBB_store_with_release(b->is_busy, false);
- }
- }
- my_is_running = false;
- return b;
- }
-};
-
-class InputFilter: public BaseFilter {
- tbb::spin_mutex input_lock;
- Buffer buffer[MaxBuffer];
- const tbb::internal::Token my_number_of_tokens;
-public:
- InputFilter( tbb::filter::mode type, tbb::internal::Token ntokens, bool done[], bool is_last ) :
- BaseFilter(type, done, is_last),
- my_number_of_tokens(ntokens)
- {}
- /*override*/Buffer* get_buffer( void* ) {
- unsigned long next_input;
- unsigned free_buffer = 0;
- { // lock protected scope
- tbb::spin_mutex::scoped_lock lock(input_lock);
- if( current_token>=StreamSize )
- return NULL;
- next_input = current_token++;
- // once in a while, emulate waiting for input; this only makes sense for serial input
- if( is_serial() && WaitTest.required() )
- WaitTest.probe( );
- while( free_buffer<MaxBuffer )
- if( __TBB_load_with_acquire(buffer[free_buffer].is_busy) )
- ++free_buffer;
- else {
- buffer[free_buffer].is_busy = true;
- break;
- }
- }
- ASSERT( free_buffer<my_number_of_tokens, "premature reuse of buffer" );
- Buffer* b = &buffer[free_buffer];
- ASSERT( &buffer[0] <= b, NULL );
- ASSERT( b <= &buffer[MaxBuffer-1], NULL );
- ASSERT( b->id == Buffer::unused, NULL);
- b->id = next_input;
- ASSERT( b->sequence_number == Buffer::unused, NULL);
- return b;
- }
-};
-
-//! The struct below repeats layout of tbb::pipeline.
-struct hacked_pipeline {
- tbb::filter* filter_list;
- tbb::filter* filter_end;
- tbb::empty_task* end_counter;
- tbb::atomic<tbb::internal::Token> input_tokens;
- tbb::atomic<tbb::internal::Token> token_counter;
- bool end_of_input;
- bool has_thread_bound_filters;
-
- virtual ~hacked_pipeline();
-};
-
-//! The struct below repeats layout of tbb::internal::input_buffer.
-struct hacked_input_buffer {
- void* array; // This should be changed to task_info* if ever used
- void* my_sem; // This should be changed to semaphore* if ever used
- tbb::internal::Token array_size;
- tbb::internal::Token low_token;
- tbb::spin_mutex array_mutex;
- tbb::internal::Token high_token;
- bool is_ordered;
- bool is_bound;
-};
-
-//! The struct below repeats layout of tbb::filter.
-struct hacked_filter {
- tbb::filter* next_filter_in_pipeline;
- hacked_input_buffer* my_input_buffer;
- unsigned char my_filter_mode;
- tbb::filter* prev_filter_in_pipeline;
- tbb::pipeline* my_pipeline;
- tbb::filter* next_segment;
-
- virtual ~hacked_filter();
-};
-
-bool do_hacking_tests = true;
-const tbb::internal::Token tokens_before_wraparound = 0xF;
-
-void TestTrivialPipeline( unsigned nthread, unsigned number_of_filters ) {
- // There are 3 filter types: parallel, serial_in_order and serial_out_of_order
- static const tbb::filter::mode filter_table[] = { tbb::filter::parallel, tbb::filter::serial_in_order, tbb::filter::serial_out_of_order};
- const unsigned number_of_filter_types = sizeof(filter_table)/sizeof(filter_table[0]);
- REMARK( "testing with %lu threads and %lu filters\n", nthread, number_of_filters );
- ASSERT( number_of_filters<=MaxFilters, "too many filters" );
- ASSERT( sizeof(hacked_pipeline) == sizeof(tbb::pipeline), "layout changed for tbb::pipeline?" );
- ASSERT( sizeof(hacked_filter) == sizeof(tbb::filter), "layout changed for tbb::filter?" );
- tbb::internal::Token ntokens = nthread<MaxBuffer ? nthread : MaxBuffer;
- // Count maximum iterations number
- unsigned limit = 1;
- for( unsigned i=0; i<number_of_filters; ++i)
- limit *= number_of_filter_types;
- // Iterate over possible filter sequences
- for( unsigned numeral=0; numeral<limit; ++numeral ) {
- // Build pipeline
- tbb::pipeline pipeline;
- if( do_hacking_tests ) {
- // A private member of pipeline is hacked there for sake of testing wrap-around immunity.
- ((hacked_pipeline*)(void*)&pipeline)->token_counter = ~tokens_before_wraparound;
- }
- tbb::filter* filter[MaxFilters];
- unsigned temp = numeral;
- // parallelism_limit is the upper bound on the possible parallelism
- unsigned parallelism_limit = 0;
- for( unsigned i=0; i<number_of_filters; ++i, temp/=number_of_filter_types ) {
- tbb::filter::mode filter_type = filter_table[temp%number_of_filter_types];
- const bool is_last = i==number_of_filters-1;
- if( i==0 )
- filter[i] = new InputFilter(filter_type,ntokens,Done[i],is_last);
- else
- filter[i] = new BaseFilter(filter_type,Done[i],is_last);
- pipeline.add_filter(*filter[i]);
- // The ordered buffer of serial filters is hacked as well.
- if ( filter[i]->is_serial() ) {
- if( do_hacking_tests ) {
- ((hacked_filter*)(void*)filter[i])->my_input_buffer->low_token = ~tokens_before_wraparound;
- ((hacked_filter*)(void*)filter[i])->my_input_buffer->high_token = ~tokens_before_wraparound;
- }
- parallelism_limit += 1;
- } else {
- parallelism_limit = nthread;
- }
- }
- // Account for clipping of parallelism.
- if( parallelism_limit>nthread )
- parallelism_limit = nthread;
- if( parallelism_limit>ntokens )
- parallelism_limit = (unsigned)ntokens;
- Harness::ConcurrencyTracker::Reset();
- unsigned streamSizeLimit = min( MaxStreamSize, nthread * MaxStreamItemsPerThread );
- for( StreamSize=0; StreamSize<=streamSizeLimit; ) {
- memset( Done, 0, sizeof(Done) );
- for( unsigned i=0; i<number_of_filters; ++i ) {
- static_cast<BaseFilter*>(filter[i])->current_token=0;
- }
- pipeline.run( ntokens );
- ASSERT( !Harness::ConcurrencyTracker::InstantParallelism(), "filter still running?" );
- for( unsigned i=0; i<number_of_filters; ++i )
- ASSERT( static_cast<BaseFilter*>(filter[i])->current_token==StreamSize, NULL );
- for( unsigned i=0; i<MaxFilters; ++i )
- for( unsigned j=0; j<StreamSize; ++j ) {
- ASSERT( Done[i][j]==(i<number_of_filters), NULL );
- }
- if( StreamSize < min(nthread*8, 32u) ) {
- ++StreamSize;
- } else {
- StreamSize = StreamSize*8/3;
- }
- }
- if( Harness::ConcurrencyTracker::PeakParallelism() < parallelism_limit )
- REMARK( "nthread=%lu ntokens=%lu MaxParallelism=%lu parallelism_limit=%lu\n",
- nthread, ntokens, Harness::ConcurrencyTracker::PeakParallelism(), parallelism_limit );
- for( unsigned i=0; i < number_of_filters; ++i ) {
- delete filter[i];
- filter[i] = NULL;
- }
- pipeline.clear();
- }
-}
-
-#include "harness_cpu.h"
-
-static int nthread; // knowing number of threads is necessary to call TestCPUUserTime
-
-void waiting_probe::probe( ) {
- if( nthread==1 ) return;
- REMARK("emulating wait for input\n");
- // Test that threads sleep while no work.
- // The master doesn't sleep so there could be 2 active threads if a worker is waiting for input
- TestCPUUserTime(nthread, 2);
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- out_of_order_count = 0;
- if( MinThread<1 ) {
- REPORT("must have at least one thread");
- exit(1);
- }
- if( tbb::TBB_runtime_interface_version()>TBB_INTERFACE_VERSION) {
- REMARK("Warning: implementation dependent tests disabled\n");
- do_hacking_tests = false;
- }
-
- // Test with varying number of threads.
- for( nthread=MinThread; nthread<=MaxThread; ++nthread ) {
- // Initialize TBB task scheduler
- tbb::task_scheduler_init init(nthread);
-
- // Test pipelines with n filters
- for( unsigned n=0; n<=MaxFilters; ++n )
- TestTrivialPipeline(nthread,n);
-
- // Test that all workers sleep when no work
- TestCPUUserTime(nthread);
- }
- if( !out_of_order_count )
- REPORT("Warning: out of order serial filter received tokens in order\n");
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/pipeline.h"
-#include "tbb/spin_mutex.h"
-#include "tbb/atomic.h"
-#include "tbb/tbb_thread.h"
-#include <cstdlib>
-#include <cstdio>
-#include "harness.h"
-
-// In the test, variables related to token counting are declared
-// as unsigned long to match definition of tbb::internal::Token.
-
-//! Id of thread that first executes work on non-thread-bound stages
-tbb::tbb_thread::id thread_id;
-//! Zero thread id
-tbb::tbb_thread::id id0;
-//! True if non-thread-bound stages must be executed on one thread
-bool is_serial_execution;
-double sleeptime; // how long is a non-thread-bound stage to sleep?
-
-struct Buffer {
- //! Indicates that the buffer is not used.
- static const unsigned long unused = ~0ul;
- unsigned long id;
- //! True if Buffer is in use.
- bool is_busy;
- unsigned long sequence_number;
- Buffer() : id(unused), is_busy(false), sequence_number(unused) {}
-};
-
-class waiting_probe {
- size_t check_counter;
-public:
- waiting_probe() : check_counter(0) {}
- bool required( ) {
- ++check_counter;
- return !((check_counter+1)&size_t(0x7FFF));
- }
- void probe( ); // defined below
-};
-
-static const unsigned MaxStreamSize = 8000;
-static const unsigned MaxStreamItemsPerThread = 1000;
-//! Maximum number of filters allowed
-static const unsigned MaxFilters = 4;
-static unsigned StreamSize;
-static const unsigned MaxBuffer = 8;
-static bool Done[MaxFilters][MaxStreamSize];
-static waiting_probe WaitTest;
-static unsigned out_of_order_count;
-
-#include "harness_concurrency_tracker.h"
-
-template<typename T>
-class BaseFilter: public T {
- bool* const my_done;
- const bool my_is_last;
- bool my_is_running;
-public:
- tbb::atomic<tbb::internal::Token> current_token;
- BaseFilter( tbb::filter::mode type, bool done[], bool is_last ) :
- T(type),
- my_done(done),
- my_is_last(is_last),
- my_is_running(false),
- current_token()
- {}
- virtual Buffer* get_buffer( void* item ) {
- current_token++;
- return static_cast<Buffer*>(item);
- }
- /*override*/void* operator()( void* item ) {
- // Check if work is done only on one thread when ntokens==1 or
- // when pipeline has only one filter that is serial and non-thread-bound
- if( is_serial_execution && !this->is_bound() ) {
- // Get id of current thread
- tbb::tbb_thread::id id = tbb::this_tbb_thread::get_id();
- // At first execution, set thread_id to current thread id.
- // Serialized execution is expected, so there should be no race.
- if( thread_id == id0 )
- thread_id = id;
- // Check if work is done on one thread
- ASSERT( thread_id == id, "non-thread-bound stages executed on different threads when must be executed on a single one");
- }
- Harness::ConcurrencyTracker ct;
- if( this->is_serial() )
- ASSERT( !my_is_running, "premature entry to serial stage" );
- my_is_running = true;
- Buffer* b = get_buffer(item);
- if( b ) {
- if(!this->is_bound() && sleeptime > 0) {
- Harness::Sleep((int)sleeptime);
- }
- if( this->is_ordered() ) {
- if( b->sequence_number == Buffer::unused )
- b->sequence_number = current_token-1;
- else
- ASSERT( b->sequence_number==current_token-1, "item arrived out of order" );
- } else if( this->is_serial() ) {
- if( b->sequence_number != current_token-1 && b->sequence_number != Buffer::unused )
- out_of_order_count++;
- }
- ASSERT( b->id < StreamSize, NULL );
- ASSERT( !my_done[b->id], "duplicate processing of token?" );
- ASSERT( b->is_busy, NULL );
- my_done[b->id] = true;
- if( my_is_last ) {
- b->id = Buffer::unused;
- b->sequence_number = Buffer::unused;
- __TBB_store_with_release(b->is_busy, false);
- }
- }
- my_is_running = false;
- return b;
- }
-};
-
-template<typename T>
-class InputFilter: public BaseFilter<T> {
- tbb::spin_mutex input_lock;
- Buffer buffer[MaxBuffer];
- const tbb::internal::Token my_number_of_tokens;
-public:
- InputFilter( tbb::filter::mode type, tbb::internal::Token ntokens, bool done[], bool is_last ) :
- BaseFilter<T>(type, done, is_last),
- my_number_of_tokens(ntokens)
- {}
- /*override*/Buffer* get_buffer( void* ) {
- unsigned long next_input;
- unsigned free_buffer = 0;
- { // lock protected scope
- tbb::spin_mutex::scoped_lock lock(input_lock);
- if( this->current_token>=StreamSize )
- return NULL;
- next_input = this->current_token++;
- // once in a while, emulate waiting for input; this only makes sense for serial input
- if( this->is_serial() && WaitTest.required() )
- WaitTest.probe( );
- while( free_buffer<MaxBuffer )
- if( __TBB_load_with_acquire(buffer[free_buffer].is_busy) )
- ++free_buffer;
- else {
- buffer[free_buffer].is_busy = true;
- break;
- }
- }
- ASSERT( free_buffer<my_number_of_tokens, "premature reuse of buffer" );
- Buffer* b = &buffer[free_buffer];
- ASSERT( &buffer[0] <= b, NULL );
- ASSERT( b <= &buffer[MaxBuffer-1], NULL );
- ASSERT( b->id == Buffer::unused, NULL);
- b->id = next_input;
- ASSERT( b->sequence_number == Buffer::unused, NULL);
- return b;
- }
-};
-
-class process_loop {
-public:
- void operator()( tbb::thread_bound_filter* tbf ) {
- tbb::thread_bound_filter::result_type flag;
- do
- flag = tbf->process_item();
- while( flag != tbb::thread_bound_filter::end_of_stream );
- }
-};
-
-//! The struct below repeats layout of tbb::pipeline.
-struct hacked_pipeline {
- tbb::filter* filter_list;
- tbb::filter* filter_end;
- tbb::empty_task* end_counter;
- tbb::atomic<tbb::internal::Token> input_tokens;
- tbb::atomic<tbb::internal::Token> global_token_counter;
- bool end_of_input;
- bool has_thread_bound_filters;
-
- virtual ~hacked_pipeline();
-};
-
-//! The struct below repeats layout of tbb::internal::ordered_buffer.
-struct hacked_ordered_buffer {
- void* array; // This should be changed to task_info* if ever used
- tbb::internal::Token array_size;
- tbb::internal::Token low_token;
- tbb::spin_mutex array_mutex;
- tbb::internal::Token high_token;
- bool is_ordered;
- bool is_bound;
-};
-
-//! The struct below repeats layout of tbb::filter.
-struct hacked_filter {
- tbb::filter* next_filter_in_pipeline;
- hacked_ordered_buffer* input_buffer;
- unsigned char my_filter_mode;
- tbb::filter* prev_filter_in_pipeline;
- tbb::pipeline* my_pipeline;
- tbb::filter* next_segment;
-
- virtual ~hacked_filter();
-};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Workaround for overzealous compiler warnings
- // Suppress compiler warning about constant conditional expression
- #pragma warning (disable: 4127)
-#endif
-
-void clear_global_state() {
- Harness::ConcurrencyTracker::Reset();
- memset( Done, 0, sizeof(Done) );
- thread_id = id0;
- is_serial_execution = false;
-}
-
-
-class PipelineTest {
- // There are 3 non-thread-bound filter types: serial_in_order and serial_out_of_order, parallel
- static const tbb::filter::mode non_tb_filters_table[3]; // = { tbb::filter::serial_in_order, tbb::filter::serial_out_of_order, tbb::filter::parallel};
- // There are 2 thread-bound filter types: serial_in_order and serial_out_of_order
- static const tbb::filter::mode tb_filters_table[2]; // = { tbb::filter::serial_in_order, tbb::filter::serial_out_of_order };
-
- static const unsigned number_of_non_tb_filter_types = sizeof(non_tb_filters_table)/sizeof(non_tb_filters_table[0]);
- static const unsigned number_of_tb_filter_types = sizeof(tb_filters_table)/sizeof(tb_filters_table[0]);
- static const unsigned number_of_filter_types = number_of_non_tb_filter_types + number_of_tb_filter_types;
- // static unsigned my_nthread;
- public:
- static double TestOneConfiguration( unsigned numeral, unsigned nthread, unsigned number_of_filters, tbb::internal::Token ntokens);
- static void TestTrivialPipeline( unsigned nthread, unsigned number_of_filters );
- static void TestIdleSpinning(unsigned nthread);
-};
-
-const tbb::filter::mode PipelineTest::non_tb_filters_table[3] = { tbb::filter::serial_in_order, tbb::filter::serial_out_of_order, tbb::filter::parallel};
-const tbb::filter::mode PipelineTest::tb_filters_table[2] = { tbb::filter::serial_in_order, tbb::filter::serial_out_of_order };
-
-#include "harness_cpu.h"
-
-double PipelineTest::TestOneConfiguration(unsigned numeral, unsigned nthread, unsigned number_of_filters, tbb::internal::Token ntokens)
-{
- // Build pipeline
- tbb::pipeline pipeline;
- tbb::filter* filter[MaxFilters];
- unsigned temp = numeral;
- // parallelism_limit is the upper bound on the possible parallelism
- unsigned parallelism_limit = 0;
- // number of thread-bound-filters in the current sequence
- unsigned number_of_tb_filters = 0;
- // ordinal numbers of thread-bound-filters in the current sequence
- unsigned array_of_tb_filter_numbers[MaxFilters];
- for( unsigned i=0; i<number_of_filters; ++i, temp/=number_of_filter_types ) {
- bool is_bound = temp%number_of_filter_types&0x1;
- tbb::filter::mode filter_type;
- if( is_bound ) {
- filter_type = tb_filters_table[temp%number_of_filter_types/number_of_non_tb_filter_types];
- } else
- filter_type = non_tb_filters_table[temp%number_of_filter_types/number_of_tb_filter_types];
- const bool is_last = i==number_of_filters-1;
- if( is_bound ) {
- if( i == 0 )
- filter[i] = new InputFilter<tbb::thread_bound_filter>(filter_type,ntokens,Done[i],is_last);
- else
- filter[i] = new BaseFilter<tbb::thread_bound_filter>(filter_type,Done[i],is_last);
- array_of_tb_filter_numbers[number_of_tb_filters] = i;
- number_of_tb_filters++;
- } else {
- if( i == 0 )
- filter[i] = new InputFilter<tbb::filter>(filter_type,ntokens,Done[i],is_last);
- else
- filter[i] = new BaseFilter<tbb::filter>(filter_type,Done[i],is_last);
- }
- pipeline.add_filter(*filter[i]);
- if ( filter[i]->is_serial() ) {
- parallelism_limit += 1;
- } else {
- parallelism_limit = nthread;
- }
- }
- clear_global_state();
- // Account for clipping of parallelism.
- if( parallelism_limit>nthread )
- parallelism_limit = nthread;
- if( parallelism_limit>ntokens )
- parallelism_limit = (unsigned)ntokens;
- StreamSize = nthread; // min( MaxStreamSize, nthread * MaxStreamItemsPerThread );
-
- for( unsigned i=0; i<number_of_filters; ++i ) {
- static_cast<BaseFilter<tbb::filter>*>(filter[i])->current_token=0;
- }
- tbb::tbb_thread* t[MaxFilters];
- for( unsigned j = 0; j<number_of_tb_filters; j++)
- t[j] = new tbb::tbb_thread(process_loop(), static_cast<tbb::thread_bound_filter*>(filter[array_of_tb_filter_numbers[j]]));
- if( ntokens == 1 || ( number_of_filters == 1 && number_of_tb_filters == 0 && filter[0]->is_serial() ))
- is_serial_execution = true;
- double strttime = GetCPUUserTime();
- pipeline.run( ntokens );
- double endtime = GetCPUUserTime();
- for( unsigned j = 0; j<number_of_tb_filters; j++)
- t[j]->join();
- ASSERT( !Harness::ConcurrencyTracker::InstantParallelism(), "filter still running?" );
- for( unsigned i=0; i<number_of_filters; ++i )
- ASSERT( static_cast<BaseFilter<tbb::filter>*>(filter[i])->current_token==StreamSize, NULL );
- for( unsigned i=0; i<MaxFilters; ++i )
- for( unsigned j=0; j<StreamSize; ++j ) {
- ASSERT( Done[i][j]==(i<number_of_filters), NULL );
- }
- if( Harness::ConcurrencyTracker::PeakParallelism() < parallelism_limit )
- REMARK( "nthread=%lu ntokens=%lu MaxParallelism=%lu parallelism_limit=%lu\n",
- nthread, ntokens, Harness::ConcurrencyTracker::PeakParallelism(), parallelism_limit );
- for( unsigned i=0; i < number_of_filters; ++i ) {
- delete filter[i];
- filter[i] = NULL;
- }
- for( unsigned j = 0; j<number_of_tb_filters; j++)
- delete t[j];
- pipeline.clear();
- return endtime - strttime;
-} // TestOneConfiguration
-
-void PipelineTest::TestTrivialPipeline( unsigned nthread, unsigned number_of_filters ) {
-
- REMARK( "testing with %lu threads and %lu filters\n", nthread, number_of_filters );
- ASSERT( number_of_filters<=MaxFilters, "too many filters" );
- tbb::internal::Token max_tokens = nthread < MaxBuffer ? nthread : MaxBuffer;
- // The loop has 1 iteration if max_tokens=1 and 2 iterations if max_tokens>1:
- // one iteration for ntokens=1 and second for ntokens=max_tokens
- // Iteration for ntokens=1 is required in each test case to check if pipeline run only on one thread
- unsigned max_iteration = max_tokens > 1 ? 2 : 1;
- tbb::internal::Token ntokens = 1;
- for( unsigned iteration = 0; iteration < max_iteration; iteration++) {
- if( iteration > 0 )
- ntokens = max_tokens;
- // Count maximum iterations number
- unsigned limit = 1;
- for( unsigned i=0; i<number_of_filters; ++i)
- limit *= number_of_filter_types;
- // Iterate over possible filter sequences
- for( unsigned numeral=0; numeral<limit; ++numeral ) {
- REMARK( "testing configuration %lu of %lu\n", numeral, limit );
- (void)TestOneConfiguration(numeral, nthread, number_of_filters, ntokens);
- }
- }
-}
-
-// varying times for sleep result in different user times for all pipelines.
-// So we compare the running time of an all non-TBF pipeline with different (with
-// luck representative) TBF configurations.
-//
-// We run the tests multiple times and compare the average runtimes for those cases
-// that don't return 0 user time. configurations that exceed the allowable extra
-// time are reported.
-void PipelineTest::TestIdleSpinning( unsigned nthread) {
- unsigned sample_setups[] = {
- // in the comments below, s == serial, B == thread bound serial, p == parallel
- 1, // B s s s
- 5, // s B s s
- 25, // s s B s
- 125, // s s s B
- 6, // B B s s
- 26, // B s B s
- 126, // B s s B
- 30, // s B B s
- 130, // s B s B
- 150, // s s B B
- 31, // B B B s
- 131, // B B s B
- 155, // s B B B
- 21, // B p s s
- 105, // s B p s
- 45, // s p B s
- 225, // s s p B
- };
- const int nsetups = sizeof(sample_setups) / sizeof(unsigned);
- const int ntests = 4;
- const double bignum = 1000000000.0;
- const double allowable_slowdown = 3.5;
- unsigned zero_count = 0;
-
- REMARK( "testing idle spinning with %lu threads\n", nthread );
- tbb::internal::Token max_tokens = nthread < MaxBuffer ? nthread : MaxBuffer;
- for( int i=0; i<nsetups; ++i ) {
- unsigned numeral = sample_setups[i];
- unsigned temp = numeral;
- unsigned nbound = 0;
- while(temp) {
- if((temp%number_of_filter_types)&0x01) nbound++;
- temp /= number_of_filter_types;
- }
- sleeptime = 20.0;
- double s0 = bignum;
- double s1 = bignum;
- int v0cnt = 0;
- int v1cnt = 0;
- double s0sum = 0.0;
- double s1sum = 0.0;
- for(int j = 0; j < ntests; ++j) {
- double s1a = TestOneConfiguration(numeral, nthread, MaxFilters, max_tokens);
- double s0a = TestOneConfiguration((unsigned)0, nthread, MaxFilters, max_tokens);
- s1sum += s1a;
- s0sum += s0a;
- if(s0a > 0.0) {
- ++v0cnt;
- s0 = (s0a < s0) ? s0a : s0;
- }
- else {
- ++zero_count;
- }
- if(s1a > 0.0) {
- ++v1cnt;
- s1 = (s1a < s1) ? s1a : s1;
- }
- else {
- ++zero_count;
- }
- }
- if(s0 == bignum || s1 == bignum) continue;
- s0sum /= (double)v0cnt;
- s1sum /= (double)v1cnt;
- double slowdown = (s1sum-s0sum)/s0sum;
- if(slowdown > allowable_slowdown)
- REMARK( "with %lu threads configuration %lu has slowdown > %g (%g)\n", nthread, numeral, allowable_slowdown, slowdown );
- }
- REMARK("Total of %lu zero times\n", zero_count);
-}
-
-static int nthread; // knowing number of threads is necessary to call TestCPUUserTime
-
-void waiting_probe::probe( ) {
- if( nthread==1 ) return;
- REMARK("emulating wait for input\n");
- // Test that threads sleep while no work.
- // The master doesn't sleep so there could be 2 active threads if a worker is waiting for input
- TestCPUUserTime(nthread, 2);
-}
-
-#include "tbb/task_scheduler_init.h"
-
-int TestMain () {
- out_of_order_count = 0;
- if( MinThread<1 ) {
- REPORT("must have at least one thread");
- exit(1);
- }
-
- sleeptime = 0.0; // msec : 0 == no_timing, > 0, each filter stage sleeps for sleeptime
- // Test with varying number of threads.
- for( nthread=MinThread; nthread<=MaxThread; ++nthread ) {
- // Initialize TBB task scheduler
- tbb::task_scheduler_init init(nthread);
-
- // Test pipelines with 1 and maximal number of filters
- for( unsigned n=1; n<=MaxFilters; n*=MaxFilters ) {
- // Thread-bound stages are serviced by user-created threads those
- // don't run the pipeline and don't service non-thread-bound stages
- PipelineTest::TestTrivialPipeline(nthread,n);
- }
-
- // Test that all workers sleep when no work
- TestCPUUserTime(nthread);
- if((unsigned)nthread >= MaxFilters) // test works when number of threads >= number of stages
- PipelineTest::TestIdleSpinning(nthread);
- }
- if( !out_of_order_count )
- REPORT("Warning: out of order serial filter received tokens in order\n");
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// test reader_writer_lock
-#include "tbb/reader_writer_lock.h"
-#include "tbb/atomic.h"
-#include "tbb/tbb_exception.h"
-#include "harness_assert.h"
-#include "harness.h"
-
-tbb::reader_writer_lock the_mutex;
-const int MAX_WORK = 10000;
-
-tbb::atomic<size_t> active_readers, active_writers;
-tbb::atomic<bool> sim_readers;
-
-
-int BusyWork(int percentOfMaxWork) {
- int iters = 0;
- for (int i=0; i<MAX_WORK*((double)percentOfMaxWork/100.0); ++i) {
- iters++;
- }
- return iters;
-}
-
-struct StressRWLBody : NoAssign {
- const int nThread;
- const int percentMax;
-
- StressRWLBody(int nThread_, int percentMax_) : nThread(nThread_), percentMax(percentMax_) {}
-
- void operator()(const int /* threadID */ ) const {
- int nIters = 100;
- int r_result=0, w_result=0;
- for(int i=0; i<nIters; ++i) {
- // test unscoped blocking write lock
- the_mutex.lock();
- w_result += BusyWork(percentMax);
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- // test exception for recursive write lock
- bool was_caught = false;
- try {
- the_mutex.lock();
- }
- catch(tbb::improper_lock& ex) {
- REMARK("improper_lock: %s\n", ex.what());
- was_caught = true;
- }
- catch(...) {
- REPORT("Wrong exception caught during recursive lock attempt.");
- }
- ASSERT(was_caught, "Recursive lock attempt exception not caught properly.");
- // test exception for recursive read lock
- was_caught = false;
- try {
- the_mutex.lock_read();
- }
- catch(tbb::improper_lock& ex) {
- REMARK("improper_lock: %s\n", ex.what());
- was_caught = true;
- }
- catch(...) {
- REPORT("Wrong exception caught during recursive lock attempt.");
- }
- ASSERT(was_caught, "Recursive lock attempt exception not caught properly.");
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- the_mutex.unlock();
- // test unscoped non-blocking write lock
- if (the_mutex.try_lock()) {
- w_result += BusyWork(percentMax);
- the_mutex.unlock();
- }
- // test unscoped blocking read lock
- the_mutex.lock_read();
- r_result += BusyWork(percentMax);
- the_mutex.unlock();
- // test unscoped non-blocking read lock
- if(the_mutex.try_lock_read()) {
- r_result += BusyWork(percentMax);
- the_mutex.unlock();
- }
- { // test scoped blocking write lock
- tbb::reader_writer_lock::scoped_lock my_lock(the_mutex);
- w_result += BusyWork(percentMax);
- }
- { // test scoped blocking read lock
- tbb::reader_writer_lock::scoped_lock_read my_lock(the_mutex);
- r_result += BusyWork(percentMax);
- }
- }
- REMARK("%d reader %d writer iterations of busy work were completed.", r_result, w_result);
- }
-};
-
-struct CorrectRWLScopedBody : NoAssign {
- const int nThread;
-
- CorrectRWLScopedBody(int nThread_) : nThread(nThread_) {}
-
- void operator()(const int /* threadID */ ) const {
- bool is_reader;
-
- for (int i=0; i<50; i++) {
- if (i%5==0) is_reader = false; // 1 writer for every 5 readers
- else is_reader = true;
-
- if (is_reader) {
- tbb::reader_writer_lock::scoped_lock_read my_lock(the_mutex);
- active_readers++;
- if (active_readers > 1) sim_readers = true;
- ASSERT(active_writers==0, "Active writers in read-locked region.");
- Harness::Sleep(10);
- active_readers--;
- }
- else { // is writer
- tbb::reader_writer_lock::scoped_lock my_lock(the_mutex);
- active_writers++;
- ASSERT(active_readers==0, "Active readers in write-locked region.");
- ASSERT(active_writers<=1, "More than one active writer in write-locked region.");
- Harness::Sleep(10);
- active_writers--;
- }
- }
- }
-};
-
-struct CorrectRWLBody : NoAssign {
- const int nThread;
-
- CorrectRWLBody(int nThread_) : nThread(nThread_) {}
-
- void operator()(const int /* threadID */ ) const {
- bool is_reader;
-
- for (int i=0; i<50; i++) {
- if (i%5==0) is_reader = false; // 1 writer for every 5 readers
- else is_reader = true;
-
- if (is_reader) {
- the_mutex.lock_read();
- active_readers++;
- if (active_readers > 1) sim_readers = true;
- ASSERT(active_writers==0, "Active writers in read-locked region.");
- }
- else { // is writer
- the_mutex.lock();
- active_writers++;
- ASSERT(active_readers==0, "Active readers in write-locked region.");
- ASSERT(active_writers<=1, "More than one active writer in write-locked region.");
- }
- Harness::Sleep(10);
- if (is_reader) {
- active_readers--;
- }
- else { // is writer
- active_writers--;
- }
- the_mutex.unlock();
- }
- }
-};
-
-void TestReaderWriterLockOnNThreads(int nThreads) {
- // Stress-test all interfaces
- for (int pc=0; pc<101; pc+=20) {
- REMARK("\nTesting reader_writer_lock with %d threads, percent of MAX_WORK=%d", nThreads, pc);
- StressRWLBody myStressBody(nThreads, pc);
- NativeParallelFor(nThreads, myStressBody);
- }
-
- // Test mutual exclusion in direct locking mode
- CorrectRWLBody myCorrectBody(nThreads);
- active_writers = active_readers = 0;
- sim_readers = false;
- NativeParallelFor(nThreads, myCorrectBody);
- ASSERT(sim_readers || nThreads<2, "There were no simultaneous readers.");
- REMARK("Unscoped lock testing succeeded on %d threads.", nThreads);
-
- // Test mutual exclusionin scoped locking mode
- CorrectRWLScopedBody myCorrectScopedBody(nThreads);
- active_writers = active_readers = 0;
- sim_readers = false;
- NativeParallelFor(nThreads, myCorrectScopedBody);
- ASSERT(sim_readers || nThreads<2, "There were no simultaneous readers.");
- REMARK("Scoped lock testing succeeded on %d threads.", nThreads);
-}
-
-void TestReaderWriterLock() {
- for(int p = MinThread; p <= MaxThread; p++) {
- TestReaderWriterLockOnNThreads(p);
- }
-}
-
-
-int TestMain() {
- if(MinThread <= 0) MinThread = 1;
- if(MaxThread > 0) {
- TestReaderWriterLock();
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/queuing_rw_mutex.h"
-#include "tbb/spin_rw_mutex.h"
-#include "harness.h"
-
-using namespace tbb;
-
-volatile int Count;
-
-template<typename RWMutex>
-struct Hammer: NoAssign {
- RWMutex &MutexProtectingCount;
- mutable volatile int dummy;
-
- Hammer(RWMutex &m): MutexProtectingCount(m) {}
- void operator()( int /*thread_id*/ ) const {
- for( int j=0; j<100000; ++j ) {
- typename RWMutex::scoped_lock lock(MutexProtectingCount,false);
- int c = Count;
- for( int k=0; k<10; ++k ) {
- ++dummy;
- }
- if( lock.upgrade_to_writer() ) {
- // The upgrade succeeded without any intervening writers
- ASSERT( c==Count, "another thread modified Count while I held a read lock" );
- } else {
- c = Count;
- }
- for( int k=0; k<10; ++k ) {
- ++Count;
- }
- lock.downgrade_to_reader();
- for( int k=0; k<10; ++k ) {
- ++dummy;
- }
- }
- }
-};
-
-queuing_rw_mutex QRW_mutex;
-spin_rw_mutex SRW_mutex;
-
-int TestMain () {
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK("Testing on %d threads", p);
- Count = 0;
- NativeParallelFor( p, Hammer<queuing_rw_mutex>(QRW_mutex) );
- Count = 0;
- NativeParallelFor( p, Hammer<spin_rw_mutex>(SRW_mutex) );
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-//
-// Test for counting semaphore.
-//
-// set semaphore to N
-// create N + M threads
-// have each thread
-// A. P()
-// B. increment atomic count
-// C. spin for awhile checking the value of the count; make sure it doesn't exceed N
-// D. decrement atomic count
-// E. V()
-//
-
-#include "tbb/semaphore.h"
-#include "tbb/atomic.h"
-
-#include <vector>
-using std::vector;
-
-#include "harness_assert.h"
-#include "harness.h"
-
-using tbb::internal::semaphore;
-
-#include "harness_barrier.h"
-
-tbb::atomic<int> pCount;
-
-Harness::SpinBarrier sBarrier;
-
-#include "tbb/tick_count.h"
-// semaphore basic function:
-// set semaphore to initial value
-// see that semaphore only allows that number of threads to be active
-class Body: NoAssign {
- const int nThreads;
- const int nIters;
- tbb::internal::semaphore &mySem;
- vector<int> &ourCounts;
- vector<double> &tottime;
- static const int tickCounts = 1; // millisecond
- static const int innerWait = 5; // millisecond
-public:
- Body(int nThread_, int nIter_, semaphore &mySem_,
- vector<int>& ourCounts_,
- vector<double>& tottime_
- ) : nThreads(nThread_), nIters(nIter_), mySem(mySem_), ourCounts(ourCounts_), tottime(tottime_) { sBarrier.initialize(nThread_); pCount = 0; }
-void operator()(const int tid) const {
- sBarrier.wait();
- for(int i=0; i < nIters; ++i) {
- Harness::Sleep( tid * tickCounts );
- tbb::tick_count t0 = tbb::tick_count::now();
- mySem.P();
- tbb::tick_count t1 = tbb::tick_count::now();
- tottime[tid] += (t1-t0).seconds();
- int curval = ++pCount;
- if(curval > ourCounts[tid]) ourCounts[tid] = curval;
- Harness::Sleep( innerWait );
- --pCount;
- ASSERT((int)pCount >= 0, NULL);
- mySem.V();
- }
-}
-};
-
-
-void testSemaphore( int semInitCnt, int extraThreads ) {
- semaphore my_sem(semInitCnt);
- // tbb::task_scheduler_init init(tbb::task_scheduler_init::deferred);
- int nThreads = semInitCnt + extraThreads;
- vector<int> maxVals(nThreads);
- vector<double> totTimes(nThreads);
- int nIters = 10;
- Body myBody(nThreads, nIters, my_sem, maxVals, totTimes);
-
- REMARK( " sem(%d) with %d extra threads\n", semInitCnt, extraThreads);
- pCount = 0;
- NativeParallelFor(nThreads, myBody);
- if(extraThreads == 0) {
- double allPWaits = 0;
- for(vector<double>::const_iterator j = totTimes.begin(); j != totTimes.end(); ++j) {
- allPWaits += *j;
- }
- allPWaits /= static_cast<double>(nThreads * nIters);
- REMARK("Average wait for P() in uncontested case for nThreads = %d is %g\n", nThreads, allPWaits);
- }
- ASSERT(!pCount, "not all threads decremented pCount");
- int maxCount = -1;
- for(vector<int>::const_iterator i=maxVals.begin(); i!= maxVals.end();++i) {
- maxCount = max(maxCount,*i);
- }
- ASSERT(maxCount <= semInitCnt,"too many threads in semaphore-protected increment");
- if(maxCount < semInitCnt) {
- REMARK("Not enough threads in semaphore-protected region (%d < %d)\n", static_cast<int>(maxCount), semInitCnt);
- }
-}
-
-// Power of 2, the most tokens that can be in flight.
-#define MAX_TOKENS 32
-enum FilterType { imaProducer, imaConsumer };
-class FilterBase : NoAssign {
-protected:
- FilterType ima;
- unsigned totTokens; // total number of tokens to be emitted, only used by producer
- tbb::atomic<unsigned>& myTokens;
- tbb::atomic<unsigned>& otherTokens;
- unsigned myWait;
- semaphore &mySem;
- semaphore &nextSem;
- unsigned* myBuffer;
- unsigned* nextBuffer;
- unsigned curToken;
-public:
- FilterBase( FilterType ima_
- ,unsigned totTokens_
- ,tbb::atomic<unsigned>& myTokens_
- ,tbb::atomic<unsigned>& otherTokens_
- ,unsigned myWait_
- ,semaphore &mySem_
- ,semaphore &nextSem_
- ,unsigned* myBuffer_
- ,unsigned* nextBuffer_
- )
- : ima(ima_),totTokens(totTokens_),myTokens(myTokens_),otherTokens(otherTokens_),myWait(myWait_),mySem(mySem_),
- nextSem(nextSem_),myBuffer(myBuffer_),nextBuffer(nextBuffer_)
- {
- curToken = 0;
- }
- void Produce(const int tid);
- void Consume(const int tid);
- void operator()(const int tid) { if(ima == imaConsumer) Consume(tid); else Produce(tid); }
-};
-
-class ProduceConsumeBody {
- FilterBase** myFilters;
- public:
- ProduceConsumeBody(FilterBase** myFilters_) : myFilters(myFilters_) {}
- void operator()(const int tid) const {
- myFilters[tid]->operator()(tid);
- }
-};
-
-// send a bunch of non-Null "tokens" to consumer, then a NULL.
-void FilterBase::Produce(const int /*tid*/) {
- nextBuffer[0] = 0; // just in case we provide no tokens
- sBarrier.wait();
- while(totTokens) {
- while(!myTokens)
- mySem.P();
- // we have a slot available.
- --myTokens; // moving this down reduces spurious wakeups
- --totTokens;
- if(totTokens)
- nextBuffer[curToken&(MAX_TOKENS-1)] = curToken*3+1;
- else
- nextBuffer[curToken&(MAX_TOKENS-1)] = (unsigned)NULL;
- ++curToken;
- Harness::Sleep(myWait);
- unsigned temp = ++otherTokens;
- if(temp == 1)
- nextSem.V();
- }
- nextSem.V(); // final wakeup
-}
-
-void FilterBase::Consume(const int /*tid*/) {
- unsigned myToken;
- sBarrier.wait();
- do {
- while(!myTokens)
- mySem.P();
- // we have a slot available.
- --myTokens; // moving this down reduces spurious wakeups
- myToken = myBuffer[curToken&(MAX_TOKENS-1)];
- if(myToken) {
- ASSERT(myToken == curToken*3+1, "Error in received token");
- ++curToken;
- Harness::Sleep(myWait);
- unsigned temp = ++otherTokens;
- if(temp == 1)
- nextSem.V();
- }
- } while(myToken);
- // end of processing
- ASSERT(curToken + 1 == totTokens, "Didn't receive enough tokens");
-}
-
-// -- test of producer/consumer with atomic buffer cnt and semaphore
-// nTokens are total number of tokens through the pipe
-// pWait is the wait time for the producer
-// cWait is the wait time for the consumer
-void testProducerConsumer( unsigned totTokens, unsigned nTokens, unsigned pWait, unsigned cWait) {
- semaphore pSem;
- semaphore cSem;
- tbb::atomic<unsigned> pTokens;
- tbb::atomic<unsigned> cTokens;
- cTokens = 0;
- unsigned cBuffer[MAX_TOKENS];
- FilterBase* myFilters[2]; // one producer, one consumer
- REMARK("Testing producer/consumer with %lu total tokens, %lu tokens at a time, producer wait(%lu), consumer wait (%lu)\n", totTokens, nTokens, pWait, cWait);
- ASSERT(nTokens <= MAX_TOKENS, "Not enough slots for tokens");
- myFilters[0] = new FilterBase(imaProducer, totTokens, pTokens, cTokens, pWait, cSem, pSem, (unsigned *)NULL, &(cBuffer[0]));
- myFilters[1] = new FilterBase(imaConsumer, totTokens, cTokens, pTokens, cWait, pSem, cSem, cBuffer, (unsigned *)NULL);
- pTokens = nTokens;
- ProduceConsumeBody myBody(myFilters);
- sBarrier.initialize(2);
- NativeParallelFor(2, myBody);
- delete myFilters[0];
- delete myFilters[1];
-}
-
-int TestMain() {
- REMARK("Started\n");
- if(MaxThread > 0) {
- for(int semSize = 1; semSize <= MaxThread; ++semSize) {
- for(int exThreads = 0; exThreads <= MaxThread - semSize; ++exThreads) {
- testSemaphore( semSize, exThreads );
- }
- }
- }
- // Test producer/consumer with varying execution times and buffer sizes
- // ( total tokens, tokens in buffer, sleep for producer, sleep for consumer )
- testProducerConsumer( 10, 2, 5, 5 );
- testProducerConsumer( 10, 2, 20, 5 );
- testProducerConsumer( 10, 2, 5, 20 );
- testProducerConsumer( 10, 1, 5, 5 );
- testProducerConsumer( 20, 10, 5, 20 );
- testProducerConsumer( 64, 32, 1, 20 );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define TBB_IMPLEMENT_CPP0X 1
-#include "tbb/compat/thread"
-#define THREAD std::thread
-#define THIS_THREAD std::this_thread
-#define THIS_THREAD_SLEEP THIS_THREAD::sleep_for
-#include "test_thread.h"
-#include "harness.h"
-
-int TestMain () {
- CheckSignatures();
- RunTests();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/task.h"
-#include "tbb/atomic.h"
-#include "tbb/tbb_thread.h"
-#include "harness_assert.h"
-#include <cstdlib>
-
-//------------------------------------------------------------------------
-// Helper for verifying that old use cases of spawn syntax still work.
-//------------------------------------------------------------------------
-tbb::task* GetTaskPtr( int& counter ) {
- ++counter;
- return NULL;
-}
-
-//------------------------------------------------------------------------
-// Test for task::spawn_children and task_list
-//------------------------------------------------------------------------
-
-class UnboundedlyRecursiveOnUnboundedStealingTask : public tbb::task {
- typedef UnboundedlyRecursiveOnUnboundedStealingTask this_type;
-
- this_type *m_Parent;
- const int m_Depth;
- volatile bool m_GoAhead;
-
- // Well, virtually unboundedly, for any practical purpose
- static const int max_depth = 1000000;
-
-public:
- UnboundedlyRecursiveOnUnboundedStealingTask( this_type *parent_ = NULL, int depth_ = max_depth )
- : m_Parent(parent_)
- , m_Depth(depth_)
- , m_GoAhead(true)
- {}
-
- /*override*/
- tbb::task* execute() {
- // Using large padding array sppeds up reaching stealing limit
- const int paddingSize = 16 * 1024;
- volatile char padding[paddingSize];
- if( !m_Parent || (m_Depth > 0 && m_Parent->m_GoAhead) ) {
- if ( m_Parent ) {
- // We are stolen, let our parent to start waiting for us
- m_Parent->m_GoAhead = false;
- }
- tbb::task &t = *new( tbb::task::allocate_child() ) this_type(this, m_Depth - 1);
- set_ref_count( 2 );
- spawn( t );
- // Give a willing thief a chance to steal
- for( int i = 0; i < 1000000 && m_GoAhead; ++i ) {
- ++padding[i % paddingSize];
- __TBB_Yield();
- }
- // If our child has not been stolen yet, then prohibit it siring ones
- // of its own (when this thread executes it inside the next wait_for_all)
- m_GoAhead = false;
- wait_for_all();
- }
- return NULL;
- }
-}; // UnboundedlyRecursiveOnUnboundedStealingTask
-
-tbb::atomic<int> Count;
-
-class RecursiveTask: public tbb::task {
- const int m_ChildCount;
- const int m_Depth;
- //! Spawn tasks in list. Exact method depends upon m_Depth&bit_mask.
- void SpawnList( tbb::task_list& list, int bit_mask ) {
- if( m_Depth&bit_mask ) {
- // Take address to check that signature of spawn(task_list&) is static.
- void (*s)(tbb::task_list&) = &tbb::task::spawn;
- (*s)(list);
- ASSERT( list.empty(), NULL );
- wait_for_all();
- } else {
- spawn_and_wait_for_all(list);
- ASSERT( list.empty(), NULL );
- }
- }
-public:
- RecursiveTask( int child_count, int depth_ ) : m_ChildCount(child_count), m_Depth(depth_) {}
- /*override*/ tbb::task* execute() {
- ++Count;
- if( m_Depth>0 ) {
- tbb::task_list list;
- ASSERT( list.empty(), NULL );
- for( int k=0; k<m_ChildCount; ++k ) {
- list.push_back( *new( tbb::task::allocate_child() ) RecursiveTask(m_ChildCount/2,m_Depth-1 ) );
- ASSERT( !list.empty(), NULL );
- }
- set_ref_count( m_ChildCount+1 );
- SpawnList( list, 1 );
- // Now try reusing this as the parent.
- set_ref_count(2);
- list.push_back( *new (tbb::task::allocate_child() ) tbb::empty_task() );
- SpawnList( list, 2 );
- }
- return NULL;
- }
-};
-
-//! Compute what Count should be after RecursiveTask(child_count,depth) runs.
-static int Expected( int child_count, int depth ) {
- return depth<=0 ? 1 : 1+child_count*Expected(child_count/2,depth-1);
-}
-
-#include "tbb/task_scheduler_init.h"
-#include "harness.h"
-
-void TestStealLimit( int nthread ) {
- REMARK( "testing steal limiting heuristics for %d threads\n", nthread );
- tbb::task_scheduler_init init(nthread);
- tbb::task &t = *new( tbb::task::allocate_root() ) UnboundedlyRecursiveOnUnboundedStealingTask();
- tbb::task::spawn_root_and_wait(t);
-}
-
-//! Test task::spawn( task_list& )
-void TestSpawnChildren( int nthread ) {
- REMARK("testing task::spawn(task_list&) for %d threads\n",nthread);
- tbb::task_scheduler_init init(nthread);
- for( int j=0; j<50; ++j ) {
- Count = 0;
- RecursiveTask& p = *new( tbb::task::allocate_root() ) RecursiveTask(j,4);
- tbb::task::spawn_root_and_wait(p);
- int expected = Expected(j,4);
- ASSERT( Count==expected, NULL );
- }
-}
-
-//! Test task::spawn_root_and_wait( task_list& )
-void TestSpawnRootList( int nthread ) {
- REMARK("testing task::spawn_root_and_wait(task_list&) for %d threads\n",nthread);
- tbb::task_scheduler_init init(nthread);
- for( int j=0; j<5; ++j )
- for( int k=0; k<10; ++k ) {
- Count = 0;
- tbb::task_list list;
- for( int i=0; i<k; ++i )
- list.push_back( *new( tbb::task::allocate_root() ) RecursiveTask(j,4) );
- tbb::task::spawn_root_and_wait(list);
- int expected = k*Expected(j,4);
- ASSERT( Count==expected, NULL );
- }
-}
-
-//------------------------------------------------------------------------
-// Test for task::recycle_as_safe_continuation
-//------------------------------------------------------------------------
-
-class TaskGenerator: public tbb::task {
- int m_ChildCount;
- int m_Depth;
-
-public:
- TaskGenerator( int child_count, int _depth ) : m_ChildCount(child_count), m_Depth(_depth) {}
- ~TaskGenerator( ) { m_ChildCount = m_Depth = -125; }
-
- /*override*/ tbb::task* execute() {
- ASSERT( m_ChildCount>=0 && m_Depth>=0, NULL );
- if( m_Depth>0 ) {
- recycle_as_safe_continuation();
- set_ref_count( m_ChildCount+1 );
- int k=0;
- for( int j=0; j<m_ChildCount; ++j ) {
- tbb::task& t = *new( allocate_child() ) TaskGenerator(m_ChildCount/2,m_Depth-1);
- GetTaskPtr(k)->spawn(t);
- }
- ASSERT(k==m_ChildCount,NULL);
- --m_Depth;
- __TBB_Yield();
- ASSERT( state()==recycle && ref_count()>0, NULL);
- }
- return NULL;
- }
-};
-
-void TestSafeContinuation( int nthread ) {
- REMARK("testing task::recycle_as_safe_continuation for %d threads\n",nthread);
- tbb::task_scheduler_init init(nthread);
- for( int j=8; j<33; ++j ) {
- TaskGenerator& p = *new( tbb::task::allocate_root() ) TaskGenerator(j,5);
- tbb::task::spawn_root_and_wait(p);
- }
-}
-
-//------------------------------------------------------------------------
-// Test affinity interface
-//------------------------------------------------------------------------
-tbb::atomic<int> TotalCount;
-
-struct AffinityTask: public tbb::task {
- const tbb::task::affinity_id expected_affinity_id;
- bool noted;
- /** Computing affinities is NOT supported by TBB, and may disappear in the future.
- It is done here for sake of unit testing. */
- AffinityTask( int expected_affinity_id_ ) :
- expected_affinity_id(tbb::task::affinity_id(expected_affinity_id_)),
- noted(false)
- {
- set_affinity(expected_affinity_id);
- ASSERT( 0u-expected_affinity_id>0u, "affinity_id not an unsigned integral type?" );
- ASSERT( affinity()==expected_affinity_id, NULL );
- }
- /*override*/ tbb::task* execute() {
- ++TotalCount;
- return NULL;
- }
- /*override*/ void note_affinity( affinity_id id ) {
- // There is no guarantee in TBB that a task runs on its affinity thread.
- // However, the current implementation does accidentally guarantee it
- // under certain conditions, such as the conditions here.
- // We exploit those conditions for sake of unit testing.
- ASSERT( id!=expected_affinity_id, NULL );
- ASSERT( !noted, "note_affinity_id called twice!" );
- ASSERT ( &tbb::task::self() == (tbb::task*)this, "Wrong innermost running task" );
- noted = true;
- }
-};
-
-/** Note: This test assumes a lot about the internal implementation of affinity.
- Do NOT use this as an example of good programming practice with TBB */
-void TestAffinity( int nthread ) {
- TotalCount = 0;
- int n = tbb::task_scheduler_init::default_num_threads();
- if( n>nthread )
- n = nthread;
- tbb::task_scheduler_init init(n);
- tbb::empty_task* t = new( tbb::task::allocate_root() ) tbb::empty_task;
- tbb::task::affinity_id affinity_id = t->affinity();
- ASSERT( affinity_id==0, NULL );
- // Set ref_count for n-1 children, plus 1 for the wait.
- t->set_ref_count(n);
- // Spawn n-1 affinitized children.
- for( int i=1; i<n; ++i )
- tbb::task::spawn( *new(t->allocate_child()) AffinityTask(i) );
- if( n>1 ) {
- // Keep master from stealing
- while( TotalCount!=n-1 )
- __TBB_Yield();
- }
- // Wait for the children
- t->wait_for_all();
- int k = 0;
- GetTaskPtr(k)->destroy(*t);
- ASSERT(k==1,NULL);
-}
-
-struct NoteAffinityTask: public tbb::task {
- bool noted;
- NoteAffinityTask( int id ) : noted(false)
- {
- set_affinity(tbb::task::affinity_id(id));
- }
- ~NoteAffinityTask () {
- ASSERT (noted, "note_affinity has not been called");
- }
- /*override*/ tbb::task* execute() {
- return NULL;
- }
- /*override*/ void note_affinity( affinity_id /*id*/ ) {
- noted = true;
- ASSERT ( &tbb::task::self() == (tbb::task*)this, "Wrong innermost running task" );
- }
-};
-
-// This test checks one of the paths inside the scheduler by affinitizing the child task
-// to non-existent thread so that it is proxied in the local task pool but not retrieved
-// by another thread.
-// If no workers requested, the extra slot #2 is allocated for a worker thread to serve
-// "enqueued" tasks. In this test, it is used only for the affinity purpose.
-void TestNoteAffinityContext() {
- tbb::task_scheduler_init init(1);
- tbb::empty_task* t = new( tbb::task::allocate_root() ) tbb::empty_task;
- t->set_ref_count(2);
- // This master in the absence of workers will have an affinity id of 1.
- // So use another number to make the task get proxied.
- tbb::task::spawn( *new(t->allocate_child()) NoteAffinityTask(2) );
- t->wait_for_all();
- tbb::task::destroy(*t);
-}
-
-//------------------------------------------------------------------------
-// Test that recovery actions work correctly for task::allocate_* methods
-// when a task's constructor throws an exception.
-//------------------------------------------------------------------------
-
-#if TBB_USE_EXCEPTIONS
-static int TestUnconstructibleTaskCount;
-
-struct ConstructionFailure {
-};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Suppress pointless "unreachable code" warning.
- #pragma warning (push)
- #pragma warning (disable: 4702)
-#endif
-
-//! Task that cannot be constructed.
-template<size_t N>
-struct UnconstructibleTask: public tbb::empty_task {
- char space[N];
- UnconstructibleTask() {
- throw ConstructionFailure();
- }
-};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning (pop)
-#endif
-
-#define TRY_BAD_CONSTRUCTION(x) \
- { \
- try { \
- new(x) UnconstructibleTask<N>; \
- } catch( const ConstructionFailure& ) { \
- ASSERT( parent()==original_parent, NULL ); \
- ASSERT( ref_count()==original_ref_count, "incorrectly changed ref_count" );\
- ++TestUnconstructibleTaskCount; \
- } \
- }
-
-template<size_t N>
-struct RootTaskForTestUnconstructibleTask: public tbb::task {
- tbb::task* execute() {
- tbb::task* original_parent = parent();
- ASSERT( original_parent!=NULL, NULL );
- int original_ref_count = ref_count();
- TRY_BAD_CONSTRUCTION( allocate_root() );
- TRY_BAD_CONSTRUCTION( allocate_child() );
- TRY_BAD_CONSTRUCTION( allocate_continuation() );
- TRY_BAD_CONSTRUCTION( allocate_additional_child_of(*this) );
- return NULL;
- }
-};
-
-template<size_t N>
-void TestUnconstructibleTask() {
- TestUnconstructibleTaskCount = 0;
- tbb::task_scheduler_init init;
- tbb::task* t = new( tbb::task::allocate_root() ) RootTaskForTestUnconstructibleTask<N>;
- tbb::task::spawn_root_and_wait(*t);
- ASSERT( TestUnconstructibleTaskCount==4, NULL );
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-//------------------------------------------------------------------------
-// Test for alignment problems with task objects.
-//------------------------------------------------------------------------
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- // Workaround for pointless warning "structure was padded due to __declspec(align())
- #pragma warning (push)
- #pragma warning (disable: 4324)
-#endif
-
-//! Task with members of type T.
-/** The task recursively creates tasks. */
-template<typename T>
-class TaskWithMember: public tbb::task {
- T x;
- T y;
- unsigned char count;
- /*override*/ tbb::task* execute() {
- x = y;
- if( count>0 ) {
- set_ref_count(2);
- tbb::task* t = new( tbb::task::allocate_child() ) TaskWithMember<T>(count-1);
- spawn_and_wait_for_all(*t);
- }
- return NULL;
- }
-public:
- TaskWithMember( unsigned char n ) : count(n) {}
-};
-
-#if _MSC_VER && !defined(__INTEL_COMPILER)
- #pragma warning (pop)
-#endif
-
-template<typename T>
-void TestAlignmentOfOneClass() {
- typedef TaskWithMember<T> task_type;
- tbb::task* t = new( tbb::task::allocate_root() ) task_type(10);
- tbb::task::spawn_root_and_wait(*t);
-}
-
-#include "harness_m128.h"
-
-void TestAlignment() {
- REMARK("testing alignment\n");
- tbb::task_scheduler_init init;
- // Try types that have variety of alignments
- TestAlignmentOfOneClass<char>();
- TestAlignmentOfOneClass<short>();
- TestAlignmentOfOneClass<int>();
- TestAlignmentOfOneClass<long>();
- TestAlignmentOfOneClass<void*>();
- TestAlignmentOfOneClass<float>();
- TestAlignmentOfOneClass<double>();
-#if HAVE_m128
- TestAlignmentOfOneClass<__m128>();
-#endif /* HAVE_m128 */
-}
-
-//------------------------------------------------------------------------
-// Test for recursing on left while spawning on right
-//------------------------------------------------------------------------
-
-int Fib( int n );
-
-struct RightFibTask: public tbb::task {
- int* y;
- const int n;
- RightFibTask( int* y_, int n_ ) : y(y_), n(n_) {}
- task* execute() {
- *y = Fib(n-1);
- return 0;
- }
-};
-
-int Fib( int n ) {
- if( n<2 ) {
- return n;
- } else {
- // y actually does not need to be initialized. It is initialized solely to suppress
- // a gratuitous warning "potentially uninitialized local variable".
- int y=-1;
- tbb::task* root_task = new( tbb::task::allocate_root() ) tbb::empty_task;
- root_task->set_ref_count(2);
- tbb::task::spawn( *new( root_task->allocate_child() ) RightFibTask(&y,n) );
- int x = Fib(n-2);
- root_task->wait_for_all();
- tbb::task::destroy(*root_task);
- return y+x;
- }
-}
-
-void TestLeftRecursion( int p ) {
- REMARK("testing non-spawned roots for %d threads\n",p);
- tbb::task_scheduler_init init(p);
- int sum = 0;
- for( int i=0; i<100; ++i )
- sum +=Fib(10);
- ASSERT( sum==5500, NULL );
-}
-
-//------------------------------------------------------------------------
-// Test for computing with DAG of tasks.
-//------------------------------------------------------------------------
-
-class DagTask: public tbb::task {
- typedef unsigned long long number_t;
- const int i, j;
- number_t sum_from_left, sum_from_above;
- void check_sum( number_t sum ) {
- number_t expected_sum = 1;
- for( int k=i+1; k<=i+j; ++k )
- expected_sum *= k;
- for( int k=1; k<=j; ++k )
- expected_sum /= k;
- ASSERT(sum==expected_sum, NULL);
- }
-public:
- DagTask *successor_to_below, *successor_to_right;
- DagTask( int i_, int j_ ) : i(i_), j(j_), sum_from_left(0), sum_from_above(0) {}
- task* execute() {
- __TBB_ASSERT( ref_count()==0, NULL );
- number_t sum = i==0 && j==0 ? 1 : sum_from_left+sum_from_above;
- check_sum(sum);
- ++execution_count;
- if( DagTask* t = successor_to_right ) {
- t->sum_from_left = sum;
- if( t->decrement_ref_count()==0 )
- // Test using spawn to evaluate DAG
- spawn( *t );
- }
- if( DagTask* t = successor_to_below ) {
- t->sum_from_above = sum;
- if( t->decrement_ref_count()==0 )
- // Test using bypass to evaluate DAG
- return t;
- }
- return NULL;
- }
- ~DagTask() {++destruction_count;}
- static tbb::atomic<int> execution_count;
- static tbb::atomic<int> destruction_count;
-};
-
-tbb::atomic<int> DagTask::execution_count;
-tbb::atomic<int> DagTask::destruction_count;
-
-void TestDag( int p ) {
- REMARK("testing evaluation of DAG for %d threads\n",p);
- tbb::task_scheduler_init init(p);
- DagTask::execution_count=0;
- DagTask::destruction_count=0;
- const int n = 10;
- DagTask* a[n][n];
- for( int i=0; i<n; ++i )
- for( int j=0; j<n; ++j )
- a[i][j] = new( tbb::task::allocate_root() ) DagTask(i,j);
- for( int i=0; i<n; ++i )
- for( int j=0; j<n; ++j ) {
- a[i][j]->successor_to_below = i+1<n ? a[i+1][j] : NULL;
- a[i][j]->successor_to_right = j+1<n ? a[i][j+1] : NULL;
- a[i][j]->set_ref_count((i>0)+(j>0));
- }
- a[n-1][n-1]->increment_ref_count();
- a[n-1][n-1]->spawn_and_wait_for_all(*a[0][0]);
- ASSERT( DagTask::execution_count == n*n - 1, NULL );
- tbb::task::destroy(*a[n-1][n-1]);
- ASSERT( DagTask::destruction_count > n*n - p, NULL );
- while ( DagTask::destruction_count != n*n )
- __TBB_Yield();
-}
-
-#include "harness_barrier.h"
-
-class RelaxedOwnershipTask: public tbb::task {
- tbb::task &m_taskToSpawn,
- &m_taskToDestroy,
- &m_taskToExecute;
- static Harness::SpinBarrier m_barrier;
-
- tbb::task* execute () {
- tbb::task &p = *parent();
- tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- r.set_ref_count( 1 );
- m_barrier.wait();
- p.spawn( *new(p.allocate_child()) tbb::empty_task );
- p.spawn( *new(task::allocate_additional_child_of(p)) tbb::empty_task );
- p.spawn( m_taskToSpawn );
- p.destroy( m_taskToDestroy );
- r.spawn_and_wait_for_all( m_taskToExecute );
- p.destroy( r );
- return NULL;
- }
-public:
- RelaxedOwnershipTask ( tbb::task& toSpawn, tbb::task& toDestroy, tbb::task& toExecute )
- : m_taskToSpawn(toSpawn)
- , m_taskToDestroy(toDestroy)
- , m_taskToExecute(toExecute)
- {}
- static void SetBarrier ( int numThreads ) { m_barrier.initialize( numThreads ); }
-};
-
-Harness::SpinBarrier RelaxedOwnershipTask::m_barrier;
-
-void TestRelaxedOwnership( int p ) {
- if ( p < 2 )
- return;
-
- if( unsigned(p)>tbb::tbb_thread::hardware_concurrency() )
- return;
-
- REMARK("testing tasks exercising relaxed ownership freedom for %d threads\n", p);
- tbb::task_scheduler_init init(p);
- RelaxedOwnershipTask::SetBarrier(p);
- tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- tbb::task_list tl;
- for ( int i = 0; i < p; ++i ) {
- tbb::task &tS = *new( r.allocate_child() ) tbb::empty_task,
- &tD = *new( r.allocate_child() ) tbb::empty_task,
- &tE = *new( r.allocate_child() ) tbb::empty_task;
- tl.push_back( *new( r.allocate_child() ) RelaxedOwnershipTask(tS, tD, tE) );
- }
- r.set_ref_count( 5 * p + 1 );
- int k=0;
- GetTaskPtr(k)->spawn( tl );
- ASSERT(k==1,NULL);
- r.wait_for_all();
- r.destroy( r );
-}
-
-//------------------------------------------------------------------------
-// Test for running TBB scheduler on user-created thread.
-//------------------------------------------------------------------------
-
-void RunSchedulerInstanceOnUserThread( int n_child ) {
- tbb::task* e = new( tbb::task::allocate_root() ) tbb::empty_task;
- e->set_ref_count(1+n_child);
- for( int i=0; i<n_child; ++i )
- tbb::task::spawn( *new(e->allocate_child()) tbb::empty_task );
- e->wait_for_all();
- e->destroy(*e);
-}
-
-void TestUserThread( int p ) {
- tbb::task_scheduler_init init(p);
- // Try with both 0 and 1 children. Only the latter scenario permits stealing.
- for( int n_child=0; n_child<2; ++n_child ) {
- tbb::tbb_thread t( RunSchedulerInstanceOnUserThread, n_child );
- t.join();
- }
-}
-
-
-class TaskWithChildToSteal : public tbb::task {
- const int m_Depth;
- volatile bool m_GoAhead;
-
-public:
- TaskWithChildToSteal( int depth_ )
- : m_Depth(depth_)
- , m_GoAhead(false)
- {}
-
- /*override*/
- tbb::task* execute() {
- m_GoAhead = true;
- if ( m_Depth > 0 ) {
- TaskWithChildToSteal &t = *new( tbb::task::allocate_child() ) TaskWithChildToSteal(m_Depth - 1);
- t.SpawnAndWaitOnParent();
- }
- else
- Harness::Sleep(50); // The last task in chain sleeps for 50 ms
- return NULL;
- }
-
- void SpawnAndWaitOnParent() {
- parent()->set_ref_count( 2 );
- parent()->spawn( *this );
- while (!this->m_GoAhead )
- __TBB_Yield();
- parent()->wait_for_all();
- }
-}; // TaskWithChildToSteal
-
-void TestDispatchLoopResponsiveness() {
- REMARK("testing that dispatch loops do not go into eternal sleep when all remaining children are stolen\n");
- // Recursion depth values test the following sorts of dispatch loops
- // 0 - master's outermost
- // 1 - worker's nested
- // 2 - master's nested
- tbb::task_scheduler_init init(2);
- tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- for ( int depth = 0; depth < 3; ++depth ) {
- TaskWithChildToSteal &t = *new( r.allocate_child() ) TaskWithChildToSteal(depth);
- t.SpawnAndWaitOnParent();
- }
- r.destroy(r);
- // The success criteria of this test is not hanging
-}
-
-void TestWaitDiscriminativenessWithoutStealing() {
- REMARK( "testing that task::wait_for_all is specific to the root it is called on (no workers)\n" );
- // The test relies on the strict LIFO scheduling order in the absence of workers
- tbb::task_scheduler_init init(1);
- tbb::task &r1 = *new( tbb::task::allocate_root() ) tbb::empty_task;
- tbb::task &r2 = *new( tbb::task::allocate_root() ) tbb::empty_task;
- const int NumChildren = 10;
- r1.set_ref_count( NumChildren + 1 );
- r2.set_ref_count( NumChildren + 1 );
- for( int i=0; i < NumChildren; ++i ) {
- tbb::empty_task &t1 = *new( r1.allocate_child() ) tbb::empty_task;
- tbb::empty_task &t2 = *new( r2.allocate_child() ) tbb::empty_task;
- tbb::task::spawn(t1);
- tbb::task::spawn(t2);
- }
- r2.wait_for_all();
- ASSERT( r2.ref_count() <= 1, "Not all children of r2 executed" );
- ASSERT( r1.ref_count() > 1, "All children of r1 prematurely executed" );
- r1.wait_for_all();
- ASSERT( r1.ref_count() <= 1, "Not all children of r1 executed" );
- r1.destroy(r1);
- r2.destroy(r2);
-}
-
-
-using tbb::internal::spin_wait_until_eq;
-
-//! Deterministic emulation of a long running task
-class LongRunningTask : public tbb::task {
- volatile bool& m_CanProceed;
-
- tbb::task* execute() {
- spin_wait_until_eq( m_CanProceed, true );
- return NULL;
- }
-public:
- LongRunningTask ( volatile bool& canProceed ) : m_CanProceed(canProceed) {}
-};
-
-void TestWaitDiscriminativenessWithStealing() {
- if( tbb::tbb_thread::hardware_concurrency() < 2 )
- return;
- REMARK( "testing that task::wait_for_all is specific to the root it is called on (one worker)\n" );
- volatile bool canProceed = false;
- tbb::task_scheduler_init init(2);
- tbb::task &r1 = *new( tbb::task::allocate_root() ) tbb::empty_task;
- tbb::task &r2 = *new( tbb::task::allocate_root() ) tbb::empty_task;
- r1.set_ref_count( 2 );
- r2.set_ref_count( 2 );
- tbb::task& t1 = *new( r1.allocate_child() ) tbb::empty_task;
- tbb::task& t2 = *new( r2.allocate_child() ) LongRunningTask(canProceed);
- tbb::task::spawn(t2);
- tbb::task::spawn(t1);
- r1.wait_for_all();
- ASSERT( r1.ref_count() <= 1, "Not all children of r1 executed" );
- ASSERT( r2.ref_count() == 2, "All children of r2 prematurely executed" );
- canProceed = true;
- r2.wait_for_all();
- ASSERT( r2.ref_count() <= 1, "Not all children of r2 executed" );
- r1.destroy(r1);
- r2.destroy(r2);
-}
-
-struct MasterBody : NoAssign, Harness::NoAfterlife {
- static Harness::SpinBarrier my_barrier;
-
- class BarrenButLongTask : public tbb::task {
- volatile bool& m_Started;
- volatile bool& m_CanProceed;
-
- tbb::task* execute() {
- m_Started = true;
- spin_wait_until_eq( m_CanProceed, true );
- volatile int k = 0;
- for ( int i = 0; i < 1000000; ++i ) ++k;
- return NULL;
- }
- public:
- BarrenButLongTask ( volatile bool& started, volatile bool& can_proceed )
- : m_Started(started), m_CanProceed(can_proceed)
- {}
- };
-
- class BinaryRecursiveTask : public tbb::task {
- int m_Depth;
-
- tbb::task* execute() {
- if( !m_Depth )
- return NULL;
- set_ref_count(3);
- spawn( *new( tbb::task::allocate_child() ) BinaryRecursiveTask(m_Depth - 1) );
- spawn( *new( tbb::task::allocate_child() ) BinaryRecursiveTask(m_Depth - 1) );
- wait_for_all();
- return NULL;
- }
-
- void note_affinity( affinity_id ) {
- __TBB_ASSERT( false, "These tasks cannot be stolen" );
- }
- public:
- BinaryRecursiveTask ( int depth_ ) : m_Depth(depth_) {}
- };
-
- void operator() ( int id ) const {
- if ( id ) {
- tbb::task_scheduler_init init(2);
- volatile bool child_started = false,
- can_proceed = false;
- tbb::task& r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- r.set_ref_count(2);
- r.spawn( *new(r.allocate_child()) BarrenButLongTask(child_started, can_proceed) );
- spin_wait_until_eq( child_started, true );
- my_barrier.wait();
- can_proceed = true;
- r.wait_for_all();
- r.destroy(r);
- }
- else {
- my_barrier.wait();
- tbb::task_scheduler_init init(1);
- Count = 0;
- int depth = 16;
- BinaryRecursiveTask& r = *new( tbb::task::allocate_root() ) BinaryRecursiveTask(depth);
- tbb::task::spawn_root_and_wait(r);
- }
- }
-public:
- MasterBody ( int num_masters ) { my_barrier.initialize(num_masters); }
-};
-
-Harness::SpinBarrier MasterBody::my_barrier;
-
-/** Ensures that tasks spawned by a master thread or one of the workers servicing
- it cannot be stolen by another master thread. **/
-void TestMastersIsolation ( int p ) {
- // The test requires at least 3-way parallelism to work correctly
- if ( p > 2 && tbb::task_scheduler_init::default_num_threads() >= p ) {
- tbb::task_scheduler_init init(p);
- NativeParallelFor( p, MasterBody(p) );
- }
-}
-
-//------------------------------------------------------------------------
-// Test for tbb::task::enqueue
-//------------------------------------------------------------------------
-
-const int PairsPerTrack = 100;
-
-class EnqueuedTask : public tbb::task {
- task* my_successor;
- int my_enqueue_order;
- int* my_track;
- tbb::task* execute() {
- // Capture execution order in the very beginning
- int execution_order = 2 - my_successor->decrement_ref_count();
- // Create some local work.
- TaskGenerator& p = *new( tbb::task::allocate_root() ) TaskGenerator(2,2);
- tbb::task::spawn_root_and_wait(p);
- if( execution_order==2 ) { // the "slower" of two peer tasks
- ++nCompletedPairs;
- // Of course execution order can differ from dequeue order.
- // But there is no better approximation at hand; and a single worker
- // will execute in dequeue order, which is enough for our check.
- if (my_enqueue_order==execution_order)
- ++nOrderedPairs;
- FireTwoTasks(my_track);
- destroy(*my_successor);
- }
- return NULL;
- }
-public:
- EnqueuedTask( task* successor, int enq_order, int* track )
- : my_successor(successor), my_enqueue_order(enq_order), my_track(track) {}
-
- // Create and enqueue two tasks
- static void FireTwoTasks( int* track ) {
- int progress = ++*track;
- if( progress < PairsPerTrack ) {
- task* successor = new (tbb::task::allocate_root()) tbb::empty_task;
- successor->set_ref_count(2);
- enqueue( *new (tbb::task::allocate_root()) EnqueuedTask(successor, 1, track) );
- enqueue( *new (tbb::task::allocate_root()) EnqueuedTask(successor, 2, track) );
- }
- }
-
- static tbb::atomic<int> nCompletedPairs;
- static tbb::atomic<int> nOrderedPairs;
-};
-
-tbb::atomic<int> EnqueuedTask::nCompletedPairs;
-tbb::atomic<int> EnqueuedTask::nOrderedPairs;
-
-const int nTracks = 10;
-static int TaskTracks[nTracks];
-const int stall_threshold = 100000;
-
-void TimedYield( double pause_time );
-
-class ProgressMonitor {
-public:
- void operator() ( ) {
- int track_snapshot[nTracks];
- int stall_count = 0, uneven_progress_count = 0, last_progress_mask = 0;
- for(int i=0; i<nTracks; ++i)
- track_snapshot[i]=0;
- bool completed;
- do {
- // Yield repeatedly for at least 1 usec
- TimedYield( 1E-6 );
- int overall_progress = 0, progress_mask = 0;
- const int all_progressed = (1<<nTracks) - 1;
- completed = true;
- for(int i=0; i<nTracks; ++i) {
- int ti = TaskTracks[i];
- int pi = ti-track_snapshot[i];
- if( pi ) progress_mask |= 1<<i;
- overall_progress += pi;
- completed = completed && ti==PairsPerTrack;
- track_snapshot[i]=ti;
- }
- // The constants in the next asserts are subjective and may need correction.
- if( overall_progress )
- stall_count=0;
- else {
- ++stall_count;
- // no progress for at least 0.1 s; consider it dead.
- ASSERT(stall_count < stall_threshold, "no progress on enqueued tasks; deadlock?");
- }
- if( progress_mask==all_progressed || progress_mask^last_progress_mask ) {
- uneven_progress_count = 0;
- last_progress_mask = progress_mask;
- }
- else if ( overall_progress > 2 ) {
- ++uneven_progress_count;
- ASSERT(uneven_progress_count < 5, "some enqueued tasks seem stalling; no simultaneous progress?");
- }
- } while( !completed );
- }
-};
-
-void TestEnqueue( int p ) {
- REMARK("testing task::enqueue for %d threads\n", p);
- for(int mode=0;mode<3;++mode) {
- tbb::task_scheduler_init init(p);
- EnqueuedTask::nCompletedPairs = EnqueuedTask::nOrderedPairs = 0;
- for(int i=0; i<nTracks; ++i) {
- TaskTracks[i] = -1; // to accomodate for the starting call
- EnqueuedTask::FireTwoTasks(TaskTracks+i);
- }
- ProgressMonitor pm;
- tbb::tbb_thread thr( pm );
- if(mode==1) {
- // do some parallel work in the meantime
- for(int i=0; i<10; i++) {
- TaskGenerator& g = *new( tbb::task::allocate_root() ) TaskGenerator(2,5);
- tbb::task::spawn_root_and_wait(g);
- TimedYield( 1E-6 );
- }
- }
- if( mode==2 ) {
- // Additionally enqueue a bunch of empty tasks. The goal is to test that tasks
- // allocated and enqueued by a thread are safe to use after the thread leaves TBB.
- tbb::task* root = new (tbb::task::allocate_root()) tbb::empty_task;
- root->set_ref_count(100);
- for( int i=0; i<100; ++i )
- tbb::task::enqueue( *new (root->allocate_child()) tbb::empty_task );
- init.terminate(); // master thread deregistered
- }
- thr.join();
- ASSERT(EnqueuedTask::nCompletedPairs==nTracks*PairsPerTrack, NULL);
- ASSERT(EnqueuedTask::nOrderedPairs<EnqueuedTask::nCompletedPairs,
- "all task pairs executed in enqueue order; de facto guarantee is too strong?");
- }
-}
-
-//------------------------------------------------------------------------
-// Run all tests.
-//------------------------------------------------------------------------
-
-int TestMain () {
-#if TBB_USE_EXCEPTIONS
- TestUnconstructibleTask<1>();
- TestUnconstructibleTask<10000>();
-#endif
- TestAlignment();
- TestNoteAffinityContext();
- TestDispatchLoopResponsiveness();
- TestWaitDiscriminativenessWithoutStealing();
- TestWaitDiscriminativenessWithStealing();
- for( int p=MinThread; p<=MaxThread; ++p ) {
- TestSpawnChildren( p );
- TestSpawnRootList( p );
- TestSafeContinuation( p );
- TestEnqueue( p );
- TestLeftRecursion( p );
- TestDag( p );
- TestAffinity( p );
- TestUserThread( p );
- TestStealLimit( p );
- TestRelaxedOwnership( p );
-#if __TBB_ARENA_PER_MASTER
- TestMastersIsolation( p );
-#endif /* __TBB_ARENA_PER_MASTER */
- }
- return Harness::Done;
-}
-
-#include "tbb/tick_count.h"
-void TimedYield( double pause_time ) {
- tbb::tick_count start = tbb::tick_count::now();
- while( (tbb::tick_count::now()-start).seconds() < pause_time )
- __TBB_Yield();
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test correctness of forceful TBB initialization before any dynamic initialization
-// of static objects inside the library took place.
-namespace tbb {
-namespace internal {
- // Forward declaration of the TBB general initialization routine from task.cpp
- void DoOneTimeInitializations();
-}}
-
-struct StaticInitializationChecker {
- StaticInitializationChecker () { tbb::internal::DoOneTimeInitializations(); }
-} theChecker;
-
-//------------------------------------------------------------------------
-// Test that important assertions in class task fail as expected.
-//------------------------------------------------------------------------
-
-#include "harness_inject_scheduler.h"
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-#include "harness_bad_expr.h"
-
-#if TRY_BAD_EXPR_ENABLED
-//! Task that will be abused.
-tbb::task* volatile AbusedTask;
-
-//! Number of times that AbuseOneTask
-int AbuseOneTaskRan;
-
-//! Body used to create task in thread 0 and abuse it in thread 1.
-struct AbuseOneTask {
- void operator()( int ) const {
- tbb::task_scheduler_init init;
- // Thread 1 attempts to incorrectly use the task created by thread 0.
- tbb::task_list list;
- // spawn_root_and_wait over empty list should vacuously succeed.
- tbb::task::spawn_root_and_wait(list);
-
- // Check that spawn_root_and_wait fails on non-empty list.
- list.push_back(*AbusedTask);
-
- // Try abusing recycle_as_continuation
- TRY_BAD_EXPR(AbusedTask->recycle_as_continuation(), "execute" );
- TRY_BAD_EXPR(AbusedTask->recycle_as_safe_continuation(), "execute" );
- TRY_BAD_EXPR(AbusedTask->recycle_to_reexecute(), "execute" );
- ++AbuseOneTaskRan;
- }
-};
-
-//! Test various __TBB_ASSERT assertions related to class tbb::task.
-void TestTaskAssertions() {
- // Catch assertion failures
- tbb::set_assertion_handler( AssertionFailureHandler );
- tbb::task_scheduler_init init;
- // Create task to be abused
- AbusedTask = new( tbb::task::allocate_root() ) tbb::empty_task;
- NativeParallelFor( 1, AbuseOneTask() );
- ASSERT( AbuseOneTaskRan==1, NULL );
- AbusedTask->destroy(*AbusedTask);
- // Restore normal assertion handling
- tbb::set_assertion_handler( NULL );
-}
-
-int TestMain () {
- TestTaskAssertions();
- return Harness::Done;
-}
-
-#else /* !TRY_BAD_EXPR_ENABLED */
-
-int TestMain () {
- return Harness::Skipped;
-}
-
-#endif /* !TRY_BAD_EXPR_ENABLED */
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Testing automatic initialization of TBB task scheduler, so do not use task_scheduler_init anywhere.
-
-#include "tbb/task.h"
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-#include "tbb/atomic.h"
-
-static tbb::atomic<int> g_NumTestsExecuted;
-
-#define TEST_PROLOGUE() ++g_NumTestsExecuted
-
-// Global data used in testing use cases with cross-thread usage of TBB objects
-static tbb::task *g_Root1 = NULL,
- *g_Root2 = NULL,
- *g_Root3 = NULL,
- *g_Task = NULL;
-static tbb::task_group_context* g_Ctx = NULL;
-
-
-void TestTaskSelf () {
- TEST_PROLOGUE();
- tbb::task& t = tbb::task::self();
- ASSERT( !t.parent() && t.ref_count() == 1 && !t.affinity(), "Master's default task properties changed?" );
-}
-
-void TestRootAllocation () {
- TEST_PROLOGUE();
- tbb::task &r = *new( tbb::task::allocate_root() ) tbb::empty_task;
- tbb::task::spawn_root_and_wait(r);
-}
-
-inline void ExecuteChildAndCleanup ( tbb::task &r, tbb::task &t ) {
- r.set_ref_count(2);
- r.spawn_and_wait_for_all(t);
- r.destroy(r);
-}
-
-void TestChildAllocation () {
- TEST_PROLOGUE();
- tbb::task &t = *new( g_Root1->allocate_child() ) tbb::empty_task;
- ExecuteChildAndCleanup( *g_Root1, t );
-}
-
-void TestAdditionalChildAllocation () {
- TEST_PROLOGUE();
- tbb::task &t = *new( g_Root2->allocate_additional_child_of(*g_Root2) ) tbb::empty_task;
- ExecuteChildAndCleanup( *g_Root2, t );
-}
-
-void TestTaskGroupContextCreation () {
- TEST_PROLOGUE();
- tbb::task_group_context ctx;
- tbb::task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task;
- tbb::task::spawn_root_and_wait(r);
-}
-
-void TestRootAllocationWithContext () {
- TEST_PROLOGUE();
- tbb::task* root = new( tbb::task::allocate_root(*g_Ctx) ) tbb::empty_task;
- tbb::task::spawn_root_and_wait(*root);
-}
-
-void TestSpawn () {
- TEST_PROLOGUE();
- g_Task->spawn(*g_Task);
-}
-
-void TestWaitForAll () {
- TEST_PROLOGUE();
- g_Root3->wait_for_all();
- g_Root3->destroy( *g_Root3 );
-}
-
-typedef void (*TestFnPtr)();
-
-const TestFnPtr TestFuncsTable[] = {
- TestTaskSelf, TestRootAllocation, TestChildAllocation, TestAdditionalChildAllocation,
- TestTaskGroupContextCreation, TestRootAllocationWithContext, TestSpawn, TestWaitForAll };
-
-const int NumTestFuncs = sizeof(TestFuncsTable) / sizeof(TestFnPtr);
-
-struct TestThreadBody : NoAssign, Harness::NoAfterlife {
- // Each invocation of operator() happens in a fresh thread with zero-based ID
- // id, and checks a specific auto-initialization scenario.
- void operator() ( int id ) const {
- ASSERT( id >= 0 && id < NumTestFuncs, "Test diver: NativeParallelFor is used incorrectly" );
- TestFuncsTable[id]();
- }
-};
-
-
-#include "../tbb/tls.h"
-
-void UseAFewNewTlsKeys () {
- tbb::internal::tls<intptr_t> tls1, tls2, tls3, tls4;
- tls1 = tls2 = tls3 = tls4 = -1;
-}
-
-using tbb::internal::spin_wait_until_eq;
-
-volatile bool FafStarted = false,
- FafCanFinish = false,
- FafCompleted = false;
-
-//! This task is supposed to be executed during termination of an auto-initialized master thread
-class FireAndForgetTask : public tbb::task {
- tbb::task* execute () {
- // Let another master thread proceed requesting new TLS keys
- FafStarted = true;
- UseAFewNewTlsKeys();
- // Wait while another master thread dirtied its new TLS slots
- spin_wait_until_eq( FafCanFinish, true );
- FafCompleted = true;
- return NULL;
- }
-public: // to make gcc 3.2.3 happy
- ~FireAndForgetTask() {
- ASSERT(FafCompleted, "FireAndForgetTask got erroneously cancelled?");
- }
-};
-
-#include "harness_barrier.h"
-Harness::SpinBarrier driver_barrier(2);
-
-struct DriverThreadBody : NoAssign, Harness::NoAfterlife {
- void operator() ( int id ) const {
- ASSERT( id < 2, "Only two test driver threads are expected" );
- // a barrier is required to ensure both threads started; otherwise the test may deadlock:
- // the first thread would execute FireAndForgetTask at shutdown and wait for FafCanFinish,
- // while the second thread wouldn't even start waiting for the loader lock hold by the first one.
- if ( id == 0 ) {
- driver_barrier.wait();
- // Prepare global data
- g_Root1 = new( tbb::task::allocate_root() ) tbb::empty_task;
- g_Root2 = new( tbb::task::allocate_root() ) tbb::empty_task;
- g_Root3 = new( tbb::task::allocate_root() ) tbb::empty_task;
- g_Task = new( g_Root3->allocate_child() ) tbb::empty_task;
- g_Root3->set_ref_count(2);
- // Run tests
- NativeParallelFor( NumTestFuncs, TestThreadBody() );
- ASSERT( g_NumTestsExecuted == NumTestFuncs, "Test driver: Wrong number of tests executed" );
-
- // This test checks the validity of temporarily restoring the value of
- // the last TLS slot for a given key during the termination of an
- // auto-initialized master thread (in governor::auto_terminate).
- // If anything goes wrong, generic_scheduler::cleanup_master() will assert.
- // The context for this task must be valid till the task completion.
- tbb::task &r = *new( tbb::task::allocate_root(*g_Ctx) ) FireAndForgetTask;
- r.spawn(r);
- }
- else {
- tbb::task_group_context ctx;
- g_Ctx = &ctx;
- driver_barrier.wait();
- spin_wait_until_eq( FafStarted, true );
- UseAFewNewTlsKeys();
- FafCanFinish = true;
- spin_wait_until_eq( FafCompleted, true );
- }
- }
-};
-
-int TestMain () {
- // Do not use any TBB functionality in the main thread!
- NativeParallelFor( 2, DriverThreadBody() );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-//! task_handle<T> cannot be instantiated with a function ptr without explicit cast
-#define __TBB_FUNC_PTR_AS_TEMPL_PARAM_BROKEN ((__linux__ || __APPLE__) && __INTEL_COMPILER && __INTEL_COMPILER < 1100) || __SUNPRO_CC
-#define __TBB_UNQUALIFIED_CALL_OF_DTOR_BROKEN (__GNUC__==3 && __GNUC_MINOR__<=3)
-
-#ifndef TBBTEST_USE_TBB
- #define TBBTEST_USE_TBB 1
-#endif
-
-#if !TBBTEST_USE_TBB
- #if defined(_MSC_VER) && _MSC_VER < 1600
- #ifdef TBBTEST_USE_TBB
- #undef TBBTEST_USE_TBB
- #endif
- #define TBBTEST_USE_TBB 1
- #endif
-#endif
-
-#if TBBTEST_USE_TBB
-
- #include "tbb/compat/ppl.h"
- #include "tbb/task_scheduler_init.h"
-
- #if _MSC_VER
- typedef tbb::internal::uint32_t uint_t;
- #else
- typedef uint32_t uint_t;
- #endif
-
-#else /* !TBBTEST_USE_TBB */
-
- #if defined(_MSC_VER)
- #pragma warning(disable: 4100 4180)
- #endif
-
- #include <ppl.h>
-
- typedef unsigned int uint_t;
-
- #define __TBB_SILENT_CANCELLATION_BROKEN (_MSC_VER == 1600)
-
-#endif /* !TBBTEST_USE_TBB */
-
-
-#include "tbb/atomic.h"
-#include "harness_concurrency_tracker.h"
-
-unsigned g_MaxConcurrency = 0;
-
-typedef tbb::atomic<uint_t> atomic_t;
-typedef Concurrency::task_handle<void(*)()> handle_type;
-
-//------------------------------------------------------------------------
-// Tests for the thread safety of the task_group manipulations
-//------------------------------------------------------------------------
-
-#include "harness_barrier.h"
-
-enum SharingMode {
- VagabondGroup = 1,
- ParallelWait = 2
-};
-
-class SharedGroupBodyImpl : NoCopy, Harness::NoAfterlife {
- static const uint_t c_numTasks0 = 4096,
- c_numTasks1 = 1024;
-
- const uint_t m_numThreads;
- const uint_t m_sharingMode;
-
- Concurrency::task_group *m_taskGroup;
- atomic_t m_tasksSpawned,
- m_threadsReady;
- Harness::SpinBarrier m_barrier;
-
- static atomic_t s_tasksExecuted;
-
- struct TaskFunctor {
- SharedGroupBodyImpl *m_pOwner;
- void operator () () const {
- if ( m_pOwner->m_sharingMode & ParallelWait ) {
- while ( Harness::ConcurrencyTracker::PeakParallelism() < m_pOwner->m_numThreads )
- __TBB_Yield();
- }
- ++s_tasksExecuted;
- }
- };
-
- TaskFunctor m_taskFunctor;
-
- void Spawn ( uint_t numTasks ) {
- for ( uint_t i = 0; i < numTasks; ++i ) {
- ++m_tasksSpawned;
- Harness::ConcurrencyTracker ct;
- m_taskGroup->run( m_taskFunctor );
- }
- ++m_threadsReady;
- }
-
- void DeleteTaskGroup () {
- delete m_taskGroup;
- m_taskGroup = NULL;
- }
-
- void Wait () {
- while ( m_threadsReady != m_numThreads )
- __TBB_Yield();
- const uint_t numSpawned = c_numTasks0 + c_numTasks1 * (m_numThreads - 1);
- ASSERT ( m_tasksSpawned == numSpawned, "Wrong number of spawned tasks. The test is broken" );
- REMARK("Max spawning parallelism is %u out of %u", Harness::ConcurrencyTracker::PeakParallelism(), g_MaxConcurrency);
- if ( m_sharingMode & ParallelWait ) {
- m_barrier.wait( &Harness::ConcurrencyTracker::Reset );
- {
- Harness::ConcurrencyTracker ct;
- m_taskGroup->wait();
- }
- if ( Harness::ConcurrencyTracker::PeakParallelism() == 1 )
- REPORT ( "Warning: No parallel waiting detected in TestParallelWait\n" );
- m_barrier.wait();
- }
- else
- m_taskGroup->wait();
- ASSERT ( m_tasksSpawned == numSpawned, "No tasks should be spawned after wait starts. The test is broken" );
- ASSERT ( s_tasksExecuted == numSpawned, "Not all spawned tasks were executed" );
- }
-
-public:
- SharedGroupBodyImpl ( uint_t numThreads, uint_t sharingMode = 0 )
- : m_numThreads(numThreads)
- , m_sharingMode(sharingMode)
- , m_taskGroup(NULL)
- , m_barrier(numThreads)
- {
- ASSERT ( m_numThreads > 1, "SharedGroupBody tests require concurrency" );
- ASSERT ( !(m_sharingMode & VagabondGroup) || m_numThreads == 2, "In vagabond mode SharedGroupBody must be used with 2 threads only" );
- Harness::ConcurrencyTracker::Reset();
- s_tasksExecuted = 0;
- m_tasksSpawned = 0;
- m_threadsReady = 0;
- m_taskFunctor.m_pOwner = this;
- }
-
- void Run ( uint_t idx ) {
-#if TBBTEST_USE_TBB
- tbb::task_scheduler_init init;
-#endif
- AssertLive();
- if ( idx == 0 ) {
- ASSERT ( !m_taskGroup && !m_tasksSpawned, "SharedGroupBody must be reset before reuse");
- m_taskGroup = new Concurrency::task_group;
- Spawn( c_numTasks0 );
- Wait();
- if ( m_sharingMode & VagabondGroup )
- m_barrier.wait();
- else
- DeleteTaskGroup();
- }
- else {
- while ( m_tasksSpawned == 0 )
- __TBB_Yield();
- ASSERT ( m_taskGroup, "Task group is not initialized");
- Spawn (c_numTasks1);
- if ( m_sharingMode & ParallelWait )
- Wait();
- if ( m_sharingMode & VagabondGroup ) {
- ASSERT ( idx == 1, "In vagabond mode SharedGroupBody must be used with 2 threads only" );
- m_barrier.wait();
- DeleteTaskGroup();
- }
- }
- AssertLive();
- }
-};
-
-atomic_t SharedGroupBodyImpl::s_tasksExecuted;
-
-class SharedGroupBody : NoAssign, Harness::NoAfterlife {
- bool m_bOwner;
- mutable SharedGroupBodyImpl *m_pImpl;
-public:
- SharedGroupBody ( uint_t numThreads, uint_t sharingMode = 0 )
- : m_bOwner(true)
- , m_pImpl( new SharedGroupBodyImpl(numThreads, sharingMode) )
- {}
- SharedGroupBody ( const SharedGroupBody& src )
- : NoAssign()
- , Harness::NoAfterlife()
- , m_bOwner(false)
- , m_pImpl(src.m_pImpl)
- {}
- ~SharedGroupBody () {
- if ( m_bOwner )
- delete m_pImpl;
- }
- void operator() ( uint_t idx ) const { m_pImpl->Run(idx); }
-};
-
-void TestParallelSpawn () {
- NativeParallelFor( g_MaxConcurrency, SharedGroupBody(g_MaxConcurrency) );
-}
-
-void TestParallelWait () {
- NativeParallelFor( g_MaxConcurrency, SharedGroupBody(g_MaxConcurrency, ParallelWait) );
-}
-
-// Tests non-stack-bound task group (the group that is allocated by one thread and destroyed by the other)
-void TestVagabondGroup () {
- NativeParallelFor( 2, SharedGroupBody(2, VagabondGroup) );
-}
-
-//------------------------------------------------------------------------
-// Common requisites of the Fibonacci tests
-//------------------------------------------------------------------------
-
-const uint_t N = 20;
-const uint_t F = 6765;
-
-atomic_t g_Sum;
-
-#define FIB_TEST_PROLOGUE() \
- const unsigned numRepeats = g_MaxConcurrency * (TBB_USE_DEBUG ? 4 : 16); \
- Harness::ConcurrencyTracker::Reset()
-
-#define FIB_TEST_EPILOGUE(sum) \
- ASSERT( sum == numRepeats * F, NULL ); \
- REMARK("Realized parallelism in Fib test is %u out of %u", Harness::ConcurrencyTracker::PeakParallelism(), g_MaxConcurrency)
-
-//------------------------------------------------------------------------
-// Test for a complex tree of task groups
-//
-// The test executes a tree of task groups of the same sort with asymmetric
-// descendant nodes distribution at each level at each level.
-//
-// The chores are specified as functor objects. Each task group contains only one chore.
-//------------------------------------------------------------------------
-
-template<uint_t Func(uint_t)>
-struct FibTask : NoAssign, Harness::NoAfterlife {
- uint_t* m_pRes;
- const uint_t m_Num;
- FibTask( uint_t* y, uint_t n ) : m_pRes(y), m_Num(n) {}
- void operator() () const {
- *m_pRes = Func(m_Num);
- }
-};
-
-uint_t Fib_SpawnRightChildOnly ( uint_t n ) {
- Harness::ConcurrencyTracker ct;
- if( n<2 ) {
- return n;
- } else {
- uint_t y = ~0u;
- Concurrency::task_group tg;
- tg.run( FibTask<Fib_SpawnRightChildOnly>(&y, n-1) );
- uint_t x = Fib_SpawnRightChildOnly(n-2);
- tg.wait();
- return y+x;
- }
-}
-
-void TestFib1 () {
- FIB_TEST_PROLOGUE();
- uint_t sum = 0;
- for( unsigned i = 0; i < numRepeats; ++i )
- sum += Fib_SpawnRightChildOnly(N);
- FIB_TEST_EPILOGUE(sum);
-}
-
-
-//------------------------------------------------------------------------
-// Test for a mixed tree of task groups.
-//
-// The test executes a tree with multiple task of one sort at the first level,
-// each of which originates in its turn a binary tree of descendant task groups.
-//
-// The chores are specified both as functor objects and as function pointers
-//------------------------------------------------------------------------
-
-uint_t Fib_SpawnBothChildren( uint_t n ) {
- Harness::ConcurrencyTracker ct;
- if( n<2 ) {
- return n;
- } else {
- uint_t y = ~0u,
- x = ~0u;
- Concurrency::task_group tg;
- tg.run( FibTask<Fib_SpawnBothChildren>(&x, n-2) );
- tg.run( FibTask<Fib_SpawnBothChildren>(&y, n-1) );
- tg.wait();
- return y + x;
- }
-}
-
-void RunFib2 () {
- g_Sum += Fib_SpawnBothChildren(N);
-}
-
-void TestFib2 () {
- FIB_TEST_PROLOGUE();
- g_Sum = 0;
- Concurrency::task_group rg;
- for( unsigned i = 0; i < numRepeats - 1; ++i )
- rg.run( &RunFib2 );
- rg.wait();
- rg.run( &RunFib2 );
- rg.wait();
- FIB_TEST_EPILOGUE(g_Sum);
-}
-
-
-//------------------------------------------------------------------------
-// Test for a complex tree of task groups
-// The chores are specified as task handles for recursive functor objects.
-//------------------------------------------------------------------------
-
-class FibTask_SpawnRightChildOnly : NoAssign, Harness::NoAfterlife {
- uint_t* m_pRes;
- mutable uint_t m_Num;
-
-public:
- FibTask_SpawnRightChildOnly( uint_t* y, uint_t n ) : m_pRes(y), m_Num(n) {}
- void operator() () const {
- Harness::ConcurrencyTracker ct;
- AssertLive();
- if( m_Num < 2 ) {
- *m_pRes = m_Num;
- } else {
- uint_t y = ~0u;
- Concurrency::task_group tg;
- Concurrency::task_handle<FibTask_SpawnRightChildOnly> h = FibTask_SpawnRightChildOnly(&y, m_Num-1);
- tg.run( h );
- m_Num -= 2;
- tg.run_and_wait( *this );
- *m_pRes += y;
- }
- }
-};
-
-uint_t RunFib3 ( uint_t n ) {
- uint_t res = ~0u;
- FibTask_SpawnRightChildOnly func(&res, n);
- func();
- return res;
-}
-
-void TestTaskHandle () {
- FIB_TEST_PROLOGUE();
- uint_t sum = 0;
- for( unsigned i = 0; i < numRepeats; ++i )
- sum += RunFib3(N);
- FIB_TEST_EPILOGUE(sum);
-}
-
-//------------------------------------------------------------------------
-// Test for a mixed tree of task groups.
-// The chores are specified as task handles for both functor objects and function pointers
-//------------------------------------------------------------------------
-
-template<class task_group_type>
-class FibTask_SpawnBothChildren : NoAssign, Harness::NoAfterlife {
- uint_t* m_pRes;
- uint_t m_Num;
-public:
- FibTask_SpawnBothChildren( uint_t* y, uint_t n ) : m_pRes(y), m_Num(n) {}
- void operator() () const {
- Harness::ConcurrencyTracker ct;
- AssertLive();
- if( m_Num < 2 ) {
- *m_pRes = m_Num;
- } else {
- uint_t x = ~0u, // initialized only to suppress warning
- y = ~0u;
- task_group_type tg;
- Concurrency::task_handle<FibTask_SpawnBothChildren> h1 = FibTask_SpawnBothChildren(&y, m_Num-1),
- h2 = FibTask_SpawnBothChildren(&x, m_Num-2);
- tg.run( h1 );
- tg.run( h2 );
- tg.wait();
- *m_pRes = x + y;
- }
- }
-};
-
-template<class task_group_type>
-void RunFib4 () {
- uint_t res = ~0u;
- FibTask_SpawnBothChildren<task_group_type> func(&res, N);
- func();
- g_Sum += res;
-}
-
-template<class task_group_type>
-void TestTaskHandle2 () {
- FIB_TEST_PROLOGUE();
- g_Sum = 0;
- task_group_type rg;
- const unsigned hSize = sizeof(handle_type);
- char *handles = new char [numRepeats * hSize];
- handle_type *h = NULL;
- for( unsigned i = 0; ; ++i ) {
- h = (handle_type*)(handles + i * hSize);
-#if __TBB_FUNC_PTR_AS_TEMPL_PARAM_BROKEN
- new ( h ) handle_type((void(*)())RunFib4<task_group_type>);
-#else
- new ( h ) handle_type(RunFib4<task_group_type>);
-#endif
- if ( i == numRepeats - 1 )
- break;
- rg.run( *h );
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- bool caught = false;
- try {
- rg.run( *h );
- }
- catch ( Concurrency::invalid_multiple_scheduling& e ) {
- ASSERT( e.what(), "Error message is absent" );
- caught = true;
- }
- catch ( ... ) {
- ASSERT ( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unrecognized exception" );
- }
- ASSERT ( caught, "Expected invalid_multiple_scheduling exception is missing" );
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
- }
- rg.run_and_wait( *h );
- for( unsigned i = 0; i < numRepeats; ++i )
-#if __TBB_UNQUALIFIED_CALL_OF_DTOR_BROKEN
- ((handle_type*)(handles + i * hSize))->Concurrency::task_handle<void(*)()>::~task_handle();
-#else
- ((handle_type*)(handles + i * hSize))->~handle_type();
-#endif
- delete []handles;
- FIB_TEST_EPILOGUE(g_Sum);
-}
-
-#if __TBB_LAMBDAS_PRESENT
-//------------------------------------------------------------------------
-// Test for a mixed tree of task groups.
-// The chores are specified as lambdas
-//------------------------------------------------------------------------
-
-void TestFibWithLambdas () {
- REMARK ("Lambdas test");
- FIB_TEST_PROLOGUE();
- atomic_t sum;
- sum = 0;
- Concurrency::task_group rg;
- for( unsigned i = 0; i < numRepeats; ++i )
- rg.run( [&](){sum += Fib_SpawnBothChildren(N);} );
- rg.wait();
- FIB_TEST_EPILOGUE(sum);
-}
-
-//------------------------------------------------------------------------
-// Test for make_task.
-// The chores are specified as lambdas converted to task_handles.
-//------------------------------------------------------------------------
-
-void TestFibWithMakeTask () {
- REMARK ("Make_task test");
- atomic_t sum;
- sum = 0;
- Concurrency::task_group rg;
- const auto &h1 = Concurrency::make_task( [&](){sum += Fib_SpawnBothChildren(N);} );
- const auto &h2 = Concurrency::make_task( [&](){sum += Fib_SpawnBothChildren(N);} );
- rg.run( h1 );
- rg.run_and_wait( h2 );
- ASSERT( sum == 2 * F, NULL );
-}
-#endif /* __TBB_LAMBDAS_PRESENT */
-
-
-//------------------------------------------------------------------------
-// Tests for exception handling and cancellation behavior.
-//------------------------------------------------------------------------
-
-class test_exception : public std::exception
-{
- const char* m_strDescription;
-public:
- test_exception ( const char* descr ) : m_strDescription(descr) {}
-
- const char* what() const throw() { return m_strDescription; }
-};
-
-#if TBB_USE_CAPTURED_EXCEPTION
- #include "tbb/tbb_exception.h"
- typedef tbb::captured_exception TestException;
-#else
- typedef test_exception TestException;
-#endif
-
-#include <string.h>
-
-#define NUM_CHORES 512
-#define NUM_GROUPS 64
-#define SKIP_CHORES (NUM_CHORES/4)
-#define SKIP_GROUPS (NUM_GROUPS/4)
-#define EXCEPTION_DESCR1 "Test exception 1"
-#define EXCEPTION_DESCR2 "Test exception 2"
-
-atomic_t g_ExceptionCount;
-atomic_t g_TaskCount;
-unsigned g_ExecutedAtCancellation;
-bool g_Rethrow;
-bool g_Throw;
-#if __TBB_SILENT_CANCELLATION_BROKEN
- volatile bool g_CancellationPropagationInProgress;
- #define CATCH_ANY() \
- __TBB_CATCH( ... ) { \
- if ( g_CancellationPropagationInProgress ) { \
- if ( g_Throw ) { \
- exceptionCaught = true; \
- ++g_ExceptionCount; \
- } \
- } else \
- ASSERT( false, "Unknown exception" ); \
- }
-#else
- #define CATCH_ANY() __TBB_CATCH( ... ) { ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unknown exception" ); }
-#endif
-
-inline
-void ResetGlobals ( bool bThrow, bool bRethrow ) {
- g_Throw = bThrow;
- g_Rethrow = bRethrow;
-#if __TBB_SILENT_CANCELLATION_BROKEN
- g_CancellationPropagationInProgress = false;
-#endif
- g_ExceptionCount = 0;
- g_TaskCount = 0;
- Harness::ConcurrencyTracker::Reset();
-}
-
-class ThrowingTask : NoAssign, Harness::NoAfterlife {
- atomic_t &m_TaskCount;
-public:
- ThrowingTask( atomic_t& counter ) : m_TaskCount(counter) {}
- void operator() () const {
- Harness::ConcurrencyTracker ct;
- AssertLive();
- if ( g_Throw ) {
- if ( ++m_TaskCount == SKIP_CHORES )
- __TBB_THROW( test_exception(EXCEPTION_DESCR1) );
- __TBB_Yield();
- }
- else {
- ++g_TaskCount;
- while( !Concurrency::is_current_task_group_canceling() )
- __TBB_Yield();
- }
- }
-};
-
-void LaunchChildren () {
- atomic_t count;
- count = 0;
- Concurrency::task_group g;
- bool exceptionCaught = false;
- for( unsigned i = 0; i < NUM_CHORES; ++i )
- g.run( ThrowingTask(count) );
- Concurrency::task_group_status status = Concurrency::not_complete;
- __TBB_TRY {
- status = g.wait();
- } __TBB_CATCH ( TestException& e ) {
-#if TBB_USE_EXCEPTIONS
- ASSERT( e.what(), "Empty what() string" );
- ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR1) == 0, "Unknown exception" );
-#endif /* TBB_USE_EXCEPTIONS */
- exceptionCaught = true;
- ++g_ExceptionCount;
- } CATCH_ANY();
- ASSERT( !g_Throw || exceptionCaught || status == Concurrency::canceled, "No exception in the child task group" );
- if ( g_Rethrow && g_ExceptionCount > SKIP_GROUPS ) {
-#if __TBB_SILENT_CANCELLATION_BROKEN
- g_CancellationPropagationInProgress = true;
-#endif
- __TBB_THROW( test_exception(EXCEPTION_DESCR2) );
- }
-}
-
-#if TBB_USE_EXCEPTIONS
-void TestEh1 () {
- ResetGlobals( true, false );
- Concurrency::task_group rg;
- for( unsigned i = 0; i < NUM_GROUPS; ++i )
- // TBB version does not require taking function address
- rg.run( &LaunchChildren );
- try {
- rg.wait();
- } catch ( ... ) {
- ASSERT( false, "Unexpected exception" );
- }
- ASSERT( g_ExceptionCount <= NUM_GROUPS, "Too many exceptions from the child groups. The test is broken" );
- ASSERT( g_ExceptionCount == NUM_GROUPS, "Not all child groups threw the exception" );
-}
-
-void TestEh2 () {
- ResetGlobals( true, true );
- Concurrency::task_group rg;
- bool exceptionCaught = false;
- for( unsigned i = 0; i < NUM_GROUPS; ++i )
- // TBB version does not require taking function address
- rg.run( &LaunchChildren );
- try {
- rg.wait();
- } catch ( TestException& e ) {
- ASSERT( e.what(), "Empty what() string" );
- ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR2) == 0, "Unknown exception" );
- ASSERT ( !rg.is_canceling(), "wait() has not reset cancellation state" );
- exceptionCaught = true;
- } CATCH_ANY();
- ASSERT( exceptionCaught, "No exception thrown from the root task group" );
- ASSERT( g_ExceptionCount >= SKIP_GROUPS, "Too few exceptions from the child groups. The test is broken" );
- ASSERT( g_ExceptionCount <= NUM_GROUPS - SKIP_GROUPS, "Too many exceptions from the child groups. The test is broken" );
- ASSERT( g_ExceptionCount < NUM_GROUPS - SKIP_GROUPS, "None of the child groups was cancelled" );
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-//------------------------------------------------------------------------
-// Tests for manual cancellation of the task_group hierarchy
-//------------------------------------------------------------------------
-
-void TestCancellation1 () {
- ResetGlobals( false, false );
- Concurrency::task_group rg;
- for( unsigned i = 0; i < NUM_GROUPS; ++i )
- // TBB version does not require taking function address
- rg.run( &LaunchChildren );
- ASSERT ( !Concurrency::is_current_task_group_canceling(), "Unexpected cancellation" );
- ASSERT ( !rg.is_canceling(), "Unexpected cancellation" );
-#if __TBB_SILENT_CANCELLATION_BROKEN
- g_CancellationPropagationInProgress = true;
-#endif
- while ( g_MaxConcurrency > 1 && g_TaskCount == 0 )
- __TBB_Yield();
- rg.cancel();
- g_ExecutedAtCancellation = g_TaskCount;
- ASSERT ( rg.is_canceling(), "No cancellation reported" );
- rg.wait();
- ASSERT( g_TaskCount <= NUM_GROUPS * NUM_CHORES, "Too many tasks reported. The test is broken" );
- ASSERT( g_TaskCount < NUM_GROUPS * NUM_CHORES, "No tasks were cancelled. Cancellation model changed?" );
- ASSERT( g_TaskCount <= g_ExecutedAtCancellation + Harness::ConcurrencyTracker::PeakParallelism(), "Too many tasks survived cancellation" );
-}
-
-//------------------------------------------------------------------------
-// Tests for manual cancellation of the structured_task_group hierarchy
-//------------------------------------------------------------------------
-
-void StructuredLaunchChildren () {
- atomic_t count;
- count = 0;
- Concurrency::structured_task_group g;
- bool exceptionCaught = false;
- typedef Concurrency::task_handle<ThrowingTask> handle_type;
- static const unsigned hSize = sizeof(handle_type);
- char handles[NUM_CHORES * hSize];
- for( unsigned i = 0; i < NUM_CHORES; ++i ) {
- handle_type *h = (handle_type*)(handles + i * hSize);
- new ( h ) handle_type( ThrowingTask(count) );
- g.run( *h );
- }
- __TBB_TRY {
- g.wait();
- } __TBB_CATCH( TestException& e ) {
-#if TBB_USE_EXCEPTIONS
- ASSERT( e.what(), "Empty what() string" );
- ASSERT( __TBB_EXCEPTION_TYPE_INFO_BROKEN || strcmp(e.what(), EXCEPTION_DESCR1) == 0, "Unknown exception" );
-#endif /* TBB_USE_EXCEPTIONS */
-#if __TBB_SILENT_CANCELLATION_BROKEN
- ASSERT ( !g.is_canceling() || g_CancellationPropagationInProgress, "wait() has not reset cancellation state" );
-#else
- ASSERT ( !g.is_canceling(), "wait() has not reset cancellation state" );
-#endif
- exceptionCaught = true;
- ++g_ExceptionCount;
- } CATCH_ANY();
- ASSERT( !g_Throw || exceptionCaught, "No exception in the child task group" );
- for( unsigned i = 0; i < NUM_CHORES; ++i )
- ((handle_type*)(handles + i * hSize))->~handle_type();
- if ( g_Rethrow && g_ExceptionCount > SKIP_GROUPS ) {
-#if __TBB_SILENT_CANCELLATION_BROKEN
- g_CancellationPropagationInProgress = true;
-#endif
- __TBB_THROW( test_exception(EXCEPTION_DESCR2) );
- }
-}
-
-class StructuredCancellationTestDriver {
- static const unsigned hSize = sizeof(handle_type);
- char m_handles[NUM_CHORES * hSize];
-
-public:
- void Launch ( Concurrency::structured_task_group& rg ) {
- ResetGlobals( false, false );
- for( unsigned i = 0; i < NUM_GROUPS; ++i ) {
- handle_type *h = (handle_type*)(m_handles + i * hSize);
- new ( h ) handle_type( StructuredLaunchChildren );
- rg.run( *h );
- }
- ASSERT ( !Concurrency::is_current_task_group_canceling(), "Unexpected cancellation" );
- ASSERT ( !rg.is_canceling(), "Unexpected cancellation" );
-#if __TBB_SILENT_CANCELLATION_BROKEN
- g_CancellationPropagationInProgress = true;
-#endif
- while ( g_MaxConcurrency > 1 && g_TaskCount == 0 )
- __TBB_Yield();
- }
-
- void Finish () {
- for( unsigned i = 0; i < NUM_GROUPS; ++i )
- ((handle_type*)(m_handles + i * hSize))->~handle_type();
- ASSERT( g_TaskCount <= NUM_GROUPS * NUM_CHORES, "Too many tasks reported. The test is broken" );
- ASSERT( g_TaskCount < NUM_GROUPS * NUM_CHORES, "No tasks were cancelled. Cancellation model changed?" );
- ASSERT( g_TaskCount <= g_ExecutedAtCancellation + g_MaxConcurrency, "Too many tasks survived cancellation" );
- }
-}; // StructuredCancellationTestDriver
-
-void TestStructuredCancellation1 () {
- StructuredCancellationTestDriver driver;
- Concurrency::structured_task_group sg;
- driver.Launch( sg );
- sg.cancel();
- g_ExecutedAtCancellation = g_TaskCount;
- ASSERT ( sg.is_canceling(), "No cancellation reported" );
- sg.wait();
- driver.Finish();
-}
-
-#if TBB_USE_EXCEPTIONS
-#if defined(_MSC_VER)
- #pragma warning (disable: 4127)
-#endif
-
-template<bool Throw>
-void TestStructuredCancellation2 () {
- bool exception_occurred = false,
- unexpected_exception = false;
- StructuredCancellationTestDriver driver;
- try {
- Concurrency::structured_task_group tg;
- driver.Launch( tg );
- if ( Throw )
- throw int(); // Initiate stack unwinding
- }
- catch ( const Concurrency::missing_wait& e ) {
- ASSERT( e.what(), "Error message is absent" );
- exception_occurred = true;
- unexpected_exception = Throw;
- }
- catch ( int ) {
- exception_occurred = true;
- unexpected_exception = !Throw;
- }
- catch ( ... ) {
- exception_occurred = unexpected_exception = true;
- }
- ASSERT( exception_occurred, NULL );
- ASSERT( !unexpected_exception, NULL );
- driver.Finish();
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-void EmptyFunction () {}
-
-void TestStructuredWait () {
- Concurrency::structured_task_group sg;
- handle_type h(EmptyFunction);
- sg.run(h);
- sg.wait();
- handle_type h2(EmptyFunction);
- sg.run(h2);
- sg.wait();
-}
-
-int TestMain () {
- REMARK ("Testing %s task_group functionality\n", TBBTEST_USE_TBB ? "TBB" : "PPL");
- for( int p=MinThread; p<=MaxThread; ++p ) {
- g_MaxConcurrency = p;
-#if TBBTEST_USE_TBB
- tbb::task_scheduler_init init(p);
-#else
- Concurrency::SchedulerPolicy sp( 4,
- Concurrency::SchedulerKind, Concurrency::ThreadScheduler,
- Concurrency::MinConcurrency, 1,
- Concurrency::MaxConcurrency , p,
- Concurrency::TargetOversubscriptionFactor, 1);
- Concurrency::Scheduler *s = Concurrency::Scheduler::Create( sp );
-#endif /* !TBBTEST_USE_TBB */
- if ( p > 1 ) {
- TestParallelSpawn();
- TestParallelWait();
- TestVagabondGroup();
- }
- TestFib1();
- TestFib2();
- TestTaskHandle();
- TestTaskHandle2<Concurrency::task_group>();
- TestTaskHandle2<Concurrency::structured_task_group>();
-#if __TBB_LAMBDAS_PRESENT
- TestFibWithLambdas();
- TestFibWithMakeTask();
-#endif
- TestCancellation1();
- TestStructuredCancellation1();
-#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- TestEh1();
- TestEh2();
- TestStructuredWait();
- TestStructuredCancellation2<true>();
- TestStructuredCancellation2<false>();
-#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
-#if !TBBTEST_USE_TBB
- s->Release();
-#endif
- }
-#if __TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- REPORT("Known issue: exception handling tests are skipped.\n");
-#endif
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-/* The test uses "single produces multiple consumers" (SPMC )pattern to check
- if the memory of the tasks stolen by consumer threads is returned to the
- producer thread and is reused.
-
- The test consists of a series of iterations, which execute a task tree.
- the test fails is the memory consumption is not stabilized during some
- number of iterations.
-
- After the memory consumption stabilized the memory state is perturbed by
- switching producer thread, and the check is repeated.
-*/
-
-#define __TBB_COUNT_TASK_NODES 1
-#include "harness_inject_scheduler.h"
-
-#include "tbb/atomic.h"
-#include "harness_assert.h"
-#include <cstdlib>
-
-
-// Test configuration parameters
-
-//! Maximal number of test iterations
-const int MaxIterations = 600;
-//! Number of iterations during which the memory consumption must stabilize
-const int AsymptoticRange = 100;
-//! Number of times the memory state is perturbed to repeat the check
-const int NumProducerSwitches = 2;
-//! Number of iterations after which the success of producer switch is checked
-const int ProducerCheckTimeout = 10;
-//! Number of initial iteration used to collect statistics to be used in later checks
-const int InitialStatsIterations = 20;
-
-tbb::atomic<int> Count;
-tbb::atomic<tbb::task*> Exchanger;
-tbb::internal::scheduler* Producer;
-
-#include "tbb/task_scheduler_init.h"
-
-#define HARNESS_DEFAULT_MIN_THREADS -1
-#include "harness.h"
-
-using namespace tbb;
-using namespace tbb::internal;
-
-class ChangeProducer: public tbb::task {
-public:
- /*override*/ tbb::task* execute() {
- if( is_stolen_task() ) {
- Producer = internal::governor::local_scheduler();
- }
- return NULL;
- }
-};
-
-class TaskGenerator: public tbb::task {
- const int my_child_count;
- int my_depth;
-public:
- TaskGenerator(int child_count, int depth) : my_child_count(child_count), my_depth(depth) {
- ASSERT(my_child_count>1, "The TaskGenerator should produce at least two children");
- }
- /*override*/ tbb::task* execute() {
- if( my_depth>0 ) {
- int child_count = my_child_count;
- scheduler* my_sched = internal::governor::local_scheduler();
- tbb::task& c = *new( tbb::task::allocate_continuation() ) tbb::empty_task;
- c.set_ref_count( child_count );
- recycle_as_child_of(c);
- --child_count;
- if( Producer==my_sched ) {
- // produce a task and put it into Exchanger
- tbb::task* t = new( c.allocate_child() ) tbb::empty_task;
- --child_count;
- t = Exchanger.fetch_and_store(t);
- if( t ) spawn(*t);
- } else {
- tbb::task* t = Exchanger.fetch_and_store(NULL);
- if( t ) spawn(*t);
- }
- while( child_count ) {
- tbb::task* t = new( c.allocate_child() ) TaskGenerator(my_child_count, my_depth-1);
- if( my_depth >4 ) enqueue(*t);
- else spawn(*t);
- --child_count;
- }
- --my_depth;
- return this;
- } else {
- tbb::task* t = Exchanger.fetch_and_store(NULL);
- if( t ) spawn(*t);
- return NULL;
- }
- }
-};
-
-#include "harness_memory.h"
-#if _MSC_VER==1500 && !defined(__INTEL_COMPILER)
- // VS2008/VC9 seems to have an issue
- #pragma warning( push )
- #pragma warning( disable: 4985 )
-#endif
-#include <math.h>
-#if _MSC_VER==1500 && !defined(__INTEL_COMPILER)
- #pragma warning( pop )
-#endif
-
-void RunTaskGenerators( bool switchProducer = false, bool checkProducer = false ) {
- if( switchProducer )
- Producer = NULL;
- tbb::task* dummy_root = new( tbb::task::allocate_root() ) tbb::empty_task;
- dummy_root->set_ref_count( 2 );
- // If no producer, start elections; some worker will take the role
- if( Producer )
- dummy_root->spawn( *new( dummy_root->allocate_child() ) tbb::empty_task );
- else
- dummy_root->spawn( *new( dummy_root->allocate_child() ) ChangeProducer );
- if( checkProducer && !Producer )
- REPORT("Warning: producer has not changed after 10 attempts; running on a single core?\n");
- for( int j=0; j<100; ++j ) {
- if( j&1 ) {
- tbb::task& t = *new( tbb::task::allocate_root() ) TaskGenerator(/*child_count=*/4, /*depth=*/6);
- tbb::task::spawn_root_and_wait(t);
- } else {
- tbb::task& t = *new (dummy_root->allocate_additional_child_of(*dummy_root))
- TaskGenerator(/*child_count=*/4, /*depth=*/6);
- tbb::task::enqueue(t);
- }
- }
- dummy_root->wait_for_all();
- dummy_root->destroy( *dummy_root );
-}
-
-//! Tests whether task scheduler allows thieves to hoard task objects.
-/** The test takes a while to run, so we run it only with the default
- number of threads. */
-void TestTaskReclamation() {
- REMARK("testing task reclamation\n");
-
- size_t initial_amount_of_memory = 0;
- double task_count_sum = 0;
- double task_count_sum_square = 0;
- double average, sigma;
-
- tbb::task_scheduler_init init (MinThread);
- REMARK("Starting with %d threads\n", MinThread);
- // For now, the master will produce "additional" tasks; later a worker will replace it;
- Producer = internal::governor::local_scheduler();
- int N = InitialStatsIterations;
- // First N iterations fill internal buffers and collect initial statistics
- for( int i=0; i<N; ++i ) {
- // First N iterations fill internal buffers and collect initial statistics
- RunTaskGenerators();
-
- size_t m = GetMemoryUsage();
- if( m-initial_amount_of_memory > 0)
- initial_amount_of_memory = m;
-
- intptr_t n = internal::governor::local_scheduler()->get_task_node_count( /*count_arena_workers=*/true );
- task_count_sum += n;
- task_count_sum_square += n*n;
-
- REMARK( "Consumed %ld bytes and %ld objects (iteration=%d)\n", long(m), long(n), i );
- }
- // Calculate statistical values
- average = task_count_sum / N;
- sigma = sqrt( (task_count_sum_square - task_count_sum*task_count_sum/N)/N );
- REMARK("Average task count: %g, sigma: %g, sum: %g, square sum:%g \n", average, sigma, task_count_sum, task_count_sum_square);
-
- int last_error_iteration = 0,
- producer_switch_iteration = 0,
- producer_switches = 0;
- bool switchProducer = false,
- checkProducer = false;
- for( int i=0; i < MaxIterations; ++i ) {
- // These iterations check for excessive memory use and unreasonable task count
- RunTaskGenerators( switchProducer, checkProducer );
-
- intptr_t n = internal::governor::local_scheduler()->get_task_node_count( /*count_arena_workers=*/true );
- size_t m = GetMemoryUsage();
-
- if( (m-initial_amount_of_memory > 0) && (n > average+4*sigma) ) {
- // Use 4*sigma interval (for normal distribution, 3*sigma contains ~99% of values).
- REMARK( "Warning: possible leak of up to %ld bytes; currently %ld cached task objects (iteration=%d)\n",
- static_cast<unsigned long>(m-initial_amount_of_memory), long(n), i );
- last_error_iteration = i;
- initial_amount_of_memory = m;
- } else {
- REMARK( "Consumed %ld bytes and %ld objects (iteration=%d)\n", long(m), long(n), i );
- }
- if ( i == last_error_iteration + AsymptoticRange ) {
- if ( producer_switches++ == NumProducerSwitches )
- break;
- else {
- last_error_iteration = producer_switch_iteration = i;
- switchProducer = true;
- }
- }
- else {
- switchProducer = false;
- checkProducer = producer_switch_iteration && (i == producer_switch_iteration + ProducerCheckTimeout);
- }
- }
- ASSERT( last_error_iteration < MaxIterations - AsymptoticRange, "The amount of allocated tasks keeps growing. Leak is possible." );
-}
-
-int TestMain () {
- if( !GetMemoryUsage() ) {
- REMARK("GetMemoryUsage is not implemented for this platform\n");
- return Harness::Skipped;
- }
- TestTaskReclamation();
- return Harness::Done;
-}
-
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/task_scheduler_init.h"
-#include <cstdlib>
-#include "harness_assert.h"
-
-#include <cstdio>
-
-#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 <stdexcept>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#include "harness.h"
-
-//! Test that task::initialize and task::terminate work when doing nothing else.
-/** maxthread is treated as the "maximum" number of worker threads. */
-void InitializeAndTerminate( int maxthread ) {
- __TBB_TRY {
- for( int i=0; i<200; ++i ) {
- switch( i&3 ) {
- default: {
- tbb::task_scheduler_init init( std::rand() % maxthread + 1 );
- ASSERT(init.is_active(), NULL);
- break;
- }
- case 0: {
- tbb::task_scheduler_init init;
- ASSERT(init.is_active(), NULL);
- break;
- }
- case 1: {
- tbb::task_scheduler_init init( tbb::task_scheduler_init::automatic );
- ASSERT(init.is_active(), NULL);
- break;
- }
- case 2: {
- tbb::task_scheduler_init init( tbb::task_scheduler_init::deferred );
- ASSERT(!init.is_active(), "init should not be active; initialization was deferred");
- init.initialize( std::rand() % maxthread + 1 );
- ASSERT(init.is_active(), NULL);
- init.terminate();
- ASSERT(!init.is_active(), "init should not be active; it was terminated");
- break;
- }
- }
- }
- } __TBB_CATCH( std::runtime_error& error ) {
-#if TBB_USE_EXCEPTIONS
- REPORT("ERROR: %s\n", error.what() );
-#endif /* TBB_USE_EXCEPTIONS */
- }
-}
-
-#if _WIN64
-namespace std { // 64-bit Windows compilers have not caught up with 1998 ISO C++ standard
- using ::srand;
-}
-#endif /* _WIN64 */
-
-struct ThreadedInit {
- void operator()( int ) const {
- InitializeAndTerminate(MaxThread);
- }
-};
-
-#if _MSC_VER
-#include "tbb/machine/windows_api.h"
-#include <tchar.h>
-#endif /* _MSC_VER */
-
-#include "harness_concurrency_tracker.h"
-#include "tbb/parallel_for.h"
-#include "tbb/blocked_range.h"
-
-typedef tbb::blocked_range<int> Range;
-
-class ConcurrencyTrackingBody {
-public:
- void operator() ( const Range& ) const {
- Harness::ConcurrencyTracker ct;
- for ( volatile int i = 0; i < 1000000; ++i )
- ;
- }
-};
-
-/** The test will fail in particular if task_scheduler_init mistakenly hooks up
- auto-initialization mechanism. **/
-void AssertExplicitInitIsNotSupplanted () {
- int hardwareConcurrency = tbb::task_scheduler_init::default_num_threads();
- tbb::task_scheduler_init init(1);
- Harness::ConcurrencyTracker::Reset();
- tbb::parallel_for( Range(0, hardwareConcurrency * 2, 1), ConcurrencyTrackingBody(), tbb::simple_partitioner() );
- ASSERT( Harness::ConcurrencyTracker::PeakParallelism() == 1,
- "Manual init provided more threads than requested. See also the comment at the beginning of main()." );
-}
-
-int TestMain () {
- // Do not use tbb::task_scheduler_init directly in the scope of main's body,
- // as a static variable, or as a member of a static variable.
-#if _MSC_VER && !__TBB_NO_IMPLICIT_LINKAGE
- #ifdef _DEBUG
- ASSERT(!GetModuleHandle(_T("tbb.dll")) && GetModuleHandle(_T("tbb_debug.dll")),
- "test linked with wrong (non-debug) tbb library");
- #else
- ASSERT(!GetModuleHandle(_T("tbb_debug.dll")) && GetModuleHandle(_T("tbb.dll")),
- "test linked with wrong (debug) tbb library");
- #endif
-#endif /* _MSC_VER && !__TBB_NO_IMPLICIT_LINKAGE */
- std::srand(2);
- InitializeAndTerminate(MaxThread);
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK("testing with %d threads\n", p );
- NativeParallelFor( p, ThreadedInit() );
- }
- AssertExplicitInitIsNotSupplanted();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/task_scheduler_observer.h"
-
-typedef uintptr_t FlagType;
-const int MaxFlagIndex = sizeof(FlagType)*8-1;
-
-class MyObserver: public tbb::task_scheduler_observer {
- FlagType flags;
- /*override*/ void on_scheduler_entry( bool is_worker );
- /*override*/ void on_scheduler_exit( bool is_worker );
-public:
- MyObserver( FlagType flags_ ) : flags(flags_) {
- observe(true);
- }
-};
-
-#include "harness_assert.h"
-#include "tbb/atomic.h"
-
-tbb::atomic<int> EntryCount;
-tbb::atomic<int> ExitCount;
-
-struct State {
- FlagType MyFlags;
- bool IsMaster;
- State() : MyFlags(), IsMaster() {}
-};
-
-#include "../tbb/tls.h"
-tbb::internal::tls<State*> LocalState;
-
-void MyObserver::on_scheduler_entry( bool is_worker ) {
- State& state = *LocalState;
- ASSERT( is_worker==!state.IsMaster, NULL );
-#if !__TBB_ARENA_PER_MASTER
- ASSERT( (state.MyFlags & flags)==0, NULL );
-#endif /* !__TBB_ARENA_PER_MASTER */
- ++EntryCount;
- state.MyFlags |= flags;
-}
-
-void MyObserver::on_scheduler_exit( bool is_worker ) {
- State& state = *LocalState;
- ASSERT( is_worker==!state.IsMaster, NULL );
- ++ExitCount;
- state.MyFlags &= ~flags;
-}
-
-#include "tbb/task.h"
-
-class FibTask: public tbb::task {
- const int n;
- FlagType flags;
-public:
- FibTask( int n_, FlagType flags_ ) : n(n_), flags(flags_) {}
- /*override*/ tbb::task* execute() {
- ASSERT( !(~LocalState->MyFlags & flags), NULL );
- if( n>=2 ) {
- set_ref_count(3);
- spawn(*new( tbb::task::allocate_child() ) FibTask(n-1,flags));
- spawn_and_wait_for_all(*new( tbb::task::allocate_child() ) FibTask(n-2,flags));
- }
- return NULL;
- }
-};
-
-void DoFib( FlagType flags ) {
- tbb::task* t = new( tbb::task::allocate_root() ) FibTask(10,flags);
- tbb::task::spawn_root_and_wait(*t);
-}
-
-#include "tbb/task_scheduler_init.h"
-#include "harness.h"
-
-class DoTest {
- int nthread;
-public:
- DoTest( int n ) : nthread(n) {}
- void operator()( int i ) const {
- LocalState->IsMaster = true;
- if( i==0 ) {
- tbb::task_scheduler_init init(nthread);
- DoFib(0);
- } else {
- FlagType f = i<=MaxFlagIndex? 1<<i : 0;
- MyObserver w(f);
- tbb::task_scheduler_init init(nthread);
- DoFib(f);
- }
- }
-};
-
-void TestObserver( int p, int q ) {
- NativeParallelFor( p, DoTest(q) );
-}
-
-int TestMain () {
- for( int p=MinThread; p<=MaxThread; ++p )
- for( int q=MinThread; q<=MaxThread; ++q )
- TestObserver(p,q);
- ASSERT( EntryCount>0, "on_scheduler_entry not exercised" );
- ASSERT( ExitCount>0, "on_scheduler_exit not exercised" );
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "test_condition_variable.h"
-
-int TestMain() {
- REMARK( "testing with tbb condvar\n" );
- DoCondVarTest<tbb::mutex,tbb::recursive_mutex>();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-/**
- This test ensures that tbb.h brings in all the public TBB interface definitions,
- and if all the necessary symbols are exported from the library.
-
- Most of the checks happen at the compilation or link phases.
-**/
-
-#include "tbb/tbb.h"
-
-static volatile size_t g_sink;
-
-#define TestTypeDefinitionPresence( Type ) g_sink = sizeof(tbb::Type);
-#define TestTypeDefinitionPresence2(TypeStart, TypeEnd) g_sink = sizeof(tbb::TypeStart,TypeEnd);
-#define TestFuncDefinitionPresence(Fn, Args, ReturnType) { ReturnType (*pfn)Args = &tbb::Fn; (void)pfn; }
-
-struct Body {
- void operator() () const {}
-};
-struct Body1 {
- void operator() ( int ) const {}
-};
-struct Body1a {
- int operator() ( const tbb::blocked_range<int>&, const int ) const { return 0; }
-};
-struct Body1b {
- int operator() ( const int, const int ) const { return 0; }
-};
-struct Body2 {
- Body2 () {}
- Body2 ( const Body2&, tbb::split ) {}
- void operator() ( const tbb::blocked_range<int>& ) const {}
- void join( const Body2& ) {}
-};
-struct Body3 {
- Body3 () {}
- Body3 ( const Body3&, tbb::split ) {}
- void operator() ( const tbb::blocked_range2d<int>&, tbb::pre_scan_tag ) const {}
- void operator() ( const tbb::blocked_range2d<int>&, tbb::final_scan_tag ) const {}
- void reverse_join( Body3& ) {}
- void assign( const Body3& ) {}
-};
-
-#if !__TBB_TEST_SECONDARY
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness.h"
-
-// Test if all the necessary symbols are exported for the exceptions thrown by TBB.
-// Missing exports result either in link error or in runtime assertion failure.
-#include "tbb/tbb_exception.h"
-
-template <typename E>
-void TestExceptionClassExports ( const E& exc, tbb::internal::exception_id eid ) {
- // The assertion here serves to shut up warnings about "eid not used".
- ASSERT( eid<tbb::internal::eid_max, NULL );
-#if TBB_USE_EXCEPTIONS
- for ( int i = 0; i < 2; ++i ) {
- try {
- if ( i == 0 )
- throw exc;
-#if !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
- else
- tbb::internal::throw_exception( eid );
-#endif
- }
- catch ( E& e ) {
- ASSERT ( e.what(), "Missing what() string" );
- }
- catch ( ... ) {
- ASSERT ( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unrecognized exception. Likely RTTI related exports are missing" );
- }
- }
-#else /* !TBB_USE_EXCEPTIONS */
- (void)exc;
-#endif /* !TBB_USE_EXCEPTIONS */
-}
-
-void TestExceptionClassesExports () {
- TestExceptionClassExports( std::bad_alloc(), tbb::internal::eid_bad_alloc );
- TestExceptionClassExports( tbb::bad_last_alloc(), tbb::internal::eid_bad_last_alloc );
- TestExceptionClassExports( std::invalid_argument("test"), tbb::internal::eid_nonpositive_step );
- TestExceptionClassExports( std::out_of_range("test"), tbb::internal::eid_out_of_range );
- TestExceptionClassExports( std::range_error("test"), tbb::internal::eid_segment_range_error );
- TestExceptionClassExports( std::range_error("test"), tbb::internal::eid_index_range_error );
- TestExceptionClassExports( tbb::missing_wait(), tbb::internal::eid_missing_wait );
- TestExceptionClassExports( tbb::invalid_multiple_scheduling(), tbb::internal::eid_invalid_multiple_scheduling );
- TestExceptionClassExports( tbb::improper_lock(), tbb::internal::eid_improper_lock );
-}
-#endif /* !__TBB_TEST_SECONDARY */
-
-
-#if __TBB_TEST_SECONDARY
-/* This mode is used to produce a secondary object file that is linked with
- the main one in order to detect "multiple definition" linker error.
-*/
-void secondary()
-#else
-int TestMain ()
-#endif
-{
- TestTypeDefinitionPresence2(aligned_space<int, 1> );
- TestTypeDefinitionPresence( atomic<int> );
- TestTypeDefinitionPresence( cache_aligned_allocator<int> );
- TestTypeDefinitionPresence( tbb_hash_compare<int> );
- TestTypeDefinitionPresence2(concurrent_hash_map<int, int> );
- TestTypeDefinitionPresence2(concurrent_unordered_map<int, int> );
- TestTypeDefinitionPresence( concurrent_bounded_queue<int> );
- TestTypeDefinitionPresence( deprecated::concurrent_queue<int> );
- TestTypeDefinitionPresence( strict_ppl::concurrent_queue<int> );
- TestTypeDefinitionPresence( combinable<int> );
- TestTypeDefinitionPresence( concurrent_vector<int> );
- TestTypeDefinitionPresence( enumerable_thread_specific<int> );
- TestTypeDefinitionPresence( mutex );
- TestTypeDefinitionPresence( null_mutex );
- TestTypeDefinitionPresence( null_rw_mutex );
- TestTypeDefinitionPresence( queuing_mutex );
- TestTypeDefinitionPresence( queuing_rw_mutex );
- TestTypeDefinitionPresence( recursive_mutex );
- TestTypeDefinitionPresence( spin_mutex );
- TestTypeDefinitionPresence( spin_rw_mutex );
- TestTypeDefinitionPresence( critical_section );
- TestTypeDefinitionPresence( reader_writer_lock );
- TestTypeDefinitionPresence( tbb_exception );
- TestTypeDefinitionPresence( captured_exception );
- TestTypeDefinitionPresence( movable_exception<int> );
-#if !TBB_USE_CAPTURED_EXCEPTION
- TestTypeDefinitionPresence( internal::tbb_exception_ptr );
-#endif /* !TBB_USE_CAPTURED_EXCEPTION */
- TestTypeDefinitionPresence( blocked_range3d<int> );
- TestFuncDefinitionPresence( parallel_invoke, (const Body&, const Body&), void );
- TestFuncDefinitionPresence( parallel_do, (int*, int*, const Body1&), void );
- TestFuncDefinitionPresence( parallel_for_each, (int*, int*, const Body1&), void );
- TestFuncDefinitionPresence( parallel_for, (int, int, int, const Body1&), void );
- TestFuncDefinitionPresence( parallel_for, (const tbb::blocked_range<int>&, const Body2&, const tbb::simple_partitioner&), void );
- TestFuncDefinitionPresence( parallel_reduce, (const tbb::blocked_range<int>&, const int&, const Body1a&, const Body1b&, const tbb::auto_partitioner&), int );
- TestFuncDefinitionPresence( parallel_reduce, (const tbb::blocked_range<int>&, Body2&, tbb::affinity_partitioner&), void );
- TestFuncDefinitionPresence( parallel_scan, (const tbb::blocked_range2d<int>&, Body3&, const tbb::auto_partitioner&), void );
- TestFuncDefinitionPresence( parallel_sort, (int*, int*), void );
- TestTypeDefinitionPresence( pipeline );
- TestFuncDefinitionPresence( parallel_pipeline, (size_t, const tbb::filter_t<void,void>&), void );
- TestTypeDefinitionPresence( task );
- TestTypeDefinitionPresence( empty_task );
- TestTypeDefinitionPresence( task_list );
- TestTypeDefinitionPresence( task_group_context );
- TestTypeDefinitionPresence( task_group );
- TestTypeDefinitionPresence( task_handle<Body> );
- TestTypeDefinitionPresence( task_scheduler_init );
- TestTypeDefinitionPresence( task_scheduler_observer );
- TestTypeDefinitionPresence( tbb_thread );
- TestTypeDefinitionPresence( tbb_allocator<int> );
- TestTypeDefinitionPresence( zero_allocator<int> );
- TestTypeDefinitionPresence( tick_count );
-#if !__TBB_TEST_SECONDARY
- TestExceptionClassesExports();
- return Harness::Done;
-#endif
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#define THREAD tbb::tbb_thread
-#define THIS_THREAD tbb::this_tbb_thread
-#define THIS_THREAD_SLEEP THIS_THREAD::sleep
-#include "test_thread.h"
-#include "harness.h"
-
-/* we want to test tbb::tbb_thread */
-int TestMain () {
- CheckSignatures();
- RunTests();
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/tbb_stddef.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#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 <vector>
-#include <string>
-#include <utility>
-
-#if !TBB_USE_EXCEPTIONS && _MSC_VER
- #pragma warning (pop)
-#endif
-
-#include "tbb/task_scheduler_init.h"
-
-#define HARNESS_CUSTOM_MAIN 1
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#define HARNESS_NO_MAIN_ARGS 0
-#include "harness.h"
-
-#if defined (_WIN32) || defined (_WIN64)
-#define TEST_SYSTEM_COMMAND "test_tbb_version.exe @"
-#define putenv _putenv
-#else
-#define TEST_SYSTEM_COMMAND "./test_tbb_version.exe @"
-#endif
-
-enum string_required {
- required,
- not_required
- };
-
-typedef std::pair <std::string, string_required> string_pair;
-
-void initialize_strings_vector(std::vector <string_pair>* vector);
-
-const char stderr_stream[] = "version_test.err";
-const char stdout_stream[] = "version_test.out";
-
-HARNESS_EXPORT
-int main(int argc, char *argv[] ) {
-/* We first introduced runtime version identification in 3014 */
-#if TBB_INTERFACE_VERSION>=3014
- // For now, just test that run-time TBB version matches the compile-time version,
- // since otherwise the subsequent test of "TBB: INTERFACE VERSION" string will fail anyway.
- // We need something more clever in future.
- ASSERT(tbb::TBB_runtime_interface_version()==TBB_INTERFACE_VERSION,
- "Running with the library of different version than the test was compiled against");
-#endif
- __TBB_TRY {
- FILE *stream_out;
- FILE *stream_err;
- char psBuffer[512];
-
- if(argc>1 && argv[1][0] == '@' ) {
- stream_err = freopen( stderr_stream, "w", stderr );
- if( stream_err == NULL ){
- REPORT( "Internal test error (freopen)\n" );
- exit( 1 );
- }
- stream_out = freopen( stdout_stream, "w", stdout );
- if( stream_out == NULL ){
- REPORT( "Internal test error (freopen)\n" );
- exit( 1 );
- }
- {
- tbb::task_scheduler_init init(1);
- }
- fclose( stream_out );
- fclose( stream_err );
- exit(0);
- }
- //1st step check that output is empty if TBB_VERSION is not defined.
- if ( getenv("TBB_VERSION") ){
- REPORT( "TBB_VERSION defined, skipping step 1 (empty output check)\n" );
- }else{
- if( ( system(TEST_SYSTEM_COMMAND) ) != 0 ){
- REPORT( "Error (step 1): Internal test error\n" );
- exit( 1 );
- }
- //Checking output streams - they should be empty
- stream_err = fopen( stderr_stream, "r" );
- if( stream_err == NULL ){
- REPORT( "Error (step 1):Internal test error (stderr open)\n" );
- exit( 1 );
- }
- while( !feof( stream_err ) ) {
- if( fgets( psBuffer, 512, stream_err ) != NULL ){
- REPORT( "Error (step 1): stderr should be empty\n" );
- exit( 1 );
- }
- }
- fclose( stream_err );
- stream_out = fopen( stdout_stream, "r" );
- if( stream_out == NULL ){
- REPORT( "Error (step 1):Internal test error (stdout open)\n" );
- exit( 1 );
- }
- while( !feof( stream_out ) ) {
- if( fgets( psBuffer, 512, stream_out ) != NULL ){
- REPORT( "Error (step 1): stdout should be empty\n" );
- exit( 1 );
- }
- }
- fclose( stream_out );
- }
-
- //Setting TBB_VERSION in case it is not set
- if ( !getenv("TBB_VERSION") ){
- putenv(const_cast<char*>("TBB_VERSION=1"));
- }
-
- if( ( system(TEST_SYSTEM_COMMAND) ) != 0 ){
- REPORT( "Error (step 2):Internal test error\n" );
- exit( 1 );
- }
- //Checking pipe - it should contain version data
- std::vector <string_pair> strings_vector;
- std::vector <string_pair>::iterator strings_iterator;
-
- initialize_strings_vector( &strings_vector );
- strings_iterator = strings_vector.begin();
-
- stream_out = fopen( stdout_stream, "r" );
- if( stream_out == NULL ){
- REPORT( "Error (step 2):Internal test error (stdout open)\n" );
- exit( 1 );
- }
- while( !feof( stream_out ) ) {
- if( fgets( psBuffer, 512, stream_out ) != NULL ){
- REPORT( "Error (step 2): stdout should be empty\n" );
- exit( 1 );
- }
- }
- fclose( stream_out );
-
- stream_err = fopen( stderr_stream, "r" );
- if( stream_err == NULL ){
- REPORT( "Error (step 1):Internal test error (stderr open)\n" );
- exit( 1 );
- }
-
- int skip_line = 0;
-
- while( !feof( stream_err ) ) {
- if( fgets( psBuffer, 512, stream_err ) != NULL ){
- do{
- if ( strings_iterator == strings_vector.end() ){
- REPORT( "Error: version string dictionary ended prematurely.\n" );
- REPORT( "No match for: \t%s", psBuffer );
- exit( 1 );
- }
- if ( strstr( psBuffer, strings_iterator->first.c_str() ) == NULL ){
- if( strings_iterator->second == required ){
- REPORT( "Error: version strings do not match.\n" );
- REPORT( "Expected \"%s\" not found in:\n\t%s", strings_iterator->first.c_str(), psBuffer );
- exit( 1 );
- }else{
- //Do we need to print in case there is no non-required string?
- skip_line = 1;
- }
- }else{
- skip_line = 0;
- }
- if ( strings_iterator != strings_vector.end() ) strings_iterator ++;
- }while( skip_line );
- }
- }
- fclose( stream_err );
- } __TBB_CATCH(...) {
- ASSERT( 0,"unexpected exception" );
- }
- REPORT("done\n");
- return 0;
-}
-
-
-// Fill dictionary with version strings for platforms
-void initialize_strings_vector(std::vector <string_pair>* vector)
-{
- vector->push_back(string_pair("TBB: VERSION\t\t3.0", required)); // check TBB_VERSION
- vector->push_back(string_pair("TBB: INTERFACE VERSION\t5003", required)); // check TBB_INTERFACE_VERSION
- vector->push_back(string_pair("TBB: BUILD_DATE", required));
- vector->push_back(string_pair("TBB: BUILD_HOST", required));
- vector->push_back(string_pair("TBB: BUILD_OS", required));
-#if _WIN32||_WIN64
-#if !__MINGW32__
- vector->push_back(string_pair("TBB: BUILD_CL", required));
-#endif
- vector->push_back(string_pair("TBB: BUILD_COMPILER", required));
-#elif __APPLE__
- vector->push_back(string_pair("TBB: BUILD_KERNEL", required));
- vector->push_back(string_pair("TBB: BUILD_GCC", required));
- vector->push_back(string_pair("TBB: BUILD_COMPILER", not_required)); //if( getenv("COMPILER_VERSION") )
-#elif __sun
- vector->push_back(string_pair("TBB: BUILD_KERNEL", required));
- vector->push_back(string_pair("TBB: BUILD_SUNCC", required));
- vector->push_back(string_pair("TBB: BUILD_COMPILER", not_required)); //if( getenv("COMPILER_VERSION") )
-#else //We use version_info_linux.sh for unsupported OSes
- vector->push_back(string_pair("TBB: BUILD_KERNEL", required));
- vector->push_back(string_pair("TBB: BUILD_GCC", required));
- vector->push_back(string_pair("TBB: BUILD_COMPILER", not_required)); //if( getenv("COMPILER_VERSION") )
- vector->push_back(string_pair("TBB: BUILD_GLIBC", required));
- vector->push_back(string_pair("TBB: BUILD_LD", required));
-#endif
- vector->push_back(string_pair("TBB: BUILD_TARGET", required));
- vector->push_back(string_pair("TBB: BUILD_COMMAND", required));
- vector->push_back(string_pair("TBB: TBB_USE_DEBUG", required));
- vector->push_back(string_pair("TBB: TBB_USE_ASSERT", required));
- vector->push_back(string_pair("TBB: DO_ITT_NOTIFY", required));
- vector->push_back(string_pair("TBB: ITT", not_required)); //#ifdef DO_ITT_NOTIFY
- vector->push_back(string_pair("TBB: ALLOCATOR", required));
- vector->push_back(string_pair("TBB: RML", not_required));
- vector->push_back(string_pair("TBB: Intel(R) RML library built:", not_required));
- vector->push_back(string_pair("TBB: Intel(R) RML library version:", not_required));
- vector->push_back(string_pair("TBB: SCHEDULER", required));
-
- return;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/tbb_thread.h"
-#include "tbb/atomic.h"
-
-#define HARNESS_NO_PARSE_COMMAND_LINE 1
-#include "harness_report.h"
-#include "harness_assert.h"
-
-static const int THRDS = 3;
-static const int THRDS_DETACH = 2;
-static tbb::atomic<int> sum;
-static tbb::atomic<int> BaseCount;
-static THREAD::id real_ids[THRDS+THRDS_DETACH];
-
-class Base {
- mutable int copy_throws;
- friend void RunTests();
- friend void CheckExceptionSafety();
- void operator=( const Base& ); // Deny access
-protected:
- Base() : copy_throws(100) {++BaseCount;}
- Base( const Base& c ) : copy_throws(c.copy_throws) {
- if( --copy_throws<=0 )
- __TBB_THROW(0);
- ++BaseCount;
- }
- ~Base() {--BaseCount;}
-};
-
-template<int N>
-class Data: Base {
- Data(); // Deny access
- explicit Data(int v) : value(v) {}
-
- friend void RunTests();
- friend void CheckExceptionSafety();
-public:
- int value;
-};
-
-
-#include "harness_barrier.h"
-
-class ThreadFunc: Base {
- ThreadFunc() {}
-
- static Harness::SpinBarrier init_barrier;
-
- friend void RunTests();
-public:
- void operator()(){
- real_ids[0] = THIS_THREAD::get_id();
- init_barrier.wait();
-
- sum.fetch_and_add(1);
- }
- void operator()(int num){
- real_ids[num] = THIS_THREAD::get_id();
- init_barrier.wait();
-
- sum.fetch_and_add(num);
- }
- void operator()(int num, Data<0> dx) {
- real_ids[num] = THIS_THREAD::get_id();
-
- const double WAIT = .1;
- const double SHORT_TOLERANCE = 1e-8;
-#if _WIN32 || _WIN64
- const double LONG_TOLERANCE = 0.120; // maximal scheduling quantum for Windows Server
-#else
- const double LONG_TOLERANCE = 0.200; // reasonable upper bound
-#endif
- tbb::tick_count t0 = tbb::tick_count::now();
- tbb::this_tbb_thread::sleep( tbb::tick_count::interval_t(WAIT) );
- tbb::tick_count t1 = tbb::tick_count::now();
- double delta = (t1-t0).seconds() - WAIT;
- if(delta+SHORT_TOLERANCE <= 0.0)
- REPORT("ERROR: Sleep interval too short (%g outside short tolerance(%g))\n", (t1-t0).seconds(), WAIT - SHORT_TOLERANCE);
- if(delta > LONG_TOLERANCE)
- REPORT("WARNING: Sleep interval too long (%g outside long tolerance(%g))\n", (t1-t0).seconds(), WAIT + LONG_TOLERANCE);
-
- init_barrier.wait();
-
- sum.fetch_and_add(num);
- sum.fetch_and_add(dx.value);
- }
- void operator()(Data<0> d) {
- tbb::this_tbb_thread::sleep( tbb::tick_count::interval_t(d.value*1.) );
- }
-};
-
-Harness::SpinBarrier ThreadFunc::init_barrier(THRDS);
-
-void CheckRelations( const THREAD::id ids[], int n, bool duplicates_allowed ) {
- for( int i=0; i<n; ++i ) {
- const THREAD::id x = ids[i];
- for( int j=0; j<n; ++j ) {
- const THREAD::id y = ids[j];
- ASSERT( (x==y)==!(x!=y), NULL );
- ASSERT( (x<y)==!(x>=y), NULL );
- ASSERT( (x>y)==!(x<=y), NULL );
- ASSERT( (x<y)+(x==y)+(x>y)==1, NULL );
- ASSERT( x!=y || i==j || duplicates_allowed, NULL );
- for( int k=0; k<n; ++k ) {
- const THREAD::id z = ids[j];
- ASSERT( !(x<y && y<z) || x<z, "< is not transitive" );
- }
- }
- }
-}
-
-class AnotherThreadFunc: Base {
-public:
- void operator()() {}
- void operator()(const Data<1>&) {}
- void operator()(const Data<1>&, const Data<2>&) {}
- friend void CheckExceptionSafety();
-};
-
-#if TBB_USE_EXCEPTIONS
-void CheckExceptionSafety() {
- int original_count = BaseCount;
- // d loops over number of copies before throw occurs
- for( int d=1; d<=3; ++d ) {
- // Try all combinations of throw/nothrow for f, x, and y's copy constructor.
- for( int i=0; i<8; ++i ) {
- {
- const AnotherThreadFunc f = AnotherThreadFunc();
- if( i&1 ) f.copy_throws = d;
- const Data<1> x(0);
- if( i&2 ) x.copy_throws = d;
- const Data<2> y(0);
- if( i&4 ) y.copy_throws = d;
- bool exception_caught = false;
- for( int j=0; j<3; ++j ) {
- try {
- switch(j) {
- case 0: {THREAD t(f); t.join();} break;
- case 1: {THREAD t(f,x); t.join();} break;
- case 2: {THREAD t(f,x,y); t.join();} break;
- }
- } catch(...) {
- exception_caught = true;
- }
- ASSERT( !exception_caught||(i&((1<<(j+1))-1))!=0, NULL );
- }
- }
-// Intel Compiler sometimes fails to destroy all implicitly generated copies
-// of an object when a copy constructor throws an exception.
-// Problem was reported as Quad issue 482935.
-// This #if should be removed or tightened when the bug is fixed.
-#if !((_WIN32 || _WIN64) && defined(__INTEL_COMPILER))
- ASSERT( BaseCount==original_count, "object leak detected" );
-#endif
- }
- }
-}
-#endif /* TBB_USE_EXCEPTIONS */
-
-#include <cstdio>
-
-void RunTests() {
-
- ThreadFunc t;
- Data<0> d100(100), d1(1), d0(0);
- THREAD::id id;
- THREAD::id id0, uniq_ids[THRDS];
-
- THREAD thrs[THRDS];
- THREAD thr;
- THREAD thr0(t);
- THREAD thr1(t, 2);
- THREAD thr2(t, 1, d100);
-
- ASSERT( thr0.get_id() != id, NULL );
- id0 = thr0.get_id();
- tbb::move(thrs[0], thr0);
- ASSERT( thr0.get_id() == id, NULL );
- ASSERT( thrs[0].get_id() == id0, NULL );
-
- THREAD::native_handle_type h1 = thr1.native_handle();
- THREAD::native_handle_type h2 = thr2.native_handle();
- THREAD::id id1 = thr1.get_id();
- THREAD::id id2 = thr2.get_id();
- tbb::swap(thr1, thr2);
- ASSERT( thr1.native_handle() == h2, NULL );
- ASSERT( thr2.native_handle() == h1, NULL );
- ASSERT( thr1.get_id() == id2, NULL );
- ASSERT( thr2.get_id() == id1, NULL );
- thr1.swap(thr2);
- ASSERT( thr1.native_handle() == h1, NULL );
- ASSERT( thr2.native_handle() == h2, NULL );
- ASSERT( thr1.get_id() == id1, NULL );
- ASSERT( thr2.get_id() == id2, NULL );
- thr1.swap(thr2);
-
- tbb::move(thrs[1], thr1);
- ASSERT( thr1.get_id() == id, NULL );
-
- tbb::move(thrs[2], thr2);
- ASSERT( thr2.get_id() == id, NULL );
-
- for (int i=0; i<THRDS; i++)
- uniq_ids[i] = thrs[i].get_id();
-
- ASSERT( thrs[2].joinable(), NULL );
-
- for (int i=0; i<THRDS; i++)
- thrs[i].join();
- for (int i=0; i<THRDS; i++)
- ASSERT( real_ids[i] == uniq_ids[i], NULL );
-
- int current_sum = sum;
- ASSERT( current_sum == 104, NULL );
- ASSERT( ! thrs[2].joinable(), NULL );
- ASSERT( BaseCount==4, "object leak detected" );
-
-#if TBB_USE_EXCEPTIONS
- CheckExceptionSafety();
-#endif
-
- // Note: all tests involving BaseCount should be put before the tests
- // involing detached threads, because there is no way of knowing when
- // a detached thread destroys its arguments.
-
- THREAD thr_detach_0(t, d0);
- real_ids[THRDS] = thr_detach_0.get_id();
- thr_detach_0.detach();
- ASSERT( thr_detach_0.get_id() == id, NULL );
-
- THREAD thr_detach_1(t, d1);
- real_ids[THRDS+1] = thr_detach_1.get_id();
- thr_detach_1.detach();
- ASSERT( thr_detach_1.get_id() == id, NULL );
-
- CheckRelations(real_ids, THRDS+THRDS_DETACH, true);
-
- CheckRelations(uniq_ids, THRDS, false);
-
- for (int i=0; i<2; i++) {
- AnotherThreadFunc empty_func;
- THREAD thr_to(empty_func), thr_from(empty_func);
- THREAD::id from_id = thr_from.get_id();
- if (i) thr_to.join();
- thr_to = thr_from;
- ASSERT( thr_from.get_id() == THREAD::id(), NULL );
- ASSERT( thr_to.get_id() == from_id, NULL );
- }
-
- ASSERT( THREAD::hardware_concurrency() > 0, NULL);
-}
-
-typedef bool (*id_relation)( THREAD::id, THREAD::id );
-
-id_relation CheckSignatures() {
- id_relation r[6] = {&tbb::operator==,
- &tbb::operator!=,
- &tbb::operator<,
- &tbb::operator>,
- &tbb::operator<=,
- &tbb::operator>=};
- return r[1];
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-#include "tbb/tick_count.h"
-#include "harness.h"
-#include <cstdio>
-
-//! Assert that two times in seconds are very close.
-void AssertNear( double x, double y ) {
- ASSERT( -1.0E-10 <= x-y && x-y <=1.0E-10, NULL );
-}
-
-//! Test arithmetic operators on tick_count::interval_t
-void TestArithmetic( const tbb::tick_count& t0, const tbb::tick_count& t1, const tbb::tick_count& t2 ) {
- tbb::tick_count::interval_t i= t1-t0;
- tbb::tick_count::interval_t j = t2-t1;
- tbb::tick_count::interval_t k = t2-t0;
- AssertSameType( tbb::tick_count::interval_t(), i-j );
- AssertSameType( tbb::tick_count::interval_t(), i+j );
- ASSERT( i.seconds()>1E-9, NULL );
- ASSERT( j.seconds()>1E-9, NULL );
- ASSERT( k.seconds()>2E-9, NULL );
- AssertNear( (i+j).seconds(), k.seconds() );
- AssertNear( (k-j).seconds(), i.seconds() );
- AssertNear( ((k-j)+(j-i)).seconds(), k.seconds()-i.seconds() );
- tbb::tick_count::interval_t sum;
- sum += i;
- sum += j;
- AssertNear( sum.seconds(), k.seconds() );
- sum -= i;
- AssertNear( sum.seconds(), j.seconds() );
- sum -= j;
- AssertNear( sum.seconds(), 0.0 );
-}
-
-//------------------------------------------------------------------------
-// Test for overhead in calls to tick_count
-//------------------------------------------------------------------------
-
-//! Wait for given duration.
-/** The duration parameter is in units of seconds. */
-static void WaitForDuration( double duration ) {
- tbb::tick_count start = tbb::tick_count::now();
- while( (tbb::tick_count::now()-start).seconds() < duration )
- continue;
-}
-
-//! Test that average timer overhead is within acceptable limit.
-/** The 'tolerance' value inside the test specifies the limit. */
-void TestSimpleDelay( int ntrial, double duration, double tolerance ) {
- double total_worktime = 0;
- // Iteration -1 warms up the code cache.
- for( int trial=-1; trial<ntrial; ++trial ) {
- tbb::tick_count t0 = tbb::tick_count::now();
- if( duration ) WaitForDuration(duration);
- tbb::tick_count t1 = tbb::tick_count::now();
- if( trial>=0 ) {
- total_worktime += (t1-t0).seconds();
- }
- }
- // Compute average worktime and average delta
- double worktime = total_worktime/ntrial;
- double delta = worktime-duration;
- REMARK("worktime=%g delta=%g tolerance=%g\n", worktime, delta, tolerance);
-
- // Check that delta is acceptable
- if( delta<0 )
- REPORT("ERROR: delta=%g < 0\n",delta);
- if( delta>tolerance )
- REPORT("%s: delta=%g > %g=tolerance where duration=%g\n",delta>3*tolerance?"ERROR":"Warning",delta,tolerance,duration);
-}
-
-//------------------------------------------------------------------------
-// Test for subtracting calls to tick_count from different threads.
-//------------------------------------------------------------------------
-
-#include "tbb/atomic.h"
-const int MAX_NTHREAD = 1000;
-static tbb::atomic<int> Counter;
-static volatile bool Flag;
-static tbb::tick_count tick_count_array[MAX_NTHREAD];
-
-struct TickCountDifferenceBody {
- void operator()( int id ) const {
- if( --Counter==0 ) Flag = true;
- while( !Flag ) continue;
- tick_count_array[id] = tbb::tick_count::now();
- }
-};
-
-//! Test that two tick_count values recorded on different threads can be meaningfully subtracted.
-void TestTickCountDifference( int n ) {
- double tolerance = 3E-4;
- for( int trial=0; trial<10; ++trial ) {
- Counter = n;
- Flag = false;
- NativeParallelFor( n, TickCountDifferenceBody() );
- ASSERT( Counter==0, NULL );
- for( int i=0; i<n; ++i )
- for( int j=0; j<i; ++j ) {
- double diff = (tick_count_array[i]-tick_count_array[j]).seconds();
- if( diff<0 ) diff = -diff;
- if( diff>tolerance ) {
- REPORT("%s: cross-thread tick_count difference = %g > %g = tolerance\n",
- diff>3*tolerance?"ERROR":"Warning",diff,tolerance);
- }
- }
- }
-}
-
-int TestMain () {
- tbb::tick_count t0 = tbb::tick_count::now();
- TestSimpleDelay(/*ntrial=*/1000000,/*duration=*/0, /*tolerance=*/2E-6);
- tbb::tick_count t1 = tbb::tick_count::now();
- TestSimpleDelay(/*ntrial=*/10, /*duration=*/0.125,/*tolerance=*/5E-6);
- tbb::tick_count t2 = tbb::tick_count::now();
- TestArithmetic(t0,t1,t2);
-
- for( int n=MinThread; n<=MaxThread; ++n ) {
- TestTickCountDifference(n);
- }
- return Harness::Done;
-}
+++ /dev/null
-/*
- Copyright 2005-2010 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.
-*/
-
-// Test that __TBB_Yield works.
-// On Red Hat EL4 U1, it does not work, because sched_yield is broken.
-
-#include "tbb/tbb_machine.h"
-#include "tbb/tick_count.h"
-#include "harness.h"
-
-static volatile long CyclicCounter;
-static volatile bool Quit;
-double SingleThreadTime;
-
-struct RoundRobin: NoAssign {
- const int number_of_threads;
- RoundRobin( long p ) : number_of_threads(p) {}
- void operator()( long k ) const {
- tbb::tick_count t0 = tbb::tick_count::now();
- for( long i=0; i<10000; ++i ) {
- // Wait for previous thread to notify us
- for( int j=0; CyclicCounter!=k && !Quit; ++j ) {
- __TBB_Yield();
- if( j%100==0 ) {
- tbb::tick_count t1 = tbb::tick_count::now();
- if( (t1-t0).seconds()>=1.0*number_of_threads ) {
- REPORT("Warning: __TBB_Yield failing to yield with %d threads (or system is heavily loaded)\n",number_of_threads);
- Quit = true;
- return;
- }
- }
- }
- // Notify next thread that it can run
- CyclicCounter = (k+1)%number_of_threads;
- }
- }
-};
-
-int TestMain () {
- for( int p=MinThread; p<=MaxThread; ++p ) {
- REMARK("testing with %d threads\n", p );
- CyclicCounter = 0;
- Quit = false;
- NativeParallelFor( long(p), RoundRobin(p) );
- }
- return Harness::Done;
-}
-