/* Copyright (c) 2005-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __TBB_test_common_custom_allocators_H #define __TBB_test_common_custom_allocators_H #include "test.h" #include #include #include #include template struct ArenaData { char* const my_buffer; const std::size_t my_size; CounterType my_allocated; // in bytes template ArenaData( T* buf, std::size_t sz ) noexcept : my_buffer(reinterpret_cast(buf)), my_size(sz * sizeof(T)) { my_allocated = 0; } ArenaData& operator=( const ArenaData& ) = delete; }; // struct ArenaData template struct ArenaAllocator { using arena_data_type = ArenaData; arena_data_type* my_data; using value_type = T; using propagate_on_container_move_assignment = POCMA; template struct rebind { using other = ArenaAllocator; }; ArenaAllocator() = default; ArenaAllocator( arena_data_type& data ) noexcept : my_data(&data) {} template ArenaAllocator( const ArenaAllocator& other ) noexcept : my_data(other.my_data) {} friend void swap( ArenaAllocator& lhs, ArenaAllocator& rhs ) { using std::swap; swap(lhs.my_data, rhs.my_data); } value_type* address( value_type& x ) const { return &x; } const value_type* address( const value_type& x ) const { return &x; } value_type* allocate( std::size_t n ) { std::size_t new_size = (my_data->my_allocated += n * sizeof(T)); REQUIRE_MESSAGE(my_data->my_allocated <= my_data->my_size, "Trying to allocate more than was reserved"); char* result = &(my_data->my_buffer[new_size - n * sizeof(T)]); return reinterpret_cast(result); } void deallocate( value_type* ptr, std::size_t n ) { char* p = reinterpret_cast(ptr); REQUIRE_MESSAGE((p >= my_data->my_buffer && p <= my_data->my_buffer + my_data->my_size), "Trying to deallocate pointer not from arena"); REQUIRE_MESSAGE((p + n * sizeof(T) <= my_data->my_buffer + my_data->my_size), "Trying to deallocate pointer not from arena"); // utils::suppress_unused_warning(p, n); } std::size_t max_size() const noexcept { return my_data->my_size / sizeof(T); } }; // class ArenaAllocator template bool operator==( const ArenaAllocator& lhs, const ArenaAllocator& rhs ) { return lhs.my_data == rhs.my_data; } template bool operator!=( const ArenaAllocator& lhs, const ArenaAllocator& rhs ) { return !(lhs == rhs); } template class LocalCountingAllocator : public BaseAllocatorType { using base_type = BaseAllocatorType; using base_traits = tbb::detail::allocator_traits; using counter_type = std::atomic; public: using value_type = typename base_type::value_type; std::size_t max_items; counter_type items_allocated; counter_type items_freed; counter_type items_constructed; counter_type items_destroyed; counter_type allocations; counter_type frees; void 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 ) { items_allocated = it_allocated; // TODO: may be store items_freed = it_freed; items_constructed = it_constructed; items_destroyed = it_destroyed; allocations = allocs; frees = fres; } template void set_counters( const Allocator& alloc ) { set_counters(alloc.items_allocated, alloc.items_freed, alloc.items_constructed, alloc.items_destroyed, alloc.allocations, alloc.frees); } void clear_counters() { set_counters(0, 0, 0, 0, 0, 0); } template struct rebind { using other = LocalCountingAllocator>; }; LocalCountingAllocator() : max_items{0} { clear_counters(); } LocalCountingAllocator( const LocalCountingAllocator& other ) : base_type(other), max_items{other.max_items} { set_counters(other); } template LocalCountingAllocator( const LocalCountingAllocator& other ) : base_type(other), max_items{other.max_items} { set_counters(other); } LocalCountingAllocator& operator=( const LocalCountingAllocator& other ) { base_type::operator=(other); max_items = other.max_items; set_counters(other); return *this; } value_type* allocate( std::size_t n ) { if (max_items != 0 && items_allocated + n >= max_items) { TBB_TEST_THROW(std::bad_alloc()); } value_type* ptr = static_cast(this)->allocate(n); ++allocations; items_allocated += n; return ptr; } void deallocate( value_type* ptr, std::size_t n ) { ++frees; items_freed += n; static_cast(this)->deallocate(ptr, n); } template void construct( U* ptr, Args&&... args ) { base_traits::construct(*this, ptr, std::forward(args)...); ++items_constructed; } template void destroy( U* ptr ) { base_traits::destroy(*this, ptr); ++items_destroyed; } void set_limits( std::size_t max ) { max_items = max; } }; // class LocalCountingAllocator struct AllocatorCounters { using counter_type = std::atomic; counter_type items_allocated; counter_type items_freed; counter_type items_constructed; counter_type items_destroyed; counter_type allocations; counter_type frees; AllocatorCounters() = default; AllocatorCounters( 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 ) : items_allocated(it_allocated), items_freed(it_freed), items_constructed(it_constructed), items_destroyed(it_destroyed), allocations(allocs), frees(fres) {} AllocatorCounters( const AllocatorCounters& other ) : items_allocated(other.items_allocated.load()), items_freed(other.items_allocated.load()), items_constructed(other.items_constructed.load()), items_destroyed(other.items_destroyed.load()), allocations(other.allocations.load()), frees(other.allocations.load()) {} AllocatorCounters& operator=( const AllocatorCounters& other ) { items_allocated.store(other.items_allocated.load()); items_freed.store(other.items_freed.load()); items_constructed.store(other.items_constructed.load()); items_destroyed.store(other.items_destroyed.load()); allocations.store(other.allocations.load()); frees.store(other.frees.load()); return *this; } friend bool operator==( const AllocatorCounters& lhs, const AllocatorCounters& rhs ) { return lhs.items_allocated == rhs.items_allocated && lhs.items_freed == rhs.items_freed && lhs.items_constructed == rhs.items_constructed && lhs.items_destroyed == rhs.items_destroyed && lhs.allocations == rhs.allocations && lhs.frees == rhs.frees; } }; // struct AllocatorCounters template class StaticCountingAllocator : public BaseAllocatorType { using base_type = BaseAllocatorType; using base_traits = tbb::detail::allocator_traits; using counter_type = std::atomic; public: using value_type = typename base_type::value_type; using pointer = value_type*; using counters_type = AllocatorCounters; static std::size_t max_items; static counter_type items_allocated; static counter_type items_freed; static counter_type items_constructed; static counter_type items_destroyed; static counter_type allocations; static counter_type frees; static bool throwing; template struct rebind { using other = StaticCountingAllocator>; }; StaticCountingAllocator() = default; template StaticCountingAllocator( const StaticCountingAllocator& other ) : base_type(other) {} value_type* allocate( std::size_t n ) { if (max_items != 0 && items_allocated + n >= max_items) { if (throwing) { TBB_TEST_THROW(std::bad_alloc{}); } return nullptr; } value_type* ptr = static_cast(this)->allocate(n); ++allocations; items_allocated += n; return ptr; } void deallocate(const pointer ptr, const std::size_t n){ ++frees; items_freed += n; static_cast(this)->deallocate(ptr, n); } template void construct( U* ptr, Args&&... args ) { ++items_constructed; base_traits::construct(*this, ptr, std::forward(args)...); } template void destroy( U* ptr ) { ++items_destroyed; base_traits::destroy(*this, ptr); } static AllocatorCounters counters() { return {items_allocated, items_freed, items_constructed, items_destroyed, allocations, frees}; } static void init_counters() { items_allocated = 0; items_freed = 0; items_constructed = 0; items_destroyed = 0; allocations = 0; frees = 0; } static void set_limits( std::size_t max = 0, bool do_throw = true ) { max_items = max; throwing = do_throw; } }; // class StaticCountingAllocator template std::size_t StaticCountingAllocator::max_items; template std::atomic StaticCountingAllocator::items_allocated; template std::atomic StaticCountingAllocator::items_freed; template std::atomic StaticCountingAllocator::items_constructed; template std::atomic StaticCountingAllocator::items_destroyed; template std::atomic StaticCountingAllocator::allocations; template std::atomic StaticCountingAllocator::frees; template bool StaticCountingAllocator::throwing; struct StaticSharedCountingAllocatorBase { using counter_type = std::atomic; using counters_type = AllocatorCounters; static std::size_t max_items; static counter_type items_allocated; static counter_type items_freed; static counter_type items_constructed; static counter_type items_destroyed; static counter_type allocations; static counter_type frees; static bool throwing; static counters_type counters() { return { items_allocated.load(), items_freed.load(), items_constructed.load(), items_destroyed.load(), allocations.load(), frees.load() }; } static void init_counters() { items_allocated = 0; items_freed = 0; items_constructed = 0; items_destroyed = 0; allocations = 0; frees = 0; } static void set_limits( std::size_t max = 0, bool do_throw = true ) { max_items = max; throwing = do_throw; } }; // class StaticSharedCountingAllocatorBase std::size_t StaticSharedCountingAllocatorBase::max_items; std::atomic StaticSharedCountingAllocatorBase::items_constructed; std::atomic StaticSharedCountingAllocatorBase::items_destroyed; std::atomic StaticSharedCountingAllocatorBase::items_allocated; std::atomic StaticSharedCountingAllocatorBase::items_freed; std::atomic StaticSharedCountingAllocatorBase::allocations; std::atomic StaticSharedCountingAllocatorBase::frees; bool StaticSharedCountingAllocatorBase::throwing; template class StaticSharedCountingAllocator : public StaticSharedCountingAllocatorBase, public BaseAllocatorType { using base_type = StaticSharedCountingAllocatorBase; using alloc_base_type = BaseAllocatorType; using base_traits = tbb::detail::allocator_traits; public: using value_type = typename alloc_base_type::value_type; using counters_type = AllocatorCounters; template struct rebind { using other = StaticSharedCountingAllocator>; }; StaticSharedCountingAllocator() = default; StaticSharedCountingAllocator( const StaticSharedCountingAllocator& ) = default; template StaticSharedCountingAllocator( const StaticSharedCountingAllocator& other) : alloc_base_type(other) {} // Constructor from the base allocator with any type template StaticSharedCountingAllocator( const Alloc& src ) noexcept : alloc_base_type(src) {} value_type* allocate( std::size_t n ) { if (base_type::max_items != 0 && base_type::items_allocated + n >= base_type::max_items) { if (base_type::throwing) { TBB_TEST_THROW(std::bad_alloc()); } return nullptr; } ++base_type::allocations; base_type::items_allocated += n; return static_cast(this)->allocate(n); } void deallocate( value_type* ptr, std::size_t n ) { ++base_type::frees; base_type::items_freed += n; static_cast(this)->deallocate(ptr, n); } template void construct( U* ptr, Args&&... args ) { base_traits::construct(*this, ptr, std::forward(args)...); ++base_type::items_constructed; } template void destroy( U* ptr ) { base_traits::destroy(*this, ptr); ++base_type::items_destroyed; } }; // class StaticSharedCountingAllocator template class AllocatorAwareData { public: static bool assert_on_constructions; using allocator_type = Allocator; AllocatorAwareData( const allocator_type& allocator = allocator_type() ) : my_allocator(allocator), my_value(0) {} AllocatorAwareData( int v, const allocator_type& allocator = allocator_type() ) : my_allocator(allocator), my_value(v) {} AllocatorAwareData( const AllocatorAwareData& rhs ) : my_allocator(rhs.my_allocator), my_value(rhs.my_value) { REQUIRE_MESSAGE(!assert_on_constructions, "Allocator should propagate to the data during copy construction"); } AllocatorAwareData( AllocatorAwareData&& rhs) : my_allocator(rhs.my_allocator), my_value(rhs.my_value) { REQUIRE_MESSAGE(!assert_on_constructions, "Allocator should propagate to the data during move construction"); } AllocatorAwareData( const AllocatorAwareData& rhs, const allocator_type& allocator ) : my_allocator(allocator), my_value(rhs.my_value) {} AllocatorAwareData( AllocatorAwareData&& rhs, const allocator_type& allocator ) : my_allocator(allocator), my_value(rhs.my_value) {} AllocatorAwareData& operator=( const AllocatorAwareData& other ) { my_value = other.my_value; return *this; } int value() const { return my_value; } static void activate() { assert_on_constructions = true; } static void deactivate() { assert_on_constructions = false; } private: allocator_type my_allocator; int my_value; }; // class AllocatorAwareData template bool AllocatorAwareData::assert_on_constructions = false; template bool operator==( const AllocatorAwareData& lhs, const AllocatorAwareData& rhs ) { return lhs.value() == rhs.value(); } template bool operator<( const AllocatorAwareData& lhs, const AllocatorAwareData& rhs ) { return lhs.value() < rhs.value(); } namespace std { template struct hash> { std::size_t operator()(const AllocatorAwareData& obj) const { return std::hash()(obj.value()); } }; } template struct PropagatingAllocator : Allocator { using base_allocator_traits = std::allocator_traits; using propagate_on_container_copy_assignment = POCCA; using propagate_on_container_move_assignment = POCMA; using propagate_on_container_swap = POCS; bool* propagated_on_copy_assignment; bool* propagated_on_move_assignment; bool* propagated_on_swap; bool* selected_on_copy_construction; template struct rebind { using other = PropagatingAllocator, POCMA, POCCA, POCS>; }; PropagatingAllocator() : propagated_on_copy_assignment(nullptr), propagated_on_move_assignment(nullptr), propagated_on_swap(nullptr), selected_on_copy_construction(nullptr) {} PropagatingAllocator( bool& poca, bool& poma, bool& pos, bool& soc ) : propagated_on_copy_assignment(&poca), propagated_on_move_assignment(&poma), propagated_on_swap(&pos), selected_on_copy_construction(&soc) {} PropagatingAllocator( const PropagatingAllocator& other ) : Allocator(other), propagated_on_copy_assignment(other.propagated_on_copy_assignment), propagated_on_move_assignment(other.propagated_on_move_assignment), propagated_on_swap(other.propagated_on_swap), selected_on_copy_construction(other.selected_on_copy_construction) {} template PropagatingAllocator( const PropagatingAllocator& other ) : Allocator(other), propagated_on_copy_assignment(other.propagated_on_copy_assignment), propagated_on_move_assignment(other.propagated_on_move_assignment), propagated_on_swap(other.propagated_on_swap), selected_on_copy_construction(other.selected_on_copy_construction) {} PropagatingAllocator& operator=( const PropagatingAllocator& ) { REQUIRE_MESSAGE(POCCA::value, "Allocator should not copy assign if POCCA is false"); if (propagated_on_copy_assignment) *propagated_on_copy_assignment = true; return *this; } PropagatingAllocator& operator=( PropagatingAllocator&& ) { REQUIRE_MESSAGE(POCMA::value, "Allocator should not move assign if POCMA is false"); if (propagated_on_move_assignment) *propagated_on_move_assignment = true; return *this; } PropagatingAllocator select_on_container_copy_construction() const { if (selected_on_copy_construction) *selected_on_copy_construction = true; return *this; } }; // struct PropagatingAllocator template void swap( PropagatingAllocator& lhs, PropagatingAllocator& ) { REQUIRE_MESSAGE(POCS::value, "Allocator should not swap if POCS is false"); if (lhs.propagated_on_swap) *lhs.propagated_on_swap = true; } template using AlwaysPropagatingAllocator = PropagatingAllocator, /*POCMA = */std::true_type, /*POCCA = */std::true_type, /*POCS = */std::true_type>; template using NeverPropagatingAllocator = PropagatingAllocator>; template using PocmaAllocator = PropagatingAllocator, /*POCMA = */std::true_type>; template using PoccaAllocator = PropagatingAllocator, /*POCMA = */std::false_type, /*POCCA = */std::true_type>; template using PocsAllocator = PropagatingAllocator, /*POCMA = */std::false_type, /*POCCA = */std::false_type, /*POCS = */std::true_type>; template class AlwaysEqualAllocator : public std::allocator { using base_allocator = std::allocator; public: using is_always_equal = std::true_type; using value_type = typename base_allocator::value_type; using propagate_on_container_move_assignment = std::false_type; template struct rebind { using other = AlwaysEqualAllocator; }; AlwaysEqualAllocator() = default; AlwaysEqualAllocator( const AlwaysEqualAllocator& ) = default; template AlwaysEqualAllocator( const AlwaysEqualAllocator& other ) : base_allocator(other) {} }; // class AlwaysEqualAllocator template class NotAlwaysEqualAllocator : public std::allocator { using base_allocator = std::allocator; public: using is_always_equal = std::false_type; using value_type = typename base_allocator::value_type; using propagate_on_container_swap = std::false_type; template struct rebind { using other = NotAlwaysEqualAllocator; }; NotAlwaysEqualAllocator() = default; NotAlwaysEqualAllocator( const NotAlwaysEqualAllocator& ) = default; template NotAlwaysEqualAllocator( const NotAlwaysEqualAllocator& other ) : base_allocator(other) {} }; template bool operator==( const AlwaysEqualAllocator&, const AlwaysEqualAllocator& ) { #ifndef __TBB_TEST_SKIP_IS_ALWAYS_EQUAL_CHECK REQUIRE_MESSAGE(false, "operator== should not be called if is_always_equal is true"); #endif return true; } #endif // __TBB_test_common_custom_allocators_H