]> granicus.if.org Git - apache/commitdiff
Introduce a dedicated cache hash adt that does not rely on pools. This
authorBill Stoddard <stoddard@apache.org>
Sat, 4 May 2002 19:54:39 +0000 (19:54 +0000)
committerBill Stoddard <stoddard@apache.org>
Sat, 4 May 2002 19:54:39 +0000 (19:54 +0000)
cures a storage leak when garbage collecting expired entries out of the
hash table. cache_hash is just apr_hash with some of the function removed
and reimplemented to use malloc/free in place of apr_pool calls.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@94932 13f79535-47bb-0310-9956-ffa450edef68

modules/experimental/cache_hash.c [new file with mode: 0644]
modules/experimental/cache_hash.h [new file with mode: 0644]
modules/experimental/mod_cache.dsp
modules/experimental/mod_mem_cache.c

diff --git a/modules/experimental/cache_hash.c b/modules/experimental/cache_hash.c
new file mode 100644 (file)
index 0000000..7142c1b
--- /dev/null
@@ -0,0 +1,322 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#include "apr_general.h"
+
+#include "mod_cache.h"
+#include "cache_hash.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if APR_HAVE_STRING_H
+#include <string.h>
+#endif
+
+
+/*
+ * The internal form of a hash table.
+ *
+ * The table is an array indexed by the hash of the key; collisions
+ * are resolved by hanging a linked list of hash entries off each
+ * element of the array. Although this is a really simple design it
+ * isn't too bad given that pools have a low allocation overhead.
+ */
+
+typedef struct cache_hash_entry_t cache_hash_entry_t;
+
+struct cache_hash_entry_t {
+    cache_hash_entry_t *next;
+    int                         hash;
+    const void         *key;
+    apr_ssize_t                 klen;
+    const void         *val;
+};
+
+/*
+ * Data structure for iterating through a hash table.
+ *
+ * We keep a pointer to the next hash entry here to allow the current
+ * hash entry to be freed or otherwise mangled between calls to
+ * cache_hash_next().
+ */
+struct cache_hash_index_t {
+    cache_hash_t        *ht;
+    cache_hash_entry_t   *this, *next;
+    int                  index;
+};
+
+/*
+ * The size of the array is always a power of two. We use the maximum
+ * index rather than the size so that we can use bitwise-AND for
+ * modular arithmetic.
+ * The count of hash entries may be greater depending on the chosen
+ * collision rate.
+ */
+struct cache_hash_t {
+    cache_hash_entry_t   **array;
+    cache_hash_index_t     iterator;  /* For cache_hash_first(NULL, ...) */
+    int                  count, max;
+};
+
+/*
+ * Hash creation functions.
+ */
+static cache_hash_entry_t **alloc_array(cache_hash_t *ht, int max)
+{
+   return calloc(1, sizeof(*ht->array) * (max + 1));
+}
+
+CACHE_DECLARE(cache_hash_t *) cache_hash_make(apr_size_t size)
+{
+    cache_hash_t *ht;
+    ht = malloc(sizeof(cache_hash_t));
+    if (!ht) {
+        return NULL;
+    }
+    ht->count = 0;
+    ht->max = size;
+    ht->array = alloc_array(ht, ht->max);
+    if (!ht->array) {
+        free(ht);
+        return NULL;
+    }
+    return ht;
+}
+
+/*
+ * Hash iteration functions.
+ */
+
+CACHE_DECLARE(cache_hash_index_t *) cache_hash_next(cache_hash_index_t *hi)
+{
+    hi->this = hi->next;
+    while (!hi->this) {
+       if (hi->index > hi->ht->max)
+           return NULL;
+       hi->this = hi->ht->array[hi->index++];
+    }
+    hi->next = hi->this->next;
+    return hi;
+}
+
+CACHE_DECLARE(cache_hash_index_t *) cache_hash_first(cache_hash_t *ht)
+{
+    cache_hash_index_t *hi;
+
+    hi = &ht->iterator;
+    hi->ht = ht;
+    hi->index = 0;
+    hi->this = NULL;
+    hi->next = NULL;
+    return cache_hash_next(hi);
+}
+
+CACHE_DECLARE(void) cache_hash_this(cache_hash_index_t *hi,
+                                  const void **key,
+                                  apr_ssize_t *klen,
+                                  void **val)
+{
+    if (key)  *key  = hi->this->key;
+    if (klen) *klen = hi->this->klen;
+    if (val)  *val  = (void *)hi->this->val;
+}
+
+
+/*
+ * This is where we keep the details of the hash function and control
+ * the maximum collision rate.
+ *
+ * If val is non-NULL it creates and initializes a new hash entry if
+ * there isn't already one there; it returns an updatable pointer so
+ * that hash entries can be removed.
+ */
+
+static cache_hash_entry_t **find_entry(cache_hash_t *ht,
+                                       const void *key,
+                                       apr_ssize_t klen,
+                                       const void *val)
+{
+    cache_hash_entry_t **hep, *he;
+    const unsigned char *p;
+    int hash;
+    apr_ssize_t i;
+
+    /*
+     * This is the popular `times 33' hash algorithm which is used by
+     * perl and also appears in Berkeley DB. This is one of the best
+     * known hash functions for strings because it is both computed
+     * very fast and distributes very well.
+     *
+     * The originator may be Dan Bernstein but the code in Berkeley DB
+     * cites Chris Torek as the source. The best citation I have found
+     * is "Chris Torek, Hash function for text in C, Usenet message
+     * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich
+     * Salz's USENIX 1992 paper about INN which can be found at
+     * <http://citeseer.nj.nec.com/salz92internetnews.html>.
+     *
+     * The magic of number 33, i.e. why it works better than many other
+     * constants, prime or not, has never been adequately explained by
+     * anyone. So I try an explanation: if one experimentally tests all
+     * multipliers between 1 and 256 (as I did while writing a low-level
+     * data structure library some time ago) one detects that even
+     * numbers are not useable at all. The remaining 128 odd numbers
+     * (except for the number 1) work more or less all equally well.
+     * They all distribute in an acceptable way and this way fill a hash
+     * table with an average percent of approx. 86%.
+     *
+     * If one compares the chi^2 values of the variants (see
+     * Bob Jenkins ``Hashing Frequently Asked Questions'' at
+     * http://burtleburtle.net/bob/hash/hashfaq.html for a description
+     * of chi^2), the number 33 not even has the best value. But the
+     * number 33 and a few other equally good numbers like 17, 31, 63,
+     * 127 and 129 have nevertheless a great advantage to the remaining
+     * numbers in the large set of possible multipliers: their multiply
+     * operation can be replaced by a faster operation based on just one
+     * shift plus either a single addition or subtraction operation. And
+     * because a hash function has to both distribute good _and_ has to
+     * be very fast to compute, those few numbers should be preferred.
+     *
+     *                  -- Ralf S. Engelschall <rse@engelschall.com>
+     */
+    hash = 0;
+    if (klen == CACHE_HASH_KEY_STRING) {
+        for (p = key; *p; p++) {
+            hash = hash * 33 + *p;
+        }
+        klen = p - (const unsigned char *)key;
+    }
+    else {
+        for (p = key, i = klen; i; i--, p++) {
+            hash = hash * 33 + *p;
+        }
+    }
+    
+    /* scan linked list */
+    for (hep = &ht->array[hash & ht->max], he = *hep;
+        he;
+        hep = &he->next, he = *hep) {
+       if (he->hash == hash &&
+           he->klen == klen &&
+           memcmp(he->key, key, klen) == 0)
+           break;
+    }
+    if (he || !val)
+       return hep;
+    /* add a new entry for non-NULL values */
+    he = malloc(sizeof(*he));
+    if (!he) {
+        return NULL;
+    }
+    he->next = NULL;
+    he->hash = hash;
+    he->key  = key;
+    he->klen = klen;
+    he->val  = val;
+    *hep = he;
+    ht->count++;
+    return hep;
+}
+
+CACHE_DECLARE(void *) cache_hash_get(cache_hash_t *ht,
+                                   const void *key,
+                                   apr_ssize_t klen)
+{
+    cache_hash_entry_t *he;
+    he = *find_entry(ht, key, klen, NULL);
+    if (he)
+       return (void *)he->val;
+    else
+       return NULL;
+}
+
+CACHE_DECLARE(void*) cache_hash_set(cache_hash_t *ht,
+                                  const void *key,
+                                  apr_ssize_t klen,
+                                  const void *val)
+{
+    cache_hash_entry_t **hep, *tmp;
+    const void *tval;
+    hep = find_entry(ht, key, klen, val);
+    if (*hep) {
+        if (!val) {
+            /* delete entry */
+            tval = (*hep)->val;
+            tmp = *hep;
+            *hep = (*hep)->next;
+            free(tmp);
+            --ht->count;
+        }
+        else {
+            /* replace entry */
+            tval = (*hep)->val;
+            (*hep)->val = val;
+        }
+        /* Return the object just removed from the cache
+         * to let the caller clean it up
+         */
+        return tval;
+    }
+    /* else key not present and val==NULL */
+    return NULL;
+}
+
+CACHE_DECLARE(int) cache_hash_count(cache_hash_t *ht)
+{
+    return ht->count;
+}
diff --git a/modules/experimental/cache_hash.h b/modules/experimental/cache_hash.h
new file mode 100644 (file)
index 0000000..b73181b
--- /dev/null
@@ -0,0 +1,194 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef CACHE_HASH_H
+#define CACHE_HASH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mod_cache.h"
+
+/**
+ * @file cache_hash.h
+ * @brief Cache Hash Tables
+ */
+
+/**
+ * @defgroup Cache_Hash  Hash Tables
+ * @ingroup CACHE
+ * @{
+ */
+
+/**
+ * When passing a key to cache_hash_set or cache_hash_get, this value can be
+ * passed to indicate a string-valued key, and have cache_hash compute the
+ * length automatically.
+ *
+ * @remark cache_hash will use strlen(key) for the length. The null-terminator
+ *         is not included in the hash value (why throw a constant in?).
+ *         Since the hash table merely references the provided key (rather
+ *         than copying it), cache_hash_this() will return the null-term'd key.
+ */
+#define CACHE_HASH_KEY_STRING     (-1)
+
+/**
+ * Abstract type for hash tables.
+ */
+typedef struct cache_hash_t cache_hash_t;
+
+/**
+ * Abstract type for scanning hash tables.
+ */
+typedef struct cache_hash_index_t cache_hash_index_t;
+
+/**
+ * Create a hash table.
+ * @param size 
+ * @return The hash table just created
+  */
+CACHE_DECLARE(cache_hash_t *) cache_hash_make(apr_size_t size);
+
+
+/**
+ * Associate a value with a key in a hash table.
+ * @param ht The hash table
+ * @param key Pointer to the key
+ * @param klen Length of the key. Can be CACHE_HASH_KEY_STRING to use the string length.
+ * @param val Value to associate with the key
+ * @remark If the value is NULL the hash entry is deleted.
+ * @return The value of the deleted cache entry (so the caller can clean it up).
+ */
+CACHE_DECLARE(void *) cache_hash_set(cache_hash_t *ht, const void *key,
+                                   apr_ssize_t klen, const void *val);
+
+/**
+ * Look up the value associated with a key in a hash table.
+ * @param ht The hash table
+ * @param key Pointer to the key
+ * @param klen Length of the key. Can be CACHE_HASH_KEY_STRING to use the string length.
+ * @return Returns NULL if the key is not present.
+ */
+CACHE_DECLARE(void *) cache_hash_get(cache_hash_t *ht, const void *key,
+                                   apr_ssize_t klen);
+
+/**
+ * Start iterating over the entries in a hash table.
+ * @param p The pool to allocate the cache_hash_index_t iterator. If this
+ *          pool is NULL, then an internal, non-thread-safe iterator is used.
+ * @param ht The hash table
+ * @example
+ */
+/**
+ * <PRE>
+ * 
+ *     int sum_values(apr_pool_t *p, cache_hash_t *ht)
+ *     {
+ *         cache_hash_index_t *hi;
+ *        void *val;
+ *        int sum = 0;
+ *        for (hi = cache_hash_first(p, ht); hi; hi = cache_hash_next(hi)) {
+ *            cache_hash_this(hi, NULL, NULL, &val);
+ *            sum += *(int *)val;
+ *        }
+ *        return sum;
+ *     }
+ * 
+ * There is no restriction on adding or deleting hash entries during an
+ * iteration (although the results may be unpredictable unless all you do
+ * is delete the current entry) and multiple iterations can be in
+ * progress at the same time.
+ * </PRE>
+  */
+CACHE_DECLARE(cache_hash_index_t *) cache_hash_first(cache_hash_t *ht);
+
+/**
+ * Continue iterating over the entries in a hash table.
+ * @param hi The iteration state
+ * @return a pointer to the updated iteration state.  NULL if there are no more  
+ *         entries.
+ */
+CACHE_DECLARE(cache_hash_index_t *) cache_hash_next(cache_hash_index_t *hi);
+
+/**
+ * Get the current entry's details from the iteration state.
+ * @param hi The iteration state
+ * @param key Return pointer for the pointer to the key.
+ * @param klen Return pointer for the key length.
+ * @param val Return pointer for the associated value.
+ * @remark The return pointers should point to a variable that will be set to the
+ *         corresponding data, or they may be NULL if the data isn't interesting.
+ */
+CACHE_DECLARE(void) cache_hash_this(cache_hash_index_t *hi, const void **key, 
+                                  apr_ssize_t *klen, void **val);
+
+/**
+ * Get the number of key/value pairs in the hash table.
+ * @param ht The hash table
+ * @return The number of key/value pairs in the hash table.
+ */
+CACHE_DECLARE(int) cache_hash_count(cache_hash_t *ht);
+
+
+/** @} */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !CACHE_HASH_H */
index b9a3263685602a8c51806a5c716b7b240315b4fa..38cd907885c222e4683fcdfbbcd3ee5ecae0804c 100644 (file)
@@ -52,8 +52,8 @@ BSC32=bscmake.exe
 # ADD BASE BSC32 /nologo
 # ADD BSC32 /nologo
 LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /machine:I386
-# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /machine:I386 /out:"Release/mod_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cache
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cache
 
 !ELSEIF  "$(CFG)" == "mod_cache - Win32 Debug"
 
@@ -92,6 +92,10 @@ LINK32=link.exe
 # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
 # Begin Source File
 
+SOURCE=.\cache_hash.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\cache_storage.c
 # End Source File
 # Begin Source File
@@ -108,6 +112,10 @@ SOURCE=.\mod_cache.c
 # PROP Default_Filter "h;hpp;hxx;hm;inl"
 # Begin Source File
 
+SOURCE=.\cache_hash.h
+# End Source File
+# Begin Source File
+
 SOURCE=.\mod_cache.h
 # End Source File
 # End Group
index fdc0cb5fe583f450c1c3a09367e18bdec8e9ae67..4429d95495487fdd97a69bde1ab06cc160d936bf 100644 (file)
@@ -58,6 +58,7 @@
 
 #define CORE_PRIVATE
 #include "mod_cache.h"
+#include "cache_hash.h"
 #include "ap_mpm.h"
 #include "apr_thread_mutex.h"
 #if APR_HAVE_UNISTD_H
 
 module AP_MODULE_DECLARE_DATA mem_cache_module;
 
-/* 
- * XXX
- * This cache uses apr_hash functions which leak storage when something is removed
- * from the cache. This can be fixed in the apr_hash functions by making them use
- * malloc/free rather than pools to manage their storage requirements.
- */
-
 typedef enum {
     CACHE_TYPE_FILE = 1,
     CACHE_TYPE_HEAP,
@@ -103,7 +97,7 @@ typedef struct mem_cache_object {
 
 typedef struct {
     apr_thread_mutex_t *lock;
-    apr_hash_t *cacheht;
+    cache_hash_t *cacheht;
     apr_size_t cache_size;
     apr_size_t object_cnt;
 
@@ -206,7 +200,7 @@ static apr_status_t decrement_refcount(void *arg)
          * removed the object from the cache (and set obj->cleanup)
          */
         if (!obj->cleanup) {
-            apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+            cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
             sconf->object_cnt--;
             sconf->cache_size -= mobj->m_len;
             obj->cleanup = 1;
@@ -243,7 +237,7 @@ static apr_status_t decrement_refcount(void *arg)
 static apr_status_t cleanup_cache_mem(void *sconfv)
 {
     cache_object_t *obj;
-    apr_hash_index_t *hi;
+    cache_hash_index_t *hi;
     mem_cache_conf *co = (mem_cache_conf*) sconfv;
 
     if (!co) {
@@ -254,12 +248,12 @@ static apr_status_t cleanup_cache_mem(void *sconfv)
         apr_thread_mutex_lock(sconf->lock);
     }
     /* Iterate over the cache and clean up each entry */
-    while ((hi = apr_hash_first(NULL, co->cacheht)) != NULL) {
+    while ((hi = cache_hash_first(NULL, co->cacheht)) != NULL) {
         /* Fetch the object from the cache */
-        apr_hash_this(hi, NULL, NULL, (void **)&obj);
+        cache_hash_this(hi, NULL, NULL, (void **)&obj);
         if (obj) {
             /* Remove the object from the cache */
-            apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+            cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
             /* Free the object if the recount == 0 */
 #ifdef USE_ATOMICS
             apr_atomic_inc(&obj->refcount);
@@ -288,12 +282,12 @@ static void *create_cache_config(apr_pool_t *p, server_rec *s)
 
     sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
 
-
     ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
     if (threaded_mpm) {
         apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
     }
-    sconf->cacheht = apr_hash_make(p);
+    /* Todo: determine hash table size from max_cache_object_cnt */
+    sconf->cacheht = cache_hash_make(512);
 
     sconf->min_cache_object_size = DEFAULT_MIN_CACHE_OBJECT_SIZE;
     sconf->max_cache_object_size = DEFAULT_MAX_CACHE_OBJECT_SIZE;
@@ -399,11 +393,11 @@ static int create_entity(cache_handle_t *h, request_rec *r,
     if (sconf->lock) {
         apr_thread_mutex_lock(sconf->lock);
     }
-    tmp_obj = (cache_object_t *) apr_hash_get(sconf->cacheht, 
+    tmp_obj = (cache_object_t *) cache_hash_get(sconf->cacheht, 
                                               key, 
-                                              APR_HASH_KEY_STRING);
+                                              CACHE_HASH_KEY_STRING);
     if (!tmp_obj) {
-        apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj);
+        cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj);
         sconf->object_cnt++;
         sconf->cache_size += len;
     }
@@ -445,8 +439,8 @@ static int open_entity(cache_handle_t *h, request_rec *r, const char *type, cons
     if (sconf->lock) {
         apr_thread_mutex_lock(sconf->lock);
     }
-    obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, 
-                                          APR_HASH_KEY_STRING);
+    obj = (cache_object_t *) cache_hash_get(sconf->cacheht, key, 
+                                          CACHE_HASH_KEY_STRING);
     if (obj) {
         if (obj->complete) {
             request_rec *rmain=r, *rtmp;
@@ -508,10 +502,11 @@ static int remove_entity(cache_handle_t *h)
      */
     if (!obj->cleanup) {
         mem_cache_object_t *mobj = (mem_cache_object_t *) obj->vobj;
-        apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+        cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
         sconf->object_cnt--;
         sconf->cache_size -= mobj->m_len;
         obj->cleanup = 1;
+        ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, NULL, "gcing a cache entry");
     }
 
     if (sconf->lock) {
@@ -585,13 +580,6 @@ static int remove_url(const char *type, const char *key)
     if (strcasecmp(type, "mem") && strcasecmp(type, "fd")) {
         return DECLINED;
     }
-
-    /* WIBNIF
-     * apr_hash_set(..,..,..,NULL) returned pointer to the object just removed.
-     * That way, we could free the object w/o doing another call to an
-     * apr_hash function.
-     */
-
     /* Order of the operations is important to avoid race conditions. 
      * First, remove the object from the cache. Remember, all additions
      * deletions from the cache are protected by sconf->lock.
@@ -604,11 +592,11 @@ static int remove_url(const char *type, const char *key)
     if (sconf->lock) {
         apr_thread_mutex_lock(sconf->lock);
     }
-    obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, 
-                                          APR_HASH_KEY_STRING);
+    /* This call will return a pointer to the cache_object just removed */
+    obj = cache_hash_set(sconf->cacheht, key, CACHE_HASH_KEY_STRING, NULL);
     if (obj) {
+        /* obj has been removed from the cache */
         mem_cache_object_t *mobj = (mem_cache_object_t *) obj->vobj;
-        apr_hash_set(sconf->cacheht, key, APR_HASH_KEY_STRING, NULL);
         sconf->object_cnt--;
         sconf->cache_size -= mobj->m_len;