--- /dev/null
+/* ====================================================================
+ * 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;
+}
--- /dev/null
+/* ====================================================================
+ * 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 */
# 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"
# 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
# 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
#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,
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;
* 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;
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) {
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);
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;
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;
}
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;
*/
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) {
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.
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;