xref: /oneTBB/test/common/custom_allocators.h (revision 1168c5cb)
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