From 5efc8b79f06c4ba7dd13ecebcaacecf0b3338811 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 6 Sep 2024 17:06:08 -0400 Subject: [PATCH] Updated thread slab with overflow protection and rewritten. --- Exception.h | 4 ++ Memory/CMakeLists.txt | 4 ++ Memory/ThreadSlab.cc | 96 +++++++++++++++++++++++++++++ Memory/ThreadSlab.h | 120 ++++++++++++++---------------------- Memory/ThreadSlab.test/0.cc | 6 +- 5 files changed, 153 insertions(+), 77 deletions(-) create mode 100644 Memory/ThreadSlab.cc diff --git a/Exception.h b/Exception.h index 8ad7d5c..c3532db 100644 --- a/Exception.h +++ b/Exception.h @@ -434,6 +434,7 @@ namespace Alepha::Hydrogen ::detail:: Exception_m using storage_type= AllocationAmountStorage; virtual ~AllocationAmountInterface()= default; virtual std::size_t allocationAmount() const noexcept= 0; + virtual void setAllocationAmount( std::size_t ) noexcept= 0; }; class AllocationAmountStorage : virtual public AllocationAmountInterface @@ -443,6 +444,8 @@ namespace Alepha::Hydrogen ::detail:: Exception_m public: std::size_t allocationAmount() const noexcept final { return amount; } + + void setAllocationAmount( const std::size_t amount ) noexcept { this->amount= amount; } }; class AllocationException : virtual public create_exception< struct allocation_throwable, Exception >, virtual public AllocationAmountInterface {}; @@ -496,6 +499,7 @@ namespace Alepha::Hydrogen ::detail:: Exception_m class Undergird : virtual public Kind, virtual protected GenericExceptionBridge< std::bad_alloc >, virtual protected MessageStorage, virtual protected AllocationAmountStorage, + virtual public AllocationAmountInterface, virtual public std::bad_alloc {}; diff --git a/Memory/CMakeLists.txt b/Memory/CMakeLists.txt index cb3030b..c22560b 100644 --- a/Memory/CMakeLists.txt +++ b/Memory/CMakeLists.txt @@ -1,2 +1,6 @@ add_subdirectory( Blob.test ) add_subdirectory( ThreadSlab.test ) + +target_sources( alepha PRIVATE + ThreadSlab.cc +) diff --git a/Memory/ThreadSlab.cc b/Memory/ThreadSlab.cc new file mode 100644 index 0000000..8049e97 --- /dev/null +++ b/Memory/ThreadSlab.cc @@ -0,0 +1,96 @@ +static_assert( __cplusplus > 2023'00 ); + +#include "ThreadSlab.h" + +#include + +namespace Alepha::Hydrogen::Memory ::detail:: ThreadSlab_m +{ + namespace + { + namespace C + { + const std::size_t slabSize= 64 * 1024 * 1024; + + const bool debug= false; + const bool debugLifecycle= false or C::debug; + const bool debugAllocation= false or C::debug; + const bool debugDeallocation= false or C::debug; + } + + template< typename SP > + void + destroy( SP *p ) + { + p->~SP(); + } + + namespace storage + { + thread_local Blob slab; + } + } + + [[nodiscard]] + void * + shim::allocate( std::size_t amt ) + { + // TODO: Alignment needs to be handled. + const std::size_t req= amt + sizeof( Blob::StorageReservation ); + + // TODO: Larger allocations may be worth bespoke allocations, if they're rare one-off cases + if( req > C::slabSize ) + { + auto exc= build_exception< AllocationError >( "Unable to allocate larger than the slab size." ); + //exc.setAllocationAmount( req ); + throw exc; + } + if( slab().size() < req ) slab().reset( std::max( req, C::slabSize ) ); + + if( C::debugAllocation ) + { + std::cerr << "Reporting " << slab().reservation().use_count() << " living allocations when " + << (void *) &slab << " made an allocation." << std::endl; + } + + auto next= slab().carveHead( req ); + void *const rv= &next.as< Blob::StorageReservation >() + 1; + + // FIXME: The placement new here is potentially unaligned -- this may significantly impact + // performance. It is also non-portable. + new ( &next.template as< Blob::StorageReservation >() ) Blob::StorageReservation{ std::move( next.reservation() ) }; + + if( C::debugAllocation ) + { + std::cerr << "Reporting " << slab().reservation().use_count() << " living allocations when " + << (void *) &slab() << " made an allocation." << std::endl; + } + + return rv; + } + + void + shim::deallocate( void *p ) noexcept + { + if( C::debugDeallocation ) + { + std::cerr << "Reporting " << slab().reservation().use_count() << " living allocations when " + << (void *) &slab() << " made a deallocation." << std::endl; + } + + auto *const hidden= reinterpret_cast< Blob::StorageReservation * >( p ) - 1; + destroy( hidden ); + + if( C::debugDeallocation ) + { + std::cerr << "Reporting " << slab().reservation().use_count() << " living allocations when " + << (void *) &slab() << " made a deallocation." << std::endl; + } + } + + Blob & + shim::slab() + { + return storage::slab; + } +} diff --git a/Memory/ThreadSlab.h b/Memory/ThreadSlab.h index 3b12023..9d7750c 100644 --- a/Memory/ThreadSlab.h +++ b/Memory/ThreadSlab.h @@ -5,35 +5,58 @@ static_assert( __cplusplus > 2020'99 ); #include #include +#include +#include #include namespace Alepha::Hydrogen::Memory ::detail:: ThreadSlab_m { - inline namespace exports + inline namespace exports {} + + namespace exports::inline ThreadSlab { template< typename T > - class ThreadSlab; + class Allocator; - using ThreadSlabString= std::basic_string< char, std::char_traits< char >, ThreadSlab< char > >; + using String= std::basic_string< char, std::char_traits< char >, Allocator< char > >; + + template< typename T > + using Vector= std::vector< T, Allocator< T > >; + + template< typename K, typename V, typename Compare= std::less< K > > + using Map= std::map< K, V, Allocator< std::pair< const K, V > > >; } - namespace C + namespace shim { - const std::size_t slabSize= 64 * 1024 * 1024; + [[nodiscard]] void *allocate( std::size_t ); + void deallocate( void * ) noexcept; - const bool debug= false; - const bool debugLifecycle= false or C::debug; - const bool debugAllocation= false or C::debug; - const bool debugDeallocation= false or C::debug; + Blob &slab(); } template< typename T > - class exports::ThreadSlab + struct BlockFromSlab { - public: - inline static thread_local Blob slab; + std::byte raw[ sizeof( T ) ]; + [[nodiscard]] static void * + operator new [] ( const std::size_t sz ) + { + return shim::allocate( sz ); + } + + static void + operator delete [] ( void *const p ) noexcept + { + return shim::deallocate( p ); + } + }; + + template< typename T > + class exports::Allocator + { public: using value_type= T; using propagate_on_container_copy_assignment= std::true_type; @@ -41,85 +64,34 @@ namespace Alepha::Hydrogen::Memory ::detail:: ThreadSlab_m using propagate_on_container_swap= std::true_type; using is_always_equal= std::true_type; - ThreadSlab select_on_container_copy_construction() { auto rv= ThreadSlab{}; } + Allocator select_on_container_copy_construction() { auto rv= Allocator{}; } - ThreadSlab()= default; + Allocator()= default; - ThreadSlab &operator= ( const ThreadSlab &other )= default; + Allocator &operator= ( const Allocator &other )= default; - ThreadSlab( const ThreadSlab &other )= default; + Allocator( const Allocator &other )= default; - ThreadSlab( ThreadSlab &&other ) : ThreadSlab( std::as_const( other ) ) {} + Allocator( Allocator &&other ) : Allocator( std::as_const( other ) ) {} - ~ThreadSlab() - { - if( C::debugLifecycle ) - { - std::cerr << "Reporting " << slab.reservation().use_count() << " living allocations when " - << (void *) this << " is retired." << std::endl; - } - } + ~Allocator()= default; + + static Blob &slab() { return shim::slab(); } [[nodiscard]] T * allocate( const std::size_t amt ) { - // TODO: Alignment needs to be handled. - const std::size_t req= amt + sizeof( Blob::StorageReservation ); - - // TODO: Larger allocations may be worth bespoke allocations, if they're rare one-off cases - if( req > C::slabSize ) throw std::bad_alloc{}; //{ "Unable to allocate larger than the slab size." }; - if( slab.size() < req ) slab.reset( std::max( req, C::slabSize ) ); - - if( C::debugAllocation ) - { - std::cerr << "Reporting " << slab.reservation().use_count() << " living allocations when " - << (void *) this << " made an allocation." << std::endl; - } - - auto next= slab.carveHead( req + sizeof( Blob::StorageReservation ) ); - const auto rv= reinterpret_cast< T * >( &next.template as< Blob::StorageReservation >() + 1 ); - - // FIXME: The placement new here is potentially unaligned -- this may significantly impact - // performance. - new ( &next.template as< Blob::StorageReservation >() ) Blob::StorageReservation{ std::move( next.reservation() ) }; - - if( C::debugAllocation ) - { - std::cerr << "Reporting " << slab.reservation().use_count() << " living allocations when " - << (void *) this << " made an allocation." << std::endl; - } - - return rv; - } - - template< typename SP > - static void - destroy( SP *p ) - { - p->~SP(); + return reinterpret_cast< T * >( new BlockFromSlab< T >[ amt ] ); } void deallocate( T *const p, const std::size_t /* ignored */ ) noexcept { - if( C::debugDeallocation ) - { - std::cerr << "Reporting " << slab.reservation().use_count() << " living allocations when " - << (void *) this << " made a deallocation." << std::endl; - } - - auto *const hidden= reinterpret_cast< Blob::StorageReservation * >( p ) - 1; - destroy( hidden ); - - if( C::debugDeallocation ) - { - std::cerr << "Reporting " << slab.reservation().use_count() << " living allocations when " - << (void *) this << " made a deallocation." << std::endl; - } + return BlockFromSlab< T >::operator delete [] ( p ); } - friend constexpr bool operator == ( const ThreadSlab &, const ThreadSlab & ) noexcept { return true; } + friend constexpr bool operator == ( const Allocator &, const Allocator & ) noexcept { return true; } }; } diff --git a/Memory/ThreadSlab.test/0.cc b/Memory/ThreadSlab.test/0.cc index 675b9f8..07f5d33 100644 --- a/Memory/ThreadSlab.test/0.cc +++ b/Memory/ThreadSlab.test/0.cc @@ -11,12 +11,12 @@ static auto init= Alepha::Utility::enroll <=[] using namespace Alepha::Testing::literals; using namespace Alepha::Memory::exports::ThreadSlab_m; - using String= ThreadSlabString; + using String= ThreadSlab::String; "Check slab usage"_test <=[] { - std::cout << "I see " << Alepha::Memory::ThreadSlab< char >::slab.reservation().use_count() << " reservations in a separate test." << + std::cout << "I see " << Alepha::Memory::detail::ThreadSlab_m::shim::slab().reservation().use_count() << " reservations in a separate test." << std::endl; }; @@ -43,7 +43,7 @@ std::endl; "Check slab usage"_test <=[] { - std::cout << "I see " << Alepha::Memory::ThreadSlab< char >::slab.reservation().use_count() << " reservations in a separate test." << + std::cout << "I see " << Alepha::Memory::detail::ThreadSlab_m::shim::slab().reservation().use_count() << " reservations in a separate test." << std::endl; }; };