* Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
295 lines
9.2 KiB
C++
295 lines
9.2 KiB
C++
/*
|
||
SIMPLE-ALLOCATOR.hpp - frontend for plain explicit allocations
|
||
|
||
Copyright (C)
|
||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
*/
|
||
|
||
|
||
/** @file simple-allocator.hpp
|
||
** Frontend and marker interface for allocating small objects explicitly.
|
||
** Contrary to the TypedAllocationManager, the SimpleAllocator doesn't provide any
|
||
** ref-counting or tracking facilities, nor does he support bulk de-allocation.
|
||
** Each object needs to be allocated and released by explicit call.
|
||
** The advantage over using std::allocator directly is the shortcut for (placement) construction,
|
||
** and -- of course -- the ability to exchange the memory model at one central location.
|
||
**
|
||
** SimpleAllocator instances will be defined for a specific collection of types; for each of those
|
||
** types, there will be an embedded dedicated custom allocator (currently as of 9/2011, just
|
||
** implemented as std::allocator<TY>). Objects of these preconfigured types can be constructed
|
||
** and destroyed through this allocator instance. Each call needs to be done explicitly, with
|
||
** the precise, concrete type to be created or destroyed. This is especially important for
|
||
** the releasing of objects: there is \em no support for any kind of virtual destruction.
|
||
**
|
||
** @see engine::BufferMetadata
|
||
** @see TypedAllocationManager (another more elaborate custom allocation scheme)
|
||
**
|
||
*/
|
||
|
||
|
||
|
||
#ifndef LIB_SIMPLE_ALLOCATOR_H
|
||
#define LIB_SIMPLE_ALLOCATOR_H
|
||
|
||
#include "lib/error.hpp"
|
||
#include "lib/meta/generator.hpp"
|
||
#include "lib/meta/typelist-util.hpp"
|
||
#include "lib/meta/util.hpp"
|
||
#include "lib/typed-counter.hpp"
|
||
#include "include/logging.h"
|
||
|
||
#include <boost/static_assert.hpp>
|
||
#include <memory>
|
||
|
||
|
||
|
||
namespace lib {
|
||
|
||
using lib::meta::Types;
|
||
using lib::meta::IsInList;
|
||
using lib::meta::InstantiateForEach;
|
||
|
||
|
||
|
||
/* === Policies for simple custom allocator === */
|
||
|
||
/**
|
||
* Policy: use just plain heap allocations
|
||
* @waring whenever you define a specialisation,
|
||
* _you_ are responsible for proper alignment
|
||
* @see TICKET #1204
|
||
*/
|
||
template<typename TY>
|
||
class CustomAllocator
|
||
: public std::allocator<TY>
|
||
{ };
|
||
|
||
|
||
/**
|
||
* Policy: maintain explicit per type instance count
|
||
* @note this imposes additional locking
|
||
*/
|
||
struct UseInstantiationCounting
|
||
{
|
||
template<class XX>
|
||
size_t
|
||
allocationCount() const
|
||
{
|
||
return allocCnt_.get<XX>();
|
||
}
|
||
|
||
template<class XX>
|
||
void
|
||
incrementCount()
|
||
{
|
||
allocCnt_.inc<XX>();
|
||
}
|
||
|
||
template<class XX>
|
||
void
|
||
decrementCount()
|
||
{
|
||
allocCnt_.dec<XX>();
|
||
}
|
||
|
||
private:
|
||
lib::TypedCounter allocCnt_;
|
||
};
|
||
|
||
/**
|
||
* Policy: no additional instantiation accounting
|
||
*/
|
||
struct NoInstantiationCount
|
||
{
|
||
template<class XX> size_t allocationCount() const { return 0; }
|
||
template<class XX> void incrementCount() { /* NOP */ }
|
||
template<class XX> void decrementCount() { /* NOP */ }
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/* === Allocator frontend === */
|
||
|
||
/**
|
||
* Frontend for explicit allocations, using a custom allocator.
|
||
* This template is to be instantiated for the collection of types
|
||
* later to be allocated through this custom memory manager/model.
|
||
* It provides convenience shortcuts for placement-construction
|
||
* and releasing of target objects.
|
||
*
|
||
* @todo currently (as of 8/09) the low-level pooled allocator
|
||
* isn't implemented; instead we do just heap allocations.
|
||
* ////////////////////////////////////////////////////////////////////////////////////////////Ticket #835
|
||
*/
|
||
template<typename TYPES
|
||
,class COUNTER = NoInstantiationCount ///< Policy: support instance accounting?
|
||
>
|
||
class SimpleAllocator
|
||
: InstantiateForEach< typename TYPES::List // for each of those types...
|
||
, CustomAllocator // ...mix in the custom allocator
|
||
>
|
||
, COUNTER // ...Instantiation accounting policy
|
||
{
|
||
|
||
/** forward plain memory allocation */
|
||
template<class XX>
|
||
XX *
|
||
allocateSlot ()
|
||
{
|
||
TRACE (memory, "allocate «%s»", util::typeStr<XX>().c_str());
|
||
XX * newStorage = CustomAllocator<XX>::allocate (1);
|
||
COUNTER::template incrementCount<XX>();
|
||
return newStorage;
|
||
}
|
||
|
||
template<class XX>
|
||
void
|
||
releaseSlot (XX* entry)
|
||
{
|
||
TRACE (memory, "release «%s»", util::typeStr<XX>().c_str());
|
||
CustomAllocator<XX>::deallocate (entry, 1);
|
||
COUNTER::template decrementCount<XX>();
|
||
}
|
||
|
||
|
||
template<class XX>
|
||
void
|
||
___assertSupportedType()
|
||
{
|
||
typedef typename TYPES::List PreconfiguredTypes;
|
||
typedef IsInList<XX, PreconfiguredTypes> IsSupportedType;
|
||
|
||
BOOST_STATIC_ASSERT (IsSupportedType::value);
|
||
}
|
||
|
||
|
||
|
||
|
||
public: /* ==== build objects with managed allocation ==== */
|
||
|
||
#define _EXCEPTION_SAFE_INVOKE(_CTOR_) \
|
||
\
|
||
___assertSupportedType<XX>(); \
|
||
XX* storage = allocateSlot<XX>(); \
|
||
try \
|
||
{ \
|
||
return (new(storage) _CTOR_ ); \
|
||
} \
|
||
catch(...) \
|
||
{ \
|
||
releaseSlot<XX>(storage); \
|
||
throw; \
|
||
}
|
||
|
||
template< class XX>
|
||
XX* //_____________________
|
||
create () ///< invoke default ctor
|
||
{
|
||
_EXCEPTION_SAFE_INVOKE ( XX() )
|
||
}
|
||
|
||
|
||
template< class XX, typename P1>
|
||
XX* //___________________
|
||
create (P1& p1) ///< invoke 1-arg ctor
|
||
{
|
||
_EXCEPTION_SAFE_INVOKE ( XX (p1) )
|
||
}
|
||
|
||
|
||
template< class XX
|
||
, typename P1
|
||
, typename P2
|
||
>
|
||
XX* //___________________
|
||
create (P1& p1, P2& p2) ///< invoke 2-arg ctor
|
||
{
|
||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2) )
|
||
}
|
||
|
||
|
||
template< class XX
|
||
, typename P1
|
||
, typename P2
|
||
, typename P3
|
||
>
|
||
XX* //___________________
|
||
create (P1& p1, P2& p2, P3& p3) ///< invoke 3-arg ctor
|
||
{
|
||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) )
|
||
}
|
||
|
||
|
||
template< class XX
|
||
, typename P1
|
||
, typename P2
|
||
, typename P3
|
||
, typename P4
|
||
>
|
||
XX* //___________________
|
||
create (P1& p1, P2& p2, P3& p3, P4& p4) ///< invoke 4-arg ctor
|
||
{
|
||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) )
|
||
}
|
||
|
||
|
||
template< class XX
|
||
, typename P1
|
||
, typename P2
|
||
, typename P3
|
||
, typename P4
|
||
, typename P5
|
||
>
|
||
XX* //___________________
|
||
create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5) ///< invoke 5-arg ctor
|
||
{
|
||
_EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) )
|
||
}
|
||
|
||
#undef _EXCEPTION_SAFE_INVOKE
|
||
|
||
|
||
|
||
template<class XX>
|
||
void
|
||
destroy (XX* entry)
|
||
{
|
||
if (!entry) return;
|
||
___assertSupportedType<XX>();
|
||
try
|
||
{
|
||
entry->~XX();
|
||
}
|
||
catch(...)
|
||
{
|
||
lumiera_err errorID = lumiera_error();
|
||
WARN (common_dbg, "dtor of «%s» failed: %s", util::typeStr(entry).c_str()
|
||
, errorID );
|
||
}
|
||
releaseSlot<XX> (entry);
|
||
}
|
||
|
||
|
||
/** diagnostics */
|
||
template<class XX>
|
||
size_t
|
||
numSlots() const
|
||
{
|
||
return COUNTER::template allocationCount<XX>();
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
} // namespace lib
|
||
#endif
|