1*51c0b2f7Stbbdev /* 2*51c0b2f7Stbbdev Copyright (c) 2005-2020 Intel Corporation 3*51c0b2f7Stbbdev 4*51c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*51c0b2f7Stbbdev you may not use this file except in compliance with the License. 6*51c0b2f7Stbbdev You may obtain a copy of the License at 7*51c0b2f7Stbbdev 8*51c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 9*51c0b2f7Stbbdev 10*51c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 11*51c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*51c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*51c0b2f7Stbbdev See the License for the specific language governing permissions and 14*51c0b2f7Stbbdev limitations under the License. 15*51c0b2f7Stbbdev */ 16*51c0b2f7Stbbdev 17*51c0b2f7Stbbdev // Basic testing of an allocator 18*51c0b2f7Stbbdev // Tests against requirements in 20.1.5 of ISO C++ Standard (1998). 19*51c0b2f7Stbbdev // Does not check for thread safety or false sharing issues. 20*51c0b2f7Stbbdev // 21*51c0b2f7Stbbdev // Tests for compatibility with the host's STL are in 22*51c0b2f7Stbbdev // test_Allocator_STL.h. Those tests are in a separate file 23*51c0b2f7Stbbdev // because they bring in lots of STL headers, and the tests here 24*51c0b2f7Stbbdev // are supposed to work in the abscense of STL. 25*51c0b2f7Stbbdev 26*51c0b2f7Stbbdev #include "common/test.h" 27*51c0b2f7Stbbdev #include "common/utils.h" 28*51c0b2f7Stbbdev #include <utility> //for std::pair 29*51c0b2f7Stbbdev #include <cstring> 30*51c0b2f7Stbbdev 31*51c0b2f7Stbbdev //! Compile-time error if x and y have different types 32*51c0b2f7Stbbdev template<typename T> 33*51c0b2f7Stbbdev void AssertSameType( const T& /*x*/, const T& /*y*/ ) {} 34*51c0b2f7Stbbdev 35*51c0b2f7Stbbdev //! The function to zero-initialize arrays; useful to avoid warnings 36*51c0b2f7Stbbdev template <typename T> 37*51c0b2f7Stbbdev void zero_fill(void* array, size_t n) { 38*51c0b2f7Stbbdev memset(array, 0, sizeof(T)*n); 39*51c0b2f7Stbbdev } 40*51c0b2f7Stbbdev 41*51c0b2f7Stbbdev template<typename A> 42*51c0b2f7Stbbdev struct is_zero_filling { 43*51c0b2f7Stbbdev static const bool value = false; 44*51c0b2f7Stbbdev }; 45*51c0b2f7Stbbdev 46*51c0b2f7Stbbdev int NumberOfFoo; 47*51c0b2f7Stbbdev 48*51c0b2f7Stbbdev template<typename T, size_t N> 49*51c0b2f7Stbbdev struct Foo { 50*51c0b2f7Stbbdev T foo_array[N]; 51*51c0b2f7Stbbdev Foo() { 52*51c0b2f7Stbbdev zero_fill<T>(foo_array, N); 53*51c0b2f7Stbbdev ++NumberOfFoo; 54*51c0b2f7Stbbdev } 55*51c0b2f7Stbbdev Foo( const Foo& x ) { 56*51c0b2f7Stbbdev *this = x; 57*51c0b2f7Stbbdev //Internal call of assignment 58*51c0b2f7Stbbdev } 59*51c0b2f7Stbbdev Foo& operator=( const Foo& x ) { 60*51c0b2f7Stbbdev for (size_t i = 0; i < N; i++) 61*51c0b2f7Stbbdev foo_array[i] = x.foo_array[i]; 62*51c0b2f7Stbbdev ++NumberOfFoo; 63*51c0b2f7Stbbdev return *this; 64*51c0b2f7Stbbdev } 65*51c0b2f7Stbbdev 66*51c0b2f7Stbbdev ~Foo() { 67*51c0b2f7Stbbdev --NumberOfFoo; 68*51c0b2f7Stbbdev } 69*51c0b2f7Stbbdev }; 70*51c0b2f7Stbbdev 71*51c0b2f7Stbbdev inline char PseudoRandomValue( size_t j, size_t k ) { 72*51c0b2f7Stbbdev return char(j*3 ^ j>>4 ^ k); 73*51c0b2f7Stbbdev } 74*51c0b2f7Stbbdev 75*51c0b2f7Stbbdev #if __APPLE__ 76*51c0b2f7Stbbdev #include <fcntl.h> 77*51c0b2f7Stbbdev #include <unistd.h> 78*51c0b2f7Stbbdev 79*51c0b2f7Stbbdev // A RAII class to disable stderr in a certain scope. It's not thread-safe. 80*51c0b2f7Stbbdev class DisableStderr { 81*51c0b2f7Stbbdev int stderrCopy; 82*51c0b2f7Stbbdev static void dupToStderrAndClose(int fd) { 83*51c0b2f7Stbbdev int ret = dup2(fd, STDERR_FILENO); // close current stderr 84*51c0b2f7Stbbdev REQUIRE(ret != -1); 85*51c0b2f7Stbbdev ret = close(fd); 86*51c0b2f7Stbbdev REQUIRE(ret != -1); 87*51c0b2f7Stbbdev } 88*51c0b2f7Stbbdev public: 89*51c0b2f7Stbbdev DisableStderr() { 90*51c0b2f7Stbbdev int devNull = open("/dev/null", O_WRONLY); 91*51c0b2f7Stbbdev REQUIRE(devNull != -1); 92*51c0b2f7Stbbdev stderrCopy = dup(STDERR_FILENO); 93*51c0b2f7Stbbdev REQUIRE(stderrCopy != -1); 94*51c0b2f7Stbbdev dupToStderrAndClose(devNull); 95*51c0b2f7Stbbdev } 96*51c0b2f7Stbbdev ~DisableStderr() { 97*51c0b2f7Stbbdev dupToStderrAndClose(stderrCopy); 98*51c0b2f7Stbbdev } 99*51c0b2f7Stbbdev }; 100*51c0b2f7Stbbdev #endif 101*51c0b2f7Stbbdev 102*51c0b2f7Stbbdev //! T is type and A is allocator for that type 103*51c0b2f7Stbbdev template<typename T, typename A> 104*51c0b2f7Stbbdev void TestBrokenAllocator(A& a) { 105*51c0b2f7Stbbdev T x; 106*51c0b2f7Stbbdev const T cx = T(); 107*51c0b2f7Stbbdev // See Table 32 in ISO ++ Standard 108*51c0b2f7Stbbdev typename A::pointer px = &x; 109*51c0b2f7Stbbdev typename A::const_pointer pcx = &cx; 110*51c0b2f7Stbbdev 111*51c0b2f7Stbbdev typename A::reference rx = x; 112*51c0b2f7Stbbdev REQUIRE(&rx == &x); 113*51c0b2f7Stbbdev 114*51c0b2f7Stbbdev typename A::const_reference rcx = cx; 115*51c0b2f7Stbbdev REQUIRE(&rcx==&cx); 116*51c0b2f7Stbbdev 117*51c0b2f7Stbbdev typename A::value_type v = x; 118*51c0b2f7Stbbdev 119*51c0b2f7Stbbdev typename A::size_type size; 120*51c0b2f7Stbbdev size = 0; 121*51c0b2f7Stbbdev --size; 122*51c0b2f7Stbbdev REQUIRE_MESSAGE(size > 0, "not an unsigned integral type?"); 123*51c0b2f7Stbbdev 124*51c0b2f7Stbbdev typename A::difference_type difference; 125*51c0b2f7Stbbdev difference = 0; 126*51c0b2f7Stbbdev --difference; 127*51c0b2f7Stbbdev REQUIRE_MESSAGE(difference<0, "not an signed integral type?"); 128*51c0b2f7Stbbdev 129*51c0b2f7Stbbdev // "rebind" tested by our caller 130*51c0b2f7Stbbdev 131*51c0b2f7Stbbdev REQUIRE(a.address(rx) == px); 132*51c0b2f7Stbbdev 133*51c0b2f7Stbbdev REQUIRE(a.address(rcx) == pcx); 134*51c0b2f7Stbbdev 135*51c0b2f7Stbbdev // Test "a.max_size()" 136*51c0b2f7Stbbdev AssertSameType(a.max_size(), typename A::size_type(0)); 137*51c0b2f7Stbbdev // Following assertion catches case where max_size() is so large that computation of 138*51c0b2f7Stbbdev // number of bytes for such an allocation would overflow size_type. 139*51c0b2f7Stbbdev REQUIRE_MESSAGE((a.max_size() * typename A::size_type(sizeof(T)) >= a.max_size()), "max_size larger than reasonable"); 140*51c0b2f7Stbbdev 141*51c0b2f7Stbbdev // Test "a.construct(p,t)" 142*51c0b2f7Stbbdev int n = NumberOfFoo; 143*51c0b2f7Stbbdev typename A::pointer p = a.allocate(1); 144*51c0b2f7Stbbdev a.construct(p, cx); 145*51c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == n + 1, "constructor for Foo not called?"); 146*51c0b2f7Stbbdev 147*51c0b2f7Stbbdev // Test "a.destroy(p)" 148*51c0b2f7Stbbdev a.destroy(p); 149*51c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == n, "destructor for Foo not called?"); 150*51c0b2f7Stbbdev a.deallocate(p, 1); 151*51c0b2f7Stbbdev 152*51c0b2f7Stbbdev { 153*51c0b2f7Stbbdev typedef typename A::template rebind<std::pair<typename A::value_type, typename A::value_type> >::other pair_allocator_type; 154*51c0b2f7Stbbdev pair_allocator_type pair_allocator(a); 155*51c0b2f7Stbbdev int NumberOfFooBeforeConstruct = NumberOfFoo; 156*51c0b2f7Stbbdev typename pair_allocator_type::pointer pair_pointer = pair_allocator.allocate(1); 157*51c0b2f7Stbbdev pair_allocator.construct(pair_pointer, cx, cx); 158*51c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == NumberOfFooBeforeConstruct+2, "constructor for Foo not called appropriate number of times?"); 159*51c0b2f7Stbbdev 160*51c0b2f7Stbbdev pair_allocator.destroy(pair_pointer); 161*51c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == NumberOfFooBeforeConstruct, "destructor for Foo not called appropriate number of times?"); 162*51c0b2f7Stbbdev pair_allocator.deallocate(pair_pointer, 1); 163*51c0b2f7Stbbdev } 164*51c0b2f7Stbbdev } 165*51c0b2f7Stbbdev 166*51c0b2f7Stbbdev //! T is type and A is allocator for that type 167*51c0b2f7Stbbdev template<typename T, typename A> 168*51c0b2f7Stbbdev void TestAllocatorConcept(A& a) { 169*51c0b2f7Stbbdev // Test "a.allocate(p,n) 170*51c0b2f7Stbbdev typename std::allocator_traits<A>::pointer array[100]; 171*51c0b2f7Stbbdev std::size_t sizeof_T = sizeof(T); 172*51c0b2f7Stbbdev for(std::size_t k = 0; k < 100; ++k) { 173*51c0b2f7Stbbdev array[k] = a.allocate(k); 174*51c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k])); 175*51c0b2f7Stbbdev for(std::size_t j=0; j < k * sizeof_T; ++j) 176*51c0b2f7Stbbdev s[j] = PseudoRandomValue(j, k); 177*51c0b2f7Stbbdev } 178*51c0b2f7Stbbdev 179*51c0b2f7Stbbdev // Test "a.deallocate(p,n) 180*51c0b2f7Stbbdev for(std::size_t k = 0; k < 100; ++k) { 181*51c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k])); 182*51c0b2f7Stbbdev for(std::size_t j = 0; j < k * sizeof_T; ++j) 183*51c0b2f7Stbbdev REQUIRE(s[j] == PseudoRandomValue(j, k)); 184*51c0b2f7Stbbdev a.deallocate(array[k], k); 185*51c0b2f7Stbbdev } 186*51c0b2f7Stbbdev } 187*51c0b2f7Stbbdev 188*51c0b2f7Stbbdev //! T is type and A is allocator for that type 189*51c0b2f7Stbbdev template<typename T, typename A> 190*51c0b2f7Stbbdev void TestAllocatorExceptions(A& a) { 191*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 192*51c0b2f7Stbbdev volatile size_t too_big = (~std::size_t(0) - 1024 * 1024) / sizeof(T); 193*51c0b2f7Stbbdev bool exception_caught = false; 194*51c0b2f7Stbbdev typename std::allocator_traits<A>::pointer p1 = nullptr; 195*51c0b2f7Stbbdev try { 196*51c0b2f7Stbbdev #if __APPLE__ 197*51c0b2f7Stbbdev // On macOS*, failure to map memory results in messages to stderr; 198*51c0b2f7Stbbdev // suppress them. 199*51c0b2f7Stbbdev DisableStderr disableStderr; 200*51c0b2f7Stbbdev #endif 201*51c0b2f7Stbbdev p1 = a.allocate(too_big); 202*51c0b2f7Stbbdev } catch (std::bad_alloc&) { 203*51c0b2f7Stbbdev exception_caught = true; 204*51c0b2f7Stbbdev } 205*51c0b2f7Stbbdev REQUIRE_MESSAGE(exception_caught, "allocate expected to throw bad_alloc"); 206*51c0b2f7Stbbdev a.deallocate(p1, too_big); 207*51c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS 208*51c0b2f7Stbbdev utils::suppress_unused_warning(a); 209*51c0b2f7Stbbdev } 210*51c0b2f7Stbbdev 211*51c0b2f7Stbbdev #if _MSC_VER && !defined(__INTEL_COMPILER) 212*51c0b2f7Stbbdev // Workaround for erroneous "conditional expression is constant" warning in method check_allocate. 213*51c0b2f7Stbbdev #pragma warning (disable: 4127) 214*51c0b2f7Stbbdev #endif 215*51c0b2f7Stbbdev 216*51c0b2f7Stbbdev // A is an allocator for some type 217*51c0b2f7Stbbdev template<typename A> 218*51c0b2f7Stbbdev struct Body: utils::NoAssign { 219*51c0b2f7Stbbdev using pointer_type = typename std::allocator_traits<A>::pointer; 220*51c0b2f7Stbbdev using value_type = typename std::allocator_traits<A>::value_type; 221*51c0b2f7Stbbdev // For the int types and above this test runs too long 222*51c0b2f7Stbbdev static const std::size_t max_k = sizeof(value_type) < sizeof(int) ? 100000 : 5000; 223*51c0b2f7Stbbdev A &a; 224*51c0b2f7Stbbdev Body(A &a_) : a(a_) {} 225*51c0b2f7Stbbdev void check_allocate(pointer_type array[], std::size_t i, std::size_t t) const 226*51c0b2f7Stbbdev { 227*51c0b2f7Stbbdev REQUIRE(array[i] == nullptr); 228*51c0b2f7Stbbdev std::size_t size = i * (i & 3); 229*51c0b2f7Stbbdev array[i] = a.allocate(size); 230*51c0b2f7Stbbdev REQUIRE_MESSAGE(array[i] != nullptr, "allocator returned null"); 231*51c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i])); 232*51c0b2f7Stbbdev for(std::size_t j = 0; j < size * sizeof(value_type); ++j) { 233*51c0b2f7Stbbdev if(is_zero_filling<typename std::allocator_traits<A>::template rebind_alloc<void>>::value) 234*51c0b2f7Stbbdev REQUIRE(!s[j]); 235*51c0b2f7Stbbdev s[j] = PseudoRandomValue(i, t); 236*51c0b2f7Stbbdev } 237*51c0b2f7Stbbdev } 238*51c0b2f7Stbbdev 239*51c0b2f7Stbbdev void check_deallocate(pointer_type array[], std::size_t i, std::size_t t) const 240*51c0b2f7Stbbdev { 241*51c0b2f7Stbbdev REQUIRE(array[i] != nullptr); 242*51c0b2f7Stbbdev size_t size = i * (i & 3); 243*51c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i])); 244*51c0b2f7Stbbdev for(std::size_t j=0; j < size * sizeof(value_type); ++j) 245*51c0b2f7Stbbdev REQUIRE_MESSAGE(s[j] == PseudoRandomValue(i, t), "Thread safety test failed"); 246*51c0b2f7Stbbdev a.deallocate(array[i], size); 247*51c0b2f7Stbbdev array[i] = 0; 248*51c0b2f7Stbbdev } 249*51c0b2f7Stbbdev 250*51c0b2f7Stbbdev void operator()(std::size_t thread_id) const { 251*51c0b2f7Stbbdev pointer_type array[256]; 252*51c0b2f7Stbbdev 253*51c0b2f7Stbbdev for(std::size_t k = 0; k < 256; ++k) 254*51c0b2f7Stbbdev array[k] = 0; 255*51c0b2f7Stbbdev for(std::size_t k = 0; k < max_k; ++k) { 256*51c0b2f7Stbbdev std::size_t i = static_cast<unsigned char>(PseudoRandomValue(k, thread_id)); 257*51c0b2f7Stbbdev if(!array[i]) check_allocate(array, i, thread_id); 258*51c0b2f7Stbbdev else check_deallocate(array, i, thread_id); 259*51c0b2f7Stbbdev } 260*51c0b2f7Stbbdev for(std::size_t k = 0; k < 256; ++k) 261*51c0b2f7Stbbdev if(array[k]) 262*51c0b2f7Stbbdev check_deallocate(array, k, thread_id); 263*51c0b2f7Stbbdev } 264*51c0b2f7Stbbdev }; 265*51c0b2f7Stbbdev 266*51c0b2f7Stbbdev template<typename A> 267*51c0b2f7Stbbdev void TestThreadSafety(A &a) { 268*51c0b2f7Stbbdev utils::NativeParallelFor(4, Body<A>(a)); 269*51c0b2f7Stbbdev } 270*51c0b2f7Stbbdev 271*51c0b2f7Stbbdev enum TestName { Concept, Broken, Exceptions, ThreadSafety, Comparison }; 272*51c0b2f7Stbbdev 273*51c0b2f7Stbbdev template<typename Allocator> 274*51c0b2f7Stbbdev void TestAllocator(TestName name, const Allocator &a = Allocator()) { 275*51c0b2f7Stbbdev 276*51c0b2f7Stbbdev using FooChar = Foo<char, 1>; 277*51c0b2f7Stbbdev using FooDouble = Foo<double, 1>; 278*51c0b2f7Stbbdev using FooInt = Foo<int, 17>; 279*51c0b2f7Stbbdev using FooFloat = Foo<float, 23>; 280*51c0b2f7Stbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 281*51c0b2f7Stbbdev using AllocatorFooChar = typename Allocator::template rebind<FooChar>::other; 282*51c0b2f7Stbbdev using AllocatorFooDouble = typename Allocator::template rebind<FooDouble>::other; 283*51c0b2f7Stbbdev using AllocatorFooInt = typename AllocatorFooChar::template rebind<FooInt>::other; 284*51c0b2f7Stbbdev using AllocatorFooFloat = typename AllocatorFooDouble::template rebind<FooFloat>::other; 285*51c0b2f7Stbbdev #else 286*51c0b2f7Stbbdev using AllocatorFooChar = typename std::allocator_traits<Allocator>::template rebind_alloc<FooChar>; 287*51c0b2f7Stbbdev using AllocatorFooDouble = typename std::allocator_traits<Allocator>::template rebind_alloc<FooDouble>; 288*51c0b2f7Stbbdev using AllocatorFooInt = typename std::allocator_traits<AllocatorFooChar>::template rebind_alloc<FooInt>; 289*51c0b2f7Stbbdev using AllocatorFooFloat = typename std::allocator_traits<AllocatorFooDouble>::template rebind_alloc<FooFloat>; 290*51c0b2f7Stbbdev #endif 291*51c0b2f7Stbbdev 292*51c0b2f7Stbbdev NumberOfFoo = 0; 293*51c0b2f7Stbbdev AllocatorFooChar a1(a); 294*51c0b2f7Stbbdev AllocatorFooDouble a2(a); 295*51c0b2f7Stbbdev AllocatorFooInt b1(a1); 296*51c0b2f7Stbbdev AllocatorFooFloat b2(a2); 297*51c0b2f7Stbbdev 298*51c0b2f7Stbbdev switch(name) { 299*51c0b2f7Stbbdev case Comparison: 300*51c0b2f7Stbbdev REQUIRE(a1 == b1); 301*51c0b2f7Stbbdev REQUIRE(!(a2 != b2)); 302*51c0b2f7Stbbdev break; 303*51c0b2f7Stbbdev case Concept: 304*51c0b2f7Stbbdev TestAllocatorConcept<FooInt>(b1); 305*51c0b2f7Stbbdev TestAllocatorConcept<typename AllocatorFooChar::value_type>(a1); 306*51c0b2f7Stbbdev TestAllocatorConcept<FooFloat>(b2); 307*51c0b2f7Stbbdev TestAllocatorConcept<typename AllocatorFooDouble::value_type>(a2); 308*51c0b2f7Stbbdev break; 309*51c0b2f7Stbbdev case Broken: 310*51c0b2f7Stbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 311*51c0b2f7Stbbdev TestBrokenAllocator<FooInt>(b1); 312*51c0b2f7Stbbdev TestBrokenAllocator<typename AllocatorFooChar::value_type>(a1); 313*51c0b2f7Stbbdev TestBrokenAllocator<FooFloat>(b2); 314*51c0b2f7Stbbdev TestBrokenAllocator<typename AllocatorFooDouble::value_type>(a2); 315*51c0b2f7Stbbdev #endif 316*51c0b2f7Stbbdev break; 317*51c0b2f7Stbbdev case Exceptions: 318*51c0b2f7Stbbdev TestAllocatorExceptions<FooInt>(b1); 319*51c0b2f7Stbbdev TestAllocatorExceptions<typename AllocatorFooChar::value_type>(a1); 320*51c0b2f7Stbbdev TestAllocatorExceptions<FooFloat>(b2); 321*51c0b2f7Stbbdev TestAllocatorExceptions<typename AllocatorFooDouble::value_type>(a2); 322*51c0b2f7Stbbdev break; 323*51c0b2f7Stbbdev case ThreadSafety: 324*51c0b2f7Stbbdev TestThreadSafety(a1); 325*51c0b2f7Stbbdev TestThreadSafety(a2); 326*51c0b2f7Stbbdev break; 327*51c0b2f7Stbbdev } 328*51c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == 0, "Allocate/deallocate count mismatched"); 329*51c0b2f7Stbbdev } 330