xref: /oneTBB/test/common/custom_allocators.h (revision 1168c5cb)
151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #ifndef __TBB_test_common_custom_allocators_H
1851c0b2f7Stbbdev #define __TBB_test_common_custom_allocators_H
1951c0b2f7Stbbdev 
2051c0b2f7Stbbdev #include "test.h"
2149e08aacStbbdev #include <oneapi/tbb/detail/_allocator_traits.h>
2251c0b2f7Stbbdev #include <memory>
2351c0b2f7Stbbdev #include <atomic>
2451c0b2f7Stbbdev #include <scoped_allocator>
2551c0b2f7Stbbdev 
2651c0b2f7Stbbdev template <typename CounterType>
2751c0b2f7Stbbdev struct ArenaData {
2851c0b2f7Stbbdev     char* const my_buffer;
2951c0b2f7Stbbdev     const std::size_t my_size;
3051c0b2f7Stbbdev     CounterType my_allocated; // in bytes
3151c0b2f7Stbbdev 
3251c0b2f7Stbbdev     template <typename T>
ArenaDataArenaData3351c0b2f7Stbbdev     ArenaData( T* buf, std::size_t sz ) noexcept
3451c0b2f7Stbbdev         : my_buffer(reinterpret_cast<char*>(buf)),
3551c0b2f7Stbbdev           my_size(sz * sizeof(T))
3651c0b2f7Stbbdev     {
3751c0b2f7Stbbdev         my_allocated = 0;
3851c0b2f7Stbbdev     }
3951c0b2f7Stbbdev 
4051c0b2f7Stbbdev     ArenaData& operator=( const ArenaData& ) = delete;
4151c0b2f7Stbbdev }; // struct ArenaData
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev template <typename T, typename POCMA = std::false_type, typename CounterType = std::size_t>
4451c0b2f7Stbbdev struct ArenaAllocator {
4551c0b2f7Stbbdev     using arena_data_type = ArenaData<CounterType>;
4651c0b2f7Stbbdev 
4751c0b2f7Stbbdev     arena_data_type* my_data;
4851c0b2f7Stbbdev 
4951c0b2f7Stbbdev     using value_type = T;
5051c0b2f7Stbbdev     using propagate_on_container_move_assignment = POCMA;
5151c0b2f7Stbbdev 
5251c0b2f7Stbbdev     template <typename U>
5351c0b2f7Stbbdev     struct rebind {
5451c0b2f7Stbbdev         using other = ArenaAllocator<U, POCMA, CounterType>;
5551c0b2f7Stbbdev     };
5651c0b2f7Stbbdev 
5751c0b2f7Stbbdev     ArenaAllocator() = default;
ArenaAllocatorArenaAllocator5851c0b2f7Stbbdev     ArenaAllocator( arena_data_type& data ) noexcept : my_data(&data) {}
5951c0b2f7Stbbdev 
6051c0b2f7Stbbdev     template <typename U, typename POCMA2>
ArenaAllocatorArenaAllocator6151c0b2f7Stbbdev     ArenaAllocator( const ArenaAllocator<U, POCMA2, CounterType>& other ) noexcept
6251c0b2f7Stbbdev         : my_data(other.my_data) {}
6351c0b2f7Stbbdev 
swapArenaAllocator6451c0b2f7Stbbdev     friend void swap( ArenaAllocator& lhs, ArenaAllocator& rhs ) {
6551c0b2f7Stbbdev         using std::swap;
6651c0b2f7Stbbdev         swap(lhs.my_data, rhs.my_data);
6751c0b2f7Stbbdev     }
6851c0b2f7Stbbdev 
addressArenaAllocator6951c0b2f7Stbbdev     value_type* address( value_type& x ) const { return &x; }
addressArenaAllocator7051c0b2f7Stbbdev     const value_type* address( const value_type& x ) const { return &x; }
7151c0b2f7Stbbdev 
allocateArenaAllocator7251c0b2f7Stbbdev     value_type* allocate( std::size_t n ) {
7351c0b2f7Stbbdev         std::size_t new_size = (my_data->my_allocated += n * sizeof(T));
7451c0b2f7Stbbdev         REQUIRE_MESSAGE(my_data->my_allocated <= my_data->my_size, "Trying to allocate more than was reserved");
7551c0b2f7Stbbdev         char* result = &(my_data->my_buffer[new_size - n * sizeof(T)]);
7651c0b2f7Stbbdev         return reinterpret_cast<value_type*>(result);
7751c0b2f7Stbbdev     }
7851c0b2f7Stbbdev 
deallocateArenaAllocator7951c0b2f7Stbbdev     void deallocate( value_type* ptr, std::size_t n ) {
8051c0b2f7Stbbdev         char* p = reinterpret_cast<char*>(ptr);
8151c0b2f7Stbbdev         REQUIRE_MESSAGE((p >= my_data->my_buffer && p <= my_data->my_buffer + my_data->my_size),
8251c0b2f7Stbbdev                         "Trying to deallocate pointer not from arena");
8351c0b2f7Stbbdev         REQUIRE_MESSAGE((p + n * sizeof(T) <= my_data->my_buffer + my_data->my_size),
8451c0b2f7Stbbdev                         "Trying to deallocate pointer not from arena");
8551c0b2f7Stbbdev         // utils::suppress_unused_warning(p, n);
8651c0b2f7Stbbdev     }
8751c0b2f7Stbbdev 
max_sizeArenaAllocator8851c0b2f7Stbbdev     std::size_t max_size() const noexcept {
8951c0b2f7Stbbdev         return my_data->my_size / sizeof(T);
9051c0b2f7Stbbdev     }
9151c0b2f7Stbbdev }; // class ArenaAllocator
9251c0b2f7Stbbdev 
9351c0b2f7Stbbdev template <typename T, typename U, typename POCMA, typename C>
9451c0b2f7Stbbdev bool operator==( const ArenaAllocator<T, POCMA, C>& lhs, const ArenaAllocator<U, POCMA, C>& rhs ) {
9551c0b2f7Stbbdev     return lhs.my_data == rhs.my_data;
9651c0b2f7Stbbdev }
9751c0b2f7Stbbdev 
9851c0b2f7Stbbdev template <typename T, typename U, typename POCMA, typename C>
9951c0b2f7Stbbdev bool operator!=( const ArenaAllocator<T, POCMA, C>& lhs, const ArenaAllocator<U, POCMA, C>& rhs ) {
10051c0b2f7Stbbdev     return !(lhs == rhs);
10151c0b2f7Stbbdev }
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev template <typename BaseAllocatorType>
10451c0b2f7Stbbdev class LocalCountingAllocator : public BaseAllocatorType {
10551c0b2f7Stbbdev     using base_type = BaseAllocatorType;
106b15aabb3Stbbdev     using base_traits = tbb::detail::allocator_traits<base_type>;
10751c0b2f7Stbbdev     using counter_type = std::atomic<std::size_t>;
10851c0b2f7Stbbdev public:
10951c0b2f7Stbbdev     using value_type = typename base_type::value_type;
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     std::size_t max_items;
11251c0b2f7Stbbdev     counter_type items_allocated;
11351c0b2f7Stbbdev     counter_type items_freed;
11451c0b2f7Stbbdev     counter_type items_constructed;
11551c0b2f7Stbbdev     counter_type items_destroyed;
11651c0b2f7Stbbdev     counter_type allocations;
11751c0b2f7Stbbdev     counter_type frees;
11851c0b2f7Stbbdev 
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)11951c0b2f7Stbbdev     void set_counters( std::size_t it_allocated, std::size_t it_freed,
12051c0b2f7Stbbdev                        std::size_t it_constructed, std::size_t it_destroyed,
12151c0b2f7Stbbdev                        std::size_t allocs, std::size_t fres ) {
12251c0b2f7Stbbdev         items_allocated = it_allocated; // TODO: may be store
12351c0b2f7Stbbdev         items_freed = it_freed;
12451c0b2f7Stbbdev         items_constructed = it_constructed;
12551c0b2f7Stbbdev         items_destroyed = it_destroyed;
12651c0b2f7Stbbdev         allocations = allocs;
12751c0b2f7Stbbdev         frees = fres;
12851c0b2f7Stbbdev     }
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev     template <typename Allocator>
set_counters(const Allocator & alloc)13151c0b2f7Stbbdev     void set_counters( const Allocator& alloc ) {
13251c0b2f7Stbbdev         set_counters(alloc.items_allocated, alloc.items_freed, alloc.items_constructed,
13351c0b2f7Stbbdev                      alloc.items_destroyed, alloc.allocations, alloc.frees);
13451c0b2f7Stbbdev     }
13551c0b2f7Stbbdev 
clear_counters()13651c0b2f7Stbbdev     void clear_counters() {
13751c0b2f7Stbbdev         set_counters(0, 0, 0, 0, 0, 0);
13851c0b2f7Stbbdev     }
13951c0b2f7Stbbdev 
14051c0b2f7Stbbdev     template <typename U>
141b15aabb3Stbbdev     struct rebind {
14251c0b2f7Stbbdev         using other = LocalCountingAllocator<typename base_traits::template rebind_alloc<U>>;
14351c0b2f7Stbbdev     };
14451c0b2f7Stbbdev 
LocalCountingAllocator()14551c0b2f7Stbbdev     LocalCountingAllocator() : max_items{0} { clear_counters(); }
14651c0b2f7Stbbdev 
LocalCountingAllocator(const LocalCountingAllocator & other)14751c0b2f7Stbbdev     LocalCountingAllocator( const LocalCountingAllocator& other )
14851c0b2f7Stbbdev         : base_type(other), max_items{other.max_items} { set_counters(other); }
14951c0b2f7Stbbdev 
15051c0b2f7Stbbdev     template <typename U>
LocalCountingAllocator(const LocalCountingAllocator<U> & other)15151c0b2f7Stbbdev     LocalCountingAllocator( const LocalCountingAllocator<U>& other )
15251c0b2f7Stbbdev         : base_type(other), max_items{other.max_items} { set_counters(other); }
15351c0b2f7Stbbdev 
15451c0b2f7Stbbdev     LocalCountingAllocator& operator=( const LocalCountingAllocator& other ) {
15551c0b2f7Stbbdev         base_type::operator=(other);
15651c0b2f7Stbbdev         max_items = other.max_items;
15751c0b2f7Stbbdev         set_counters(other);
15851c0b2f7Stbbdev         return *this;
15951c0b2f7Stbbdev     }
16051c0b2f7Stbbdev 
allocate(std::size_t n)16151c0b2f7Stbbdev     value_type* allocate( std::size_t n ) {
16251c0b2f7Stbbdev         if (max_items != 0 && items_allocated + n >= max_items) {
16351c0b2f7Stbbdev             TBB_TEST_THROW(std::bad_alloc());
16451c0b2f7Stbbdev         }
16551c0b2f7Stbbdev         value_type* ptr = static_cast<base_type*>(this)->allocate(n);
16651c0b2f7Stbbdev         ++allocations;
16751c0b2f7Stbbdev         items_allocated += n;
16851c0b2f7Stbbdev         return ptr;
16951c0b2f7Stbbdev     }
17051c0b2f7Stbbdev 
deallocate(value_type * ptr,std::size_t n)17151c0b2f7Stbbdev     void deallocate( value_type* ptr, std::size_t n ) {
17251c0b2f7Stbbdev         ++frees;
17351c0b2f7Stbbdev         items_freed += n;
17451c0b2f7Stbbdev         static_cast<base_type*>(this)->deallocate(ptr, n);
17551c0b2f7Stbbdev     }
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev     template <typename U, typename... Args>
construct(U * ptr,Args &&...args)17851c0b2f7Stbbdev     void construct( U* ptr, Args&&... args ) {
179b15aabb3Stbbdev         base_traits::construct(*this, ptr, std::forward<Args>(args)...);
18051c0b2f7Stbbdev         ++items_constructed;
18151c0b2f7Stbbdev     }
18251c0b2f7Stbbdev 
18351c0b2f7Stbbdev     template <typename U>
destroy(U * ptr)18451c0b2f7Stbbdev     void destroy( U* ptr ) {
18551c0b2f7Stbbdev         base_traits::destroy(*this, ptr);
18651c0b2f7Stbbdev         ++items_destroyed;
18751c0b2f7Stbbdev     }
18851c0b2f7Stbbdev 
set_limits(std::size_t max)18951c0b2f7Stbbdev     void set_limits( std::size_t max ) {
19051c0b2f7Stbbdev         max_items = max;
19151c0b2f7Stbbdev     }
19251c0b2f7Stbbdev }; // class LocalCountingAllocator
19351c0b2f7Stbbdev 
19451c0b2f7Stbbdev struct AllocatorCounters {
19551c0b2f7Stbbdev     using counter_type = std::atomic<std::size_t>;
19651c0b2f7Stbbdev 
19751c0b2f7Stbbdev     counter_type items_allocated;
19851c0b2f7Stbbdev     counter_type items_freed;
19951c0b2f7Stbbdev     counter_type items_constructed;
20051c0b2f7Stbbdev     counter_type items_destroyed;
20151c0b2f7Stbbdev     counter_type allocations;
20251c0b2f7Stbbdev     counter_type frees;
20351c0b2f7Stbbdev 
20451c0b2f7Stbbdev     AllocatorCounters() = default;
20551c0b2f7Stbbdev 
AllocatorCountersAllocatorCounters20651c0b2f7Stbbdev     AllocatorCounters( std::size_t it_allocated, std::size_t it_freed, std::size_t it_constructed,
20751c0b2f7Stbbdev                        std::size_t it_destroyed, std::size_t allocs, std::size_t fres )
20851c0b2f7Stbbdev         : items_allocated(it_allocated), items_freed(it_freed),
20951c0b2f7Stbbdev           items_constructed(it_constructed), items_destroyed(it_destroyed),
21051c0b2f7Stbbdev           allocations(allocs), frees(fres) {}
21151c0b2f7Stbbdev 
AllocatorCountersAllocatorCounters21251c0b2f7Stbbdev     AllocatorCounters( const AllocatorCounters& other )
21351c0b2f7Stbbdev         : items_allocated(other.items_allocated.load()),
21451c0b2f7Stbbdev           items_freed(other.items_allocated.load()),
21551c0b2f7Stbbdev           items_constructed(other.items_constructed.load()),
21651c0b2f7Stbbdev           items_destroyed(other.items_destroyed.load()),
21751c0b2f7Stbbdev           allocations(other.allocations.load()),
21851c0b2f7Stbbdev           frees(other.allocations.load()) {}
21951c0b2f7Stbbdev 
22051c0b2f7Stbbdev     AllocatorCounters& operator=( const AllocatorCounters& other ) {
22151c0b2f7Stbbdev         items_allocated.store(other.items_allocated.load());
22251c0b2f7Stbbdev         items_freed.store(other.items_freed.load());
22351c0b2f7Stbbdev         items_constructed.store(other.items_constructed.load());
22451c0b2f7Stbbdev         items_destroyed.store(other.items_destroyed.load());
22551c0b2f7Stbbdev         allocations.store(other.allocations.load());
22651c0b2f7Stbbdev         frees.store(other.frees.load());
22751c0b2f7Stbbdev         return *this;
22851c0b2f7Stbbdev     }
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev     friend bool operator==( const AllocatorCounters& lhs, const AllocatorCounters& rhs ) {
23151c0b2f7Stbbdev         return lhs.items_allocated == rhs.items_allocated &&
23251c0b2f7Stbbdev                lhs.items_freed == rhs.items_freed &&
23351c0b2f7Stbbdev                lhs.items_constructed == rhs.items_constructed &&
23451c0b2f7Stbbdev                lhs.items_destroyed == rhs.items_destroyed &&
23551c0b2f7Stbbdev                lhs.allocations == rhs.allocations &&
23651c0b2f7Stbbdev                lhs.frees == rhs.frees;
23751c0b2f7Stbbdev     }
23851c0b2f7Stbbdev }; // struct AllocatorCounters
23951c0b2f7Stbbdev 
24051c0b2f7Stbbdev template <typename BaseAllocatorType>
24151c0b2f7Stbbdev class StaticCountingAllocator : public BaseAllocatorType {
24251c0b2f7Stbbdev     using base_type = BaseAllocatorType;
243b15aabb3Stbbdev     using base_traits = tbb::detail::allocator_traits<BaseAllocatorType>;
24451c0b2f7Stbbdev     using counter_type = std::atomic<std::size_t>;
24551c0b2f7Stbbdev public:
24651c0b2f7Stbbdev     using value_type = typename base_type::value_type;
24751c0b2f7Stbbdev     using pointer = value_type*;
24851c0b2f7Stbbdev     using counters_type = AllocatorCounters;
24951c0b2f7Stbbdev 
25051c0b2f7Stbbdev     static std::size_t max_items;
25151c0b2f7Stbbdev     static counter_type items_allocated;
25251c0b2f7Stbbdev     static counter_type items_freed;
25351c0b2f7Stbbdev     static counter_type items_constructed;
25451c0b2f7Stbbdev     static counter_type items_destroyed;
25551c0b2f7Stbbdev     static counter_type allocations;
25651c0b2f7Stbbdev     static counter_type frees;
25751c0b2f7Stbbdev     static bool throwing;
25851c0b2f7Stbbdev 
25951c0b2f7Stbbdev     template <typename U>
260b15aabb3Stbbdev     struct rebind {
26151c0b2f7Stbbdev         using other = StaticCountingAllocator<typename base_traits::template rebind_alloc<U>>;
26251c0b2f7Stbbdev     };
26351c0b2f7Stbbdev 
26451c0b2f7Stbbdev     StaticCountingAllocator() = default;
26551c0b2f7Stbbdev 
26651c0b2f7Stbbdev     template <typename U>
StaticCountingAllocator(const StaticCountingAllocator<U> & other)26751c0b2f7Stbbdev     StaticCountingAllocator( const StaticCountingAllocator<U>& other ) : base_type(other) {}
26851c0b2f7Stbbdev 
allocate(std::size_t n)26951c0b2f7Stbbdev     value_type* allocate( std::size_t n ) {
27051c0b2f7Stbbdev         if (max_items != 0 && items_allocated + n >= max_items) {
27151c0b2f7Stbbdev             if (throwing) {
27251c0b2f7Stbbdev                 TBB_TEST_THROW(std::bad_alloc{});
27351c0b2f7Stbbdev             }
27451c0b2f7Stbbdev             return nullptr;
27551c0b2f7Stbbdev         }
27651c0b2f7Stbbdev         value_type* ptr = static_cast<base_type*>(this)->allocate(n);
27751c0b2f7Stbbdev         ++allocations;
27851c0b2f7Stbbdev         items_allocated += n;
27951c0b2f7Stbbdev         return ptr;
28051c0b2f7Stbbdev     }
28151c0b2f7Stbbdev 
deallocate(const pointer ptr,const std::size_t n)28251c0b2f7Stbbdev     void deallocate(const pointer ptr, const std::size_t n){
28351c0b2f7Stbbdev         ++frees;
28451c0b2f7Stbbdev         items_freed += n;
28551c0b2f7Stbbdev         static_cast<base_type*>(this)->deallocate(ptr, n);
28651c0b2f7Stbbdev     }
28751c0b2f7Stbbdev 
28851c0b2f7Stbbdev     template <typename U, typename... Args>
construct(U * ptr,Args &&...args)28951c0b2f7Stbbdev     void construct( U* ptr, Args&&... args ) {
29051c0b2f7Stbbdev         ++items_constructed;
291b15aabb3Stbbdev         base_traits::construct(*this, ptr, std::forward<Args>(args)...);
29251c0b2f7Stbbdev     }
29351c0b2f7Stbbdev 
29451c0b2f7Stbbdev     template <typename U>
destroy(U * ptr)29551c0b2f7Stbbdev     void destroy( U* ptr ) {
29651c0b2f7Stbbdev         ++items_destroyed;
297b15aabb3Stbbdev         base_traits::destroy(*this, ptr);
29851c0b2f7Stbbdev     }
29951c0b2f7Stbbdev 
counters()30051c0b2f7Stbbdev     static AllocatorCounters counters() {
30151c0b2f7Stbbdev         return {items_allocated, items_freed, items_constructed, items_destroyed, allocations, frees};
30251c0b2f7Stbbdev     }
30351c0b2f7Stbbdev 
init_counters()30451c0b2f7Stbbdev     static void init_counters() {
30551c0b2f7Stbbdev         items_allocated = 0;
30651c0b2f7Stbbdev         items_freed = 0;
30751c0b2f7Stbbdev         items_constructed = 0;
30851c0b2f7Stbbdev         items_destroyed = 0;
30951c0b2f7Stbbdev         allocations = 0;
31051c0b2f7Stbbdev         frees = 0;
31151c0b2f7Stbbdev     }
31251c0b2f7Stbbdev 
31351c0b2f7Stbbdev     static void set_limits( std::size_t max = 0, bool do_throw = true ) {
31451c0b2f7Stbbdev         max_items = max;
31551c0b2f7Stbbdev         throwing = do_throw;
31651c0b2f7Stbbdev     }
31751c0b2f7Stbbdev }; // class StaticCountingAllocator
31851c0b2f7Stbbdev 
31951c0b2f7Stbbdev template <typename T>
32051c0b2f7Stbbdev std::size_t StaticCountingAllocator<T>::max_items;
32151c0b2f7Stbbdev template <typename T>
32251c0b2f7Stbbdev std::atomic<std::size_t> StaticCountingAllocator<T>::items_allocated;
32351c0b2f7Stbbdev template <typename T>
32451c0b2f7Stbbdev std::atomic<std::size_t> StaticCountingAllocator<T>::items_freed;
32551c0b2f7Stbbdev template <typename T>
32651c0b2f7Stbbdev std::atomic<std::size_t> StaticCountingAllocator<T>::items_constructed;
32751c0b2f7Stbbdev template <typename T>
32851c0b2f7Stbbdev std::atomic<std::size_t> StaticCountingAllocator<T>::items_destroyed;
32951c0b2f7Stbbdev template <typename T>
33051c0b2f7Stbbdev std::atomic<std::size_t> StaticCountingAllocator<T>::allocations;
33151c0b2f7Stbbdev template <typename T>
33251c0b2f7Stbbdev std::atomic<std::size_t> StaticCountingAllocator<T>::frees;
33351c0b2f7Stbbdev template <typename T>
33451c0b2f7Stbbdev bool StaticCountingAllocator<T>::throwing;
33551c0b2f7Stbbdev 
33651c0b2f7Stbbdev struct StaticSharedCountingAllocatorBase {
33751c0b2f7Stbbdev     using counter_type = std::atomic<std::size_t>;
33851c0b2f7Stbbdev     using counters_type = AllocatorCounters;
33951c0b2f7Stbbdev     static std::size_t max_items;
34051c0b2f7Stbbdev     static counter_type items_allocated;
34151c0b2f7Stbbdev     static counter_type items_freed;
34251c0b2f7Stbbdev     static counter_type items_constructed;
34351c0b2f7Stbbdev     static counter_type items_destroyed;
34451c0b2f7Stbbdev     static counter_type allocations;
34551c0b2f7Stbbdev     static counter_type frees;
34651c0b2f7Stbbdev     static bool throwing;
34751c0b2f7Stbbdev 
countersStaticSharedCountingAllocatorBase34851c0b2f7Stbbdev     static counters_type counters() {
34951c0b2f7Stbbdev         return { items_allocated.load(), items_freed.load(), items_constructed.load(),
35051c0b2f7Stbbdev                  items_destroyed.load(), allocations.load(), frees.load() };
35151c0b2f7Stbbdev     }
35251c0b2f7Stbbdev 
init_countersStaticSharedCountingAllocatorBase35351c0b2f7Stbbdev     static void init_counters() {
35451c0b2f7Stbbdev         items_allocated = 0;
35551c0b2f7Stbbdev         items_freed = 0;
35651c0b2f7Stbbdev         items_constructed = 0;
35751c0b2f7Stbbdev         items_destroyed = 0;
35851c0b2f7Stbbdev         allocations = 0;
35951c0b2f7Stbbdev         frees = 0;
36051c0b2f7Stbbdev     }
36151c0b2f7Stbbdev 
36251c0b2f7Stbbdev     static void set_limits( std::size_t max = 0, bool do_throw = true ) {
36351c0b2f7Stbbdev         max_items = max;
36451c0b2f7Stbbdev         throwing = do_throw;
36551c0b2f7Stbbdev     }
36651c0b2f7Stbbdev }; // class StaticSharedCountingAllocatorBase
36751c0b2f7Stbbdev 
36851c0b2f7Stbbdev std::size_t StaticSharedCountingAllocatorBase::max_items;
36951c0b2f7Stbbdev std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_constructed;
37051c0b2f7Stbbdev std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_destroyed;
37151c0b2f7Stbbdev std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_allocated;
37251c0b2f7Stbbdev std::atomic<std::size_t> StaticSharedCountingAllocatorBase::items_freed;
37351c0b2f7Stbbdev std::atomic<std::size_t> StaticSharedCountingAllocatorBase::allocations;
37451c0b2f7Stbbdev std::atomic<std::size_t> StaticSharedCountingAllocatorBase::frees;
37551c0b2f7Stbbdev bool StaticSharedCountingAllocatorBase::throwing;
37651c0b2f7Stbbdev 
37751c0b2f7Stbbdev template <typename BaseAllocatorType>
37851c0b2f7Stbbdev class StaticSharedCountingAllocator
37951c0b2f7Stbbdev     : public StaticSharedCountingAllocatorBase, public BaseAllocatorType
38051c0b2f7Stbbdev {
38151c0b2f7Stbbdev     using base_type = StaticSharedCountingAllocatorBase;
38251c0b2f7Stbbdev     using alloc_base_type = BaseAllocatorType;
38351c0b2f7Stbbdev     using base_traits = tbb::detail::allocator_traits<BaseAllocatorType>;
38451c0b2f7Stbbdev public:
38551c0b2f7Stbbdev     using value_type = typename alloc_base_type::value_type;
38651c0b2f7Stbbdev     using counters_type = AllocatorCounters;
38751c0b2f7Stbbdev 
38851c0b2f7Stbbdev     template <typename U>
38951c0b2f7Stbbdev     struct rebind {
39051c0b2f7Stbbdev         using other = StaticSharedCountingAllocator<typename base_traits::template rebind_alloc<U>>;
39151c0b2f7Stbbdev     };
39251c0b2f7Stbbdev 
39351c0b2f7Stbbdev     StaticSharedCountingAllocator() = default;
39451c0b2f7Stbbdev     StaticSharedCountingAllocator( const StaticSharedCountingAllocator& ) = default;
395*1168c5cbSvlserov     StaticSharedCountingAllocator& operator=( const StaticSharedCountingAllocator& ) = default;
39651c0b2f7Stbbdev 
39751c0b2f7Stbbdev     template <typename U>
StaticSharedCountingAllocator(const StaticSharedCountingAllocator<U> & other)39851c0b2f7Stbbdev     StaticSharedCountingAllocator( const StaticSharedCountingAllocator<U>& other) : alloc_base_type(other) {}
39951c0b2f7Stbbdev 
40051c0b2f7Stbbdev     // Constructor from the base allocator with any type
40151c0b2f7Stbbdev     template <typename Alloc>
StaticSharedCountingAllocator(const Alloc & src)40251c0b2f7Stbbdev     StaticSharedCountingAllocator( const Alloc& src ) noexcept
40351c0b2f7Stbbdev         : alloc_base_type(src) {}
40451c0b2f7Stbbdev 
allocate(std::size_t n)40551c0b2f7Stbbdev     value_type* allocate( std::size_t n ) {
40651c0b2f7Stbbdev         if (base_type::max_items != 0 &&
40751c0b2f7Stbbdev             base_type::items_allocated + n >= base_type::max_items) {
40851c0b2f7Stbbdev             if (base_type::throwing) {
40951c0b2f7Stbbdev                 TBB_TEST_THROW(std::bad_alloc());
41051c0b2f7Stbbdev             }
41151c0b2f7Stbbdev             return nullptr;
41251c0b2f7Stbbdev         }
41351c0b2f7Stbbdev         ++base_type::allocations;
41451c0b2f7Stbbdev         base_type::items_allocated += n;
41551c0b2f7Stbbdev         return static_cast<alloc_base_type*>(this)->allocate(n);
41651c0b2f7Stbbdev     }
41751c0b2f7Stbbdev 
deallocate(value_type * ptr,std::size_t n)41851c0b2f7Stbbdev     void deallocate( value_type* ptr, std::size_t n ) {
41951c0b2f7Stbbdev         ++base_type::frees;
42051c0b2f7Stbbdev         base_type::items_freed += n;
42151c0b2f7Stbbdev         static_cast<alloc_base_type*>(this)->deallocate(ptr, n);
42251c0b2f7Stbbdev     }
42351c0b2f7Stbbdev 
42451c0b2f7Stbbdev     template <typename U, typename... Args>
construct(U * ptr,Args &&...args)42551c0b2f7Stbbdev     void construct( U* ptr, Args&&... args ) {
42651c0b2f7Stbbdev         base_traits::construct(*this, ptr, std::forward<Args>(args)...);
42751c0b2f7Stbbdev         ++base_type::items_constructed;
42851c0b2f7Stbbdev     }
42951c0b2f7Stbbdev 
43051c0b2f7Stbbdev     template <typename U>
destroy(U * ptr)43151c0b2f7Stbbdev     void destroy( U* ptr ) {
43251c0b2f7Stbbdev         base_traits::destroy(*this, ptr);
43351c0b2f7Stbbdev         ++base_type::items_destroyed;
43451c0b2f7Stbbdev     }
43551c0b2f7Stbbdev }; // class StaticSharedCountingAllocator
43651c0b2f7Stbbdev 
43751c0b2f7Stbbdev template <typename Allocator>
43851c0b2f7Stbbdev class AllocatorAwareData {
43951c0b2f7Stbbdev public:
44051c0b2f7Stbbdev     static bool assert_on_constructions;
44151c0b2f7Stbbdev     using allocator_type = Allocator;
44251c0b2f7Stbbdev 
44351c0b2f7Stbbdev     AllocatorAwareData( const allocator_type& allocator = allocator_type() )
my_allocator(allocator)44451c0b2f7Stbbdev         : my_allocator(allocator), my_value(0) {}
44551c0b2f7Stbbdev 
44651c0b2f7Stbbdev     AllocatorAwareData( int v, const allocator_type& allocator = allocator_type() )
my_allocator(allocator)44751c0b2f7Stbbdev         : my_allocator(allocator), my_value(v) {}
44851c0b2f7Stbbdev 
AllocatorAwareData(const AllocatorAwareData & rhs)44951c0b2f7Stbbdev     AllocatorAwareData( const AllocatorAwareData& rhs )
45051c0b2f7Stbbdev         : my_allocator(rhs.my_allocator), my_value(rhs.my_value)
45151c0b2f7Stbbdev     {
45251c0b2f7Stbbdev         REQUIRE_MESSAGE(!assert_on_constructions, "Allocator should propagate to the data during copy construction");
45351c0b2f7Stbbdev     }
45451c0b2f7Stbbdev 
AllocatorAwareData(AllocatorAwareData && rhs)45551c0b2f7Stbbdev     AllocatorAwareData( AllocatorAwareData&& rhs)
45651c0b2f7Stbbdev         : my_allocator(rhs.my_allocator), my_value(rhs.my_value)
45751c0b2f7Stbbdev     {
45851c0b2f7Stbbdev         REQUIRE_MESSAGE(!assert_on_constructions, "Allocator should propagate to the data during move construction");
45951c0b2f7Stbbdev     }
46051c0b2f7Stbbdev 
AllocatorAwareData(const AllocatorAwareData & rhs,const allocator_type & allocator)46151c0b2f7Stbbdev     AllocatorAwareData( const AllocatorAwareData& rhs, const allocator_type& allocator )
46251c0b2f7Stbbdev         : my_allocator(allocator), my_value(rhs.my_value) {}
46351c0b2f7Stbbdev 
AllocatorAwareData(AllocatorAwareData && rhs,const allocator_type & allocator)46451c0b2f7Stbbdev     AllocatorAwareData( AllocatorAwareData&& rhs, const allocator_type& allocator )
46551c0b2f7Stbbdev         : my_allocator(allocator), my_value(rhs.my_value) {}
46651c0b2f7Stbbdev 
46751c0b2f7Stbbdev     AllocatorAwareData& operator=( const AllocatorAwareData& other ) {
46851c0b2f7Stbbdev         my_value = other.my_value;
46951c0b2f7Stbbdev         return *this;
47051c0b2f7Stbbdev     }
47151c0b2f7Stbbdev 
value()47251c0b2f7Stbbdev     int value() const { return my_value; }
47351c0b2f7Stbbdev 
activate()47451c0b2f7Stbbdev     static void activate() { assert_on_constructions = true; }
deactivate()47551c0b2f7Stbbdev     static void deactivate() { assert_on_constructions = false; }
47651c0b2f7Stbbdev private:
47751c0b2f7Stbbdev     allocator_type my_allocator;
47851c0b2f7Stbbdev     int my_value;
47951c0b2f7Stbbdev }; // class AllocatorAwareData
48051c0b2f7Stbbdev 
48151c0b2f7Stbbdev template <typename Allocator>
48251c0b2f7Stbbdev bool AllocatorAwareData<Allocator>::assert_on_constructions = false;
48351c0b2f7Stbbdev 
48451c0b2f7Stbbdev template <typename Allocator>
48551c0b2f7Stbbdev bool operator==( const AllocatorAwareData<Allocator>& lhs, const AllocatorAwareData<Allocator>& rhs ) {
48651c0b2f7Stbbdev     return lhs.value() == rhs.value();
48751c0b2f7Stbbdev }
48851c0b2f7Stbbdev 
48951c0b2f7Stbbdev template <typename Allocator>
49051c0b2f7Stbbdev bool operator<( const AllocatorAwareData<Allocator>& lhs, const AllocatorAwareData<Allocator>& rhs ) {
49151c0b2f7Stbbdev     return lhs.value() < rhs.value();
49251c0b2f7Stbbdev }
49351c0b2f7Stbbdev 
49451c0b2f7Stbbdev namespace std {
49551c0b2f7Stbbdev template <typename Allocator>
49651c0b2f7Stbbdev struct hash<AllocatorAwareData<Allocator>> {
49751c0b2f7Stbbdev     std::size_t operator()(const AllocatorAwareData<Allocator>& obj) const {
49851c0b2f7Stbbdev         return std::hash<int>()(obj.value());
49951c0b2f7Stbbdev     }
50051c0b2f7Stbbdev };
50151c0b2f7Stbbdev }
50251c0b2f7Stbbdev 
50351c0b2f7Stbbdev template <typename Allocator, typename POCMA = std::false_type, typename POCCA = std::false_type,
50451c0b2f7Stbbdev           typename POCS = std::false_type>
50551c0b2f7Stbbdev struct PropagatingAllocator : Allocator {
50651c0b2f7Stbbdev     using base_allocator_traits = std::allocator_traits<Allocator>;
50751c0b2f7Stbbdev     using propagate_on_container_copy_assignment = POCCA;
50851c0b2f7Stbbdev     using propagate_on_container_move_assignment = POCMA;
50951c0b2f7Stbbdev     using propagate_on_container_swap = POCS;
51051c0b2f7Stbbdev     bool* propagated_on_copy_assignment;
51151c0b2f7Stbbdev     bool* propagated_on_move_assignment;
51251c0b2f7Stbbdev     bool* propagated_on_swap;
51351c0b2f7Stbbdev     bool* selected_on_copy_construction;
51451c0b2f7Stbbdev 
51551c0b2f7Stbbdev     template <typename U>
51651c0b2f7Stbbdev     struct rebind {
51751c0b2f7Stbbdev         using other = PropagatingAllocator<typename base_allocator_traits::template rebind_alloc<U>,
51851c0b2f7Stbbdev                                            POCMA, POCCA, POCS>;
51951c0b2f7Stbbdev     };
52051c0b2f7Stbbdev 
52151c0b2f7Stbbdev     PropagatingAllocator()
52251c0b2f7Stbbdev         : propagated_on_copy_assignment(nullptr),
52351c0b2f7Stbbdev           propagated_on_move_assignment(nullptr),
52451c0b2f7Stbbdev           propagated_on_swap(nullptr),
52551c0b2f7Stbbdev           selected_on_copy_construction(nullptr) {}
52651c0b2f7Stbbdev 
52751c0b2f7Stbbdev     PropagatingAllocator( bool& poca, bool& poma, bool& pos, bool& soc )
52851c0b2f7Stbbdev         : propagated_on_copy_assignment(&poca),
52951c0b2f7Stbbdev           propagated_on_move_assignment(&poma),
53051c0b2f7Stbbdev           propagated_on_swap(&pos),
53151c0b2f7Stbbdev           selected_on_copy_construction(&soc) {}
53251c0b2f7Stbbdev 
53351c0b2f7Stbbdev     PropagatingAllocator( const PropagatingAllocator& other )
53451c0b2f7Stbbdev         : Allocator(other),
53551c0b2f7Stbbdev           propagated_on_copy_assignment(other.propagated_on_copy_assignment),
53651c0b2f7Stbbdev           propagated_on_move_assignment(other.propagated_on_move_assignment),
53751c0b2f7Stbbdev           propagated_on_swap(other.propagated_on_swap),
53851c0b2f7Stbbdev           selected_on_copy_construction(other.selected_on_copy_construction) {}
53951c0b2f7Stbbdev 
54051c0b2f7Stbbdev     template <typename Allocator2>
54151c0b2f7Stbbdev     PropagatingAllocator( const PropagatingAllocator<Allocator2, POCMA, POCCA, POCS>& other )
54251c0b2f7Stbbdev         : Allocator(other),
54351c0b2f7Stbbdev           propagated_on_copy_assignment(other.propagated_on_copy_assignment),
54451c0b2f7Stbbdev           propagated_on_move_assignment(other.propagated_on_move_assignment),
54551c0b2f7Stbbdev           propagated_on_swap(other.propagated_on_swap),
54651c0b2f7Stbbdev           selected_on_copy_construction(other.selected_on_copy_construction) {}
54751c0b2f7Stbbdev 
54851c0b2f7Stbbdev     PropagatingAllocator& operator=( const PropagatingAllocator& ) {
54951c0b2f7Stbbdev         REQUIRE_MESSAGE(POCCA::value, "Allocator should not copy assign if POCCA is false");
55051c0b2f7Stbbdev         if (propagated_on_copy_assignment)
55151c0b2f7Stbbdev             *propagated_on_copy_assignment = true;
55251c0b2f7Stbbdev         return *this;
55351c0b2f7Stbbdev     }
55451c0b2f7Stbbdev 
55551c0b2f7Stbbdev     PropagatingAllocator& operator=( PropagatingAllocator&& ) {
55651c0b2f7Stbbdev         REQUIRE_MESSAGE(POCMA::value, "Allocator should not move assign if POCMA is false");
55751c0b2f7Stbbdev         if (propagated_on_move_assignment)
55851c0b2f7Stbbdev             *propagated_on_move_assignment = true;
55951c0b2f7Stbbdev         return *this;
56051c0b2f7Stbbdev     }
56151c0b2f7Stbbdev 
56251c0b2f7Stbbdev     PropagatingAllocator select_on_container_copy_construction() const {
56351c0b2f7Stbbdev         if (selected_on_copy_construction)
56451c0b2f7Stbbdev             *selected_on_copy_construction = true;
56551c0b2f7Stbbdev         return *this;
56651c0b2f7Stbbdev     }
56751c0b2f7Stbbdev }; // struct PropagatingAllocator
56851c0b2f7Stbbdev 
56951c0b2f7Stbbdev template <typename Allocator, typename POCMA, typename POCCA, typename POCS>
57051c0b2f7Stbbdev void swap( PropagatingAllocator<Allocator, POCMA, POCCA, POCS>& lhs,
57151c0b2f7Stbbdev            PropagatingAllocator<Allocator, POCMA, POCCA, POCS>& )
57251c0b2f7Stbbdev {
57351c0b2f7Stbbdev     REQUIRE_MESSAGE(POCS::value, "Allocator should not swap if POCS is false");
57451c0b2f7Stbbdev     if (lhs.propagated_on_swap)
57551c0b2f7Stbbdev         *lhs.propagated_on_swap = true;
57651c0b2f7Stbbdev }
57751c0b2f7Stbbdev 
57851c0b2f7Stbbdev template <typename T>
57951c0b2f7Stbbdev using AlwaysPropagatingAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::true_type,
58051c0b2f7Stbbdev                                                         /*POCCA = */std::true_type, /*POCS = */std::true_type>;
58151c0b2f7Stbbdev template <typename T>
58251c0b2f7Stbbdev using NeverPropagatingAllocator = PropagatingAllocator<std::allocator<T>>;
58351c0b2f7Stbbdev template <typename T>
58451c0b2f7Stbbdev using PocmaAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::true_type>;
58551c0b2f7Stbbdev template <typename T>
58651c0b2f7Stbbdev using PoccaAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::false_type, /*POCCA = */std::true_type>;
58751c0b2f7Stbbdev template <typename T>
58851c0b2f7Stbbdev using PocsAllocator = PropagatingAllocator<std::allocator<T>, /*POCMA = */std::false_type, /*POCCA = */std::false_type,
58951c0b2f7Stbbdev                                            /*POCS = */std::true_type>;
59051c0b2f7Stbbdev 
59151c0b2f7Stbbdev template <typename T>
59251c0b2f7Stbbdev class AlwaysEqualAllocator : public std::allocator<T> {
59351c0b2f7Stbbdev     using base_allocator = std::allocator<T>;
59451c0b2f7Stbbdev public:
59551c0b2f7Stbbdev     using is_always_equal = std::true_type;
59651c0b2f7Stbbdev     using value_type = typename base_allocator::value_type;
59751c0b2f7Stbbdev     using propagate_on_container_move_assignment = std::false_type;
59851c0b2f7Stbbdev 
59951c0b2f7Stbbdev     template <typename U>
60051c0b2f7Stbbdev     struct rebind {
60151c0b2f7Stbbdev         using other = AlwaysEqualAllocator<U>;
60251c0b2f7Stbbdev     };
60351c0b2f7Stbbdev 
60451c0b2f7Stbbdev     AlwaysEqualAllocator() = default;
60551c0b2f7Stbbdev 
60651c0b2f7Stbbdev     AlwaysEqualAllocator( const AlwaysEqualAllocator& ) = default;
60751c0b2f7Stbbdev 
60851c0b2f7Stbbdev     template <typename U>
60951c0b2f7Stbbdev     AlwaysEqualAllocator( const AlwaysEqualAllocator<U>& other )
61051c0b2f7Stbbdev         : base_allocator(other) {}
61151c0b2f7Stbbdev }; // class AlwaysEqualAllocator
61251c0b2f7Stbbdev 
61351c0b2f7Stbbdev template <typename T>
61451c0b2f7Stbbdev class NotAlwaysEqualAllocator : public std::allocator<T> {
61551c0b2f7Stbbdev     using base_allocator = std::allocator<T>;
61651c0b2f7Stbbdev public:
61751c0b2f7Stbbdev     using is_always_equal = std::false_type;
61851c0b2f7Stbbdev     using value_type = typename base_allocator::value_type;
61951c0b2f7Stbbdev     using propagate_on_container_swap = std::false_type;
62051c0b2f7Stbbdev 
62151c0b2f7Stbbdev     template <typename U>
62251c0b2f7Stbbdev     struct rebind {
62351c0b2f7Stbbdev         using other = NotAlwaysEqualAllocator<U>;
62451c0b2f7Stbbdev     };
62551c0b2f7Stbbdev 
62651c0b2f7Stbbdev     NotAlwaysEqualAllocator() = default;
62751c0b2f7Stbbdev 
62851c0b2f7Stbbdev     NotAlwaysEqualAllocator( const NotAlwaysEqualAllocator& ) = default;
62951c0b2f7Stbbdev 
63051c0b2f7Stbbdev     template <typename U>
63151c0b2f7Stbbdev     NotAlwaysEqualAllocator( const NotAlwaysEqualAllocator<U>& other )
63251c0b2f7Stbbdev         : base_allocator(other) {}
63351c0b2f7Stbbdev };
63451c0b2f7Stbbdev 
63551c0b2f7Stbbdev template <typename T>
63651c0b2f7Stbbdev bool operator==( const AlwaysEqualAllocator<T>&, const AlwaysEqualAllocator<T>& ) {
63751c0b2f7Stbbdev #ifndef __TBB_TEST_SKIP_IS_ALWAYS_EQUAL_CHECK
63851c0b2f7Stbbdev     REQUIRE_MESSAGE(false, "operator== should not be called if is_always_equal is true");
63951c0b2f7Stbbdev #endif
64051c0b2f7Stbbdev     return true;
64151c0b2f7Stbbdev }
64251c0b2f7Stbbdev 
64351c0b2f7Stbbdev #endif // __TBB_test_common_custom_allocators_H
644