1 //===----------------------------------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is dual licensed under the MIT and the University of Illinois Open 6 // Source Licenses. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef SUPPORT_TEST_MEMORY_RESOURCE_HPP 11 #define SUPPORT_TEST_MEMORY_RESOURCE_HPP 12 13 #include <experimental/memory_resource> 14 #include <memory> 15 #include <type_traits> 16 #include <cstddef> 17 #include <cstdlib> 18 #include <cstring> 19 #include <cassert> 20 #include "test_macros.h" 21 22 struct AllocController; 23 // 'AllocController' is a concrete type that instruments and controls the 24 // behavior of of test allocators. 25 26 template <class T> 27 class CountingAllocator; 28 // 'CountingAllocator' is an basic implementation of the 'Allocator' 29 // requirements that use the 'AllocController' interface. 30 31 template <class T> 32 class MinAlignAllocator; 33 // 'MinAlignAllocator' is an instrumented test type which implements the 34 // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never* 35 // returns a pointer to over-aligned storage. For example 36 // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned 37 // pointer. 38 39 template <class T> 40 class NullAllocator; 41 // 'NullAllocator' is an instrumented test type which implements the 42 // 'Allocator' requirements except that 'allocator' and 'deallocate' are 43 // nops. 44 45 46 #define DISALLOW_COPY(Type) \ 47 Type(Type const&) = delete; \ 48 Type& operator=(Type const&) = delete 49 50 constexpr std::size_t MaxAlignV = alignof(std::max_align_t); 51 52 struct TestException {}; 53 54 struct AllocController { 55 int copy_constructed = 0; 56 int move_constructed = 0; 57 58 int alive = 0; 59 int alloc_count = 0; 60 int dealloc_count = 0; 61 int is_equal_count = 0; 62 63 std::size_t alive_size; 64 std::size_t allocated_size; 65 std::size_t deallocated_size; 66 67 std::size_t last_size = 0; 68 std::size_t last_align = 0; 69 void * last_pointer = 0; 70 71 std::size_t last_alloc_size = 0; 72 std::size_t last_alloc_align = 0; 73 void * last_alloc_pointer = nullptr; 74 75 std::size_t last_dealloc_size = 0; 76 std::size_t last_dealloc_align = 0; 77 void * last_dealloc_pointer = nullptr; 78 79 bool throw_on_alloc = false; 80 81 AllocController() = default; 82 83 void countAlloc(void* p, size_t s, size_t a) { 84 ++alive; 85 ++alloc_count; 86 alive_size += s; 87 allocated_size += s; 88 last_pointer = last_alloc_pointer = p; 89 last_size = last_alloc_size = s; 90 last_align = last_alloc_align = a; 91 } 92 93 void countDealloc(void* p, size_t s, size_t a) { 94 --alive; 95 ++dealloc_count; 96 alive_size -= s; 97 deallocated_size += s; 98 last_pointer = last_dealloc_pointer = p; 99 last_size = last_dealloc_size = s; 100 last_align = last_dealloc_align = a; 101 } 102 103 void reset() { std::memset(this, 0, sizeof(*this)); } 104 105 public: 106 bool checkAlloc(void* p, size_t s, size_t a) const { 107 return p == last_alloc_pointer && 108 s == last_alloc_size && 109 a == last_alloc_align; 110 } 111 112 bool checkAlloc(void* p, size_t s) const { 113 return p == last_alloc_pointer && 114 s == last_alloc_size; 115 } 116 117 bool checkAllocAtLeast(void* p, size_t s, size_t a) const { 118 return p == last_alloc_pointer && 119 s <= last_alloc_size && 120 a <= last_alloc_align; 121 } 122 123 bool checkAllocAtLeast(void* p, size_t s) const { 124 return p == last_alloc_pointer && 125 s <= last_alloc_size; 126 } 127 128 bool checkDealloc(void* p, size_t s, size_t a) const { 129 return p == last_dealloc_pointer && 130 s == last_dealloc_size && 131 a == last_dealloc_align; 132 } 133 134 bool checkDealloc(void* p, size_t s) const { 135 return p == last_dealloc_pointer && 136 s == last_dealloc_size; 137 } 138 139 bool checkDeallocMatchesAlloc() const { 140 return last_dealloc_pointer == last_alloc_pointer && 141 last_dealloc_size == last_alloc_size && 142 last_dealloc_align == last_alloc_align; 143 } 144 145 void countIsEqual() { 146 ++is_equal_count; 147 } 148 149 bool checkIsEqualCalledEq(int n) const { 150 return is_equal_count == n; 151 } 152 private: 153 DISALLOW_COPY(AllocController); 154 }; 155 156 template <class T> 157 class CountingAllocator 158 { 159 public: 160 typedef T value_type; 161 typedef T* pointer; 162 CountingAllocator() = delete; 163 explicit CountingAllocator(AllocController& PP) : P(&PP) {} 164 165 CountingAllocator(CountingAllocator const& other) : P(other.P) { 166 P->copy_constructed += 1; 167 } 168 169 CountingAllocator(CountingAllocator&& other) : P(other.P) { 170 P->move_constructed += 1; 171 } 172 173 template <class U> 174 CountingAllocator(CountingAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { 175 P->copy_constructed += 1; 176 } 177 178 template <class U> 179 CountingAllocator(CountingAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { 180 P->move_constructed += 1; 181 } 182 183 T* allocate(std::size_t n) 184 { 185 void* ret = ::operator new(n*sizeof(T)); 186 P->countAlloc(ret, n*sizeof(T), alignof(T)); 187 return static_cast<T*>(ret); 188 } 189 190 void deallocate(T* p, std::size_t n) 191 { 192 void* vp = static_cast<void*>(p); 193 P->countDealloc(vp, n*sizeof(T), alignof(T)); 194 ::operator delete(vp); 195 } 196 197 AllocController& getController() const { return *P; } 198 199 private: 200 template <class Tp> friend class CountingAllocator; 201 AllocController *P; 202 }; 203 204 template <class T, class U> 205 inline bool operator==(CountingAllocator<T> const& x, 206 CountingAllocator<U> const& y) { 207 return &x.getController() == &y.getController(); 208 } 209 210 template <class T, class U> 211 inline bool operator!=(CountingAllocator<T> const& x, 212 CountingAllocator<U> const& y) { 213 return !(x == y); 214 } 215 216 template <class T> 217 class MinAlignedAllocator 218 { 219 public: 220 typedef T value_type; 221 typedef T* pointer; 222 223 MinAlignedAllocator() = delete; 224 225 explicit MinAlignedAllocator(AllocController& R) : P(&R) {} 226 227 MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) { 228 P->copy_constructed += 1; 229 } 230 231 MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) { 232 P->move_constructed += 1; 233 } 234 235 template <class U> 236 MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { 237 P->copy_constructed += 1; 238 } 239 240 template <class U> 241 MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { 242 P->move_constructed += 1; 243 } 244 245 T* allocate(std::size_t n) { 246 char* aligned_ptr = (char*)::operator new(alloc_size(n*sizeof(T))); 247 assert(is_max_aligned(aligned_ptr)); 248 249 char* unaligned_ptr = aligned_ptr + alignof(T); 250 assert(is_min_aligned(unaligned_ptr)); 251 252 P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T)); 253 254 return ((T*)unaligned_ptr); 255 } 256 257 void deallocate(T* p, std::size_t n) { 258 assert(is_min_aligned(p)); 259 260 char* aligned_ptr = ((char*)p) - alignof(T); 261 assert(is_max_aligned(aligned_ptr)); 262 263 P->countDealloc(p, n*sizeof(T), alignof(T)); 264 265 return ::operator delete(static_cast<void*>(aligned_ptr)); 266 } 267 268 AllocController& getController() const { return *P; } 269 270 private: 271 static const std::size_t BlockSize = alignof(std::max_align_t); 272 273 static std::size_t alloc_size(std::size_t s) { 274 std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1); 275 bytes += BlockSize; 276 assert(bytes % BlockSize == 0); 277 return bytes / BlockSize; 278 } 279 280 static bool is_max_aligned(void* p) { 281 return reinterpret_cast<std::size_t>(p) % BlockSize == 0; 282 } 283 284 static bool is_min_aligned(void* p) { 285 if (alignof(T) == BlockSize) { 286 return is_max_aligned(p); 287 } else { 288 return reinterpret_cast<std::size_t>(p) % BlockSize == alignof(T); 289 } 290 } 291 292 template <class Tp> friend class MinAlignedAllocator; 293 mutable AllocController *P; 294 }; 295 296 297 template <class T, class U> 298 inline bool operator==(MinAlignedAllocator<T> const& x, 299 MinAlignedAllocator<U> const& y) { 300 return &x.getController() == &y.getController(); 301 } 302 303 template <class T, class U> 304 inline bool operator!=(MinAlignedAllocator<T> const& x, 305 MinAlignedAllocator<U> const& y) { 306 return !(x == y); 307 } 308 309 template <class T> 310 class NullAllocator 311 { 312 public: 313 typedef T value_type; 314 typedef T* pointer; 315 NullAllocator() = delete; 316 explicit NullAllocator(AllocController& PP) : P(&PP) {} 317 318 NullAllocator(NullAllocator const& other) : P(other.P) { 319 P->copy_constructed += 1; 320 } 321 322 NullAllocator(NullAllocator&& other) : P(other.P) { 323 P->move_constructed += 1; 324 } 325 326 template <class U> 327 NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) { 328 P->copy_constructed += 1; 329 } 330 331 template <class U> 332 NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) { 333 P->move_constructed += 1; 334 } 335 336 T* allocate(std::size_t n) 337 { 338 P->countAlloc(nullptr, n*sizeof(T), alignof(T)); 339 return nullptr; 340 } 341 342 void deallocate(T* p, std::size_t n) 343 { 344 void* vp = static_cast<void*>(p); 345 P->countDealloc(vp, n*sizeof(T), alignof(T)); 346 } 347 348 AllocController& getController() const { return *P; } 349 350 private: 351 template <class Tp> friend class NullAllocator; 352 AllocController *P; 353 }; 354 355 template <class T, class U> 356 inline bool operator==(NullAllocator<T> const& x, 357 NullAllocator<U> const& y) { 358 return &x.getController() == &y.getController(); 359 } 360 361 template <class T, class U> 362 inline bool operator!=(NullAllocator<T> const& x, 363 NullAllocator<U> const& y) { 364 return !(x == y); 365 } 366 367 368 369 template <class ProviderT, int = 0> 370 class TestResourceImp : public std::experimental::pmr::memory_resource 371 { 372 public: 373 static int resource_alive; 374 static int resource_constructed; 375 static int resource_destructed; 376 377 static void resetStatics() { 378 assert(resource_alive == 0); 379 resource_alive = 0; 380 resource_constructed = 0; 381 resource_destructed = 0; 382 } 383 384 using memory_resource = std::experimental::pmr::memory_resource; 385 using Provider = ProviderT; 386 387 int value; 388 389 explicit TestResourceImp(int val = 0) : value(val) { 390 ++resource_alive; 391 ++resource_constructed; 392 } 393 394 ~TestResourceImp() noexcept { 395 --resource_alive; 396 ++resource_destructed; 397 } 398 399 void reset() { C.reset(); P.reset(); } 400 AllocController& getController() { return C; } 401 402 bool checkAlloc(void* p, std::size_t s, std::size_t a) const 403 { return C.checkAlloc(p, s, a); } 404 405 bool checkDealloc(void* p, std::size_t s, std::size_t a) const 406 { return C.checkDealloc(p, s, a); } 407 408 bool checkIsEqualCalledEq(int n) const { return C.checkIsEqualCalledEq(n); } 409 410 protected: 411 virtual void * do_allocate(std::size_t s, std::size_t a) { 412 if (C.throw_on_alloc) { 413 #ifndef TEST_HAS_NO_EXCEPTIONS 414 throw TestException{}; 415 #else 416 assert(false); 417 #endif 418 } 419 void* ret = P.allocate(s, a); 420 C.countAlloc(ret, s, a); 421 return ret; 422 } 423 424 virtual void do_deallocate(void * p, std::size_t s, std::size_t a) { 425 C.countDealloc(p, s, a); 426 P.deallocate(p, s, a); 427 } 428 429 virtual bool do_is_equal(memory_resource const & other) const noexcept { 430 C.countIsEqual(); 431 TestResourceImp const * o = dynamic_cast<TestResourceImp const *>(&other); 432 return o && o->value == value; 433 } 434 private: 435 mutable AllocController C; 436 mutable Provider P; 437 DISALLOW_COPY(TestResourceImp); 438 }; 439 440 template <class Provider, int N> 441 int TestResourceImp<Provider, N>::resource_alive = 0; 442 443 template <class Provider, int N> 444 int TestResourceImp<Provider, N>::resource_constructed = 0; 445 446 template <class Provider, int N> 447 int TestResourceImp<Provider, N>::resource_destructed = 0; 448 449 450 struct NullProvider { 451 NullProvider() {} 452 void* allocate(size_t, size_t) { return nullptr; } 453 void deallocate(void*, size_t, size_t) {} 454 void reset() {} 455 private: 456 DISALLOW_COPY(NullProvider); 457 }; 458 459 struct NewDeleteProvider { 460 NewDeleteProvider() {} 461 void* allocate(size_t s, size_t) { return ::operator new(s); } 462 void deallocate(void* p, size_t, size_t) { ::operator delete(p); } 463 void reset() {} 464 private: 465 DISALLOW_COPY(NewDeleteProvider); 466 }; 467 468 template <size_t Size = 4096 * 10> // 10 pages worth of memory. 469 struct BufferProvider { 470 char buffer[Size]; 471 void* next = &buffer; 472 size_t space = Size; 473 474 BufferProvider() {} 475 476 void* allocate(size_t s, size_t a) { 477 void* ret = std::align(s, a, next, space); 478 if (ret == nullptr) { 479 #ifndef TEST_HAS_NO_EXCEPTIONS 480 throw std::bad_alloc(); 481 #else 482 assert(false); 483 #endif 484 } 485 486 return ret; 487 } 488 489 void deallocate(void*, size_t, size_t) {} 490 491 void reset() { 492 next = &buffer; 493 space = Size; 494 } 495 private: 496 DISALLOW_COPY(BufferProvider); 497 }; 498 499 using NullResource = TestResourceImp<NullProvider, 0>; 500 using NewDeleteResource = TestResourceImp<NewDeleteProvider, 0>; 501 using TestResource = TestResourceImp<BufferProvider<>, 0>; 502 using TestResource1 = TestResourceImp<BufferProvider<>, 1>; 503 using TestResource2 = TestResourceImp<BufferProvider<>, 2>; 504 505 506 #endif /* SUPPORT_TEST_MEMORY_RESOURCE_HPP */ 507