1 /* 2 Copyright (c) 2005-2021 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 #ifndef __TBB_test_common_custom_allocators_H 18 #define __TBB_test_common_custom_allocators_H 19 20 #include "test.h" 21 #include <oneapi/tbb/detail/_allocator_traits.h> 22 #include <memory> 23 #include <atomic> 24 #include <scoped_allocator> 25 26 template <typename CounterType> 27 struct ArenaData { 28 char* const my_buffer; 29 const std::size_t my_size; 30 CounterType my_allocated; // in bytes 31 32 template <typename T> ArenaDataArenaData33 ArenaData( T* buf, std::size_t sz ) noexcept 34 : my_buffer(reinterpret_cast<char*>(buf)), 35 my_size(sz * sizeof(T)) 36 { 37 my_allocated = 0; 38 } 39 40 ArenaData& operator=( const ArenaData& ) = delete; 41 }; // struct ArenaData 42 43 template <typename T, typename POCMA = std::false_type, typename CounterType = std::size_t> 44 struct ArenaAllocator { 45 using arena_data_type = ArenaData<CounterType>; 46 47 arena_data_type* my_data; 48 49 using value_type = T; 50 using propagate_on_container_move_assignment = POCMA; 51 52 template <typename U> 53 struct rebind { 54 using other = ArenaAllocator<U, POCMA, CounterType>; 55 }; 56 57 ArenaAllocator() = default; ArenaAllocatorArenaAllocator58 ArenaAllocator( arena_data_type& data ) noexcept : my_data(&data) {} 59 60 template <typename U, typename POCMA2> ArenaAllocatorArenaAllocator61 ArenaAllocator( const ArenaAllocator<U, POCMA2, CounterType>& other ) noexcept 62 : my_data(other.my_data) {} 63 swapArenaAllocator64 friend void swap( ArenaAllocator& lhs, ArenaAllocator& rhs ) { 65 using std::swap; 66 swap(lhs.my_data, rhs.my_data); 67 } 68 addressArenaAllocator69 value_type* address( value_type& x ) const { return &x; } addressArenaAllocator70 const value_type* address( const value_type& x ) const { return &x; } 71 allocateArenaAllocator72 value_type* allocate( std::size_t n ) { 73 std::size_t new_size = (my_data->my_allocated += n * sizeof(T)); 74 REQUIRE_MESSAGE(my_data->my_allocated <= my_data->my_size, "Trying to allocate more than was reserved"); 75 char* result = &(my_data->my_buffer[new_size - n * sizeof(T)]); 76 return reinterpret_cast<value_type*>(result); 77 } 78 deallocateArenaAllocator79 void deallocate( value_type* ptr, std::size_t n ) { 80 char* p = reinterpret_cast<char*>(ptr); 81 REQUIRE_MESSAGE((p >= my_data->my_buffer && p <= my_data->my_buffer + my_data->my_size), 82 "Trying to deallocate pointer not from arena"); 83 REQUIRE_MESSAGE((p + n * sizeof(T) <= my_data->my_buffer + my_data->my_size), 84 "Trying to deallocate pointer not from arena"); 85 // utils::suppress_unused_warning(p, n); 86 } 87 max_sizeArenaAllocator88 std::size_t max_size() const noexcept { 89 return my_data->my_size / sizeof(T); 90 } 91 }; // class ArenaAllocator 92 93 template <typename T, typename U, typename POCMA, typename C> 94 bool operator==( const ArenaAllocator<T, POCMA, C>& lhs, const ArenaAllocator<U, POCMA, C>& rhs ) { 95 return lhs.my_data == rhs.my_data; 96 } 97 98 template <typename T, typename U, typename POCMA, typename C> 99 bool operator!=( const ArenaAllocator<T, POCMA, C>& lhs, const ArenaAllocator<U, POCMA, C>& rhs ) { 100 return !(lhs == rhs); 101 } 102 103 template <typename BaseAllocatorType> 104 class LocalCountingAllocator : public BaseAllocatorType { 105 using base_type = BaseAllocatorType; 106 using base_traits = tbb::detail::allocator_traits<base_type>; 107 using counter_type = std::atomic<std::size_t>; 108 public: 109 using value_type = typename base_type::value_type; 110 111 std::size_t max_items; 112 counter_type items_allocated; 113 counter_type items_freed; 114 counter_type items_constructed; 115 counter_type items_destroyed; 116 counter_type allocations; 117 counter_type frees; 118 set_counters(std::size_t it_allocated,std::size_t it_freed,std::size_t it_constructed,std::size_t it_destroyed,std::size_t allocs,std::size_t fres)119 void set_counters( std::size_t it_allocated, std::size_t it_freed, 120 std::size_t it_constructed, std::size_t it_destroyed, 121 std::size_t allocs, std::size_t fres ) { 122 items_allocated = it_allocated; // TODO: may be store 123 items_freed = it_freed; 124 items_constructed = it_constructed; 125 items_destroyed = it_destroyed; 126 allocations = allocs; 127 frees = fres; 128 } 129 130 template <typename Allocator> set_counters(const Allocator & alloc)131 void set_counters( const Allocator& alloc ) { 132 set_counters(alloc.items_allocated, alloc.items_freed, alloc.items_constructed, 133 alloc.items_destroyed, alloc.allocations, alloc.frees); 134 } 135 clear_counters()136 void clear_counters() { 137 set_counters(0, 0, 0, 0, 0, 0); 138 } 139 140 template <typename U> 141 struct rebind { 142 using other = LocalCountingAllocator<typename base_traits::template rebind_alloc<U>>; 143 }; 144 LocalCountingAllocator()145 LocalCountingAllocator() : max_items{0} { clear_counters(); } 146 LocalCountingAllocator(const LocalCountingAllocator & other)147 LocalCountingAllocator( const LocalCountingAllocator& other ) 148 : base_type(other), max_items{other.max_items} { set_counters(other); } 149 150 template <typename U> LocalCountingAllocator(const LocalCountingAllocator<U> & other)151 LocalCountingAllocator( const LocalCountingAllocator<U>& other ) 152 : base_type(other), max_items{other.max_items} { set_counters(other); } 153 154 LocalCountingAllocator& operator=( const LocalCountingAllocator& other ) { 155 base_type::operator=(other); 156 max_items = other.max_items; 157 set_counters(other); 158 return *this; 159 } 160 allocate(std::size_t n)161 value_type* allocate( std::size_t n ) { 162 if (max_items != 0 && items_allocated + n >= max_items) { 163 TBB_TEST_THROW(std::bad_alloc()); 164 } 165 value_type* ptr = static_cast<base_type*>(this)->allocate(n); 166 ++allocations; 167 items_allocated += n; 168 return ptr; 169 } 170 deallocate(value_type * ptr,std::size_t n)171 void deallocate( value_type* ptr, std::size_t n ) { 172 ++frees; 173 items_freed += n; 174 static_cast<base_type*>(this)->deallocate(ptr, n); 175 } 176 177 template <typename U, typename... Args> construct(U * ptr,Args &&...args)178 void construct( U* ptr, Args&&... args ) { 179 base_traits::construct(*this, ptr, std::forward<Args>(args)...); 180 ++items_constructed; 181 } 182 183 template <typename U> destroy(U * ptr)184 void destroy( U* ptr ) { 185 base_traits::destroy(*this, ptr); 186 ++items_destroyed; 187 } 188 set_limits(std::size_t max)189 void set_limits( std::size_t max ) { 190 max_items = max; 191 } 192 }; // class LocalCountingAllocator 193 194 struct AllocatorCounters { 195 using counter_type = std::atomic<std::size_t>; 196 197 counter_type items_allocated; 198 counter_type items_freed; 199 counter_type items_constructed; 200 counter_type items_destroyed; 201 counter_type allocations; 202 counter_type frees; 203 204 AllocatorCounters() = default; 205 AllocatorCountersAllocatorCounters206 AllocatorCounters( std::size_t it_allocated, std::size_t it_freed, std::size_t it_constructed, 207 std::size_t it_destroyed, std::size_t allocs, std::size_t fres ) 208 : items_allocated(it_allocated), items_freed(it_freed), 209 items_constructed(it_constructed), items_destroyed(it_destroyed), 210 allocations(allocs), frees(fres) {} 211 AllocatorCountersAllocatorCounters212 AllocatorCounters( const AllocatorCounters& other ) 213 : items_allocated(other.items_allocated.load()), 214 items_freed(other.items_allocated.load()), 215 items_constructed(other.items_constructed.load()), 216 items_destroyed(other.items_destroyed.load()), 217 allocations(other.allocations.load()), 218 frees(other.allocations.load()) {} 219 220 AllocatorCounters& operator=( const AllocatorCounters& other ) { 221 items_allocated.store(other.items_allocated.load()); 222 items_freed.store(other.items_freed.load()); 223 items_constructed.store(other.items_constructed.load()); 224 items_destroyed.store(other.items_destroyed.load()); 225 allocations.store(other.allocations.load()); 226 frees.store(other.frees.load()); 227 return *this; 228 } 229 230 friend bool operator==( const AllocatorCounters& lhs, const AllocatorCounters& rhs ) { 231 return lhs.items_allocated == rhs.items_allocated && 232 lhs.items_freed == rhs.items_freed && 233 lhs.items_constructed == rhs.items_constructed && 234 lhs.items_destroyed == rhs.items_destroyed && 235 lhs.allocations == rhs.allocations && 236 lhs.frees == rhs.frees; 237 } 238 }; // struct AllocatorCounters 239 240 template <typename BaseAllocatorType> 241 class StaticCountingAllocator : public BaseAllocatorType { 242 using base_type = BaseAllocatorType; 243 using base_traits = tbb::detail::allocator_traits<BaseAllocatorType>; 244 using counter_type = std::atomic<std::size_t>; 245 public: 246 using value_type = typename base_type::value_type; 247 using pointer = value_type*; 248 using counters_type = AllocatorCounters; 249 250 static std::size_t max_items; 251 static counter_type items_allocated; 252 static counter_type items_freed; 253 static counter_type items_constructed; 254 static counter_type items_destroyed; 255 static counter_type allocations; 256 static counter_type frees; 257 static bool throwing; 258 259 template <typename U> 260 struct rebind { 261 using other = StaticCountingAllocator<typename base_traits::template rebind_alloc<U>>; 262 }; 263 264 StaticCountingAllocator() = default; 265 266 template <typename U> StaticCountingAllocator(const StaticCountingAllocator<U> & other)267 StaticCountingAllocator( const StaticCountingAllocator<U>& other ) : base_type(other) {} 268 allocate(std::size_t n)269 value_type* allocate( std::size_t n ) { 270 if (max_items != 0 && items_allocated + n >= max_items) { 271 if (throwing) { 272 TBB_TEST_THROW(std::bad_alloc{}); 273 } 274 return nullptr; 275 } 276 value_type* ptr = static_cast<base_type*>(this)->allocate(n); 277 ++allocations; 278 items_allocated += n; 279 return ptr; 280 } 281 deallocate(const pointer ptr,const std::size_t n)282 void deallocate(const pointer ptr, const std::size_t n){ 283 ++frees; 284 items_freed += n; 285 static_cast<base_type*>(this)->deallocate(ptr, n); 286 } 287 288 template <typename U, typename... Args> construct(U * ptr,Args &&...args)289 void construct( U* ptr, Args&&... args ) { 290 ++items_constructed; 291 base_traits::construct(*this, ptr, std::forward<Args>(args)...); 292 } 293 294 template <typename U> destroy(U * ptr)295 void destroy( U* ptr ) { 296 ++items_destroyed; 297 base_traits::destroy(*this, ptr); 298 } 299 counters()300 static AllocatorCounters counters() { 301 return {items_allocated, items_freed, items_constructed, items_destroyed, allocations, frees}; 302 } 303 init_counters()304 static void init_counters() { 305 items_allocated = 0; 306 items_freed = 0; 307 items_constructed = 0; 308 items_destroyed = 0; 309 allocations = 0; 310 frees = 0; 311 } 312 313 static void set_limits( std::size_t max = 0, bool do_throw = true ) { 314 max_items = max; 315 throwing = do_throw; 316 } 317 }; // class StaticCountingAllocator 318 319 template <typename T> 320 std::size_t StaticCountingAllocator<T>::max_items; 321 template <typename T> 322 std::atomic<std::size_t> StaticCountingAllocator<T>::items_allocated; 323 template <typename T> 324 std::atomic<std::size_t> StaticCountingAllocator<T>::items_freed; 325 template <typename T> 326 std::atomic<std::size_t> StaticCountingAllocator<T>::items_constructed; 327 template <typename T> 328 std::atomic<std::size_t> StaticCountingAllocator<T>::items_destroyed; 329 template <typename T> 330 std::atomic<std::size_t> StaticCountingAllocator<T>::allocations; 331 template <typename T> 332 std::atomic<std::size_t> StaticCountingAllocator<T>::frees; 333 template <typename T> 334 bool StaticCountingAllocator<T>::throwing; 335 336 struct StaticSharedCountingAllocatorBase { 337 using counter_type = std::atomic<std::size_t>; 338 using counters_type = AllocatorCounters; 339 static std::size_t max_items; 340 static counter_type items_allocated; 341 static counter_type items_freed; 342 static counter_type items_constructed; 343 static counter_type items_destroyed; 344 static counter_type allocations; 345 static counter_type frees; 346 static bool throwing; 347 countersStaticSharedCountingAllocatorBase348 static counters_type counters() { 349 return { items_allocated.load(), items_freed.load(), items_constructed.load(), 350 items_destroyed.load(), allocations.load(), frees.load() }; 351 } 352 init_countersStaticSharedCountingAllocatorBase353 static void init_counters() { 354 items_allocated = 0; 355 items_freed = 0; 356 items_constructed = 0; 357 items_destroyed = 0; 358 allocations = 0; 359 frees = 0; 360 } 361 362 static void set_limits( std::size_t max = 0, bool do_throw = true ) { 363 max_items = max; 364 throwing = do_throw; 365 } 366 }; // class StaticSharedCountingAllocatorBase 367 368 std::size_t StaticSharedCountingAllocatorBase::max_items; 369 std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_constructed; 370 std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_destroyed; 371 std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_allocated; 372 std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_freed; 373 std::atomic<std::size_t> StaticSharedCountingAllocatorBase::allocations; 374 std::atomic<std::size_t> StaticSharedCountingAllocatorBase::frees; 375 bool StaticSharedCountingAllocatorBase::throwing; 376 377 template <typename BaseAllocatorType> 378 class StaticSharedCountingAllocator 379 : public StaticSharedCountingAllocatorBase, public BaseAllocatorType 380 { 381 using base_type = StaticSharedCountingAllocatorBase; 382 using alloc_base_type = BaseAllocatorType; 383 using base_traits = tbb::detail::allocator_traits<BaseAllocatorType>; 384 public: 385 using value_type = typename alloc_base_type::value_type; 386 using counters_type = AllocatorCounters; 387 388 template <typename U> 389 struct rebind { 390 using other = StaticSharedCountingAllocator<typename base_traits::template rebind_alloc<U>>; 391 }; 392 393 StaticSharedCountingAllocator() = default; 394 StaticSharedCountingAllocator( const StaticSharedCountingAllocator& ) = default; 395 StaticSharedCountingAllocator& operator=( const StaticSharedCountingAllocator& ) = default; 396 397 template <typename U> StaticSharedCountingAllocator(const StaticSharedCountingAllocator<U> & other)398 StaticSharedCountingAllocator( const StaticSharedCountingAllocator<U>& other) : alloc_base_type(other) {} 399 400 // Constructor from the base allocator with any type 401 template <typename Alloc> StaticSharedCountingAllocator(const Alloc & src)402 StaticSharedCountingAllocator( const Alloc& src ) noexcept 403 : alloc_base_type(src) {} 404 allocate(std::size_t n)405 value_type* allocate( std::size_t n ) { 406 if (base_type::max_items != 0 && 407 base_type::items_allocated + n >= base_type::max_items) { 408 if (base_type::throwing) { 409 TBB_TEST_THROW(std::bad_alloc()); 410 } 411 return nullptr; 412 } 413 ++base_type::allocations; 414 base_type::items_allocated += n; 415 return static_cast<alloc_base_type*>(this)->allocate(n); 416 } 417 deallocate(value_type * ptr,std::size_t n)418 void deallocate( value_type* ptr, std::size_t n ) { 419 ++base_type::frees; 420 base_type::items_freed += n; 421 static_cast<alloc_base_type*>(this)->deallocate(ptr, n); 422 } 423 424 template <typename U, typename... Args> construct(U * ptr,Args &&...args)425 void construct( U* ptr, Args&&... args ) { 426 base_traits::construct(*this, ptr, std::forward<Args>(args)...); 427 ++base_type::items_constructed; 428 } 429 430 template <typename U> destroy(U * ptr)431 void destroy( U* ptr ) { 432 base_traits::destroy(*this, ptr); 433 ++base_type::items_destroyed; 434 } 435 }; // class StaticSharedCountingAllocator 436 437 template <typename Allocator> 438 class AllocatorAwareData { 439 public: 440 static bool assert_on_constructions; 441 using allocator_type = Allocator; 442 443 AllocatorAwareData( const allocator_type& allocator = allocator_type() ) my_allocator(allocator)444 : my_allocator(allocator), my_value(0) {} 445 446 AllocatorAwareData( int v, const allocator_type& allocator = allocator_type() ) my_allocator(allocator)447 : my_allocator(allocator), my_value(v) {} 448 AllocatorAwareData(const AllocatorAwareData & rhs)449 AllocatorAwareData( const AllocatorAwareData& rhs ) 450 : my_allocator(rhs.my_allocator), my_value(rhs.my_value) 451 { 452 REQUIRE_MESSAGE(!assert_on_constructions, "Allocator should propagate to the data during copy construction"); 453 } 454 AllocatorAwareData(AllocatorAwareData && rhs)455 AllocatorAwareData( AllocatorAwareData&& rhs) 456 : my_allocator(rhs.my_allocator), my_value(rhs.my_value) 457 { 458 REQUIRE_MESSAGE(!assert_on_constructions, "Allocator should propagate to the data during move construction"); 459 } 460 AllocatorAwareData(const AllocatorAwareData & rhs,const allocator_type & allocator)461 AllocatorAwareData( const AllocatorAwareData& rhs, const allocator_type& allocator ) 462 : my_allocator(allocator), my_value(rhs.my_value) {} 463 AllocatorAwareData(AllocatorAwareData && rhs,const allocator_type & allocator)464 AllocatorAwareData( AllocatorAwareData&& rhs, const allocator_type& allocator ) 465 : my_allocator(allocator), my_value(rhs.my_value) {} 466 467 AllocatorAwareData& operator=( const AllocatorAwareData& other ) { 468 my_value = other.my_value; 469 return *this; 470 } 471 value()472 int value() const { return my_value; } 473 activate()474 static void activate() { assert_on_constructions = true; } deactivate()475 static void deactivate() { assert_on_constructions = false; } 476 private: 477 allocator_type my_allocator; 478 int my_value; 479 }; // class AllocatorAwareData 480 481 template <typename Allocator> 482 bool AllocatorAwareData<Allocator>::assert_on_constructions = false; 483 484 template <typename Allocator> 485 bool operator==( const AllocatorAwareData<Allocator>& lhs, const AllocatorAwareData<Allocator>& rhs ) { 486 return lhs.value() == rhs.value(); 487 } 488 489 template <typename Allocator> 490 bool operator<( const AllocatorAwareData<Allocator>& lhs, const AllocatorAwareData<Allocator>& rhs ) { 491 return lhs.value() < rhs.value(); 492 } 493 494 namespace std { 495 template <typename Allocator> 496 struct hash<AllocatorAwareData<Allocator>> { 497 std::size_t operator()(const AllocatorAwareData<Allocator>& obj) const { 498 return std::hash<int>()(obj.value()); 499 } 500 }; 501 } 502 503 template <typename Allocator, typename POCMA = std::false_type, typename POCCA = std::false_type, 504 typename POCS = std::false_type> 505 struct PropagatingAllocator : Allocator { 506 using base_allocator_traits = std::allocator_traits<Allocator>; 507 using propagate_on_container_copy_assignment = POCCA; 508 using propagate_on_container_move_assignment = POCMA; 509 using propagate_on_container_swap = POCS; 510 bool* propagated_on_copy_assignment; 511 bool* propagated_on_move_assignment; 512 bool* propagated_on_swap; 513 bool* selected_on_copy_construction; 514 515 template <typename U> 516 struct rebind { 517 using other = PropagatingAllocator<typename base_allocator_traits::template rebind_alloc<U>, 518 POCMA, POCCA, POCS>; 519 }; 520 521 PropagatingAllocator() 522 : propagated_on_copy_assignment(nullptr), 523 propagated_on_move_assignment(nullptr), 524 propagated_on_swap(nullptr), 525 selected_on_copy_construction(nullptr) {} 526 527 PropagatingAllocator( bool& poca, bool& poma, bool& pos, bool& soc ) 528 : propagated_on_copy_assignment(&poca), 529 propagated_on_move_assignment(&poma), 530 propagated_on_swap(&pos), 531 selected_on_copy_construction(&soc) {} 532 533 PropagatingAllocator( const PropagatingAllocator& other ) 534 : Allocator(other), 535 propagated_on_copy_assignment(other.propagated_on_copy_assignment), 536 propagated_on_move_assignment(other.propagated_on_move_assignment), 537 propagated_on_swap(other.propagated_on_swap), 538 selected_on_copy_construction(other.selected_on_copy_construction) {} 539 540 template <typename Allocator2> 541 PropagatingAllocator( const PropagatingAllocator<Allocator2, POCMA, POCCA, POCS>& other ) 542 : Allocator(other), 543 propagated_on_copy_assignment(other.propagated_on_copy_assignment), 544 propagated_on_move_assignment(other.propagated_on_move_assignment), 545 propagated_on_swap(other.propagated_on_swap), 546 selected_on_copy_construction(other.selected_on_copy_construction) {} 547 548 PropagatingAllocator& operator=( const PropagatingAllocator& ) { 549 REQUIRE_MESSAGE(POCCA::value, "Allocator should not copy assign if POCCA is false"); 550 if (propagated_on_copy_assignment) 551 *propagated_on_copy_assignment = true; 552 return *this; 553 } 554 555 PropagatingAllocator& operator=( PropagatingAllocator&& ) { 556 REQUIRE_MESSAGE(POCMA::value, "Allocator should not move assign if POCMA is false"); 557 if (propagated_on_move_assignment) 558 *propagated_on_move_assignment = true; 559 return *this; 560 } 561 562 PropagatingAllocator select_on_container_copy_construction() const { 563 if (selected_on_copy_construction) 564 *selected_on_copy_construction = true; 565 return *this; 566 } 567 }; // struct PropagatingAllocator 568 569 template <typename Allocator, typename POCMA, typename POCCA, typename POCS> 570 void swap( PropagatingAllocator<Allocator, POCMA, POCCA, POCS>& lhs, 571 PropagatingAllocator<Allocator, POCMA, POCCA, POCS>& ) 572 { 573 REQUIRE_MESSAGE(POCS::value, "Allocator should not swap if POCS is false"); 574 if (lhs.propagated_on_swap) 575 *lhs.propagated_on_swap = true; 576 } 577 578 template <typename T> 579 using AlwaysPropagatingAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::true_type, 580 /*POCCA = */std::true_type, /*POCS = */std::true_type>; 581 template <typename T> 582 using NeverPropagatingAllocator = PropagatingAllocator<std::allocator<T>>; 583 template <typename T> 584 using PocmaAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::true_type>; 585 template <typename T> 586 using PoccaAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::false_type, /*POCCA = */std::true_type>; 587 template <typename T> 588 using PocsAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::false_type, /*POCCA = */std::false_type, 589 /*POCS = */std::true_type>; 590 591 template <typename T> 592 class AlwaysEqualAllocator : public std::allocator<T> { 593 using base_allocator = std::allocator<T>; 594 public: 595 using is_always_equal = std::true_type; 596 using value_type = typename base_allocator::value_type; 597 using propagate_on_container_move_assignment = std::false_type; 598 599 template <typename U> 600 struct rebind { 601 using other = AlwaysEqualAllocator<U>; 602 }; 603 604 AlwaysEqualAllocator() = default; 605 606 AlwaysEqualAllocator( const AlwaysEqualAllocator& ) = default; 607 608 template <typename U> 609 AlwaysEqualAllocator( const AlwaysEqualAllocator<U>& other ) 610 : base_allocator(other) {} 611 }; // class AlwaysEqualAllocator 612 613 template <typename T> 614 class NotAlwaysEqualAllocator : public std::allocator<T> { 615 using base_allocator = std::allocator<T>; 616 public: 617 using is_always_equal = std::false_type; 618 using value_type = typename base_allocator::value_type; 619 using propagate_on_container_swap = std::false_type; 620 621 template <typename U> 622 struct rebind { 623 using other = NotAlwaysEqualAllocator<U>; 624 }; 625 626 NotAlwaysEqualAllocator() = default; 627 628 NotAlwaysEqualAllocator( const NotAlwaysEqualAllocator& ) = default; 629 630 template <typename U> 631 NotAlwaysEqualAllocator( const NotAlwaysEqualAllocator<U>& other ) 632 : base_allocator(other) {} 633 }; 634 635 template <typename T> 636 bool operator==( const AlwaysEqualAllocator<T>&, const AlwaysEqualAllocator<T>& ) { 637 #ifndef __TBB_TEST_SKIP_IS_ALWAYS_EQUAL_CHECK 638 REQUIRE_MESSAGE(false, "operator== should not be called if is_always_equal is true"); 639 #endif 640 return true; 641 } 642 643 #endif // __TBB_test_common_custom_allocators_H 644