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