| /* |
| 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; |
| } |
| |