2008-10-18 02:32:34 +02:00
|
|
|
/*
|
2008-10-30 04:34:05 +01:00
|
|
|
AllocationCluster - allocating and owning a pile of objects
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
2010-12-17 23:28:49 +01:00
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
|
|
|
|
2016-11-03 18:22:31 +01:00
|
|
|
/** @file allocation-cluster.cpp
|
|
|
|
|
** TODO allocation-cluster.cpp
|
2016-11-03 18:20:10 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2010-12-18 00:58:19 +01:00
|
|
|
#include "lib/allocation-cluster.hpp"
|
2008-12-27 00:53:35 +01:00
|
|
|
#include "lib/error.hpp"
|
2008-12-17 17:53:32 +01:00
|
|
|
#include "lib/util.hpp"
|
2008-12-28 05:20:35 +01:00
|
|
|
#include "lib/sync.hpp"
|
2008-10-18 02:32:34 +02:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
using util::isnil;
|
2008-10-30 04:34:05 +01:00
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
/**
|
|
|
|
|
* "Low-level" Memory manager for allocating small objects of a fixed size.
|
|
|
|
|
* The usage pattern is definite: Objects will be allocated in the course of
|
|
|
|
|
* a build process and then live until all Objects will be purged in one sway.
|
|
|
|
|
* Allocations will be requested one by one and immediately committed after
|
|
|
|
|
* successful ctor call of the object being allocated. Allocations and commits
|
|
|
|
|
* can be assumed to come in pairs, thus if an allocation immediately follows
|
|
|
|
|
* another one (without commit), the previous allocation can be considered
|
2008-12-28 05:20:35 +01:00
|
|
|
* a failure and can be dropped silently. After an allocation succeeds
|
2008-10-23 23:08:27 +02:00
|
|
|
* (i.e. was committed), the MemoryManager is in charge for the lifecycle
|
2008-12-28 05:20:35 +01:00
|
|
|
* of the object within the allocated space and has to guarantee calling
|
2008-10-23 23:08:27 +02:00
|
|
|
* it's dtor, either on shutdown or on explicit #purge() -- the type info
|
|
|
|
|
* structure handed in on initialisation provides a means for invoking
|
|
|
|
|
* the dtor without actually knowing the object's type.
|
|
|
|
|
*
|
|
|
|
|
* @todo this is a preliminary or pseudo-implementation based on
|
2008-10-30 04:34:05 +01:00
|
|
|
* a vector of raw pointers, i.e. actually the objects are heap
|
2008-10-23 23:08:27 +02:00
|
|
|
* allocated. What actually should happen is for the MemoryManager to
|
|
|
|
|
* allocate raw memory chunk wise, sub partition it and place the objects
|
|
|
|
|
* into this private memory buffer. Further, possibly we could maintain
|
|
|
|
|
* a pool of raw memory chunks used by all MemoryManager instances. I am
|
|
|
|
|
* skipping those details for now (10/2008) because they should be based
|
|
|
|
|
* on real-world measurements, not guessing.
|
|
|
|
|
*/
|
2008-10-20 03:13:02 +02:00
|
|
|
class AllocationCluster::MemoryManager
|
2008-12-28 05:20:35 +01:00
|
|
|
: public Sync<RecursiveLock_NoWait>
|
2008-10-20 03:13:02 +02:00
|
|
|
{
|
2008-10-23 23:08:27 +02:00
|
|
|
typedef std::vector<char*> MemTable;
|
|
|
|
|
TypeInfo type_;
|
|
|
|
|
MemTable mem_;
|
2008-10-30 04:03:14 +01:00
|
|
|
size_t top_; ///< index of the next slot available for allocation
|
2008-10-23 23:08:27 +02:00
|
|
|
|
2008-10-22 04:55:28 +02:00
|
|
|
public:
|
2008-10-24 06:06:24 +02:00
|
|
|
MemoryManager(TypeInfo info) : top_(0) { reset(info); }
|
|
|
|
|
~MemoryManager() { purge(); }
|
2008-10-23 23:08:27 +02:00
|
|
|
|
2012-04-30 04:28:16 +02:00
|
|
|
size_t size() const;
|
|
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
void purge();
|
|
|
|
|
void reset(TypeInfo info);
|
2008-10-20 03:13:02 +02:00
|
|
|
|
2008-10-22 04:55:28 +02:00
|
|
|
void* allocate();
|
|
|
|
|
|
|
|
|
|
void commit (void* pendingAlloc);
|
2008-10-30 04:03:14 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void clearStorage();
|
2008-10-20 03:13:02 +02:00
|
|
|
};
|
2008-10-30 04:34:05 +01:00
|
|
|
|
2008-10-22 04:55:28 +02:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
|
2012-04-30 04:28:16 +02:00
|
|
|
/** the top_ index always points at the next slot
|
|
|
|
|
* not yet holding a finished, committed allocation.
|
|
|
|
|
* Index is zero based, thus top_ == count of living objects
|
|
|
|
|
*/
|
|
|
|
|
size_t
|
|
|
|
|
AllocationCluster::MemoryManager::size() const
|
|
|
|
|
{
|
|
|
|
|
return top_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
void
|
|
|
|
|
AllocationCluster::MemoryManager::reset (TypeInfo info)
|
2008-10-22 04:55:28 +02:00
|
|
|
{
|
2009-08-29 02:16:28 +02:00
|
|
|
Lock sync(this);
|
2008-10-23 23:08:27 +02:00
|
|
|
|
|
|
|
|
if (0 < mem_.size()) purge();
|
|
|
|
|
type_ = info;
|
|
|
|
|
|
|
|
|
|
ENSURE (0==top_);
|
|
|
|
|
ENSURE (isnil (mem_));
|
|
|
|
|
ENSURE (0 < type_.allocSize);
|
|
|
|
|
ENSURE (type_.killIt);
|
2008-10-22 04:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AllocationCluster::MemoryManager::purge()
|
|
|
|
|
{
|
2009-08-29 02:16:28 +02:00
|
|
|
Lock sync(this);
|
2008-10-30 04:34:05 +01:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
REQUIRE (type_.killIt, "we need a deleter function");
|
|
|
|
|
REQUIRE (0 < type_.allocSize, "allocation size unknown");
|
|
|
|
|
REQUIRE (top_ == mem_.size() || (top_+1) == mem_.size());
|
|
|
|
|
|
|
|
|
|
while (top_)
|
2008-10-30 04:03:14 +01:00
|
|
|
type_.killIt (mem_[--top_]);
|
|
|
|
|
clearStorage();
|
2008-10-23 23:08:27 +02:00
|
|
|
}// note: unnecessary to kill pending allocations
|
|
|
|
|
|
|
|
|
|
|
2008-10-30 04:03:14 +01:00
|
|
|
inline void
|
|
|
|
|
AllocationCluster::MemoryManager::clearStorage()
|
|
|
|
|
{
|
|
|
|
|
for (size_t i=mem_.size(); 0 < i; )
|
|
|
|
|
delete[] mem_[--i];
|
|
|
|
|
mem_.clear();
|
|
|
|
|
}
|
2008-10-30 04:34:05 +01:00
|
|
|
|
2008-10-30 04:03:14 +01:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
inline void*
|
2008-10-22 04:55:28 +02:00
|
|
|
AllocationCluster::MemoryManager::allocate()
|
|
|
|
|
{
|
2009-08-29 02:16:28 +02:00
|
|
|
Lock sync(this);
|
2008-10-23 23:08:27 +02:00
|
|
|
|
|
|
|
|
REQUIRE (0 < type_.allocSize);
|
|
|
|
|
REQUIRE (top_ <= mem_.size());
|
2008-10-30 04:34:05 +01:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
if (top_==mem_.size())
|
|
|
|
|
mem_.resize(top_+1);
|
2008-10-30 04:34:05 +01:00
|
|
|
|
|
|
|
|
if (!mem_[top_]) // re-use existing allocation, if any
|
2008-10-30 04:03:14 +01:00
|
|
|
mem_[top_] = new char[type_.allocSize];
|
2008-10-23 23:08:27 +02:00
|
|
|
|
|
|
|
|
ENSURE (top_ < mem_.size());
|
2008-10-30 04:03:14 +01:00
|
|
|
ENSURE (mem_[top_]);
|
2008-10-23 23:08:27 +02:00
|
|
|
return mem_[top_];
|
2008-10-22 04:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
|
|
|
|
|
inline void
|
2008-10-22 04:55:28 +02:00
|
|
|
AllocationCluster::MemoryManager::commit (void* pendingAlloc)
|
|
|
|
|
{
|
2009-08-29 02:16:28 +02:00
|
|
|
Lock sync(this);
|
2008-10-30 04:34:05 +01:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
REQUIRE (pendingAlloc);
|
|
|
|
|
ASSERT (top_ < mem_.size());
|
|
|
|
|
ASSERT (pendingAlloc == mem_[top_], "allocation protocol violated");
|
|
|
|
|
|
|
|
|
|
++top_;
|
|
|
|
|
|
|
|
|
|
ENSURE (top_ == mem_.size());
|
2008-10-22 04:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** storage for static bookkeeping of type allocation slots */
|
|
|
|
|
size_t AllocationCluster::maxTypeIDs;
|
2008-10-30 04:34:05 +01:00
|
|
|
|
|
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
/** creating a new AllocationCluster prepares a table capable
|
|
|
|
|
* of holding the individual object families to come. Each of those
|
|
|
|
|
* is managed by a separate instance of the low-level memory manager.
|
|
|
|
|
*/
|
|
|
|
|
AllocationCluster::AllocationCluster()
|
|
|
|
|
{
|
2008-11-28 21:45:18 +01:00
|
|
|
TRACE (memory, "new AllocationCluster");
|
2008-10-18 02:32:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** On shutdown of the AllocationCluster we need to assure a certain
|
|
|
|
|
* destruction order is maintained by explicitly invoking a cleanup
|
|
|
|
|
* operation on each of the low-level memory manager objects.
|
|
|
|
|
*/
|
|
|
|
|
AllocationCluster::~AllocationCluster() throw()
|
|
|
|
|
{
|
|
|
|
|
try
|
2008-12-28 05:20:35 +01:00
|
|
|
{ // avoiding a per-instance lock for now.
|
2014-08-26 03:31:03 +02:00
|
|
|
ClassLock<AllocationCluster> guard; // (see note in the class description)
|
2008-10-23 23:08:27 +02:00
|
|
|
|
2008-11-28 21:45:18 +01:00
|
|
|
TRACE (memory, "shutting down AllocationCluster");
|
2008-10-23 23:08:27 +02:00
|
|
|
for (size_t i = typeHandlers_.size(); 0 < i; --i)
|
2008-10-30 04:03:14 +01:00
|
|
|
if (handler(i))
|
|
|
|
|
handler(i)->purge();
|
2008-10-23 23:08:27 +02:00
|
|
|
|
2008-10-24 06:06:24 +02:00
|
|
|
typeHandlers_.clear();
|
2008-10-18 02:32:34 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (lumiera::Error & ex)
|
|
|
|
|
{
|
2009-01-24 03:15:02 +01:00
|
|
|
WARN (progress, "Exception while closing AllocationCluster: %s", ex.what());
|
2008-10-18 02:32:34 +02:00
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
2009-01-24 03:15:02 +01:00
|
|
|
ALERT (progress, "Unexpected fatal Exception while closing AllocationCluster.");
|
2008-10-22 04:55:28 +02:00
|
|
|
lumiera::error::lumiera_unexpectedException(); // terminate
|
2008-10-18 02:32:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-10-22 04:55:28 +02:00
|
|
|
void*
|
|
|
|
|
AllocationCluster::initiateAlloc (size_t& slot)
|
|
|
|
|
{
|
2008-10-23 23:08:27 +02:00
|
|
|
if (!slot || slot > typeHandlers_.size() || !handler(slot) )
|
|
|
|
|
return 0; // Memory manager not yet initialised
|
2008-10-22 04:55:28 +02:00
|
|
|
else
|
2008-10-23 23:08:27 +02:00
|
|
|
return handler(slot)->allocate();
|
2008-10-22 04:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void*
|
|
|
|
|
AllocationCluster::initiateAlloc (TypeInfo type, size_t& slot)
|
|
|
|
|
{
|
2008-10-23 19:47:08 +02:00
|
|
|
ASSERT (0 < slot);
|
2008-10-22 04:55:28 +02:00
|
|
|
|
2008-12-28 05:20:35 +01:00
|
|
|
{ // avoiding a per-instance lock for now.
|
2014-08-26 03:31:03 +02:00
|
|
|
ClassLock<AllocationCluster> guard; // (see note in the class description)
|
2008-10-23 23:08:27 +02:00
|
|
|
|
2008-10-23 19:47:08 +02:00
|
|
|
if (slot > typeHandlers_.size())
|
2008-10-28 06:07:23 +01:00
|
|
|
typeHandlers_.resize(slot);
|
2008-10-23 23:08:27 +02:00
|
|
|
if (!handler(slot))
|
|
|
|
|
handler(slot).reset (new MemoryManager (type));
|
2008-10-23 19:47:08 +02:00
|
|
|
|
|
|
|
|
}
|
2008-10-22 04:55:28 +02:00
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
ASSERT (handler(slot));
|
2008-10-30 04:34:05 +01:00
|
|
|
return initiateAlloc(slot);
|
2008-10-22 04:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AllocationCluster::finishAlloc (size_t& slot, void* allocatedObj)
|
|
|
|
|
{
|
2008-10-23 23:08:27 +02:00
|
|
|
ASSERT (handler(slot));
|
2008-10-22 04:55:28 +02:00
|
|
|
ASSERT (allocatedObj);
|
|
|
|
|
|
2008-10-23 23:08:27 +02:00
|
|
|
handler(slot)->commit(allocatedObj);
|
2008-10-22 04:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-04-30 04:28:16 +02:00
|
|
|
/* === diagnostics helpers === */
|
|
|
|
|
|
|
|
|
|
/** @return total number of objects
|
|
|
|
|
* currently managed by this allocator */
|
|
|
|
|
size_t
|
|
|
|
|
AllocationCluster::size() const
|
|
|
|
|
{
|
|
|
|
|
size_t size(0);
|
|
|
|
|
typedef ManagerTable::const_iterator Iter;
|
|
|
|
|
|
|
|
|
|
for (Iter ii= typeHandlers_.begin(); ii != typeHandlers_.end(); ++ii )
|
|
|
|
|
if (*ii)
|
|
|
|
|
size += (*ii)->size();
|
|
|
|
|
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
AllocationCluster::countActiveInstances (size_t& slot) const
|
|
|
|
|
{
|
|
|
|
|
if (handler (slot))
|
|
|
|
|
return handler(slot)->size();
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-10-18 02:32:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace lib
|