2023-06-11 04:37:38 +02:00
/*
ALLOCATOR - HANDLE . hpp - front - end handle for custom allocation schemes
Copyright ( C ) Lumiera . org
2023 , Hermann Vosseler < Ichthyostega @ web . de >
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of
the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/** @file allocator-handle.hpp
* * A front - end / concept to allow access to custom memory management .
* * Minimalistic definition scheme for a functor - like object , which can be
* * passed to client code , offering a callback to generate new objects into
* * some custom allocation scheme not further disclosed .
* *
* * Lumiera employs various flavours of custom memory management , to handle
* * allocation demands from performance critical parts of the application .
* * Irrespective of the actual specifics of the allocation , typically there
* * is some _instance_ of an allocator maintained within a carefully crafted
* * context — leading to the necessity to dependency - inject a suitable front - end
* * into various connected parts of the application , to allow for coherent use
* * of allocation while avoiding tight coupling of implementation internals .
* *
* * Reduced to the bare minimum , the _ability to allocate_ can be represented
* * as a functor , which accepts arbitrary ( suitable ) arguments and returns a
* * reference to a newly allocated instance of some specific type ; such an
* * _allocation front - end_ may then be passed as additional ( template )
* * parameter to associated classes or functions , allowing to generate new
* * objects at stable memory location , which can then be wired internally .
* *
* * @ todo 6 / 2023 this specification describes a * Concept * , not an actual
* * interface type . After the migration to C + + 20 , it will thus be
* * possible to mark some arbitrary custom allocator / front - end
* * with such a concept , thereby documenting proper API usage .
* *
* * @ see allocation - cluster . hpp
* * @ see steam : : fixture : : Segment
* * @ see steam : : engine : : JobTicket
*/
# ifndef LIB_ALLOCATOR_HANDLE_H
# define LIB_ALLOCATOR_HANDLE_H
# include "lib/error.hpp"
# include <utility>
# include <list>
namespace lib {
/**
* Placeholder implementation for a custom allocator
* @ todo shall be replaced by an AllocationCluster eventually
2023-06-12 17:21:41 +02:00
* @ note conforming to a prospective C + + 20 Concept ` Allo < TYPE > `
2023-06-11 04:37:38 +02:00
* @ remark using ` std : : list ` container , since re - entrant allocation calls are possible ,
2023-06-12 17:21:41 +02:00
* meaning that further allocations will be requested recursively from a ctor .
* Moreover , for the same reason we separate the allocation from the ctor call ,
* so we can capture the address of the new allocation prior to any possible
* re - entrant call , and handle clean - up of allocation without requiring any
* additional state flags . . . . .
2023-06-11 04:37:38 +02:00
*/
2023-06-12 17:21:41 +02:00
template < typename TY >
2023-06-11 04:37:38 +02:00
class AllocatorHandle
{
2023-06-12 17:21:41 +02:00
struct Allocation
{
char buf_ [ sizeof ( TY ) ] ;
template < typename . . . ARGS >
TY &
create ( ARGS & & . . . args )
{
return * new ( & buf_ ) TY { std : : forward < ARGS > ( args ) . . . } ;
}
TY &
access ( )
{
return reinterpret_cast < TY & > ( buf_ ) ;
}
void
discard ( ) /// @warning strong assumption made here: Payload was created
{
access ( ) . ~ TY ( ) ;
}
} ;
std : : list < Allocation > storage_ ;
2023-06-11 04:37:38 +02:00
public :
template < typename . . . ARGS >
2023-06-12 17:21:41 +02:00
TY &
2023-06-11 04:37:38 +02:00
operator ( ) ( ARGS & & . . . args )
2023-06-12 17:21:41 +02:00
{ // EX_STRONG
auto pos = storage_ . emplace ( storage_ . end ( ) ) ; ////////////////////////////////////////////////////TICKET #230 : real implementation should care for concurrency here
try {
return pos - > create ( std : : forward < ARGS > ( args ) . . . ) ;
}
catch ( . . . )
{
storage_ . erase ( pos ) ; // EX_FREE
const char * errID = lumiera_error ( ) ;
ERROR ( memory , " Allocation failed with unknown exception. "
" Lumiera errorID=%s " , errID ? errID : " ?? " ) ;
throw ;
}
2023-06-11 04:37:38 +02:00
}
2023-06-12 17:21:41 +02:00
/** @note need to do explicit clean-up, since a ctor-call might have been failed,
* and we have no simple way to record this fact internally in Allocation ,
* short of wasting additional memory for a flag to mark this situation */
~ AllocatorHandle ( )
try {
for ( auto & alloc : storage_ )
alloc . discard ( ) ;
}
ERROR_LOG_AND_IGNORE ( memory , " clean-up of custom AllocatorHandle " )
2023-06-11 04:37:38 +02:00
} ;
} // namespace lib
# endif /*LIB_ALLOCATOR_HANDLE_H*/