/* ALLOCATION-CLUSTER.hpp - allocating and owning a pile of objects Copyright (C) Lumiera.org 2008, Hermann Vosseler 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 #include #include #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 TY& create () { TY* obj = new(allocation()) TY(); return commit(obj); } template TY& create (P0& p0) { TY* obj = new(allocation()) TY (p0); return commit(obj); } template TY& create (P0& p0, P1& p1) { TY* obj = new(allocation()) TY (p0,p1); return commit(obj); } template TY& create (P0& p0, P1& p1, P2& p2) { TY* obj = new(allocation()) TY (p0,p1,p2); return commit(obj); } template TY& create (P0& p0, P1& p1, P2& p2, P3& p3) { TY* obj = new(allocation()) TY (p0,p1,p2,p3); return commit(obj); } private: /** initiate an allocation for the given type */ template void* allocation(); /** finish the allocation after the ctor is successful */ template 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 struct TypeSlot; static size_t maxTypeIDs; typedef ScopedPtrHolder HMemManager; typedef Allocator_TransferNoncopyable Allo; typedef std::vector ManagerTable; ManagerTable typeHandlers_; ///< table of active MemoryManager instances HMemManager& handler (size_t slot) { ASSERT (0 TypeInfo(TY*) : allocSize(sizeof(TY)), killIt(&TypeSlot::kill) { } TypeInfo() ///< denotes "unknown" type : allocSize(0), killIt(0) { } }; template 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 guard(); if (!id_) id_= ++maxTypeIDs; return TypeInfo ((TY*) 0 ); } static void kill (void* obj) { TY* p = static_cast(obj); ASSERT (p); ASSERT (INSTANCEOF (TY,p)); p->~TY(); } }; /** storage for static bookkeeping of type allocation slots */ template size_t AllocationCluster::TypeSlot::id_; template inline void* AllocationCluster::allocation() { void *mem = initiateAlloc (TypeSlot::get()); if (!mem) mem = initiateAlloc (TypeSlot::setup(),TypeSlot::get()); ENSURE (mem); return mem; } template inline TY& AllocationCluster::commit (TY* obj) { REQUIRE (obj); finishAlloc (TypeSlot::get(), obj); return *obj; } } // namespace lib #endif