forked from Alepha/Alepha
Recording how I _want_ it to work.
It looks like the move operation on allocators is actually a copy!
This commit is contained in:
@ -23,6 +23,7 @@ link_libraries( alepha )
|
||||
add_subdirectory( Meta )
|
||||
add_subdirectory( Atomic )
|
||||
add_subdirectory( Proof )
|
||||
add_subdirectory( Memory )
|
||||
add_subdirectory( IOStreams )
|
||||
add_subdirectory( Reflection )
|
||||
add_subdirectory( Algorithm )
|
||||
|
1
Memory/CMakeLists.txt
Normal file
1
Memory/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory( ThreadSlab.test )
|
151
Memory/ThreadSlab.h
Normal file
151
Memory/ThreadSlab.h
Normal file
@ -0,0 +1,151 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Alepha/Blob.h>
|
||||
|
||||
namespace Alepha::Hydrogen::Memory ::detail:: ThreadSlab_m
|
||||
{
|
||||
inline namespace exports
|
||||
{
|
||||
template< typename T >
|
||||
class ThreadSlab;
|
||||
|
||||
using ThreadSlabString= std::basic_string< char, std::char_traits< char >, ThreadSlab< char > >;
|
||||
}
|
||||
|
||||
namespace C
|
||||
{
|
||||
const std::size_t slabSize= 64 * 1024 * 1024;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
class exports::ThreadSlab
|
||||
{
|
||||
private:
|
||||
class Impl
|
||||
{
|
||||
public:
|
||||
std::vector< Blob > leases;
|
||||
|
||||
inline static thread_local Blob slab;
|
||||
|
||||
Impl() { leases.reserve( 8 ); }
|
||||
};
|
||||
|
||||
std::shared_ptr< Impl > pimpl;
|
||||
|
||||
public:
|
||||
using value_type= T;
|
||||
using propagate_on_container_copy_assignment= std::true_type;
|
||||
using propagate_on_container_move_assignment= std::true_type;
|
||||
using propagate_on_container_swap= std::true_type;
|
||||
using is_always_equal= std::false_type;
|
||||
|
||||
ThreadSlab select_on_container_copy_construction() { auto rv= ThreadSlab{}; std::cerr << "selection creation at " << (void *) &rv << std::endl; return rv; }
|
||||
|
||||
ThreadSlab()
|
||||
: pimpl( std::make_unique< Impl >() )
|
||||
{
|
||||
std::cerr << (void *) this << " owns " << (void *) pimpl.get() << " (creation)" << std::endl;
|
||||
}
|
||||
|
||||
~ThreadSlab() { std::cerr << "Retired allocator external " << (void *) this << " which held " << (void *) pimpl.get() << std::endl; }
|
||||
|
||||
ThreadSlab &
|
||||
operator= ( ThreadSlab &&other )
|
||||
{
|
||||
std::cerr << "Allocator move assign from " << (void *) &other << " which owns " << (void *) other.pimpl.get()
|
||||
<< " to " << (void *) this << " which owns " << (void *) pimpl.get() << std::endl;
|
||||
|
||||
pimpl= std::move( other.pimpl );
|
||||
other.pimpl.reset();
|
||||
std::cerr << "After the move assign from " << (void *) &other << " it now owns " << (void *) other.pimpl.get()
|
||||
<< " and I'm still " << (void *) this << " which now owns " << (void *) pimpl.get() << std::endl;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ThreadSlab &
|
||||
operator= ( const ThreadSlab &other )
|
||||
{
|
||||
std::cerr << "Allocator copy assign from " << (void *) &other << " which owns " << (void *) other.pimpl.get()
|
||||
<< " to " << (void *) this << " which owns " << (void *) pimpl.get() << std::endl;
|
||||
|
||||
pimpl= other.pimpl;
|
||||
std::cerr << "After the copy assign from " << (void *) &other << " it now owns " << (void *) other.pimpl.get()
|
||||
<< " and I'm still " << (void *) this << " which now owns " << (void *) pimpl.get() << std::endl;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ThreadSlab( const ThreadSlab &other )
|
||||
: pimpl( other.pimpl )
|
||||
{
|
||||
std::cerr << "Allocator copy construction created " << (void *) this << " (from " << (void *) &other << ") which held " << (void *) pimpl.get() << std::endl;
|
||||
}
|
||||
|
||||
ThreadSlab( ThreadSlab &&other )
|
||||
: pimpl( std::move( other.pimpl ) )
|
||||
{
|
||||
std::cerr << "Allocator move construction created " << (void *) this << " (from " << (void *) &other << ") which held " << (void *) pimpl.get() << std::endl;
|
||||
assert( pimpl );
|
||||
std::cerr << "Allocator move construction still holds " << pimpl.get() << std::endl;
|
||||
}
|
||||
|
||||
[[nodiscard]] T *
|
||||
allocate( const std::size_t amt )
|
||||
{
|
||||
assert( pimpl );
|
||||
std::cerr << (void *) this << " owns " << (void *) pimpl.get() << " (alloc)" << std::endl;
|
||||
if( pimpl->slab.size() < amt ) pimpl->slab.reset( std::max( amt, C::slabSize ) );
|
||||
|
||||
auto next= pimpl->slab.carveHead( amt );
|
||||
const auto rv= &next.template as< T >();
|
||||
std::cerr << "Carved off " << amt << " bytes to address " << (void *) rv << " as allocator at " << (void *) pimpl.get() << std::endl;
|
||||
|
||||
pimpl->leases.push_back( std::move( next ) );
|
||||
|
||||
std::cerr << "The allocator at 0x" << (void *) pimpl.get() << " has " << pimpl->leases.size() << " leases. (alloc)" << std::endl;
|
||||
assert( pimpl );
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
deallocate( T *const p, const std::size_t /* ignored */ ) noexcept
|
||||
{
|
||||
assert( pimpl );
|
||||
std::cerr << (void *) this << " owns " << (void *) pimpl.get() << " (dealloc)" << std::endl;
|
||||
std::cerr << "Deallocation attempt of " << (void *) p << " by " << (void *) pimpl.get() << " when there are " << pimpl->leases.size() << " leases left." << std::endl;
|
||||
auto found= std::find_if( begin( pimpl->leases ), end( pimpl->leases ), [p]( const auto &x ) { return x.data() == p; } );
|
||||
if( found != end( pimpl->leases ) )
|
||||
{
|
||||
pimpl->leases.erase( found );
|
||||
std::cerr << "The allocator at 0x" << (void *) pimpl.get() << " has " << pimpl->leases.size() << " leases. (dealloc)" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
abort(); // Panic because it wasn't found in the list!
|
||||
}
|
||||
assert( pimpl );
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
operator == ( const ThreadSlab &a, const ThreadSlab &b ) noexcept
|
||||
{
|
||||
assert( a.pimpl );
|
||||
assert( b.pimpl );
|
||||
return a.pimpl == b.pimpl;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Alepha::Hydrogen::Memory::inline exports::inline ThreadSlab_m
|
||||
{
|
||||
using namespace detail::ThreadSlab_m::exports;
|
||||
}
|
32
Memory/ThreadSlab.test/0.cc
Normal file
32
Memory/ThreadSlab.test/0.cc
Normal file
@ -0,0 +1,32 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#include "../ThreadSlab.h"
|
||||
|
||||
#include <Alepha/Testing/test.h>
|
||||
|
||||
#include <Alepha/Utility/evaluation_helpers.h>
|
||||
|
||||
static auto init= Alepha::Utility::enroll <=[]
|
||||
{
|
||||
using namespace Alepha::Testing::literals;
|
||||
|
||||
using namespace Alepha::Memory::exports::ThreadSlab_m;
|
||||
using String= ThreadSlabString;
|
||||
|
||||
"Can we work with simple `ThreadSlabStrings` without errors?"_test <=[]
|
||||
{
|
||||
String s;
|
||||
std::cerr << "s is empty" << std::endl;
|
||||
|
||||
String s2= "Hello World";
|
||||
std::cerr << "small hello world string." << std::endl;
|
||||
|
||||
String s3= s2 + ": and bob";
|
||||
std::cerr << "appended..." << std::endl;
|
||||
s3= s3 + s3 + s2;
|
||||
|
||||
s2= std::move( s3 );
|
||||
|
||||
std::cout << s3 << std::endl;
|
||||
};
|
||||
};
|
1
Memory/ThreadSlab.test/CMakeLists.txt
Normal file
1
Memory/ThreadSlab.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
unit_test( 0 )
|
Reference in New Issue
Block a user