# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: henrik@henriknordstrom.net-20100529010638-\
#   2cuq58qvx58gf5ej
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk/
# testament_sha1: 75a36627905a58be51174b7e0929dca3a85d4921
# timestamp: 2010-05-29 03:07:21 +0200
# base_revision_id: henrik@henriknordstrom.net-20100528215627-\
#   agkq7l8rn7lertwg
# 
# Begin patch
=== modified file 'include/MemPool.h'
--- include/MemPool.h	2010-03-21 03:08:26 +0000
+++ include/MemPool.h	2010-05-29 00:59:35 +0000
@@ -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
@@ -97,17 +96,20 @@
 class MemPoolMeter
 {
 public:
+    MemPoolMeter();
     void flush();
     MemMeter alloc;
     MemMeter inuse;
     MemMeter idle;
 
-    /** account Allocations */
+
+    /** history Allocations */
+    mgb_t gb_allocated;
+    mgb_t gb_oallocated;
+
+    /** account Saved Allocations */
     mgb_t gb_saved;
 
-    /** history Allocations */
-    mgb_t gb_osaved;
-
     /** account Free calls */
     mgb_t gb_freed;
 };
@@ -130,13 +132,6 @@
     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,
      * totally free chunks are immediately considered for release. Otherwise
@@ -197,7 +192,7 @@
      \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;
 
@@ -325,6 +320,7 @@
 {
 public:
     MemImplementingAllocator(char const *aLabel, size_t aSize);
+    virtual ~MemImplementingAllocator();
     virtual MemPoolMeter const &getMeter() const;
     virtual MemPoolMeter &getMeter();
     virtual void flushMetersFull();
@@ -342,111 +338,23 @@
 
     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:
+    virtual void deallocate(void *, bool aggressive) = 0;
     MemPoolMeter meter;
+    int memPID;
 public:
     MemImplementingAllocator *next;
 public:
     size_t alloc_calls;
     size_t free_calls;
+    size_t saved_calls;
     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
 {
 public:
@@ -536,4 +444,4 @@
 }
 
 
-#endif /* _MEM_POOLS_H_ */
+#endif /* _MEM_POOL_H_ */

=== added file 'include/MemPoolChunked.h'
--- include/MemPoolChunked.h	1970-01-01 00:00:00 +0000
+++ include/MemPoolChunked.h	2010-05-29 00:59:35 +0000
@@ -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 *, bool aggressive);
+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_ */

=== added file 'include/MemPoolMalloc.h'
--- include/MemPoolMalloc.h	1970-01-01 00:00:00 +0000
+++ include/MemPoolMalloc.h	2010-05-29 00:59:35 +0000
@@ -0,0 +1,49 @@
+#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);
+    ~MemPoolMalloc();
+    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 *, bool aggressive);
+private:
+    Stack<void *> freelist;
+};
+
+
+#endif /* _MEM_POOL_MALLOC_H_ */

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2009-12-19 11:56:02 +0000
+++ lib/Makefile.am	2010-05-28 21:53:06 +0000
@@ -49,6 +49,8 @@
 	win32lib.c
 libmiscutil_a_SOURCES = \
 	MemPool.cc \
+	MemPoolChunked.cc \
+	MemPoolMalloc.cc \
 	base64.c \
 	charset.c \
 	getfullhostname.c \

=== modified file 'lib/MemPool.cc'
--- lib/MemPool.cc	2010-04-14 21:04:28 +0000
+++ lib/MemPool.cc	2010-05-29 00:59:35 +0000
@@ -33,63 +33,16 @@
  *
  */
 
-/*
- * 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 "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
 
 #if HAVE_STRING_H
 #include <string.h>
@@ -107,10 +60,6 @@
 
 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 +114,6 @@
     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.
@@ -364,54 +124,16 @@
     char *cfg = getenv("MEMPOOLS");
     if (cfg)
         defaultIsChunked = atoi(cfg);
-#if HAVE_MALLOPT && M_MMAP_MAX
-    mallopt(M_MMAP_MAX, MEM_MAX_MMAP_CHUNKS);
-#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 +142,6 @@
     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,17 +161,18 @@
 
     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_allocated.count += calls;
+        alloc_calls = 0;
+    }
+    calls = saved_calls;
+    if (calls) {
         meter.gb_saved.count += calls;
-        memMeterAdd(meter.inuse, calls);
-        memMeterDel(meter.idle, calls);
-        alloc_calls = 0;
+        saved_calls = 0;
     }
 }
 
@@ -491,6 +180,7 @@
 MemImplementingAllocator::flushMetersFull()
 {
     flushMeters();
+    getMeter().gb_allocated.bytes = getMeter().gb_allocated.count * obj_size;
     getMeter().gb_saved.bytes = getMeter().gb_saved.count * obj_size;
     getMeter().gb_freed.bytes = getMeter().gb_freed.count * obj_size;
 }
@@ -501,11 +191,21 @@
     alloc.level = 0;
     inuse.level = 0;
     idle.level = 0;
+    gb_allocated.count = 0;
+    gb_allocated.bytes = 0;
+    gb_oallocated.count = 0;
+    gb_oallocated.bytes = 0;
     gb_saved.count = 0;
     gb_saved.bytes = 0;
     gb_freed.count = 0;
     gb_freed.bytes = 0;
 }
+
+MemPoolMeter::MemPoolMeter()
+{
+    flush();
+}
+
 /*
  * Updates all pool counters, and recreates TheMeter totals from all pools
  */
@@ -523,8 +223,10 @@
         memMeterAdd(TheMeter.alloc, pool->getMeter().alloc.level * pool->obj_size);
         memMeterAdd(TheMeter.inuse, pool->getMeter().inuse.level * pool->obj_size);
         memMeterAdd(TheMeter.idle, pool->getMeter().idle.level * pool->obj_size);
+        TheMeter.gb_allocated.count += pool->getMeter().gb_allocated.count;
         TheMeter.gb_saved.count += pool->getMeter().gb_saved.count;
         TheMeter.gb_freed.count += pool->getMeter().gb_freed.count;
+        TheMeter.gb_allocated.bytes += pool->getMeter().gb_allocated.bytes;
         TheMeter.gb_saved.bytes += pool->getMeter().gb_saved.bytes;
         TheMeter.gb_freed.bytes += pool->getMeter().gb_freed.bytes;
     }
@@ -532,20 +234,6 @@
 }
 
 void *
-MemMalloc::allocate()
-{
-    inuse++;
-    return xcalloc(1, obj_size);
-}
-
-void
-MemMalloc::deallocate(void *obj)
-{
-    inuse--;
-    xfree(obj);
-}
-
-void *
 MemImplementingAllocator::alloc()
 {
     if (++alloc_calls == FLUSH_LIMIT)
@@ -559,127 +247,10 @@
 {
     assert(obj != NULL);
     (void) VALGRIND_CHECK_MEM_IS_ADDRESSABLE(obj, obj_size);
-    deallocate(obj);
+    deallocate(obj, MemPools::GetInstance().mem_idle_limit == 0);
     ++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,96 +278,10 @@
     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
  */
 int
@@ -814,7 +299,7 @@
     /* 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 +318,7 @@
     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 +333,6 @@
     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)
 {
@@ -935,8 +407,39 @@
         next(NULL),
         alloc_calls(0),
         free_calls(0),
+        saved_calls(0),
         obj_size(RoundedSize(aSize))
 {
+    memPID = ++Pool_id_counter;
+
+    MemImplementingAllocator *last_pool;
+
+    assert(aLabel != NULL && aSize);
+    /* 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;
+}
+
+MemImplementingAllocator::~MemImplementingAllocator()
+{
+    MemImplementingAllocator *find_pool, *prev_pool;
+
+    assert(MemPools::GetInstance().pools != NULL && "Called MemImplementingAllocator::~MemImplementingAllocator, 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;
 }
 
 void

=== added file 'lib/MemPoolChunked.cc'
--- lib/MemPoolChunked.cc	1970-01-01 00:00:00 +0000
+++ lib/MemPoolChunked.cc	2010-05-29 00:59:35 +0000
@@ -0,0 +1,501 @@
+
+/*
+ * $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 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;
+
+    setChunkSize(MEM_CHUNK_SIZE);
+
+#if HAVE_MALLOPT && M_MMAP_MAX
+    mallopt(M_MMAP_MAX, MEM_MAX_MMAP_CHUNKS);
+#endif
+}
+
+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;
+
+    saved_calls++;
+
+    /* 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 */
+	saved_calls--; // compensate for the ++ above
+        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;
+
+    flushMetersFull();
+    clean(0);
+    assert(meter.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. */
+
+}
+
+int
+MemPoolChunked::getInUseCount()
+{
+    return meter.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, bool aggressive)
+{
+    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 meter.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 = &meter;
+    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 += meter.alloc.level;
+    stats->items_inuse += meter.inuse.level;
+    stats->items_idle += meter.idle.level;
+
+    stats->overhead += sizeof(MemPoolChunked) + chunkCount * sizeof(MemChunk) + strlen(objectType()) + 1;
+
+    return meter.inuse.level;
+}

=== added file 'lib/MemPoolMalloc.cc'
--- lib/MemPoolMalloc.cc	1970-01-01 00:00:00 +0000
+++ lib/MemPoolMalloc.cc	2010-05-29 01:06:38 +0000
@@ -0,0 +1,142 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 63    Low Level Memory Pool Management
+ * AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins, Henrik Nordstrom
+ *
+ * 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()
+{
+    void *obj = freelist.pop();
+    if (obj) {
+	memMeterDec(meter.idle);
+	saved_calls++;
+    } else {
+	obj = xcalloc(1, obj_size);
+	memMeterInc(meter.alloc);
+    }
+    memMeterInc(meter.inuse);
+    return obj;
+}
+
+void
+MemPoolMalloc::deallocate(void *obj, bool aggressive)
+{
+    memMeterDec(meter.inuse);
+    if (aggressive) {
+	xfree(obj);
+	memMeterDec(meter.alloc);
+    } else {
+	if (doZeroOnPush)
+	    memset(obj, 0, obj_size);
+	memMeterInc(meter.idle);
+	freelist.push_back(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 = &meter;
+    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)
+{
+}
+
+MemPoolMalloc::~MemPoolMalloc()
+{
+    assert(meter.inuse.level == 0 && "While trying to destroy pool");
+    clean(0);
+}
+
+bool
+MemPoolMalloc::idleTrigger(int shift) const
+{
+    return freelist.count >> (shift ? 8 : 0);
+}
+
+void
+MemPoolMalloc::clean(time_t maxage)
+{
+    while (void *obj = freelist.pop()) {
+	memMeterDec(meter.idle);
+	memMeterDec(meter.alloc);
+	xfree(obj);
+    }
+}
+

=== modified file 'src/mem.cc'
--- src/mem.cc	2010-03-21 03:08:26 +0000
+++ src/mem.cc	2010-05-28 23:51:08 +0000
@@ -580,6 +580,9 @@
     MemPoolMeter *pm = mp_st->meter;
     const char *delim = "\t ";
 
+#if HAVE_IOMANIP
+    stream.setf(std::ios_base::fixed);
+#endif
     stream << std::setw(20) << std::left << mp_st->label << delim;
     stream << std::setw(4) << std::right << mp_st->obj_size << delim;
 
@@ -627,10 +630,10 @@
     stream << toKB(mp_st->obj_size * pm->idle.hwater_level) << delim;
     /* saved */
     stream << (int)pm->gb_saved.count << delim;
-    stream << std::setprecision(3) << xpercent(pm->gb_saved.count, AllMeter->gb_saved.count) << delim;
-    stream << std::setprecision(3) << xpercent(pm->gb_saved.bytes, AllMeter->gb_saved.bytes) << delim;
-    stream << std::setprecision(3) << xdiv(pm->gb_saved.count - pm->gb_osaved.count, xm_deltat) << "\n";
-    pm->gb_osaved.count = pm->gb_saved.count;
+    stream << std::setprecision(3) << xpercent(pm->gb_saved.count, AllMeter->gb_allocated.count) << delim;
+    stream << std::setprecision(3) << xpercent(pm->gb_saved.bytes, AllMeter->gb_allocated.bytes) << delim;
+    stream << std::setprecision(3) << xdiv(pm->gb_allocated.count - pm->gb_oallocated.count, xm_deltat) << "\n";
+    pm->gb_oallocated.count = pm->gb_allocated.count;
 }
 
 static int
@@ -683,7 +686,7 @@
     "In Use\t\t\t\t\t"
     "Idle\t\t\t"
     "Allocations Saved\t\t\t"
-    "Hit Rate\t"
+    "Rate\t"
     "\n"
     " \t (bytes)\t"
     "KB/ch\t obj/ch\t"
@@ -692,7 +695,7 @@
     "(#)\t (KB)\t high (KB)\t high (hrs)\t %alloc\t"
     "(#)\t (KB)\t high (KB)\t"
     "(#)\t %cnt\t %vol\t"
-    "(#) / sec\t"
+    "(#)/sec\t"
     "\n";
     xm_deltat = current_dtime - xm_time;
     xm_time = current_dtime;
@@ -712,7 +715,7 @@
         if (!mp_stats.pool)	/* pool destroyed */
             continue;
 
-        if (mp_stats.pool->getMeter().gb_saved.count > 0)	/* this pool has been used */
+        if (mp_stats.pool->getMeter().gb_allocated.count > 0)	/* this pool has been used */
             sortme[npools++] = mp_stats;
         else
             not_used++;
@@ -746,7 +749,7 @@
     PoolReport(&mp_stats, mp_total.TheMeter, stream);
 
     /* Cumulative */
-    stream << "Cumulative allocated volume: "<< double_to_str(buf, 64, mp_total.TheMeter->gb_saved.bytes) << "\n";
+    stream << "Cumulative allocated volume: "<< double_to_str(buf, 64, mp_total.TheMeter->gb_allocated.bytes) << "\n";
     /* overhead */
     stream << "Current overhead: " << mp_total.tot_overhead << " bytes (" <<
     std::setprecision(3) << xpercent(mp_total.tot_overhead, mp_total.TheMeter->inuse.level) << "%)\n";

# Begin bundle
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

