]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Split MemPool.cc into generic framework and specific implementation
authorHenrik Nordstrom <henrik@henriknordstrom.net>
Fri, 28 May 2010 21:53:06 +0000 (23:53 +0200)
committerHenrik Nordstrom <henrik@henriknordstrom.net>
Fri, 28 May 2010 21:53:06 +0000 (23:53 +0200)
include/MemPool.h
include/MemPoolChunked.h [new file with mode: 0644]
include/MemPoolMalloc.h [new file with mode: 0644]
lib/Makefile.am
lib/MemPool.cc
lib/MemPoolChunked.cc [new file with mode: 0644]
lib/MemPoolMalloc.cc [new file with mode: 0644]

index b51ce23ac1bd6882c50de9a02c5d589a2bfd7ee7..6a43ca600a4f3faafdcd58e231713ecb73cb46c5 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _MEM_POOLS_H_
-#define _MEM_POOLS_H_
+#ifndef _MEM_POOL_H_
+#define _MEM_POOL_H_
 
 /**
  \defgroup MemPoolsAPI  Memory Management (Memory Pool Allocator)
@@ -63,7 +63,6 @@
 #define MEM_MAX_FREE  65535    /* ushort is max number of items per chunk */
 
 class MemImplementingAllocator;
-class MemChunk;
 class MemPoolStats;
 
 /// \ingroup MemPoolsAPI
@@ -129,13 +128,6 @@ public:
      */
     MemImplementingAllocator * create(const char *label, size_t obj_size);
 
-    /**
-     \param label      Name for the pool. Displayed in stats.
-     \param obj_size   Size of elements in MemPool.
-     \param chunked    ??
-     */
-    MemImplementingAllocator * create(const char *label, size_t obj_size, bool const chunked);
-
     /**
      * Sets upper limit in bytes to amount of free ram kept in pools. This is
      * not strict upper limit, but a hint. When MemPools are over this limit,
@@ -197,7 +189,7 @@ public:
      \param stats      Object to be filled with statistical data about pool.
      \retval           Number of objects in use, ie. allocated.
      */
-    virtual int getStats(MemPoolStats * stats) = 0;
+    virtual int getStats(MemPoolStats * stats, int accumulate = 0) = 0;
 
     virtual MemPoolMeter const &getMeter() const = 0;
 
@@ -342,15 +334,13 @@ public:
 
     virtual bool idleTrigger(int shift) const = 0;
     virtual void clean(time_t maxage) = 0;
-    /** Hint to the allocator - may be ignored */
-    virtual void setChunkSize(size_t chunksize) {}
     virtual size_t objectSize() const;
     virtual int getInUseCount() = 0;
 protected:
     virtual void *allocate() = 0;
     virtual void deallocate(void *) = 0;
-private:
     MemPoolMeter meter;
+    int memPID;
 public:
     MemImplementingAllocator *next;
 public:
@@ -359,93 +349,6 @@ public:
     size_t obj_size;
 };
 
-/// \ingroup MemPoolsAPI
-class MemPool : public MemImplementingAllocator
-{
-public:
-    friend class MemChunk;
-    MemPool(const char *label, size_t obj_size);
-    ~MemPool();
-    void convertFreeCacheToChunkFreeCache();
-    virtual void clean(time_t maxage);
-
-    /**
-     \param stats      Object to be filled with statistical data about pool.
-     \retval           Number of objects in use, ie. allocated.
-     */
-    virtual int getStats(MemPoolStats * stats);
-
-    void createChunk();
-    void *get();
-    void push(void *obj);
-    virtual int getInUseCount();
-protected:
-    virtual void *allocate();
-    virtual void deallocate(void *);
-public:
-    /**
-     * Allows you tune chunk size of pooling. Objects are allocated in chunks
-     * instead of individually. This conserves memory, reduces fragmentation.
-     * Because of that memory can be freed also only in chunks. Therefore
-     * there is tradeoff between memory conservation due to chunking and free
-     * memory fragmentation.
-     *
-     \note  As a general guideline, increase chunk size only for pools that keep
-     *      very many items for relatively long time.
-     */
-    virtual void setChunkSize(size_t chunksize);
-
-    virtual bool idleTrigger(int shift) const;
-
-    size_t chunk_size;
-    int chunk_capacity;
-    int memPID;
-    int chunkCount;
-    size_t inuse;
-    size_t idle;
-    void *freeCache;
-    MemChunk *nextFreeChunk;
-    MemChunk *Chunks;
-    Splay<MemChunk *> allChunks;
-};
-
-/// \ingroup MemPoolsAPI
-class MemMalloc : public MemImplementingAllocator
-{
-public:
-    MemMalloc(char const *label, size_t aSize);
-    virtual bool idleTrigger(int shift) const;
-    virtual void clean(time_t maxage);
-
-    /**
-     \param stats      Object to be filled with statistical data about pool.
-     \retval           Number of objects in use, ie. allocated.
-     */
-    virtual int getStats(MemPoolStats * stats);
-
-    virtual int getInUseCount();
-protected:
-    virtual void *allocate();
-    virtual void deallocate(void *);
-private:
-    int inuse;
-};
-
-/// \ingroup MemPoolsAPI
-class MemChunk
-{
-public:
-    MemChunk(MemPool *pool);
-    ~MemChunk();
-    void *freeList;
-    void *objCache;
-    int inuse_count;
-    MemChunk *nextFreeChunk;
-    MemChunk *next;
-    time_t lastref;
-    MemPool *pool;
-};
-
 /// \ingroup MemPoolsAPI
 class MemPoolStats
 {
@@ -536,4 +439,4 @@ MemAllocatorProxy::MemAllocatorProxy(char const *aLabel, size_t const &aSize) :
 }
 
 
-#endif /* _MEM_POOLS_H_ */
+#endif /* _MEM_POOL_H_ */
diff --git a/include/MemPoolChunked.h b/include/MemPoolChunked.h
new file mode 100644 (file)
index 0000000..b3b82c0
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef _MEM_POOL_CHUNKED_H_
+#define _MEM_POOL_CHUNKED_H_
+
+#include "MemPool.h"
+
+/// \ingroup MemPoolsAPI
+#define MEM_PAGE_SIZE 4096
+/// \ingroup MemPoolsAPI
+#define MEM_CHUNK_SIZE 4096 * 4
+/// \ingroup MemPoolsAPI
+#define MEM_CHUNK_MAX_SIZE  256 * 1024 /* 2MB */
+/// \ingroup MemPoolsAPI
+#define MEM_MIN_FREE  32
+/// \ingroup MemPoolsAPI
+#define MEM_MAX_FREE  65535    /* ushort is max number of items per chunk */
+
+class MemChunk;
+
+/// \ingroup MemPoolsAPI
+class MemPoolChunked : public MemImplementingAllocator
+{
+public:
+    friend class MemChunk;
+    MemPoolChunked(const char *label, size_t obj_size);
+    ~MemPoolChunked();
+    void convertFreeCacheToChunkFreeCache();
+    virtual void clean(time_t maxage);
+
+    /**
+     \param stats      Object to be filled with statistical data about pool.
+     \retval           Number of objects in use, ie. allocated.
+     */
+    virtual int getStats(MemPoolStats * stats, int accumulate);
+
+    void createChunk();
+    void *get();
+    void push(void *obj);
+    virtual int getInUseCount();
+protected:
+    virtual void *allocate();
+    virtual void deallocate(void *);
+public:
+    /**
+     * Allows you tune chunk size of pooling. Objects are allocated in chunks
+     * instead of individually. This conserves memory, reduces fragmentation.
+     * Because of that memory can be freed also only in chunks. Therefore
+     * there is tradeoff between memory conservation due to chunking and free
+     * memory fragmentation.
+     *
+     \note  As a general guideline, increase chunk size only for pools that keep
+     *      very many items for relatively long time.
+     */
+    virtual void setChunkSize(size_t chunksize);
+
+    virtual bool idleTrigger(int shift) const;
+
+    size_t chunk_size;
+    int chunk_capacity;
+    int memPID;
+    int chunkCount;
+    void *freeCache;
+    MemChunk *nextFreeChunk;
+    MemChunk *Chunks;
+    Splay<MemChunk *> allChunks;
+};
+
+/// \ingroup MemPoolsAPI
+class MemChunk
+{
+public:
+    MemChunk(MemPoolChunked *pool);
+    ~MemChunk();
+    void *freeList;
+    void *objCache;
+    int inuse_count;
+    MemChunk *nextFreeChunk;
+    MemChunk *next;
+    time_t lastref;
+    MemPoolChunked *pool;
+};
+
+#endif /* _MEM_POOL_CHUNKED_H_ */
diff --git a/include/MemPoolMalloc.h b/include/MemPoolMalloc.h
new file mode 100644 (file)
index 0000000..cef967e
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef _MEM_POOL_MALLOC_H_
+#define _MEM_POOL_MALLOC_H_
+
+/**
+ \defgroup MemPoolsAPI  Memory Management (Memory Pool Allocator)
+ \ingroup Components
+ *
+ *\par
+ *  MemPools are a pooled memory allocator running on top of malloc(). It's
+ *  purpose is to reduce memory fragmentation and provide detailed statistics
+ *  on memory consumption.
+ *
+ \par
+ *  Preferably all memory allocations in Squid should be done using MemPools
+ *  or one of the types built on top of it (i.e. cbdata).
+ *
+ \note Usually it is better to use cbdata types as these gives you additional
+ *     safeguards in references and typechecking. However, for high usage pools where
+ *     the cbdata functionality of cbdata is not required directly using a MemPool
+ *     might be the way to go.
+ */
+
+#include "MemPool.h"
+
+/// \ingroup MemPoolsAPI
+class MemPoolMalloc : public MemImplementingAllocator
+{
+public:
+    MemPoolMalloc(char const *label, size_t aSize);
+    virtual bool idleTrigger(int shift) const;
+    virtual void clean(time_t maxage);
+
+    /**
+     \param stats      Object to be filled with statistical data about pool.
+     \retval           Number of objects in use, ie. allocated.
+     */
+    virtual int getStats(MemPoolStats * stats, int accumulate);
+
+    virtual int getInUseCount();
+protected:
+    virtual void *allocate();
+    virtual void deallocate(void *);
+private:
+};
+
+
+#endif /* _MEM_POOL_MALLOC_H_ */
index ce46223510f921627126247717c695ec85e1a03a..dc2119b5196b1869311cab51607bfec52121a4d2 100644 (file)
@@ -49,6 +49,8 @@ EXTRA_libmiscutil_a_SOURCES = \
        win32lib.c
 libmiscutil_a_SOURCES = \
        MemPool.cc \
+       MemPoolChunked.cc \
+       MemPoolMalloc.cc \
        base64.c \
        charset.c \
        getfullhostname.c \
index 3266bae8051ccbc82343b4b025342dfde3448831..183869527751a2417d96426f4a600ad9277a5e3f 100644 (file)
@@ -87,6 +87,8 @@
 #endif
 
 #include "MemPool.h"
+#include "MemPoolChunked.h"
+#include "MemPoolMalloc.h"
 
 #define FLUSH_LIMIT 1000       /* Flush memPool counters to memMeters after flush limit calls */
 #define MEM_MAX_MMAP_CHUNKS 2048
@@ -107,10 +109,6 @@ static MemPoolIterator Iterator;
 
 static int Pool_id_counter = 0;
 
-/* local prototypes */
-static int memCompChunks(MemChunk * const &, MemChunk * const &);
-static int memCompObjChunks(void * const &, MemChunk * const &);
-
 MemPools &
 MemPools::GetInstance()
 {
@@ -165,195 +163,6 @@ MemPools::idleLimit() const
     return mem_idle_limit;
 }
 
-/* Compare chunks */
-static int
-memCompChunks(MemChunk * const &chunkA, MemChunk * const &chunkB)
-{
-    if (chunkA->objCache > chunkB->objCache)
-        return 1;
-    else if (chunkA->objCache < chunkB->objCache)
-        return -1;
-    else
-        return 0;
-}
-
-/* Compare object to chunk */
-static int
-memCompObjChunks(void *const &obj, MemChunk * const &chunk)
-{
-    /* object is lower in memory than the chunks arena */
-    if (obj < chunk->objCache)
-        return -1;
-    /* object is within the pool */
-    if (obj < (void *) ((char *) chunk->objCache + chunk->pool->chunk_size))
-        return 0;
-    /* object is above the pool */
-    return 1;
-}
-
-MemChunk::MemChunk(MemPool *aPool)
-{
-    /* should have a pool for this too -
-     * note that this requres:
-     * allocate one chunk for the pool of chunks's first chunk
-     * allocate a chunk from that pool
-     * move the contents of one chunk into the other
-     * free the first chunk.
-     */
-    inuse_count = 0;
-    next = NULL;
-    pool = aPool;
-
-    objCache = xcalloc(1, pool->chunk_size);
-    freeList = objCache;
-    void **Free = (void **)freeList;
-
-    for (int i = 1; i < pool->chunk_capacity; i++) {
-        *Free = (void *) ((char *) Free + pool->obj_size);
-        void **nextFree = (void **)*Free;
-        (void) VALGRIND_MAKE_MEM_NOACCESS(Free, pool->obj_size);
-        Free = nextFree;
-    }
-    nextFreeChunk = pool->nextFreeChunk;
-    pool->nextFreeChunk = this;
-
-    memMeterAdd(pool->getMeter().alloc, pool->chunk_capacity);
-    memMeterAdd(pool->getMeter().idle, pool->chunk_capacity);
-    pool->idle += pool->chunk_capacity;
-    pool->chunkCount++;
-    lastref = squid_curtime;
-    pool->allChunks.insert(this, memCompChunks);
-}
-
-MemPool::MemPool(const char *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize)
-{
-    chunk_size = 0;
-    chunk_capacity = 0;
-    memPID = 0;
-    chunkCount = 0;
-    inuse = 0;
-    idle = 0;
-    freeCache = 0;
-    nextFreeChunk = 0;
-    Chunks = 0;
-    next = 0;
-    MemImplementingAllocator *last_pool;
-
-    assert(aLabel != NULL && aSize);
-
-    setChunkSize(MEM_CHUNK_SIZE);
-
-    /* Append as Last */
-    for (last_pool = MemPools::GetInstance().pools; last_pool && last_pool->next;)
-        last_pool = last_pool->next;
-    if (last_pool)
-        last_pool->next = this;
-    else
-        MemPools::GetInstance().pools = this;
-
-    memPID = ++Pool_id_counter;
-}
-
-MemChunk::~MemChunk()
-{
-    memMeterDel(pool->getMeter().alloc, pool->chunk_capacity);
-    memMeterDel(pool->getMeter().idle, pool->chunk_capacity);
-    pool->idle -= pool->chunk_capacity;
-    pool->chunkCount--;
-    pool->allChunks.remove(this, memCompChunks);
-    xfree(objCache);
-}
-
-void
-MemPool::push(void *obj)
-{
-    void **Free;
-    /* XXX We should figure out a sane way of avoiding having to clear
-     * all buffers. For example data buffers such as used by MemBuf do
-     * not really need to be cleared.. There was a condition based on
-     * the object size here, but such condition is not safe.
-     */
-    if (doZeroOnPush)
-        memset(obj, 0, obj_size);
-    Free = (void **)obj;
-    *Free = freeCache;
-    freeCache = obj;
-    (void) VALGRIND_MAKE_MEM_NOACCESS(obj, obj_size);
-}
-
-/*
- * Find a chunk with a free item.
- * Create new chunk on demand if no chunk with frees found.
- * Insert new chunk in front of lowest ram chunk, making it preferred in future,
- * and resulting slow compaction towards lowest ram area.
- */
-void *
-MemPool::get()
-{
-    void **Free;
-
-    /* first, try cache */
-    if (freeCache) {
-        Free = (void **)freeCache;
-        (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size);
-        freeCache = *Free;
-        *Free = NULL;
-        return Free;
-    }
-    /* then try perchunk freelist chain */
-    if (nextFreeChunk == NULL) {
-        /* no chunk with frees, so create new one */
-        createChunk();
-    }
-    /* now we have some in perchunk freelist chain */
-    MemChunk *chunk = nextFreeChunk;
-
-    Free = (void **)chunk->freeList;
-    chunk->freeList = *Free;
-    *Free = NULL;
-    chunk->inuse_count++;
-    chunk->lastref = squid_curtime;
-
-    if (chunk->freeList == NULL) {
-        /* last free in this chunk, so remove us from perchunk freelist chain */
-        nextFreeChunk = chunk->nextFreeChunk;
-    }
-    (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size);
-    return Free;
-}
-
-/* just create a new chunk and place it into a good spot in the chunk chain */
-void
-MemPool::createChunk()
-{
-    MemChunk *chunk, *newChunk;
-
-    newChunk = new MemChunk(this);
-
-    chunk = Chunks;
-    if (chunk == NULL) {       /* first chunk in pool */
-        Chunks = newChunk;
-        return;
-    }
-    if (newChunk->objCache < chunk->objCache) {
-        /* we are lowest ram chunk, insert as first chunk */
-        newChunk->next = chunk;
-        Chunks = newChunk;
-        return;
-    }
-    while (chunk->next) {
-        if (newChunk->objCache < chunk->next->objCache) {
-            /* new chunk is in lower ram, insert here */
-            newChunk->next = chunk->next;
-            chunk->next = newChunk;
-            return;
-        }
-        chunk = chunk->next;
-    }
-    /* we are the worst chunk in chain, add as last */
-    chunk->next = newChunk;
-}
-
 /* Change the default calue of defaultIsChunked to override
  * all pools - including those used before main() starts where
  * MemPools::GetInstance().setDefaultPoolChunking() can be called.
@@ -369,49 +178,14 @@ MemPools::MemPools() : pools(NULL), mem_idle_limit(2 * MB),
 #endif
 }
 
-void
-MemPool::setChunkSize(size_t chunksize)
-{
-    int cap;
-    size_t csize = chunksize;
-
-    if (Chunks)                /* unsafe to tamper */
-        return;
-
-    csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE;     /* round up to page size */
-    cap = csize / obj_size;
-
-    if (cap < MEM_MIN_FREE)
-        cap = MEM_MIN_FREE;
-    if (cap * obj_size > MEM_CHUNK_MAX_SIZE)
-        cap = MEM_CHUNK_MAX_SIZE / obj_size;
-    if (cap > MEM_MAX_FREE)
-        cap = MEM_MAX_FREE;
-    if (cap < 1)
-        cap = 1;
-
-    csize = cap * obj_size;
-    csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE;     /* round up to page size */
-    cap = csize / obj_size;
-
-    chunk_capacity = cap;
-    chunk_size = csize;
-}
-
 MemImplementingAllocator *
 MemPools::create(const char *label, size_t obj_size)
-{
-    return create (label, obj_size, defaultIsChunked);
-}
-
-MemImplementingAllocator *
-MemPools::create(const char *label, size_t obj_size, bool const chunked)
 {
     ++poolCount;
-    if (chunked)
-        return new MemPool (label, obj_size);
+    if (defaultIsChunked)
+        return new MemPoolChunked (label, obj_size);
     else
-        return new MemMalloc (label, obj_size);
+        return new MemPoolMalloc (label, obj_size);
 }
 
 void
@@ -420,40 +194,6 @@ MemPools::setDefaultPoolChunking(bool const &aBool)
     defaultIsChunked = aBool;
 }
 
-/*
- * warning: we do not clean this entry from Pools assuming destruction
- * is used at the end of the program only
- */
-MemPool::~MemPool()
-{
-    MemChunk *chunk, *fchunk;
-    MemImplementingAllocator *find_pool, *prev_pool;
-
-    flushMetersFull();
-    clean(0);
-    assert(inuse == 0 && "While trying to destroy pool");
-
-    chunk = Chunks;
-    while ( (fchunk = chunk) != NULL) {
-        chunk = chunk->next;
-        delete fchunk;
-    }
-    /* TODO we should be doing something about the original Chunks pointer here. */
-
-    assert(MemPools::GetInstance().pools != NULL && "Called MemPool::~MemPool, but no pool exists!");
-
-    /* Pool clean, remove it from List and free */
-    for (find_pool = MemPools::GetInstance().pools, prev_pool = NULL; (find_pool && this != find_pool); find_pool = find_pool->next)
-        prev_pool = find_pool;
-    assert(find_pool != NULL && "pool to destroy not found");
-
-    if (prev_pool)
-        prev_pool->next = next;
-    else
-        MemPools::GetInstance().pools = next;
-    --MemPools::GetInstance().poolCount;
-}
-
 char const *
 MemAllocator::objectType() const
 {
@@ -473,16 +213,12 @@ MemImplementingAllocator::flushMeters()
 
     calls = free_calls;
     if (calls) {
-        getMeter().gb_freed.count += calls;
-        memMeterDel(getMeter().inuse, calls);
-        memMeterAdd(getMeter().idle, calls);
+        meter.gb_freed.count += calls;
         free_calls = 0;
     }
     calls = alloc_calls;
     if (calls) {
         meter.gb_saved.count += calls;
-        memMeterAdd(meter.inuse, calls);
-        memMeterDel(meter.idle, calls);
         alloc_calls = 0;
     }
 }
@@ -531,20 +267,6 @@ MemPools::flushMeters()
     memPoolIterateDone(&iter);
 }
 
-void *
-MemMalloc::allocate()
-{
-    inuse++;
-    return xcalloc(1, obj_size);
-}
-
-void
-MemMalloc::deallocate(void *obj)
-{
-    inuse--;
-    xfree(obj);
-}
-
 void *
 MemImplementingAllocator::alloc()
 {
@@ -563,123 +285,6 @@ MemImplementingAllocator::freeOne(void *obj)
     ++free_calls;
 }
 
-int
-MemPool::getInUseCount()
-{
-    return inuse;
-}
-
-void *
-MemPool::allocate()
-{
-    void *p = get();
-    assert(idle);
-    --idle;
-    ++inuse;
-    return p;
-}
-
-void
-MemPool::deallocate(void *obj)
-{
-    push(obj);
-    assert(inuse);
-    --inuse;
-    ++idle;
-}
-
-void
-MemPool::convertFreeCacheToChunkFreeCache()
-{
-    void *Free;
-    /*
-     * OK, so we have to go through all the global freeCache and find the Chunk
-     * any given Free belongs to, and stuff it into that Chunk's freelist
-     */
-
-    while ((Free = freeCache) != NULL) {
-        MemChunk *chunk = NULL;
-        chunk = const_cast<MemChunk *>(*allChunks.find(Free, memCompObjChunks));
-        assert(splayLastResult == 0);
-        assert(chunk->inuse_count > 0);
-        chunk->inuse_count--;
-        (void) VALGRIND_MAKE_MEM_DEFINED(Free, sizeof(void *));
-        freeCache = *(void **)Free;    /* remove from global cache */
-        *(void **)Free = chunk->freeList;      /* stuff into chunks freelist */
-        (void) VALGRIND_MAKE_MEM_NOACCESS(Free, sizeof(void *));
-        chunk->freeList = Free;
-        chunk->lastref = squid_curtime;
-    }
-
-}
-
-/* removes empty Chunks from pool */
-void
-MemPool::clean(time_t maxage)
-{
-    MemChunk *chunk, *freechunk, *listTail;
-    time_t age;
-
-    if (!this)
-        return;
-    if (!Chunks)
-        return;
-
-    flushMetersFull();
-    convertFreeCacheToChunkFreeCache();
-    /* Now we have all chunks in this pool cleared up, all free items returned to their home */
-    /* We start now checking all chunks to see if we can release any */
-    /* We start from Chunks->next, so first chunk is not released */
-    /* Recreate nextFreeChunk list from scratch */
-
-    chunk = Chunks;
-    while ((freechunk = chunk->next) != NULL) {
-        age = squid_curtime - freechunk->lastref;
-        freechunk->nextFreeChunk = NULL;
-        if (freechunk->inuse_count == 0)
-            if (age >= maxage) {
-                chunk->next = freechunk->next;
-                delete freechunk;
-                freechunk = NULL;
-            }
-        if (chunk->next == NULL)
-            break;
-        chunk = chunk->next;
-    }
-
-    /* Recreate nextFreeChunk list from scratch */
-    /* Populate nextFreeChunk list in order of "most filled chunk first" */
-    /* in case of equal fill, put chunk in lower ram first */
-    /* First (create time) chunk is always on top, no matter how full */
-
-    chunk = Chunks;
-    nextFreeChunk = chunk;
-    chunk->nextFreeChunk = NULL;
-
-    while (chunk->next) {
-        chunk->next->nextFreeChunk = NULL;
-        if (chunk->next->inuse_count < chunk_capacity) {
-            listTail = nextFreeChunk;
-            while (listTail->nextFreeChunk) {
-                if (chunk->next->inuse_count > listTail->nextFreeChunk->inuse_count)
-                    break;
-                if ((chunk->next->inuse_count == listTail->nextFreeChunk->inuse_count) &&
-                        (chunk->next->objCache < listTail->nextFreeChunk->objCache))
-                    break;
-                listTail = listTail->nextFreeChunk;
-            }
-            chunk->next->nextFreeChunk = listTail->nextFreeChunk;
-            listTail->nextFreeChunk = chunk->next;
-        }
-        chunk = chunk->next;
-    }
-    /* We started from 2nd chunk. If first chunk is full, remove it */
-    if (nextFreeChunk->inuse_count == chunk_capacity)
-        nextFreeChunk = nextFreeChunk->nextFreeChunk;
-
-    return;
-}
-
 /*
  * Returns all cached frees to their home chunks
  * If chunks unreferenced age is over, destroys Idle chunk
@@ -707,95 +312,9 @@ MemPools::clean(time_t maxage)
     memPoolIterateDone(&iter);
 }
 
-bool
-MemPool::idleTrigger(int shift) const
-{
-    return getMeter().idle.level > (chunk_capacity << shift);
-}
-
 /* Persistent Pool stats. for GlobalStats accumulation */
 static MemPoolStats pp_stats;
 
-/*
- * Update MemPoolStats struct for single pool
- */
-int
-MemPool::getStats(MemPoolStats * stats)
-{
-    MemChunk *chunk;
-    int chunks_free = 0;
-    int chunks_partial = 0;
-
-    if (stats != &pp_stats)    /* need skip memset for GlobalStats accumulation */
-        /* XXX Fixme */
-        memset(stats, 0, sizeof(MemPoolStats));
-
-    clean((time_t) 555555);    /* don't want to get chunks released before reporting */
-
-    stats->pool = this;
-    stats->label = objectType();
-    stats->meter = &getMeter();
-    stats->obj_size = obj_size;
-    stats->chunk_capacity = chunk_capacity;
-
-    /* gather stats for each Chunk */
-    chunk = Chunks;
-    while (chunk) {
-        if (chunk->inuse_count == 0)
-            chunks_free++;
-        else if (chunk->inuse_count < chunk_capacity)
-            chunks_partial++;
-        chunk = chunk->next;
-    }
-
-    stats->chunks_alloc += chunkCount;
-    stats->chunks_inuse += chunkCount - chunks_free;
-    stats->chunks_partial += chunks_partial;
-    stats->chunks_free += chunks_free;
-
-    stats->items_alloc += getMeter().alloc.level;
-    stats->items_inuse += getMeter().inuse.level;
-    stats->items_idle += getMeter().idle.level;
-
-    stats->overhead += sizeof(MemPool) + chunkCount * sizeof(MemChunk) + strlen(objectType()) + 1;
-
-    return getMeter().inuse.level;
-}
-
-/* TODO extract common logic to MemAllocate */
-int
-MemMalloc::getStats(MemPoolStats * stats)
-{
-    if (stats != &pp_stats)    /* need skip memset for GlobalStats accumulation */
-        /* XXX Fixme */
-        memset(stats, 0, sizeof(MemPoolStats));
-
-    stats->pool = this;
-    stats->label = objectType();
-    stats->meter = &getMeter();
-    stats->obj_size = obj_size;
-    stats->chunk_capacity = 0;
-
-    stats->chunks_alloc += 0;
-    stats->chunks_inuse += 0;
-    stats->chunks_partial += 0;
-    stats->chunks_free += 0;
-
-    stats->items_alloc += getMeter().alloc.level;
-    stats->items_inuse += getMeter().inuse.level;
-    stats->items_idle += getMeter().idle.level;
-
-    stats->overhead += sizeof(MemMalloc) + strlen(objectType()) + 1;
-
-    return getMeter().inuse.level;
-}
-
-int
-MemMalloc::getInUseCount()
-{
-    return inuse;
-}
-
 /*
  * Totals statistics is returned
  */
@@ -814,7 +333,7 @@ memPoolGetGlobalStats(MemPoolGlobalStats * stats)
     /* gather all stats for Totals */
     iter = memPoolIterate();
     while ((pool = memPoolIterateNext(iter))) {
-        if (pool->getStats(&pp_stats) > 0)
+        if (pool->getStats(&pp_stats, 1) > 0)
             pools_inuse++;
     }
     memPoolIterateDone(&iter);
@@ -833,7 +352,7 @@ memPoolGetGlobalStats(MemPoolGlobalStats * stats)
     stats->tot_items_inuse = pp_stats.items_inuse;
     stats->tot_items_idle = pp_stats.items_idle;
 
-    stats->tot_overhead += pp_stats.overhead + MemPools::GetInstance().poolCount * sizeof(MemPool *);
+    stats->tot_overhead += pp_stats.overhead + MemPools::GetInstance().poolCount * sizeof(MemAllocator *);
     stats->mem_idle_limit = MemPools::GetInstance().mem_idle_limit;
 
     return pools_inuse;
@@ -848,19 +367,6 @@ size_t MemAllocator::RoundedSize(size_t s)
     return ((s + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*);
 }
 
-MemMalloc::MemMalloc(char const *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize) { inuse = 0; }
-
-bool
-MemMalloc::idleTrigger(int shift) const
-{
-    return false;
-}
-
-void
-MemMalloc::clean(time_t maxage)
-{
-}
-
 int
 memPoolInUseCount(MemAllocator * pool)
 {
@@ -937,6 +443,7 @@ MemImplementingAllocator::MemImplementingAllocator(char const *aLabel, size_t aS
         free_calls(0),
         obj_size(RoundedSize(aSize))
 {
+       memPID = ++Pool_id_counter;
 }
 
 void
diff --git a/lib/MemPoolChunked.cc b/lib/MemPoolChunked.cc
new file mode 100644 (file)
index 0000000..5768f0b
--- /dev/null
@@ -0,0 +1,519 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 63    Low Level Memory Pool Management
+ * AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  the Regents of the University of California.  Please see the
+ *  COPYRIGHT file for full details.  Squid incorporates software
+ *  developed and/or copyrighted by other sources.  Please see the
+ *  CREDITS file for full details.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+/*
+ * Old way:
+ *   xmalloc each item separately, upon free stack into idle pool array.
+ *   each item is individually malloc()ed from system, imposing libmalloc
+ *   overhead, and additionally we add our overhead of pointer size per item
+ *   as we keep a list of pointer to free items.
+ *
+ * Chunking:
+ *   xmalloc Chunk that fits at least MEM_MIN_FREE (32) items in an array, but
+ *   limit Chunk size to MEM_CHUNK_MAX_SIZE (256K). Chunk size is rounded up to
+ *   MEM_PAGE_SIZE (4K), trying to have chunks in multiples of VM_PAGE size.
+ *   Minimum Chunk size is MEM_CHUNK_SIZE (16K).
+ *   A number of items fits into a single chunk, depending on item size.
+ *   Maximum number of items per chunk is limited to MEM_MAX_FREE (65535).
+ *
+ *   We populate Chunk with a linkedlist, each node at first word of item,
+ *   and pointing at next free item. Chunk->FreeList is pointing at first
+ *   free node. Thus we stuff free housekeeping into the Chunk itself, and
+ *   omit pointer overhead per item.
+ *
+ *   Chunks are created on demand, and new chunks are inserted into linklist
+ *   of chunks so that Chunks with smaller pointer value are placed closer
+ *   to the linklist head. Head is a hotspot, servicing most of requests, so
+ *   slow sorting occurs and Chunks in highest memory tend to become idle
+ *   and freeable.
+ *
+ *   event is registered that runs every 15 secs and checks reference time
+ *   of each idle chunk. If a chunk is not referenced for 15 secs, it is
+ *   released.
+ *
+ *   [If mem_idle_limit is exceeded with pools, every chunk that becomes
+ *   idle is immediately considered for release, unless this is the only
+ *   chunk with free items in it.] (not implemented)
+ *
+ *   In cachemgr output, there are new columns for chunking. Special item,
+ *   Frag, is shown to estimate approximately fragmentation of chunked
+ *   pools. Fragmentation is calculated by taking amount of items in use,
+ *   calculating needed amount of chunks to fit all, and then comparing to
+ *   actual amount of chunks in use. Frag number, in percent, is showing
+ *   how many percent of chunks are in use excessively. 100% meaning that
+ *   twice the needed amount of chunks are in use.
+ *   "part" item shows number of chunks partially filled. This shows how
+ *   badly fragmentation is spread across all chunks.
+ *
+ *   Andres Kroonmaa.
+ *   Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
+ */
+
+#include "config.h"
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include "MemPoolChunked.h"
+
+#define FLUSH_LIMIT 1000       /* Flush memPool counters to memMeters after flush limit calls */
+#define MEM_MAX_MMAP_CHUNKS 2048
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+/*
+ * XXX This is a boundary violation between lib and src.. would be good
+ * if it could be solved otherwise, but left for now.
+ */
+extern time_t squid_curtime;
+
+/* local prototypes */
+static int memCompChunks(MemChunk * const &, MemChunk * const &);
+static int memCompObjChunks(void * const &, MemChunk * const &);
+
+/* Compare chunks */
+static int
+memCompChunks(MemChunk * const &chunkA, MemChunk * const &chunkB)
+{
+    if (chunkA->objCache > chunkB->objCache)
+        return 1;
+    else if (chunkA->objCache < chunkB->objCache)
+        return -1;
+    else
+        return 0;
+}
+
+/* Compare object to chunk */
+static int
+memCompObjChunks(void *const &obj, MemChunk * const &chunk)
+{
+    /* object is lower in memory than the chunks arena */
+    if (obj < chunk->objCache)
+        return -1;
+    /* object is within the pool */
+    if (obj < (void *) ((char *) chunk->objCache + chunk->pool->chunk_size))
+        return 0;
+    /* object is above the pool */
+    return 1;
+}
+
+MemChunk::MemChunk(MemPoolChunked *aPool)
+{
+    /* should have a pool for this too -
+     * note that this requres:
+     * allocate one chunk for the pool of chunks's first chunk
+     * allocate a chunk from that pool
+     * move the contents of one chunk into the other
+     * free the first chunk.
+     */
+    inuse_count = 0;
+    next = NULL;
+    pool = aPool;
+
+    objCache = xcalloc(1, pool->chunk_size);
+    freeList = objCache;
+    void **Free = (void **)freeList;
+
+    for (int i = 1; i < pool->chunk_capacity; i++) {
+        *Free = (void *) ((char *) Free + pool->obj_size);
+        void **nextFree = (void **)*Free;
+        (void) VALGRIND_MAKE_MEM_NOACCESS(Free, pool->obj_size);
+        Free = nextFree;
+    }
+    nextFreeChunk = pool->nextFreeChunk;
+    pool->nextFreeChunk = this;
+
+    memMeterAdd(pool->getMeter().alloc, pool->chunk_capacity);
+    memMeterAdd(pool->getMeter().idle, pool->chunk_capacity);
+    pool->chunkCount++;
+    lastref = squid_curtime;
+    pool->allChunks.insert(this, memCompChunks);
+}
+
+MemPoolChunked::MemPoolChunked(const char *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize)
+{
+    chunk_size = 0;
+    chunk_capacity = 0;
+    chunkCount = 0;
+    freeCache = 0;
+    nextFreeChunk = 0;
+    Chunks = 0;
+    next = 0;
+    MemImplementingAllocator *last_pool;
+
+    assert(aLabel != NULL && aSize);
+
+    setChunkSize(MEM_CHUNK_SIZE);
+
+    /* Append as Last */
+    for (last_pool = MemPools::GetInstance().pools; last_pool && last_pool->next;)
+        last_pool = last_pool->next;
+    if (last_pool)
+        last_pool->next = this;
+    else
+        MemPools::GetInstance().pools = this;
+}
+
+MemChunk::~MemChunk()
+{
+    memMeterDel(pool->getMeter().alloc, pool->chunk_capacity);
+    memMeterDel(pool->getMeter().idle, pool->chunk_capacity);
+    pool->chunkCount--;
+    pool->allChunks.remove(this, memCompChunks);
+    xfree(objCache);
+}
+
+void
+MemPoolChunked::push(void *obj)
+{
+    void **Free;
+    /* XXX We should figure out a sane way of avoiding having to clear
+     * all buffers. For example data buffers such as used by MemBuf do
+     * not really need to be cleared.. There was a condition based on
+     * the object size here, but such condition is not safe.
+     */
+    if (doZeroOnPush)
+        memset(obj, 0, obj_size);
+    Free = (void **)obj;
+    *Free = freeCache;
+    freeCache = obj;
+    (void) VALGRIND_MAKE_MEM_NOACCESS(obj, obj_size);
+}
+
+/*
+ * Find a chunk with a free item.
+ * Create new chunk on demand if no chunk with frees found.
+ * Insert new chunk in front of lowest ram chunk, making it preferred in future,
+ * and resulting slow compaction towards lowest ram area.
+ */
+void *
+MemPoolChunked::get()
+{
+    void **Free;
+
+    /* first, try cache */
+    if (freeCache) {
+        Free = (void **)freeCache;
+        (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size);
+        freeCache = *Free;
+        *Free = NULL;
+        return Free;
+    }
+    /* then try perchunk freelist chain */
+    if (nextFreeChunk == NULL) {
+        /* no chunk with frees, so create new one */
+        createChunk();
+    }
+    /* now we have some in perchunk freelist chain */
+    MemChunk *chunk = nextFreeChunk;
+
+    Free = (void **)chunk->freeList;
+    chunk->freeList = *Free;
+    *Free = NULL;
+    chunk->inuse_count++;
+    chunk->lastref = squid_curtime;
+
+    if (chunk->freeList == NULL) {
+        /* last free in this chunk, so remove us from perchunk freelist chain */
+        nextFreeChunk = chunk->nextFreeChunk;
+    }
+    (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size);
+    return Free;
+}
+
+/* just create a new chunk and place it into a good spot in the chunk chain */
+void
+MemPoolChunked::createChunk()
+{
+    MemChunk *chunk, *newChunk;
+
+    newChunk = new MemChunk(this);
+
+    chunk = Chunks;
+    if (chunk == NULL) {       /* first chunk in pool */
+        Chunks = newChunk;
+        return;
+    }
+    if (newChunk->objCache < chunk->objCache) {
+        /* we are lowest ram chunk, insert as first chunk */
+        newChunk->next = chunk;
+        Chunks = newChunk;
+        return;
+    }
+    while (chunk->next) {
+        if (newChunk->objCache < chunk->next->objCache) {
+            /* new chunk is in lower ram, insert here */
+            newChunk->next = chunk->next;
+            chunk->next = newChunk;
+            return;
+        }
+        chunk = chunk->next;
+    }
+    /* we are the worst chunk in chain, add as last */
+    chunk->next = newChunk;
+}
+
+void
+MemPoolChunked::setChunkSize(size_t chunksize)
+{
+    int cap;
+    size_t csize = chunksize;
+
+    if (Chunks)                /* unsafe to tamper */
+        return;
+
+    csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE;     /* round up to page size */
+    cap = csize / obj_size;
+
+    if (cap < MEM_MIN_FREE)
+        cap = MEM_MIN_FREE;
+    if (cap * obj_size > MEM_CHUNK_MAX_SIZE)
+        cap = MEM_CHUNK_MAX_SIZE / obj_size;
+    if (cap > MEM_MAX_FREE)
+        cap = MEM_MAX_FREE;
+    if (cap < 1)
+        cap = 1;
+
+    csize = cap * obj_size;
+    csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE;     /* round up to page size */
+    cap = csize / obj_size;
+
+    chunk_capacity = cap;
+    chunk_size = csize;
+}
+
+/*
+ * warning: we do not clean this entry from Pools assuming destruction
+ * is used at the end of the program only
+ */
+MemPoolChunked::~MemPoolChunked()
+{
+    MemChunk *chunk, *fchunk;
+    MemImplementingAllocator *find_pool, *prev_pool;
+
+    flushMetersFull();
+    clean(0);
+    assert(getMeter().inuse.level == 0 && "While trying to destroy pool");
+
+    chunk = Chunks;
+    while ( (fchunk = chunk) != NULL) {
+        chunk = chunk->next;
+        delete fchunk;
+    }
+    /* TODO we should be doing something about the original Chunks pointer here. */
+
+    assert(MemPools::GetInstance().pools != NULL && "Called MemPoolChunked::~MemPoolChunked, but no pool exists!");
+
+    /* Pool clean, remove it from List and free */
+    for (find_pool = MemPools::GetInstance().pools, prev_pool = NULL; (find_pool && this != find_pool); find_pool = find_pool->next)
+        prev_pool = find_pool;
+    assert(find_pool != NULL && "pool to destroy not found");
+
+    if (prev_pool)
+        prev_pool->next = next;
+    else
+        MemPools::GetInstance().pools = next;
+    --MemPools::GetInstance().poolCount;
+}
+
+int
+MemPoolChunked::getInUseCount()
+{
+    return getMeter().inuse.level;
+}
+
+void *
+MemPoolChunked::allocate()
+{
+    void *p = get();
+    assert(meter.idle.level > 0);
+    memMeterDec(meter.idle);
+    memMeterInc(meter.inuse);
+    return p;
+}
+
+void
+MemPoolChunked::deallocate(void *obj)
+{
+    push(obj);
+    assert(meter.inuse.level > 0);
+    memMeterDec(meter.inuse);
+    memMeterInc(meter.idle);
+}
+
+void
+MemPoolChunked::convertFreeCacheToChunkFreeCache()
+{
+    void *Free;
+    /*
+     * OK, so we have to go through all the global freeCache and find the Chunk
+     * any given Free belongs to, and stuff it into that Chunk's freelist
+     */
+
+    while ((Free = freeCache) != NULL) {
+        MemChunk *chunk = NULL;
+        chunk = const_cast<MemChunk *>(*allChunks.find(Free, memCompObjChunks));
+        assert(splayLastResult == 0);
+        assert(chunk->inuse_count > 0);
+        chunk->inuse_count--;
+        (void) VALGRIND_MAKE_MEM_DEFINED(Free, sizeof(void *));
+        freeCache = *(void **)Free;    /* remove from global cache */
+        *(void **)Free = chunk->freeList;      /* stuff into chunks freelist */
+        (void) VALGRIND_MAKE_MEM_NOACCESS(Free, sizeof(void *));
+        chunk->freeList = Free;
+        chunk->lastref = squid_curtime;
+    }
+
+}
+
+/* removes empty Chunks from pool */
+void
+MemPoolChunked::clean(time_t maxage)
+{
+    MemChunk *chunk, *freechunk, *listTail;
+    time_t age;
+
+    if (!this)
+        return;
+    if (!Chunks)
+        return;
+
+    flushMetersFull();
+    convertFreeCacheToChunkFreeCache();
+    /* Now we have all chunks in this pool cleared up, all free items returned to their home */
+    /* We start now checking all chunks to see if we can release any */
+    /* We start from Chunks->next, so first chunk is not released */
+    /* Recreate nextFreeChunk list from scratch */
+
+    chunk = Chunks;
+    while ((freechunk = chunk->next) != NULL) {
+        age = squid_curtime - freechunk->lastref;
+        freechunk->nextFreeChunk = NULL;
+        if (freechunk->inuse_count == 0)
+            if (age >= maxage) {
+                chunk->next = freechunk->next;
+                delete freechunk;
+                freechunk = NULL;
+            }
+        if (chunk->next == NULL)
+            break;
+        chunk = chunk->next;
+    }
+
+    /* Recreate nextFreeChunk list from scratch */
+    /* Populate nextFreeChunk list in order of "most filled chunk first" */
+    /* in case of equal fill, put chunk in lower ram first */
+    /* First (create time) chunk is always on top, no matter how full */
+
+    chunk = Chunks;
+    nextFreeChunk = chunk;
+    chunk->nextFreeChunk = NULL;
+
+    while (chunk->next) {
+        chunk->next->nextFreeChunk = NULL;
+        if (chunk->next->inuse_count < chunk_capacity) {
+            listTail = nextFreeChunk;
+            while (listTail->nextFreeChunk) {
+                if (chunk->next->inuse_count > listTail->nextFreeChunk->inuse_count)
+                    break;
+                if ((chunk->next->inuse_count == listTail->nextFreeChunk->inuse_count) &&
+                        (chunk->next->objCache < listTail->nextFreeChunk->objCache))
+                    break;
+                listTail = listTail->nextFreeChunk;
+            }
+            chunk->next->nextFreeChunk = listTail->nextFreeChunk;
+            listTail->nextFreeChunk = chunk->next;
+        }
+        chunk = chunk->next;
+    }
+    /* We started from 2nd chunk. If first chunk is full, remove it */
+    if (nextFreeChunk->inuse_count == chunk_capacity)
+        nextFreeChunk = nextFreeChunk->nextFreeChunk;
+
+    return;
+}
+
+bool
+MemPoolChunked::idleTrigger(int shift) const
+{
+    return getMeter().idle.level > (chunk_capacity << shift);
+}
+
+/*
+ * Update MemPoolStats struct for single pool
+ */
+int
+MemPoolChunked::getStats(MemPoolStats * stats, int accumulate)
+{
+    MemChunk *chunk;
+    int chunks_free = 0;
+    int chunks_partial = 0;
+
+    if (!accumulate)   /* need skip memset for GlobalStats accumulation */
+        memset(stats, 0, sizeof(MemPoolStats));
+
+    clean((time_t) 555555);    /* don't want to get chunks released before reporting */
+
+    stats->pool = this;
+    stats->label = objectType();
+    stats->meter = &getMeter();
+    stats->obj_size = obj_size;
+    stats->chunk_capacity = chunk_capacity;
+
+    /* gather stats for each Chunk */
+    chunk = Chunks;
+    while (chunk) {
+        if (chunk->inuse_count == 0)
+            chunks_free++;
+        else if (chunk->inuse_count < chunk_capacity)
+            chunks_partial++;
+        chunk = chunk->next;
+    }
+
+    stats->chunks_alloc += chunkCount;
+    stats->chunks_inuse += chunkCount - chunks_free;
+    stats->chunks_partial += chunks_partial;
+    stats->chunks_free += chunks_free;
+
+    stats->items_alloc += getMeter().alloc.level;
+    stats->items_inuse += getMeter().inuse.level;
+    stats->items_idle += getMeter().idle.level;
+
+    stats->overhead += sizeof(MemPoolChunked) + chunkCount * sizeof(MemChunk) + strlen(objectType()) + 1;
+
+    return getMeter().inuse.level;
+}
diff --git a/lib/MemPoolMalloc.cc b/lib/MemPoolMalloc.cc
new file mode 100644 (file)
index 0000000..fbb1999
--- /dev/null
@@ -0,0 +1,117 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 63    Low Level Memory Pool Management
+ * AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins
+ *
+ * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from the
+ *  Internet community.  Development is led by Duane Wessels of the
+ *  National Laboratory for Applied Network Research and funded by the
+ *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
+ *  the Regents of the University of California.  Please see the
+ *  COPYRIGHT file for full details.  Squid incorporates software
+ *  developed and/or copyrighted by other sources.  Please see the
+ *  CREDITS file for full details.
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+
+#include "config.h"
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include "MemPoolMalloc.h"
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+/*
+ * XXX This is a boundary violation between lib and src.. would be good
+ * if it could be solved otherwise, but left for now.
+ */
+extern time_t squid_curtime;
+
+void *
+MemPoolMalloc::allocate()
+{
+    memMeterInc(meter.alloc);
+    memMeterInc(meter.inuse);
+    return xcalloc(1, obj_size);
+}
+
+void
+MemPoolMalloc::deallocate(void *obj)
+{
+    memMeterDec(meter.inuse);
+    memMeterDec(meter.alloc);
+    xfree(obj);
+}
+
+/* TODO extract common logic to MemAllocate */
+int
+MemPoolMalloc::getStats(MemPoolStats * stats, int accumulate)
+{
+    if (!accumulate)   /* need skip memset for GlobalStats accumulation */
+        memset(stats, 0, sizeof(MemPoolStats));
+
+    stats->pool = this;
+    stats->label = objectType();
+    stats->meter = &getMeter();
+    stats->obj_size = obj_size;
+    stats->chunk_capacity = 0;
+
+    stats->chunks_alloc += 0;
+    stats->chunks_inuse += 0;
+    stats->chunks_partial += 0;
+    stats->chunks_free += 0;
+
+    stats->items_alloc += meter.alloc.level;
+    stats->items_inuse += meter.inuse.level;
+    stats->items_idle += meter.idle.level;
+
+    stats->overhead += sizeof(MemPoolMalloc) + strlen(objectType()) + 1;
+
+    return meter.inuse.level;
+}
+
+int
+MemPoolMalloc::getInUseCount()
+{
+    return meter.inuse.level;
+}
+
+MemPoolMalloc::MemPoolMalloc(char const *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize)
+{
+}
+
+bool
+MemPoolMalloc::idleTrigger(int shift) const
+{
+    return false;
+}
+
+void
+MemPoolMalloc::clean(time_t maxage)
+{
+}
+