281 lines
7.9 KiB
C++
281 lines
7.9 KiB
C++
/*
|
|
ALLOCATION-CLUSTER.hpp - allocating and owning a pile of objects
|
|
|
|
Copyright (C) Lumiera.org
|
|
2008, 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 02139, USA.
|
|
|
|
*/
|
|
|
|
/** @file allocation-cluster.hpp
|
|
** Memory management for the low-level model (render nodes network).
|
|
** The model is organised into temporal segments, which are considered
|
|
** to be structurally constant and uniform. The objects within each
|
|
** segment are strongly interconnected, and thus each segment is
|
|
** being built in a single build process and is replaced or released
|
|
** as a whole. AllocationCluster implements memory management to
|
|
** support this usage pattern.
|
|
**
|
|
** @note this file is organised in a way which doesn't bind the
|
|
** client code to the memory manager implementation. Parts of the
|
|
** interface depending on the usage situation are implemented using
|
|
** templates, and thus need to be in the header. This way they can
|
|
** exploit the type information available in call context. This
|
|
** information is passed to generic implementation functions
|
|
** defined in allocation-cluster.cpp . In a similar vein, the
|
|
** AllocationCluster::MemoryManger is just forward declared.
|
|
**
|
|
** @see allocation-cluster-test.cpp
|
|
** @see builder::ToolFactory
|
|
** @see frameid.hpp
|
|
*/
|
|
|
|
|
|
#ifndef LIB_ALLOCATION_CLUSTER_H
|
|
#define LIB_ALLOCATION_CLUSTER_H
|
|
|
|
#include <vector>
|
|
#include <boost/scoped_ptr.hpp>
|
|
#include <boost/noncopyable.hpp>
|
|
|
|
#include "lib/error.hpp"
|
|
#include "lib/sync-classlock.hpp"
|
|
#include "lib/scoped-holder.hpp"
|
|
#include "lib/scoped-holder-transfer.hpp"
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
using boost::scoped_ptr;
|
|
|
|
/**
|
|
* A pile of objects sharing common allocation and lifecycle.
|
|
* AllocationCluster owns a number of object families of various types.
|
|
* Each of those contains a initially undetermined (but rather large)
|
|
* number of individual objects, which can be expected to be allocated
|
|
* within a short timespan and which are to be released cleanly on
|
|
* destruction of the AllocationCluster. We provide a service creating
|
|
* individual objects with arbitrary ctor parameters.
|
|
* @warning make sure the objects dtors aren't called and object references
|
|
* aren't used after shutting down a given AllocationCluster.
|
|
* @todo implement a facility to control the oder in which
|
|
* the object families are to be discarded. Currently
|
|
* they are just purged in reverse order defined by
|
|
* the first request for allocating a certain type.
|
|
* @todo should we use an per-instance lock? We can't avoid
|
|
* the class-wide lock, unless also the type-ID registration
|
|
* is done on a per-instance base. AllocationCluster is intended
|
|
* to be used within the builder, which executes in a dedicated
|
|
* thread. Thus I doubt lock contention could be a problem and
|
|
* we can avoid using a mutex per instance. Re-evaluate this!
|
|
*/
|
|
class AllocationCluster
|
|
: boost::noncopyable
|
|
{
|
|
|
|
public:
|
|
AllocationCluster ();
|
|
~AllocationCluster () throw();
|
|
|
|
|
|
template<class TY>
|
|
TY&
|
|
create ()
|
|
{
|
|
TY* obj = new(allocation<TY>()) TY();
|
|
return commit(obj);
|
|
}
|
|
|
|
template<class TY, typename P0>
|
|
TY&
|
|
create (P0& p0)
|
|
{
|
|
TY* obj = new(allocation<TY>()) TY (p0);
|
|
return commit(obj);
|
|
}
|
|
|
|
template<class TY, typename P0, typename P1>
|
|
TY&
|
|
create (P0& p0, P1& p1)
|
|
{
|
|
TY* obj = new(allocation<TY>()) TY (p0,p1);
|
|
return commit(obj);
|
|
}
|
|
|
|
template<class TY, typename P0, typename P1, typename P2>
|
|
TY&
|
|
create (P0& p0, P1& p1, P2& p2)
|
|
{
|
|
TY* obj = new(allocation<TY>()) TY (p0,p1,p2);
|
|
return commit(obj);
|
|
}
|
|
|
|
template<class TY, typename P0, typename P1, typename P2, typename P3>
|
|
TY&
|
|
create (P0& p0, P1& p1, P2& p2, P3& p3)
|
|
{
|
|
TY* obj = new(allocation<TY>()) TY (p0,p1,p2,p3);
|
|
return commit(obj);
|
|
}
|
|
|
|
|
|
private:
|
|
/** initiate an allocation for the given type */
|
|
template<class TY>
|
|
void*
|
|
allocation();
|
|
|
|
/** finish the allocation after the ctor is successful */
|
|
template<class TY>
|
|
TY&
|
|
commit (TY* obj);
|
|
|
|
|
|
/**
|
|
* The type-specific configuration information
|
|
* any low-level memory manager needs to know
|
|
*/
|
|
struct TypeInfo;
|
|
|
|
/**
|
|
* low-level memory manager responsible for
|
|
* the allocations of one specific type.
|
|
*/
|
|
class MemoryManager;
|
|
|
|
/**
|
|
* organising the association Type -> table entry
|
|
*/
|
|
template<class TY>
|
|
struct TypeSlot;
|
|
|
|
static size_t maxTypeIDs;
|
|
|
|
|
|
typedef ScopedPtrHolder<MemoryManager> HMemManager;
|
|
typedef Allocator_TransferNoncopyable<HMemManager> Allo;
|
|
typedef std::vector<HMemManager,Allo> ManagerTable;
|
|
|
|
ManagerTable typeHandlers_; ///< table of active MemoryManager instances
|
|
|
|
|
|
HMemManager&
|
|
handler (size_t slot)
|
|
{
|
|
ASSERT (0<slot && slot<=typeHandlers_.size());
|
|
return typeHandlers_[slot-1];
|
|
}
|
|
|
|
/** implementation of the actual memory allocation
|
|
* is pushed down to the MemoryManager impl. */
|
|
void* initiateAlloc (size_t& slot);
|
|
void* initiateAlloc (TypeInfo type, size_t& slot);
|
|
|
|
/** enrol the allocation after successful ctor call */
|
|
void finishAlloc (size_t& slot, void*);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-----implementation-details------------------------
|
|
|
|
struct AllocationCluster::TypeInfo
|
|
{
|
|
size_t allocSize;
|
|
void (*killIt)(void*); ///< deleter function
|
|
|
|
template<class TY>
|
|
TypeInfo(TY*)
|
|
: allocSize(sizeof(TY)),
|
|
killIt(&TypeSlot<TY>::kill)
|
|
{ }
|
|
|
|
TypeInfo() ///< denotes "unknown" type
|
|
: allocSize(0),
|
|
killIt(0)
|
|
{ }
|
|
};
|
|
|
|
|
|
|
|
template<class TY>
|
|
struct AllocationCluster::TypeSlot
|
|
{
|
|
static size_t id_; ///< table pos+1 of the memory manager in charge for type TY
|
|
|
|
static size_t &
|
|
get()
|
|
{
|
|
return id_;
|
|
}
|
|
|
|
static TypeInfo
|
|
setup()
|
|
{
|
|
ClassLock<AllocationCluster> guard();
|
|
if (!id_)
|
|
id_= ++maxTypeIDs;
|
|
|
|
return TypeInfo ((TY*) 0 );
|
|
}
|
|
|
|
static void
|
|
kill (void* obj)
|
|
{
|
|
TY* p = static_cast<TY*>(obj);
|
|
ASSERT (p);
|
|
ASSERT (INSTANCEOF (TY,p));
|
|
p->~TY();
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/** storage for static bookkeeping of type allocation slots */
|
|
template<class TY>
|
|
size_t AllocationCluster::TypeSlot<TY>::id_;
|
|
|
|
|
|
|
|
template<class TY>
|
|
inline void*
|
|
AllocationCluster::allocation()
|
|
{
|
|
void *mem = initiateAlloc (TypeSlot<TY>::get());
|
|
if (!mem)
|
|
mem = initiateAlloc (TypeSlot<TY>::setup(),TypeSlot<TY>::get());
|
|
ENSURE (mem);
|
|
return mem;
|
|
}
|
|
|
|
template<class TY>
|
|
inline TY&
|
|
AllocationCluster::commit (TY* obj)
|
|
{
|
|
REQUIRE (obj);
|
|
finishAlloc (TypeSlot<TY>::get(), obj);
|
|
return *obj;
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace lib
|
|
#endif
|