Enhanced cache replacement policies

From: John Dilley <jad@dont-contact.us>
Date: Thu, 10 Jun 1999 12:45:31 -0700

        I have completed the implementation, testing, validation, and
documentation of our enhancement to the Squid cache replacement policy.
I reported on this work as a work in progress at the recent WCW, and
have updated the document describing the work. The report is at
http://www.hpl.hp.com/personal/John_Dilley/caching/wcw.html and a
description of the replacement policies is documented in an HPL TR,
http://www.hpl.hp.com/techreports/98/HPL-98-173.html.

        I've had a copy of the 2.2.STABLE3 proxy with these policies up
and serving local cache requests for a while now and it is doing fine.
I have also tested the policies with a workload driver and there have
been no crashes or other anomalies (at least not recently :-). Enclosed
below is a shar with the patch to squid-2.2.STABLE3. It includes:

    - The modifications to the source (store*.c mainly).
    - Two new files, include/heap.h and lib/heap.c, that implement a
      heap for the metadata instead of the LRU linked list.
    - Modifications to the squid.conf file to select the policy.

I have not changed the external documentation on the squid web pages,
but at some point that would be useful, if these policies are accepted
by the community as a useful piece of work. So far they have shown to
be more efficient in CPU resource consumption than the LRU list (even
though they are a heap not a linked list), and yield better hit rate
and/or byte hit rate than LRU. I have tested on both Linux and HP-UX.

        Please have a look over this package and let me know if you have
any questions, concerns, or suggestions. I hope this turns out to be a
useful enhancement to Squid! Best regards,

                             -- jad --
                          John Dilley <jad@hpl.hp.com>

#!/bin/sh
# This is HPL_REPL.shar, a shell archive (produced by GNU sharutils 4.2)
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1999-06-10 11:09 PDT by <jad@hpl.hp.com>.
# Source directory was `/home/jad/work/Squid/squid-2.2.STABLE3-jad'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
# This format requires very little intelligence at unshar time.
# "if test", "echo", "mkdir", and "sed" may be needed.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 454 -rw-r--r-- jad.README
# 50479 -rw-r--r-- jad.diff
# 4865 -rw-r--r-- include/heap.h
# 13250 -rw-r--r-- lib/heap.c
#
echo=echo
if mkdir _sh02595; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= jad.README ==============
if test -f 'jad.README' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'jad.README' '(file already exists)'
else
  $echo 'x -' extracting 'jad.README' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'jad.README' &&
XTo build the version of Squid using the HPL Cache Replacement Policies,
Xfollow these steps:
X - start with a squid-2.2.STABLE3 distribution
X - move the tar file jad.tar into that directory and unpack it to create
X jad.README (this file), jad.diff, include/heap.h, lib/heap.c
X - cd into the squid-2.2.STABLE3 directory
X - run patch -p1 < jad.diff
X - run CFLAGS="-g -O2 -Wall -DHPL_REPL" ./configure
X - touch makefile
X - make clean all
SHAR_EOF
  : || $echo 'restore of' 'jad.README' 'failed'
fi
# ============= jad.diff ==============
if test -f 'jad.diff' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'jad.diff' '(file already exists)'
else
  $echo 'x -' extracting 'jad.diff' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'jad.diff' &&
Xdiff -r -u squid-2.2.STABLE3/lib/Makefile.in squid-2.2.STABLE3-jad/lib/Makefile.in
X--- squid-2.2.STABLE3/lib/Makefile.in Mon Aug 17 16:01:24 1998
X+++ squid-2.2.STABLE3-jad/lib/Makefile.in Wed Jun 2 15:30:30 1999
X@@ -37,6 +37,7 @@
X Array.o \
X Stack.o \
X hash.o \
X+ heap.o \
X $(LIBOBJS)
X REGEXOBJS = GNUregex.o
X DLMALLOCOBJS = dlmalloc.o
Xdiff -r -u squid-2.2.STABLE3/makefile.in squid-2.2.STABLE3-jad/makefile.in
X--- squid-2.2.STABLE3/makefile.in Fri Jan 29 15:47:41 1999
X+++ squid-2.2.STABLE3-jad/makefile.in Tue Jun 1 10:19:59 1999
X@@ -8,6 +8,7 @@
X INSTALL = @INSTALL@
X INSTALL_PROGRAM = @INSTALL_PROGRAM@
X INSTALL_DATA = @INSTALL_DATA@
X+CFLAGS = @CFLAGS@ -DHPL_REPL
X SHELL = /bin/sh
X
X # Where to install
Xdiff -r -u squid-2.2.STABLE3/src/cache_cf.c squid-2.2.STABLE3-jad/src/cache_cf.c
X--- squid-2.2.STABLE3/src/cache_cf.c Sun Apr 18 17:14:34 1999
X+++ squid-2.2.STABLE3-jad/src/cache_cf.c Fri Jun 4 08:55:37 1999
X@@ -303,10 +303,14 @@
X debug(3, 0) ("WARNING: resetting 'maximum_single_addr_tries to 1\n");
X Config.retry.maxtries = 1;
X }
X+ #ifdef HPL_REPL
X+ /* The non-LRU policies do not use referenceAge */
X+ #else /* HPL_REPL */
X if (Config.referenceAge < 300) {
X debug(3, 0) ("WARNING: resetting 'reference_age' to 1 week\n");
X Config.referenceAge = 86400 * 7;
X }
X+ #endif /* HPL_REPL */
X requirePathnameExists("MIME Config Table", Config.mimeTablePathname);
X requirePathnameExists("cache_dns_program", Config.Program.dnsserver);
X requirePathnameExists("unlinkd_program", Config.Program.unlinkd);
Xdiff -r -u squid-2.2.STABLE3/src/cf.data.pre squid-2.2.STABLE3-jad/src/cf.data.pre
X--- squid-2.2.STABLE3/src/cf.data.pre Fri May 7 14:44:11 1999
X+++ squid-2.2.STABLE3-jad/src/cf.data.pre Sat Jun 5 22:51:47 1999
X@@ -494,12 +494,17 @@
X DEFAULT: 95
X LOC: Config.Swap.highWaterMark
X DOC_START
X- The low- and high-water marks for cache LRU replacement. LRU
X- replacement begins when the high-water mark is reached and ends
X- when enough objects have been removed and the low-water mark is
X- reached. Defaults are 90% and 95%. If you have a large cache, 5%
X- could be hundreds of MB. If this is the case you may wish to
X- set these numbers closer together.
X+
X+ The low- and high-water marks for cache object replacement.
X+ Replacement begins when the swap (disk) usage is above the
X+ low-water mark and attempts to maintain utilization near the
X+ low-water mark. As swap utilization gets close to high-water
X+ mark object eviction becomes more aggressive. If utilization is
X+ close to the low-water mark less replacement is done each time.
X+
X+ Defaults are 90% and 95%. If you have a large cache, 5% could be
X+ hundreds of MB. If this is the case you may wish to set these
X+ numbers closer together.
X
X cache_swap_low 90
X cache_swap_high 95
X@@ -518,6 +523,10 @@
X hits). If you wish to increase speed more than your want to
X save bandwidth you should leave this low.
X
X+ NOTE: if using the LFUDA replacement policy you should increase
X+ this value to maximize the byte hit rate improvement of LFUDA!
X+ See replacement_policy below for a discussion of this policy.
X+
X maximum_object_size 4096 KB
X DOC_END
X
X@@ -1092,6 +1101,49 @@
X DOC_END
X
X
X+NAME: replacement_policy
X+TYPE: string
X+LOC: Config.replPolicy
X+DEFAULT: LFUDA
X+IFDEF: HPL_REPL
X+DOC_START
X+ The cache replacement policy parameter determines which objects
X+ are evicted (replaced) when disk space is needed. Squid used to
X+ have only a single replacement policy, LRU. But when built with
X+ -DHPL_REPL you can choose between two new, enhanced policies:
X+
X+ GDSF: Greedy-Dual Size Frequency
X+ LFUDA: Least Frequently Used with Dynamic Aging
X+
X+ Both of these policies are frequency based rather than recency
X+ based, and perform better than LRU.
X+
X+ The GDSF policy optimizes object hit rate by keeping smaller
X+ popular objects in cache so it has a better chance of getting a
X+ hit. It achieves a lower byte hit rate than LFUDA though since
X+ it evicts larger (possibly popular) objects.
X+
X+ The LFUDA policy keeps popular objects in cache regardless of
X+ their size and thus optimizes byte hit rate at the expense of
X+ hit rate since one large, popular object will prevent many
X+ smaller, slightly less popular objects from being cached.
X+
X+ Both policies utilize a dynamic aging mechanism that prevents
X+ cache pollution that can otherwise occur with frequency-based
X+ replacement policies.
X+
X+ NOTE: if using the LFUDA replacement policy you should increase
X+ the value of maximum_object_size above its default of 4096 KB to
X+ to maximize the potential byte hit rate improvement of LFUDA.
X+
X+ For more information about these cache replacement policies see
X+ http://www.hpl.hp.com/personal/John_Dilley/caching/wcw.html and
X+ http://fog.hpl.external.hp.com/techreports/98/HPL-98-173.html.
X+
X+replacement_policy LFUDA
X+DOC_END
X+
X+
X NAME: reference_age
X TYPE: time_t
X LOC: Config.referenceAge
X@@ -1113,6 +1165,9 @@
X 3.5 days
X 4 months
X 2.2 hours
X+
X+ NOTE: this parameter is not used when using the enhanced
X+ replacement policies, GDSH or LFUDA.
X
X reference_age 1 month
X DOC_END
Xdiff -r -u squid-2.2.STABLE3/src/client_side.c squid-2.2.STABLE3-jad/src/client_side.c
X--- squid-2.2.STABLE3/src/client_side.c Mon May 10 09:00:40 1999
X+++ squid-2.2.STABLE3-jad/src/client_side.c Tue Jun 1 09:47:56 1999
X@@ -46,6 +46,9 @@
X #include <ip_nat.h>
X #endif
X
X+#ifdef HPL_REPL
X+#include "heap.h"
X+#endif /* HPL_REPL */
X
X
X #if LINGERING_CLOSE
X@@ -314,6 +317,17 @@
X entry->lastmod = http->old_entry->lastmod;
X debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod);
X entry->refcount++; /* EXPIRED CASE */
X+#ifdef HPL_REPL
X+ /*
X+ * Update the position of this object in the store heap and memory
X+ * heap. The cache replacement policy uses refcount to determie the
X+ * priority value of the object.
X+ */
X+ if (entry->node)
X+ heap_update(store_heap, entry->node, entry);
X+ if (entry->mem_obj->node)
X+ heap_update(inmem_heap, entry->mem_obj->node, entry);
X+#endif /* HPL_REPL */
X http->entry = entry;
X http->out.offset = 0;
X fwdStart(http->conn->fd, http->entry, http->request,
X@@ -399,6 +413,13 @@
X storeUnlockObject(entry);
X entry = http->entry = http->old_entry;
X entry->refcount++;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (entry->node)
X+ heap_update(store_heap, entry->node, entry);
X+ if (entry->mem_obj->node)
X+ heap_update(inmem_heap, entry->mem_obj->node, entry);
X+#endif /* HPL_REPL */
X } else if (STORE_PENDING == entry->store_status && 0 == status) {
X debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url);
X if (size >= CLIENT_SOCK_SZ) {
X@@ -410,6 +431,13 @@
X storeUnlockObject(entry);
X entry = http->entry = http->old_entry;
X entry->refcount++;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (entry->node)
X+ heap_update(store_heap, entry->node, entry);
X+ if (entry->mem_obj->node)
X+ heap_update(inmem_heap, entry->mem_obj->node, entry);
X+#endif /* HPL_REPL */
X /* continue */
X } else {
X storeClientCopy(entry,
X@@ -451,6 +479,13 @@
X if (HTTP_NOT_MODIFIED == mem->reply->sline.status) {
X http->old_entry->timestamp = squid_curtime;
X http->old_entry->refcount++;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (http->old_entry->node)
X+ heap_update(store_heap, http->old_entry->node, http->old_entry);
X+ if (http->old_entry->mem_obj->node)
X+ heap_update(inmem_heap, http->old_entry->mem_obj->node, http->old_entry);
X+#endif /* HPL_REPL */
X http->log_type = LOG_TCP_REFRESH_HIT;
X }
X storeUnregister(http->old_entry, http);
X@@ -1810,6 +1845,13 @@
X delaySetStoreClient(http->entry, http, delayClient(r));
X #endif
X http->entry->refcount++;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (http->entry->node)
X+ heap_update(store_heap, http->entry->node, http->entry);
X+ if (http->entry->mem_obj->node)
X+ heap_update(inmem_heap, http->entry->mem_obj->node, http->entry);
X+#endif /* HPL_REPL */
X storeClientCopy(http->entry,
X http->out.offset,
X http->out.offset,
X@@ -1865,6 +1907,13 @@
X assert(http->out.offset == 0);
X http->entry = clientCreateStoreEntry(http, r->method, r->flags);
X http->entry->refcount++;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (http->entry->node)
X+ heap_update(store_heap, http->entry->node, http->entry);
X+ if (http->entry->mem_obj->node)
X+ heap_update(inmem_heap, http->entry->mem_obj->node, http->entry);
X+#endif /* HPL_REPL */
X if (http->redirect.status) {
X HttpReply *rep = httpReplyCreate();
X storeReleaseRequest(http->entry);
Xdiff -r -u squid-2.2.STABLE3/src/globals.h squid-2.2.STABLE3-jad/src/globals.h
X--- squid-2.2.STABLE3/src/globals.h Fri Feb 12 14:32:17 1999
X+++ squid-2.2.STABLE3-jad/src/globals.h Tue Jun 1 09:55:44 1999
X@@ -30,6 +30,9 @@
X * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
X *
X */
X+#ifdef HPL_REPL
X+#include "heap.h"
X+#endif /* HPL_REPL */
X
X extern FILE *debug_log; /* NULL */
X extern FILE *cache_useragent_log; /* NULL */
X@@ -124,7 +127,12 @@
X extern double current_dtime;
X extern int store_hash_buckets; /* 0 */
X extern hash_table *store_table; /* NULL */
X+#ifdef HPL_REPL
X+extern heap_p store_heap;
X+extern heap_p inmem_heap;
X+#else /* HPL_REPL */
X extern dlink_list store_list;
X+#endif /* HPL_REPL */
X extern dlink_list ClientActiveRequests;
X extern const String StringNull; /* { 0, 0, NULL } */
X extern const MemBuf MemBufNull; /* MemBufNULL */
Xdiff -r -u squid-2.2.STABLE3/src/stat.c squid-2.2.STABLE3-jad/src/stat.c
X--- squid-2.2.STABLE3/src/stat.c Wed Apr 7 16:27:31 1999
X+++ squid-2.2.STABLE3-jad/src/stat.c Fri Jun 4 08:57:03 1999
X@@ -489,8 +489,12 @@
X store_swap_size);
X storeAppendPrintf(sentry, "\tStorage Mem size:\t%d KB\n",
X (int) (store_mem_size >> 10));
X+ #ifdef HPL_REPL
X+ /* The non-LRU policies do not use referenceAge */
X+ #else /* HPL_REPL */
X storeAppendPrintf(sentry, "\tStorage LRU Expiration Age:\t%6.2f days\n",
X (double) storeExpiredReferenceAge() / 86400.0);
X+ #endif /* HPL_REPL */
X storeAppendPrintf(sentry, "\tMean Object Size:\t%0.2f KB\n",
X n_disk_objects ? (double) store_swap_size / n_disk_objects : 0.0);
X storeAppendPrintf(sentry, "\tRequests given to unlinkd:\t%d\n",
Xdiff -r -u squid-2.2.STABLE3/src/store.c squid-2.2.STABLE3-jad/src/store.c
X--- squid-2.2.STABLE3/src/store.c Mon May 10 09:02:04 1999
X+++ squid-2.2.STABLE3-jad/src/store.c Sun Jun 6 11:36:31 1999
X@@ -21,12 +21,12 @@
X * it under the terms of the GNU General Public License as published by
X * the Free Software Foundation; either version 2 of the License, or
X * (at your option) any later version.
X- *
X+ *
X * This program is distributed in the hope that it will be useful,
X * but WITHOUT ANY WARRANTY; without even the implied warranty of
X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X * GNU General Public License for more details.
X- *
X+ *
X * You should have received a copy of the GNU General Public License
X * along with this program; if not, write to the Free Software
X * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
X@@ -35,6 +35,10 @@
X
X #include "squid.h"
X
X+#ifdef HPL_REPL
X+#include "heap.h"
X+#endif /* HPL_REPL */
X+
X #define REBUILD_TIMESTAMP_DELTA_MAX 2
X
X #define STORE_IN_MEM_BUCKETS (229)
X@@ -92,7 +96,14 @@
X /*
X * local variables
X */
X+#ifdef HPL_REPL
X+/*
X+ * The heap equivalent of inmem_list, inmem_heap, is in globals.c so other
X+ * modules can access it when updating object metadata (e.g., refcount)
X+ */
X+#else /* HPL_REPL */
X static dlink_list inmem_list;
X+#endif /* HPL_REPL */
X static int store_pages_max = 0;
X static int store_swap_high = 0;
X static int store_swap_low = 0;
X@@ -132,7 +143,7 @@
X {
X MemObject *mem = e->mem_obj;
X const Ctx ctx = ctx_enter(mem->url);
X- debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
X+ debug(20, 4) ("destroy_MemObject: destroying %p\n", mem);
X e->mem_obj = NULL;
X if (!shutting_down)
X assert(mem->swapout.fd == -1);
X@@ -161,7 +172,7 @@
X destroy_StoreEntry(void *data)
X {
X StoreEntry *e = data;
X- debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
X+ debug(20, 4) ("destroy_StoreEntry: destroying %p\n", e);
X assert(e != NULL);
X if (e->mem_obj)
X destroy_MemObject(e);
X@@ -175,18 +186,35 @@
X void
X storeHashInsert(StoreEntry * e, const cache_key * key)
X {
X- debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
X+ debug(20, 4) ("storeHashInsert: Inserting Entry %p key '%s'\n",
X e, storeKeyText(key));
X e->key = storeKeyDup(key);
X hash_join(store_table, (hash_link *) e);
X+ #ifdef HPL_REPL
X+ if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
X+ debug(20, 4) ("storeHashInsert: not inserting special into store heap\n");
X+ } else {
X+ e->node = heap_insert(store_heap, e);
X+ debug(20, 4) ("storeHashInsert: inserted node 0x%x\n", e->node);
X+ }
X+ #else /* HPL_REPL */
X dlinkAdd(e, &e->lru, &store_list);
X+ #endif /* HPL_REPL */
X }
X
X static void
X storeHashDelete(StoreEntry * e)
X {
X hash_remove_link(store_table, (hash_link *) e);
X+ #ifdef HPL_REPL
X+ if (e->node) {
X+ debug(20, 4) ("storeHashDelete: deleting node 0x%x\n", e->node);
X+ heap_delete(store_heap, e->node);
X+ e->node = 0;
X+ }
X+ #else /* HPL_REPL */
X dlinkDelete(&e->lru, &store_list);
X+ #endif /* HPL_REPL */
X storeKeyFree(e->key);
X e->key = NULL;
X }
X@@ -201,7 +229,7 @@
X {
X if (e->mem_obj == NULL)
X return;
X- debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
X+ debug(20, 4) ("storePurgeMem: Freeing memory-copy of %s\n",
X storeKeyText(e->key));
X storeSetMemStatus(e, NOT_IN_MEMORY);
X destroy_MemObject(e);
X@@ -213,10 +241,19 @@
X storeLockObject(StoreEntry * e)
X {
X if (e->lock_count++ == 0) {
X+ #ifdef HPL_REPL
X+ /* there is no reason to take any action here.
X+ Squid by default is moving locked objects to the end of the LRU
X+ list to keep them from getting bumped into by the replacement
X+ algorithm. We can't do that so we will just have to handle them.
X+ */
X+ debug(20, 4) ("storeLockObject: just locked node 0x%x\n", e->node);
X+ #else /* HPL_REPL */
X dlinkDelete(&e->lru, &store_list);
X dlinkAdd(e, &e->lru, &store_list);
X+ #endif /* HPL_REPL */
X }
X- debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
X+ debug(20, 4) ("storeLockObject: key '%s' count=%d\n",
X storeKeyText(e->key), (int) e->lock_count);
X e->lastref = squid_curtime;
X }
X@@ -243,7 +280,7 @@
X storeUnlockObject(StoreEntry * e)
X {
X e->lock_count--;
X- debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
X+ debug(20, 4) ("storeUnlockObject: key '%s' count=%d\n",
X storeKeyText(e->key), e->lock_count);
X if (e->lock_count)
X return (int) e->lock_count;
X@@ -259,14 +296,22 @@
X } else {
X storePurgeMem(e);
X if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
X+ #ifdef HPL_REPL
X+ /*
X+ * Squid/LRU is moving things around in the linked list in order
X+ * to keep from bumping into them when purging from the LRU list.
X+ */
X+ debug(20, 4) ("storeUnlockObject: purged private node 0x%x\n", e->node);
X+ #else /* HPL_REPL */
X dlinkDelete(&e->lru, &store_list);
X dlinkAddTail(e, &e->lru, &store_list);
X+ #endif /* HPL_REPL */
X }
X }
X return 0;
X }
X
X-/* Lookup an object in the cache.
X+/* Lookup an object in the cache.
X * return just a reference to object, don't start swapping in yet. */
X StoreEntry *
X storeGet(const cache_key * key)
X@@ -332,10 +377,14 @@
X * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
X * be set, and storeSetPublicKey() should not be called.
X */
X+#ifdef HPL_REPL_DEBUG
X+ if (EBIT_TEST(e->flags, RELEASE_REQUEST))
X+ debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n", e->key, mem->url);
X+#endif /* HPL_REPL */
X assert(!EBIT_TEST(e->flags, RELEASE_REQUEST));
X newkey = storeKeyPublic(mem->url, mem->method);
X if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
X- debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
X+ debug(20, 4) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
X storeSetPrivateKey(e2);
X storeRelease(e2);
X newkey = storeKeyPublic(mem->url, mem->method);
X@@ -399,7 +448,7 @@
X assert(len >= 0);
X assert(e->store_status == STORE_PENDING);
X if (len) {
X- debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
X+ debug(20, 6) ("storeAppend: appending %d bytes for '%s'\n",
X len,
X storeKeyText(e->key));
X storeGetMemSpace(len);
X@@ -484,25 +533,25 @@
X {
X #if CACHE_ALL_METHODS
X if (e->mem_obj->method != METHOD_GET) {
X- debug(20, 2) ("storeCheckCachable: NO: non-GET method\n");
X+ debug(20, 3) ("storeCheckCachable: NO: non-GET method\n");
X store_check_cachable_hist.no.non_get++;
X } else
X #endif
X if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) {
X- debug(20, 2) ("storeCheckCachable: NO: not cachable\n");
X+ debug(20, 3) ("storeCheckCachable: NO: not cachable\n");
X store_check_cachable_hist.no.not_entry_cachable++;
X } else if (EBIT_TEST(e->flags, RELEASE_REQUEST)) {
X- debug(20, 2) ("storeCheckCachable: NO: release requested\n");
X+ debug(20, 3) ("storeCheckCachable: NO: release requested\n");
X store_check_cachable_hist.no.release_request++;
X } else if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) {
X- debug(20, 2) ("storeCheckCachable: NO: wrong content-length\n");
X+ debug(20, 3) ("storeCheckCachable: NO: wrong content-length\n");
X store_check_cachable_hist.no.wrong_content_length++;
X } else if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) {
X debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
X store_check_cachable_hist.no.negative_cached++;
X return 0; /* avoid release call below */
X } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
X- debug(20, 2) ("storeCheckCachable: NO: too big\n");
X+ debug(20, 3) ("storeCheckCachable: NO: too big\n");
X store_check_cachable_hist.no.too_big++;
X } else if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
X debug(20, 3) ("storeCheckCachable: NO: private key\n");
X@@ -515,15 +564,22 @@
X */
X return 1;
X } else if (storeTooManyDiskFilesOpen()) {
X- debug(20, 2) ("storeCheckCachable: NO: too many disk files open\n");
X+ debug(20, 3) ("storeCheckCachable: NO: too many disk files open\n");
X store_check_cachable_hist.no.too_many_open_files++;
X } else if (fdNFree() < RESERVED_FD) {
X- debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n");
X+ debug(20, 3) ("storeCheckCachable: NO: too many FD's open\n");
X store_check_cachable_hist.no.too_many_open_fds++;
X+#ifdef HPL_REPL
X+ /*
X+ * With the HPL replacement policies a low reference age should not
X+ * prevent cacheability of an object. We do not use LRU age at all.
X+ */
X+#else /* HPL_REPL */
X } else if (storeExpiredReferenceAge() < 300) {
X- debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n",
X+ debug(20, 3) ("storeCheckCachable: NO: LRU Age = %d\n",
X storeExpiredReferenceAge());
X store_check_cachable_hist.no.lru_age_too_low++;
X+#endif /* HPL_REPL */
X } else {
X store_check_cachable_hist.yes.Default++;
X return 1;
X@@ -566,7 +622,7 @@
X void
X storeComplete(StoreEntry * e)
X {
X- debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->key));
X+ debug(20, 4) ("storeComplete: '%s'\n", storeKeyText(e->key));
X if (e->store_status != STORE_PENDING) {
X /*
X * if we're not STORE_PENDING, then probably we got aborted
X@@ -594,7 +650,7 @@
X /*
X * Someone wants to abort this transfer. Set the reason in the
X * request structure, call the server-side callback and mark the
X- * entry for releasing
X+ * entry for releasing
X */
X void
X storeAbort(StoreEntry * e)
X@@ -602,7 +658,7 @@
X MemObject *mem = e->mem_obj;
X assert(e->store_status == STORE_PENDING);
X assert(mem != NULL);
X- debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->key));
X+ debug(20, 7) ("storeAbort: %s\n", storeKeyText(e->key));
X storeLockObject(e); /* lock while aborting */
X storeNegativeCache(e);
X storeReleaseRequest(e);
X@@ -656,17 +712,63 @@
X static time_t last_check = 0;
X int pages_needed;
X dlink_node *m;
X- dlink_node *head;
X dlink_node *prev = NULL;
X+ int locked = 0;
X+ #ifdef HPL_REPL
X+ heap_p heap = inmem_heap;
X+ _HEAPKEY age, min_age = 0;
X+ dlink_list locked_entries;
X+
X+ locked_entries.head = locked_entries.tail = NULL;
X+ #else /* HPL_REPL */
X+ dlink_node *head;
X+ #endif /* HPL_REPL */
X+
X if (squid_curtime == last_check)
X return;
X last_check = squid_curtime;
X pages_needed = (size / SM_PAGE_SIZE) + 1;
X+ debug(20, 3) ("storeGetMemSpace: inuse %d, need %d, have %d\n",
X+ memInUse(MEM_STMEM_BUF), pages_needed, store_pages_max);
X if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
X return;
X if (store_rebuilding)
X return;
X debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
X+ #ifdef HPL_REPL
X+ while (heap_nodes(heap) > 0) {
X+ age = heap_peepminkey(heap);
X+ e = heap_extractmin(heap);
X+ e->mem_obj->node = NULL; /* no longer in the heap */
X+ if (storeEntryLocked(e)) {
X+ locked++;
X+ debug(20, 5) ("storeGetMemSpace: locked key %s\n", storeKeyText(e->key));
X+ dlinkAdd(e, &e->lock_list, &locked_entries);
X+ continue;
X+ }
X+ released++;
X+ debug(20, 3) ("Released memory object with key %f size %d refs %d url %s\n",
X+ age, e->swap_file_sz, e->refcount, e->mem_obj->url);
X+ min_age = age;
X+ storePurgeMem(e);
X+ if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
X+ break;
X+ }
X+ /*
X+ * Increase the heap age factor.
X+ */
X+ if (min_age > 0)
X+ heap->age = min_age;
X+ /*
X+ * Reinsert all bumped locked entries back into heap...
X+ */
X+ for (m = locked_entries.tail; m; m = prev) {
X+ prev = m->prev;
X+ e = m->data;
X+ e->mem_obj->node = heap_insert(inmem_heap, e);
X+ }
X+
X+ #else /* HPL_REPL */
X head = inmem_list.head;
X for (m = inmem_list.tail; m; m = prev) {
X if (m == head)
X@@ -674,6 +776,7 @@
X prev = m->prev;
X e = m->data;
X if (storeEntryLocked(e)) {
X+ locked++;
X dlinkDelete(m, &inmem_list);
X dlinkAdd(e, m, &inmem_list);
X continue;
X@@ -683,16 +786,19 @@
X if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
X break;
X }
X- debug(20, 3) ("storeGetMemSpace stats:\n");
X- debug(20, 3) (" %6d HOT objects\n", hot_obj_count);
X- debug(20, 3) (" %6d were released\n", released);
X+ #endif /* HPL_REPL */
X+ debug(20, 3) ("storeGetMemSpace: released %d/%d locked %d\n",
X+ released, hot_obj_count, locked);
X+ debug(20, 4) ("storeGetMemSpace stats:\n");
X+ debug(20, 4) (" %6d HOT objects\n", hot_obj_count);
X+ debug(20, 4) (" %6d were released\n", released);
X }
X
X /* The maximum objects to scan for maintain storage space */
X #define MAINTAIN_MAX_SCAN 1024
X #define MAINTAIN_MAX_REMOVE 64
X
X-/*
X+/*
X * This routine is to be called by main loop in main.c.
X * It removes expired objects on only one bucket for each time called.
X * returns the number of objects removed
X@@ -712,19 +818,102 @@
X int max_remove;
X double f;
X static time_t last_warn_time = 0;
X+ #ifdef HPL_REPL
X+ heap_p heap = store_heap;
X+ _HEAPKEY age, min_age = 0;
X+ dlink_list locked_entries;
X+
X+ locked_entries.head = locked_entries.tail = NULL;
X+ #ifdef HPL_REPL_DEBUG
X+ if (!verify_heap_property(store_heap)) {
X+ debug(20, 1) ("Heap property violated!\n");
X+ }
X+ #endif /* HPL_REPL_DEBUG */
X+ #endif /* HPL_REPL */
X+
X /* We can't delete objects while rebuilding swap */
X if (store_rebuilding) {
X eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
X return;
X } else {
X- f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
X+ f = ((double) (store_swap_size - store_swap_low) /
X+ (store_swap_high - store_swap_low));
X f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
X max_scan = (int) (f * 400.0 + 100.0);
X- max_remove = (int) (f * 70.0 + 10.0);
X- eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
X+ max_remove = (int) (f * 40.0 + 10.0);
X+ eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 10*(1.0 - f), 1);
X+ }
X+
X+ if (store_swap_size < store_swap_low) {
X+ /* Just return since we still have enougy memory */
X+ return;
X }
X- debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n",
X+
X+ debug(20, 2) ("storeMaintainSwapSpace: Starting, f=%f, max_scan=%d, max_remove=%d\n",
X f, max_scan, max_remove);
X+
X+#ifdef HPL_REPL
X+
X+ while (heap_nodes(heap) > 0) {
X+ age = heap_peepminkey(heap);
X+ e = heap_extractmin(heap);
X+ e->node = NULL; /* no longer in the heap */
X+ scanned++;
X+ if (storeEntryLocked(e)) {
X+ /*
X+ * Entry is in use ... put it in a linked list to ignore it.
X+ */
X+ if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
X+ /*
X+ * If this was a "SPECIAL" do not add it back into the heap.
X+ * It will always be "SPECIAL" and therefore never removed.
X+ */
X+ debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n",
X+ (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->key));
X+ dlinkAdd(e, &e->lock_list, &locked_entries);
X+ }
X+ locked++;
X+ continue;
X+ } else if (storeCheckExpired(e)) {
X+ /*
X+ * Note: This will not check the reference age ifdef HPL_REPL,
X+ * but it does some other useful checks...
X+ */
X+ expired++;
X+ debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
X+ age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
X+ min_age = age;
X+ storeRelease(e);
X+ } else {
X+ /*
X+ * Did not expire the object so we need to add it back into the heap!
X+ */
X+ debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n", storeKeyText(e->key));
X+ dlinkAdd(e, &e->lock_list, &locked_entries);
X+ continue;
X+ }
X+ if ((store_swap_size < store_swap_low)
X+ || (expired >= max_remove)
X+ || (scanned >= max_scan))
X+ break;
X+ }
X+ /*
X+ * Bump the heap age factor.
X+ */
X+ if (min_age > 0)
X+ heap->age = min_age;
X+
X+ /*
X+ * Reinsert all bumped locked entries back into heap...
X+ */
X+ for (m = locked_entries.tail; m; m = prev) {
X+ prev = m->prev;
X+ e = m->data;
X+ e->node = heap_insert(store_heap, e);
X+ }
X+
X+#else /* HPL_REPL */
X+
X for (m = store_list.tail; m; m = prev) {
X prev = m->prev;
X e = m->data;
X@@ -751,11 +940,16 @@
X if (scanned >= max_scan)
X break;
X }
X- debug(20, 3) ("storeMaintainSwapSpace stats:\n");
X- debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY));
X- debug(20, 3) (" %6d were scanned\n", scanned);
X- debug(20, 3) (" %6d were locked\n", locked);
X- debug(20, 3) (" %6d were expired\n", expired);
X+
X+#endif /* HPL_REPL */
X+
X+ debug(20, (expired?2:3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n",
X+ scanned, max_scan, expired, max_remove, locked, f);
X+ debug(20, 4) ("storeMaintainSwapSpace stats:\n");
X+ debug(20, 4) (" %6d objects\n", memInUse(MEM_STOREENTRY));
X+ debug(20, 4) (" %6d were scanned\n", scanned);
X+ debug(20, 4) (" %6d were locked\n", locked);
X+ debug(20, 4) (" %6d were expired\n", expired);
X if (store_swap_size < Config.Swap.maxSize)
X return;
X if (squid_curtime - last_warn_time < 10)
X@@ -771,12 +965,12 @@
X void
X storeRelease(StoreEntry * e)
X {
X- debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
X+ debug(20, 4) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
X /* If, for any reason we can't discard this object because of an
X * outstanding request, mark it for pending release */
X if (storeEntryLocked(e)) {
X storeExpireNow(e);
X- debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
X+ debug(20, 4) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
X storeReleaseRequest(e);
X return;
X }
X@@ -811,6 +1005,7 @@
X storeDirSwapLog(e, SWAP_LOG_DEL);
X }
X storeSetMemStatus(e, NOT_IN_MEMORY);
X+
X destroy_StoreEntry(e);
X }
X
X@@ -865,25 +1060,25 @@
X const HttpReply *reply;
X assert(e->mem_obj != NULL);
X reply = e->mem_obj->reply;
X- debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
X- debug(20, 5) ("storeEntryValidLength: object_len = %d\n",
X+ debug(20, 4) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
X+ debug(20, 6) ("storeEntryValidLength: object_len = %d\n",
X objectLen(e));
X- debug(20, 5) ("storeEntryValidLength: hdr_sz = %d\n",
X+ debug(20, 6) ("storeEntryValidLength: hdr_sz = %d\n",
X reply->hdr_sz);
X- debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
X+ debug(20, 6) ("storeEntryValidLength: content_length = %d\n",
X reply->content_length);
X if (reply->content_length < 0) {
X- debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
X+ debug(20, 6) ("storeEntryValidLength: Unspecified content length: %s\n",
X storeKeyText(e->key));
X return 1;
X }
X if (reply->hdr_sz == 0) {
X- debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
X+ debug(20, 6) ("storeEntryValidLength: Zero header size: %s\n",
X storeKeyText(e->key));
X return 1;
X }
X if (e->mem_obj->method == METHOD_HEAD) {
X- debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
X+ debug(20, 6) ("storeEntryValidLength: HEAD request: %s\n",
X storeKeyText(e->key));
X return 1;
X }
X@@ -894,7 +1089,7 @@
X diff = reply->hdr_sz + reply->content_length - objectLen(e);
X if (diff == 0)
X return 1;
X- debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
X+ debug(20, 4) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
X diff < 0 ? -diff : diff,
X diff < 0 ? "big" : "small",
X storeKeyText(e->key));
X@@ -924,6 +1119,70 @@
X debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
X }
X
X+#ifdef HPL_REPL
X+
X+/*
X+ * For a description of these cache replacement policies see --
X+ http://www.hpl.hp.com/personal/John_Dilley/caching/wcw.html
X+ */
X+
X+
X+/*
X+ * Key generation function to implement the LFU-DA policy (Least Frequently
X+ * Used with Dynamic Aging). Similar to classical LFU but with aging to
X+ * handle turnover of the popular document set. Maximizes byte hit rate by
X+ * keeping more currently popular objects in cache regardless of size.
X+ * Achieves lower hit rate than GDS because there are more large objects in
X+ * cache (so less room for smaller popular objects).
X+ *
X+ * This version implements a tie-breaker based upon recency (e->lastref):
X+ * for objects that have the same reference count the most recent object
X+ * wins (gets a higher key value).
X+ */
X+double
X+HeapKeyGen_StoreEntry_LFUDA(void * entry, double age)
X+{
X+ StoreEntry * e = entry;
X+ double tie = (e->lastref>1)?(1.0/e->lastref):1;
X+ return age + e->refcount - tie;
X+}
X+
X+
X+/*
X+ * Key generation function to implement the GDS-Frequency policy.
X+ * Similar to Greedy Dual-Size Hits policy, but adds aging of documents to
X+ * prevent pollution. Maximizes object hit rate by keeping more small,
X+ * popular objects in cache. Achieves lower byte hit rate than LFUDA
X+ * because there are fewer large objects in cache.
X+ *
X+ * This version implements a tie-breaker based upon recency (e->lastref):
X+ * for objects that have the same reference count the most recent object
X+ * wins (gets a higher key value).
X+ */
X+double
X+HeapKeyGen_StoreEntry_GDSF(void * entry, double age)
X+{
X+ StoreEntry * e = entry;
X+ double size = e->swap_file_sz ? e->swap_file_sz : 1.0;
X+ double tie = (e->lastref>1)?(1.0/e->lastref):1;
X+ return age + ((double)e->refcount / size) - tie;
X+}
X+
X+/*
X+ * Key generation function to implement the LRU policy. Normally one would
X+ * not do this with a heap -- use the linked list instead. For testing and
X+ * performance characterization it was useful. Don't use it unless you are
X+ * trying to compare performance among heap-based replacement policies...
X+ */
X+double
X+HeapKeyGen_StoreEntry_LRU(void * entry, double age)
X+{
X+ StoreEntry * e = entry;
X+ return e->lastref;
X+}
X+
X+#endif /* HPL_REPL */
X+
X void
X storeInit(void)
X {
X@@ -942,8 +1201,45 @@
X fatal(tmp_error_buf);
X }
X storeDirOpenSwapLogs();
X+
X+ #ifdef HPL_REPL
X+ /*
X+ * Create new heaps with cache replacement policies attached to them.
X+ * The cache replacement policy is specified as either GDSF or LFUDA in
X+ * the squid.conf configuration file. Note that the replacement policy
X+ * applies only to the disk replacement algorithm. Memory replacement
X+ * always uses GDSF since we want to maximize object hit rate.
X+ */
X+ inmem_heap = new_heap(1000, HeapKeyGen_StoreEntry_GDSF);
X+
X+ if (Config.replPolicy) {
X+ if (tolower(Config.replPolicy[0]) == 'g') {
X+ debug(20, 1) ("Using GDSF disk replacement policy\n");
X+ store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
X+ } else if (tolower(Config.replPolicy[0]) == 'l') {
X+ if (tolower(Config.replPolicy[1]) == 'f') {
X+ debug(20, 1) ("Using LFUDA disk replacement policy\n");
X+ store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
X+ } else if (tolower(Config.replPolicy[1]) == 'r') {
X+ debug(20, 1) ("Using LRU heap disk replacement policy\n");
X+ store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
X+ }
X+ } else {
X+ debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
X+ store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
X+ }
X+ } else {
X+ debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
X+ store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
X+ }
X+
X+ #else /* HPL_REPL */
X+
X store_list.head = store_list.tail = NULL;
X inmem_list.head = inmem_list.tail = NULL;
X+
X+ #endif /* HPL_REPL */
X+
X stackInit(&LateReleaseStack);
X eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
X storeRebuildStart();
X@@ -986,12 +1282,25 @@
X return 1;
X if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
X return 1;
X+#ifdef HPL_REPL
X+ /*
X+ * With HPL_REPL we are not using the LRU reference age, the heap
X+ * controls the replacement of objects.
X+ */
X+ return 1;
X+#else /* HPL_REPL */
X if (squid_curtime - e->lastref > storeExpiredReferenceAge())
X return 1;
X return 0;
X+#endif /* HPL_REPL */
X }
X
X-/*
X+#ifdef HPL_REPL
X+/*
X+ * The non-LRU cache replacement policies do not use LRU referenceAge
X+ */
X+#else /* HPL_REPL */
X+/*
X * storeExpiredReferenceAge
X *
X * The LRU age is scaled exponentially between 1 minute and
X@@ -1018,6 +1327,7 @@
X age = 31536000;
X return age;
X }
X+#endif /* HPL_REPL */
X
X void
X storeNegativeCache(StoreEntry * e)
X@@ -1153,10 +1463,33 @@
X assert(mem != NULL);
X if (new_status == IN_MEMORY) {
X assert(mem->inmem_lo == 0);
X+ #ifdef HPL_REPL
X+ if (mem->node == NULL) {
X+ if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
X+ debug(20, 4) ("storeSetMemStatus: not inserting special %s\n", mem->url);
X+ } else {
X+ mem->node = heap_insert(inmem_heap, e);
X+ debug(20, 4) ("storeSetMemStatus: inserted mem node 0x%x\n", mem->node);
X+ }
X+ }
X+ #else /* HPL_REPL */
X dlinkAdd(e, &mem->lru, &inmem_list);
X+ #endif /* HPL_REPL */
X hot_obj_count++;
X } else {
X+ #ifdef HPL_REPL
X+ /*
X+ * It's being removed from the memory heap; is it already gone?
X+ */
X+ if (mem->node) {
X+ heap_delete(inmem_heap, mem->node);
X+ debug(20, 4) ("storeSetMemStatus: deleted mem node 0x%x\n",
X+ mem->node);
X+ mem->node = 0;
X+ }
X+ #else /* HPL_REPL */
X dlinkDelete(&mem->lru, &inmem_list);
X+ #endif /* HPL_REPL */
X hot_obj_count--;
X }
X e->mem_status = new_status;
X@@ -1200,7 +1533,7 @@
X void
X storeUnlinkFileno(int fileno)
X {
X- debug(20, 5) ("storeUnlinkFileno: %08X\n", fileno);
X+ debug(20, 6) ("storeUnlinkFileno: %08X\n", fileno);
X #if USE_ASYNC_IO
X safeunlink(storeSwapFullPath(fileno, NULL), 1);
X #else
X@@ -1237,7 +1570,7 @@
X storeEntryReset(StoreEntry * e)
X {
X MemObject *mem = e->mem_obj;
X- debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e));
X+ debug(20, 4) ("storeEntryReset: %s\n", storeUrl(e));
X assert(mem->swapout.fd == -1);
X stmemFree(&mem->data_hdr);
X mem->inmem_hi = mem->inmem_lo = 0;
Xdiff -r -u squid-2.2.STABLE3/src/store_client.c squid-2.2.STABLE3-jad/src/store_client.c
X--- squid-2.2.STABLE3/src/store_client.c Wed May 12 11:49:21 1999
X+++ squid-2.2.STABLE3-jad/src/store_client.c Fri Jun 4 14:49:23 1999
X@@ -141,7 +141,7 @@
X storeClientCopyEvent(void *data)
X {
X store_client *sc = data;
X- debug(20, 3) ("storeClientCopyEvent: Running\n");
X+ debug(20, 4) ("storeClientCopyEvent: Running\n");
X sc->flags.copy_event_pending = 0;
X if (!sc->callback)
X return;
X@@ -160,7 +160,7 @@
X {
X store_client *sc;
X assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
X- debug(20, 3) ("storeClientCopy: %s, seen %d, want %d, size %d, cb %p, cbdata %p\n",
X+ debug(20, 4) ("storeClientCopy: %s, seen %d, want %d, size %d, cb %p, cbdata %p\n",
X storeKeyText(e->key),
X (int) seen_offset,
X (int) copy_offset,
X@@ -215,13 +215,13 @@
X }
X if (sc->flags.store_copying) {
X sc->flags.copy_event_pending = 1;
X- debug(20, 3) ("storeClientCopy2: Queueing storeClientCopyEvent()\n");
X+ debug(20, 4) ("storeClientCopy2: Queueing storeClientCopyEvent()\n");
X eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0);
X return;
X }
X cbdataLock(sc); /* ick, prevent sc from getting freed */
X sc->flags.store_copying = 1;
X- debug(20, 3) ("storeClientCopy2: %s\n", storeKeyText(e->key));
X+ debug(20, 4) ("storeClientCopy2: %s\n", storeKeyText(e->key));
X assert(callback != NULL);
X /*
X * We used to check for ENTRY_ABORTED here. But there were some
X@@ -245,10 +245,10 @@
X callback(sc->callback_data, sc->copy_buf, 0);
X } else if (e->store_status == STORE_PENDING && sc->seen_offset >= mem->inmem_hi) {
X /* client has already seen this, wait for more */
X- debug(20, 3) ("storeClientCopy2: Waiting for more\n");
X+ debug(20, 4) ("storeClientCopy2: Waiting for more\n");
X } else if (sc->copy_offset >= mem->inmem_lo && sc->copy_offset < mem->inmem_hi) {
X /* What the client wants is in memory */
X- debug(20, 3) ("storeClientCopy2: Copying from memory\n");
X+ debug(20, 4) ("storeClientCopy2: Copying from memory\n");
X sz = stmemCopy(&mem->data_hdr, sc->copy_offset, sc->copy_buf, sc->copy_size);
X #if USE_ASYNC_IO
X if (sc->flags.disk_io_pending) {
X@@ -262,7 +262,7 @@
X sc->callback = NULL;
X callback(sc->callback_data, sc->copy_buf, sz);
X } else if (sc->swapin_fd < 0) {
X- debug(20, 3) ("storeClientCopy2: Need to open swap in file\n");
X+ debug(20, 4) ("storeClientCopy2: Need to open swap in file\n");
X assert(sc->type == STORE_DISK_CLIENT);
X /* gotta open the swapin file */
X if (storeTooManyDiskFilesOpen()) {
X@@ -276,7 +276,7 @@
X debug(20, 2) ("storeClientCopy2: Averted multiple fd operation\n");
X }
X } else {
X- debug(20, 3) ("storeClientCopy: reading from disk FD %d\n",
X+ debug(20, 4) ("storeClientCopy: reading from disk FD %d\n",
X sc->swapin_fd);
X assert(sc->type == STORE_DISK_CLIENT);
X if (!sc->flags.disk_io_pending) {
X@@ -295,7 +295,7 @@
X store_client *sc = data;
X STCB *callback = sc->callback;
X if (fd < 0) {
X- debug(20, 3) ("storeClientFileOpened: failed\n");
X+ debug(20, 4) ("storeClientFileOpened: failed\n");
X sc->flags.disk_io_pending = 0;
X sc->callback = NULL;
X callback(sc->callback_data, sc->copy_buf, -1);
X@@ -344,7 +344,7 @@
X assert(sc->flags.disk_io_pending);
X sc->flags.disk_io_pending = 0;
X assert(sc->callback != NULL);
X- debug(20, 3) ("storeClientReadBody: FD %d, len %d\n", fd, len);
X+ debug(20, 4) ("storeClientReadBody: FD %d, len %d\n", fd, len);
X if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
X httpReplyParse(mem->reply, sc->copy_buf);
X sc->callback = NULL;
X@@ -365,9 +365,9 @@
X assert(sc->flags.disk_io_pending);
X sc->flags.disk_io_pending = 0;
X assert(sc->callback != NULL);
X- debug(20, 3) ("storeClientReadHeader: FD %d, len %d\n", fd, len);
X+ debug(20, 4) ("storeClientReadHeader: FD %d, len %d\n", fd, len);
X if (len < 0) {
X- debug(20, 3) ("storeClientReadHeader: FD %d: %s\n", fd, xstrerror());
X+ debug(20, 4) ("storeClientReadHeader: FD %d: %s\n", fd, xstrerror());
X sc->callback = NULL;
X callback(sc->callback_data, sc->copy_buf, len);
X return;
X@@ -396,7 +396,7 @@
X * we have (part of) what they want
X */
X copy_sz = XMIN(sc->copy_size, body_sz);
X- debug(20, 3) ("storeClientReadHeader: copying %d bytes of body\n",
X+ debug(20, 4) ("storeClientReadHeader: copying %d bytes of body\n",
X copy_sz);
X xmemmove(sc->copy_buf, sc->copy_buf + swap_hdr_sz, copy_sz);
X if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
X@@ -433,7 +433,7 @@
X STCB *callback;
X if (mem == NULL)
X return 0;
X- debug(20, 3) ("storeUnregister: called for '%s'\n", storeKeyText(e->key));
X+ debug(20, 4) ("storeUnregister: called for '%s'\n", storeKeyText(e->key));
X for (S = &mem->clients; (sc = *S) != NULL; S = &(*S)->next) {
X if (sc->callback_data == data)
X break;
X@@ -464,7 +464,7 @@
X #endif
X if ((callback = sc->callback) != NULL) {
X /* callback with ssize = -1 to indicate unexpected termination */
X- debug(20, 3) ("storeUnregister: store_client for %s has a callback\n",
X+ debug(20, 4) ("storeUnregister: store_client for %s has a callback\n",
X mem->url);
X sc->callback = NULL;
X callback(sc->callback_data, sc->copy_buf, -1);
X@@ -507,11 +507,11 @@
X store_client *sc;
X store_client *nx = NULL;
X assert(mem->clients != NULL || mem->nclients == 0);
X- debug(20, 3) ("InvokeHandlers: %s\n", storeKeyText(e->key));
X+ debug(20, 4) ("InvokeHandlers: %s\n", storeKeyText(e->key));
X /* walk the entire list looking for valid callbacks */
X for (sc = mem->clients; sc; sc = nx) {
X nx = sc->next;
X- debug(20, 3) ("InvokeHandlers: checking client #%d\n", i++);
X+ debug(20, 4) ("InvokeHandlers: checking client #%d\n", i++);
X if (sc->callback_data == NULL)
X continue;
X if (sc->callback == NULL)
X@@ -525,7 +525,7 @@
X {
X MemObject *mem = e->mem_obj;
X int npend = NULL == mem ? 0 : mem->nclients;
X- debug(20, 3) ("storePendingNClients: returning %d\n", npend);
X+ debug(20, 4) ("storePendingNClients: returning %d\n", npend);
X return npend;
X }
X
X@@ -538,43 +538,43 @@
X int expectlen;
X MemObject *mem = entry->mem_obj;
X assert(mem);
X- debug(20, 3) ("CheckQuickAbort2: entry=%p, mem=%p\n", entry, mem);
X+ debug(20, 4) ("CheckQuickAbort2: entry=%p, mem=%p\n", entry, mem);
X if (mem->request && !mem->request->flags.cachable) {
X- debug(20, 3) ("CheckQuickAbort2: YES !mem->request->flags.cachable\n");
X+ debug(20, 4) ("CheckQuickAbort2: YES !mem->request->flags.cachable\n");
X return 1;
X }
X if (EBIT_TEST(entry->flags, KEY_PRIVATE)) {
X- debug(20, 3) ("CheckQuickAbort2: YES KEY_PRIVATE\n");
X+ debug(20, 4) ("CheckQuickAbort2: YES KEY_PRIVATE\n");
X return 1;
X }
X expectlen = mem->reply->content_length + mem->reply->hdr_sz;
X curlen = (int) mem->inmem_hi;
X minlen = (int) Config.quickAbort.min << 10;
X if (minlen < 0) {
X- debug(20, 3) ("CheckQuickAbort2: NO disabled\n");
X+ debug(20, 4) ("CheckQuickAbort2: NO disabled\n");
X return 0;
X }
X if (curlen > expectlen) {
X- debug(20, 3) ("CheckQuickAbort2: YES bad content length\n");
X+ debug(20, 4) ("CheckQuickAbort2: YES bad content length\n");
X return 1;
X }
X if ((expectlen - curlen) < minlen) {
X- debug(20, 3) ("CheckQuickAbort2: NO only little more left\n");
X+ debug(20, 4) ("CheckQuickAbort2: NO only little more left\n");
X return 0;
X }
X if ((expectlen - curlen) > (Config.quickAbort.max << 10)) {
X- debug(20, 3) ("CheckQuickAbort2: YES too much left to go\n");
X+ debug(20, 4) ("CheckQuickAbort2: YES too much left to go\n");
X return 1;
X }
X if (expectlen < 100) {
X- debug(20, 3) ("CheckQuickAbort2: NO avoid FPE\n");
X+ debug(20, 4) ("CheckQuickAbort2: NO avoid FPE\n");
X return 0;
X }
X if ((curlen / (expectlen / 100)) > Config.quickAbort.pct) {
X- debug(20, 3) ("CheckQuickAbort2: NO past point of no return\n");
X+ debug(20, 4) ("CheckQuickAbort2: NO past point of no return\n");
X return 0;
X }
X- debug(20, 3) ("CheckQuickAbort2: YES default, returning 1\n");
X+ debug(20, 4) ("CheckQuickAbort2: YES default, returning 1\n");
X return 1;
X }
X
Xdiff -r -u squid-2.2.STABLE3/src/store_dir.c squid-2.2.STABLE3-jad/src/store_dir.c
X--- squid-2.2.STABLE3/src/store_dir.c Sat Feb 13 16:23:11 1999
X+++ squid-2.2.STABLE3-jad/src/store_dir.c Fri Jun 4 10:37:50 1999
X@@ -662,11 +662,16 @@
X char **cln;
X int dirn;
X int N = Config.cacheSwap.n_configured;
X- dlink_node *m;
X char **outbuf;
X off_t *outbufoffset;
X storeSwapLogData s;
X size_t ss = sizeof(storeSwapLogData);
X+ #ifdef HPL_REPL
X+ int node;
X+ #else /* HPL_REPL */
X+ dlink_node *m;
X+ #endif /* HPL_REPL */
X+
X if (store_rebuilding) {
X debug(20, 1) ("Not currently OK to rewrite swap log.\n");
X debug(20, 1) ("storeDirWriteCleanLogs: Operation aborted.\n");
X@@ -707,8 +712,17 @@
X outbuf[dirn] = xcalloc(CLEAN_BUF_SZ, 1);
X outbufoffset[dirn] = 0;
X }
X- for (m = store_list.tail; m; m = m->prev) {
X+ #ifdef HPL_REPL
X+ for (node=0; node < heap_nodes(store_heap); node++)
X+ #else /* HPL_REPL */
X+ for (m = store_list.tail; m; m = m->prev)
X+ #endif /* HPL_REPL */
X+ {
X+ #ifdef HPL_REPL
X+ e = (StoreEntry*)heap_peep(store_heap, node);
X+ #else /* HPL_REPL */
X e = m->data;
X+ #endif /* HPL_REPL */
X if (e->swap_file_number < 0)
X continue;
X if (e->swap_status != SWAPOUT_DONE)
Xdiff -r -u squid-2.2.STABLE3/src/store_rebuild.c squid-2.2.STABLE3-jad/src/store_rebuild.c
X--- squid-2.2.STABLE3/src/store_rebuild.c Tue Apr 27 14:54:24 1999
X+++ squid-2.2.STABLE3-jad/src/store_rebuild.c Fri Jun 4 14:44:01 1999
X@@ -119,7 +119,7 @@
X tlv *tlv_list;
X tlv *t;
X assert(d != NULL);
X- debug(20, 3) ("storeRebuildFromDirectory: DIR #%d\n", d->dirn);
X+ debug(20, 2) ("storeRebuildFromDirectory: DIR #%d\n", d->dirn);
X for (count = 0; count < d->speed; count++) {
X assert(fd == -1);
X fd = storeGetNextFile(d, &sfileno, &size);
X@@ -327,6 +327,14 @@
X e->lastmod = s.lastmod;
X e->flags = s.flags;
X e->refcount += s.refcount;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (e->node)
X+ heap_update(store_heap, e->node, e);
X+ if (e->mem_obj->node)
X+ heap_update(inmem_heap, e->mem_obj->node, e);
X+#endif /* HPL_REPL */
X+
X } else {
X debug_trap("storeRebuildFromSwapLog: bad condition");
X debug(20, 1) ("\tSee %s:%d\n", __FILE__, __LINE__);
X@@ -565,7 +573,6 @@
X e->swap_file_number = file_number;
X e->swap_file_sz = swap_file_sz;
X e->lock_count = 0;
X- e->refcount = 0;
X e->lastref = lastref;
X e->timestamp = timestamp;
X e->expires = expires;
Xdiff -r -u squid-2.2.STABLE3/src/store_swapout.c squid-2.2.STABLE3-jad/src/store_swapout.c
X--- squid-2.2.STABLE3/src/store_swapout.c Fri Jan 29 09:36:37 1999
X+++ squid-2.2.STABLE3-jad/src/store_swapout.c Tue Jun 1 09:47:56 1999
X@@ -114,6 +114,13 @@
X debug(20, 5) ("storeSwapOutHandle: SwapOut complete: '%s' to %s.\n",
X storeUrl(e), storeSwapFullPath(e->swap_file_number, NULL));
X e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz;
X+#ifdef HPL_REPL
X+ /* Update the position of this object in the store and memory heaps. */
X+ if (e->node)
X+ heap_update(store_heap, e->node, e);
X+ if (e->mem_obj->node)
X+ heap_update(inmem_heap, e->mem_obj->node, e);
X+#endif /* HPL_REPL */
X e->swap_status = SWAPOUT_DONE;
X storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1);
X if (storeCheckCachable(e)) {
Xdiff -r -u squid-2.2.STABLE3/src/structs.h squid-2.2.STABLE3-jad/src/structs.h
X--- squid-2.2.STABLE3/src/structs.h Fri Feb 19 14:35:36 1999
X+++ squid-2.2.STABLE3-jad/src/structs.h Fri Jun 4 10:35:15 1999
X@@ -231,6 +231,13 @@
X int pct;
X size_t max;
X } quickAbort;
X+#ifdef HPL_REPL
X+ char * replPolicy;
X+#endif /* HPL_REPL */
X+ /*
X+ * Note: the non-LRU policies do not use referenceAge, but we cannot
X+ * remove it until we find out how to implement #else for cf_parser.c
X+ */
X time_t referenceAge;
X time_t negativeTtl;
X time_t negativeDnsTtl;
X@@ -1195,6 +1202,9 @@
X #endif
X };
X
X+#ifdef HPL_REPL
X+#include "heap.h"
X+#endif /* HPL_REPL */
X
X /* This structure can be freed while object is purged out from memory */
X struct _MemObject {
X@@ -1222,7 +1232,14 @@
X void *data;
X } abort;
X char *log_url;
X+ #ifdef HPL_REPL
X+ /*
X+ * A MemObject knows where it is in the in-memory heap.
X+ */
X+ heap_node_p node;
X+ #else /* HPL_REPL */
X dlink_node lru;
X+ #endif /* HPL_REPL */
X int id;
X ssize_t object_sz;
X size_t swap_hdr_sz;
X@@ -1241,7 +1258,12 @@
X u_short refcount;
X u_short flags;
X int swap_file_number;
X+ #ifdef HPL_REPL
X+ heap_node_p node;
X+ dlink_node lock_list;
X+ #else /* HPL_REPL */
X dlink_node lru;
X+ #endif /* HPL_REPL */
X u_short lock_count; /* Assume < 65536! */
X mem_status_t mem_status:3;
X ping_status_t ping_status:3;
Xdiff -r -u squid-2.2.STABLE3/src/tools.c squid-2.2.STABLE3-jad/src/tools.c
X--- squid-2.2.STABLE3/src/tools.c Tue May 11 13:37:20 1999
X+++ squid-2.2.STABLE3-jad/src/tools.c Tue Jun 1 09:47:56 1999
X@@ -211,12 +211,17 @@
X }
X
X
X+
X+
X void
X PrintRusage(void)
X {
X struct rusage rusage;
X squid_getrusage(&rusage);
X- fprintf(debug_log, "CPU Usage: %.3f seconds\n", rusage_cputime(&rusage));
X+ fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n",
X+ rusage_cputime(&rusage),
X+ rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0),
X+ rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0));
X fprintf(debug_log, "Maximum Resident Size: %d KB\n",
X rusage_maxrss(&rusage));
X fprintf(debug_log, "Page faults with physical i/o: %d\n",
SHAR_EOF
  : || $echo 'restore of' 'jad.diff' 'failed'
fi
# ============= include/heap.h ==============
if test ! -d 'include'; then
  $echo 'x -' 'creating directory' 'include'
  mkdir 'include'
fi
if test -f 'include/heap.h' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'include/heap.h' '(file already exists)'
else
  $echo 'x -' extracting 'include/heap.h' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'include/heap.h' &&
X/****************************************************************************
X * Heap data structure. Used to store objects for cache replacement. The
X * heap is implemented as a contiguous array in memory. Heap sort and heap
X * update are done in-place. The heap is ordered with the smallest value at
X * the top of the heap (as in the smallest object key value). Child nodes
X * are larger than their parent.
X ****************************************************************************/
X
X#ifndef _heap_h_INCLUDED
X#define _heap_h_INCLUDED
X
X/*
X * Typdefs for non-synchronized heap implementation.
X */
Xtypedef unsigned long mutex_t;
X# define mutex_lock(m) (void)0
X# define mutex_unlock(m) (void)0
X# define mutex_trylock(m) (void)0
X# define mutex_init(m) ((m)=123456)
X
X/*
X * Function for generating heap keys. The first argument will typically be
X * a dws_md_p passed in as a void *. Should find a way to get type safety
X * without having heap know all about metadata objects... The second arg is
X * the current aging factor for the heap.
X */
X# define _HEAPTYPE void *
X# define _HEAPKEY double
X
Xtypedef _HEAPKEY (*key_func) (_HEAPTYPE, _HEAPKEY);
X
X
X/*
X * Heap node. Has a key value generated by a key_func, id (array index) so
X * it can be quickly found in its heap, and a pointer to a data object that
X * key_func can generate a key from.
X */
Xtypedef struct _heap_node {
X _HEAPKEY key;
X unsigned long id;
X _HEAPTYPE data;
X} heap_node, *heap_node_p;
X
X
X/*
X * Heap object. Holds an array of heap_node objects along with a heap size
X * (array length), the index of the last heap element, and a key generation
X * function. Also stores aging factor for this heap.
X */
Xtypedef struct _heap {
X mutex_t lock;
X unsigned long size;
X unsigned long last;
X key_func gen_key; /* key generator for heap */
X _HEAPKEY age; /* aging factor for heap */
X heap_node ** nodes;
X} heap, *heap_p;
X
X/****************************************************************************
X * Public functions
X ****************************************************************************/
X
X/*
X * Create and initialize a new heap.
X */
Xextern heap_p new_heap(int init_size, key_func gen_key);
X
X/*
X * Delete a heap and clean up its memory. Does not delete what the heap
X * nodes are pointing to!
X */
Xextern void delete_heap(heap_p);
X
X/*
X * Insert a new node into a heap, returning a pointer to it. The heap_node
X * object returned is used to update or delete a heap object. Nothing else
X * should be done with this data structure (especially modifying it!) The
X * heap does not assume ownership of the data passed to it.
X */
Xextern heap_node_p heap_insert(heap_p, _HEAPTYPE dat);
X
X/*
X * Delete a node out of a heap. Returns the heap data from the deleted
X * node. The caller is responsible for freeing this data.
X */
Xextern _HEAPTYPE heap_delete(heap_p, heap_node_p elm);
X
X/*
X * The semantics of this routine is the same as the followings:
X * heap_delete(hp, elm);
X * heap_insert(hp, dat);
X * Returns the old data object from elm (the one being replaced). The
X * caller must free this as necessary.
X */
Xextern _HEAPTYPE heap_update(heap_p, heap_node* elm, _HEAPTYPE dat);
X
X/*
X * Generate a heap key for a given data object. Alternative macro form:
X */
X#ifdef MACRO_DEBUG
Xextern _HEAPKEY heap_gen_key(heap_p hp, _HEAPTYPE dat);
X#else
X# define heap_gen_key(hp,md) ((hp)->gen_key((md),(hp)->age))
X#endif /* MACRO_DEBUG */
X
X
X/*
X * Extract the minimum (root) element and maintain the heap property.
X * Returns the data pointed to by the root node, which the caller must
X * free as necessary.
X */
Xextern _HEAPTYPE heap_extractmin(heap_p);
X
X/*
X * Extract the last leaf node (does not change the heap property).
X * Returns the data that had been in the heap which the caller must free if
X * necessary. Note that the last node is guaranteed to be less than its
X * parent, but may not be less than any of the other (leaf or parent) notes
X * in the tree. This operation is fast but imprecise.
X */
Xextern _HEAPTYPE heap_extractlast(heap_p hp);
X
X/*
X * Get the root key, the nth key, the root (smallest) element, or the nth
X * element. None of these operations modify the heap.
X */
Xextern _HEAPKEY heap_peepminkey(heap_p);
Xextern _HEAPKEY heap_peepkey(heap_p, int n);
X
Xextern _HEAPTYPE heap_peepmin(heap_p);
Xextern _HEAPTYPE heap_peep(heap_p, int n);
X
X/*
X * Is the heap empty? How many nodes (data objects) are in it?
X */
X#ifdef MACRO_DEBUG
Xextern int heap_empty(heap_p);
Xextern int heap_nodes(heap_p);
X#else /* MACRO_DEBUG */
X# define heap_nodes(heap) ((heap)->last)
X# define heap_empty(heap) (((heap)->last <= 0) ? 1 : 0)
X#endif /* MACRO_DEBUG */
X
X/*
X * Print the heap or a node in the heap.
X */
Xextern void heap_print(heap_p);
Xextern void heap_printnode(char *msg, heap_node_p elm);
X
Xextern int verify_heap_property(heap_p);
X
X#endif /* _heap_h_INCLUDED */
SHAR_EOF
  : || $echo 'restore of' 'include/heap.h' 'failed'
fi
# ============= lib/heap.c ==============
if test ! -d 'lib'; then
  $echo 'x -' 'creating directory' 'lib'
  mkdir 'lib'
fi
if test -f 'lib/heap.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'lib/heap.c' '(file already exists)'
else
  $echo 'x -' extracting 'lib/heap.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'lib/heap.c' &&
X/****************************************************************************
X * Heap implementation
X ****************************************************************************/
X
X#include <stdlib.h>
X#include "malloc.h"
X#include <assert.h>
X#include <string.h>
X#include <stdio.h>
X
X#include "heap.h"
X
X/*
X * Private function prototypes.
X */
Xstatic void _heap_ify_up(heap_p hp, heap_node_p elm);
Xstatic void _heap_ify_down(heap_p hp, heap_node_p elm);
Xstatic int _heap_should_grow(heap_p hp);
Xstatic void _heap_grow(heap_p hp);
Xstatic void _heap_swap_element(heap_p hp, heap_node_p elm1, heap_node_p elm2);
Xstatic int _heap_node_exist(heap_p hp, int id);
X
X#ifdef HEAP_DEBUG
Xvoid _heap_print_tree(heap_p hp, heap_node_p node);
X#endif /* HEAP_DEBUG */
X
X#define Left(x) (2 * (x) + 1)
X#define Right(x) (2 * (x) + 2)
X#define Parent(x) ((int)((x)-1)/2)
X
X#define Threshold 10000
X#define NormalRate 2
X#define SlowRate 1.5
X#define MinSize 32
X
X/****************************************************************************
X * Public functions
X ****************************************************************************/
X
X/*
X * Return a newly created heap. INITSIZE is the initial size of the heap.
X */
Xheap_p
Xnew_heap(int initSize, key_func gen_key)
X{
X heap_p hp = (heap_p) malloc(sizeof(heap));
X assert(hp != NULL);
X
X hp->nodes = (heap_node_p *)calloc(initSize, sizeof(heap_node_p));
X assert(hp->nodes != NULL);
X
X if (initSize <= 0)
X initSize = MinSize;
X hp->size = initSize;
X hp->last = 0;
X hp->gen_key = gen_key;
X hp->age = 0;
X
X return hp;
X}
X
X
X/*
X * Free memory used by a heap. Does not free the metadata pointed to by the
X * heap nodes, only the heap's internal memory.
X */
Xvoid
Xdelete_heap(heap_p hp)
X{
X int i;
X assert(hp);
X for (i = 0; i < hp->last; i++) {
X free(hp->nodes[i]);
X }
X free(hp->nodes);
X free(hp);
X}
X
X/*
X * Insert DAT based on KY into HP maintaining the heap property. Return the
X * newly inserted heap node. The fields of ELM other than ID are never
X * changed until ELM is deleted from HP, i.e. caller can assume that the
X * heap node always exist at the same place in memory unless heap_delete or
X * heap_extractmin is called on that node. This function exposes the heap's
X * internal data structure to the caller. This is required in order to do
X * O(lgN) deletion.
X */
Xheap_node_p
Xheap_insert(heap_p hp, void * dat)
X{
X heap_node* elm = (heap_node*)malloc(sizeof(heap_node));
X
X elm->key = heap_gen_key(hp, dat);
X elm->data = dat;
X
X if (_heap_should_grow(hp))
X _heap_grow(hp);
X
X hp->nodes[hp->last] = elm;
X elm->id = hp->last;
X hp->last += 1;
X
X _heap_ify_up(hp, elm);
X
X /*
X * Set the pointer in the metadata to point to this heap node.
X */
X #ifdef HPL_REPL
X /* The following must be done immediately upon return! */
X #else /* HPL_REPL */
X md_set_node(dat, elm);
X #endif /* HPL_REPL */
X
X return elm;
X}
X
X
X/*
X * Delete ELM while maintaining the heap property. ELM may be modified.
X * Assumes that ELM is not NULL and frees it. Returns the data pointed to
X * in, which the caller must free if necessary.
X */
X_HEAPTYPE
Xheap_delete(heap_p hp, heap_node_p elm)
X{
X heap_node_p lastNode;
X _HEAPTYPE data = elm->data;
X
X assert(_heap_node_exist(hp, hp->last - 1));
X
X lastNode = hp->nodes[hp->last - 1];
X _heap_swap_element(hp, lastNode, elm);
X heap_extractlast(hp);
X
X if (hp->last > 0) {
X if (lastNode->key < hp->nodes[Parent(lastNode->id)]->key)
X _heap_ify_up(hp, lastNode); /* COOL! */
X _heap_ify_down(hp, lastNode);
X }
X
X /*
X * Remove the pointer back to the heap from the metadata.
X * Then free the element that was just deleted.
X */
X #ifdef HPL_REPL
X /* The following must be done immediately upon return! */
X #else /* HPL_REPL */
X md_set_node(data, NULL);
X #endif /* HPL_REPL */
X
X return data;
X}
X
X/*
X * Delete the last element (leaf) out of the heap. Does not require a
X * heapify operation.
X */
X
X#ifndef heap_gen_key
X/*
X * Function to generate keys. See macro definition in heap.h.
X */
X_HEAPKEY
Xheap_gen_key(heap_p hp, _HEAPTYPE dat)
X{
X return hp->gen_key(dat, hp->age);
X}
X#endif /* heap_gen_key */
X
X
X/*
X * Returns the data of the node with the largest KEY value and removes that
X * node from the heap. Returns NULL if the heap was empty.
X */
X_HEAPTYPE
Xheap_extractmin(heap_p hp)
X{
X _HEAPTYPE data;
X
X if (hp->last <= 0)
X return NULL;
X
X mutex_lock(hp->lock);
X
X data = hp->nodes[0]->data;
X heap_delete(hp, hp->nodes[0]); /* Delete the root */
X
X mutex_unlock(hp->lock);
X
X return data;
X}
X
X
X/*
X * Remove the last node in HP. Frees the heap internal structure and
X * returns the data pointes to by the last node.
X */
X_HEAPTYPE
Xheap_extractlast(heap_p hp)
X{
X _HEAPTYPE data;
X assert (_heap_node_exist(hp, hp->last -1));
X hp->last -= 1;
X data = hp->nodes[hp->last]->data;
X free(hp->nodes[hp->last]);
X return data;
X}
X
X
X/*
X * The semantics of this routine is the same as the followings:
X * heap_delete(hp, elm);
X * heap_insert(hp, dat);
X * Returns the old data object from elm (the one being replaced). The
X * caller must free this as necessary.
X */
X_HEAPTYPE
Xheap_update(heap_p hp, heap_node* elm, void* dat)
X{
X _HEAPTYPE old = elm->data;
X _HEAPKEY ky = heap_gen_key(hp, dat);
X
X elm->key = ky;
X elm->data = dat;
X
X if (elm->key < hp->nodes[Parent(elm->id)]->key)
X _heap_ify_up(hp, elm);
X _heap_ify_down(hp, elm);
X
X return old;
X}
X
X
X/*
X * A pointer to the root node's DATA.
X */
Xvoid*
Xheap_peepmin(heap_p hp)
X{
X assert(_heap_node_exist(hp, 0));
X return hp->nodes[0]->data;
X}
X
X
X/*
X * The KEY of the root node.
X */
X_HEAPKEY
Xheap_peepminkey(heap_p hp)
X{
X assert(_heap_node_exist(hp, 0));
X return hp->nodes[0]->key;
X}
X
X
X/*
X * Same as heap_peep except that this return the KEY of the node.
X * Only meant for iteration.
X */
X_HEAPKEY
Xheap_peepkey(heap_p hp, int n)
X{
X assert(_heap_node_exist(hp, n));
X return hp->nodes[n]->key;
X}
X
X
X/*
X * A pointer to Nth node's DATA. The caller can iterate through HP by
X * calling this routine. eg. Caller can execute the following code:
X * for(i = 0; i < heap_nodes(hp); i++)
X * data = heap_peep(hp, i);
X */
Xvoid*
Xheap_peep(heap_p hp, int n)
X{
X void* data;
X assert(_heap_node_exist(hp, n));
X data = hp->nodes[n]->data;
X return data;
X}
X
X
X#ifndef heap_nodes
X/*
X * Current number of nodes in HP.
X */
Xint
Xheap_nodes(heap_p hp)
X{
X return hp->last;
X}
X#endif /* heap_nodes */
X
X
X#ifndef heap_empty
X/*
X * Determine if the heap is empty. Returns 1 if HP has no elements and 0
X * otherwise.
X */
Xint
Xheap_empty(heap_p hp)
X{
X return (hp->last <= 0) ? 1 : 0;
X}
X#endif /* heap_empty */
X
X/****************** Private Functions *******************/
X
X/*
X * Maintain the heap order property (parent is smaller than children) which
X * may only be violated at ELM downwards. Assumes caller has locked the heap.
X */
Xstatic void
X_heap_ify_down(heap_p hp, heap_node_p elm)
X{
X heap_node* kid;
X int left = 0, right = 0;
X int true = 1;
X while (true) {
X left = Left(elm->id);
X right = Right(elm->id);
X if (! _heap_node_exist(hp, left)) { // At the bottom of the heap (no child).
X assert(!_heap_node_exist(hp, right));
X break;
X }
X else if (! _heap_node_exist(hp, right)) // Only left child exists.
X kid = hp->nodes[left];
X else {
X if (hp->nodes[right]->key < hp->nodes[left]->key)
X kid = hp->nodes[right];
X else
X kid = hp->nodes[left];
X }
X if (elm->key <= kid->key)
X break;
X _heap_swap_element(hp, kid, elm);
X }
X}
X
X
X/*
X * Maintain the heap property above ELM. Caller has locked the heap.
X */
Xstatic void
X_heap_ify_up(heap_p hp, heap_node_p elm)
X{
X heap_node_p parentNode;
X while (elm->id > 0) {
X parentNode = hp->nodes[Parent(elm->id)];
X if (parentNode->key <= elm->key)
X break;
X _heap_swap_element(hp, parentNode, elm); /* Demote the parent. */
X }
X}
X
X
X/*
X * Swap the position of ELM1 and ELM2 in heap structure. Their IDs are also
X * swapped.
X */
Xstatic void
X_heap_swap_element(heap_p hp, heap_node_p elm1, heap_node_p elm2)
X{
X int elm1Id = elm1->id;
X elm1->id = elm2->id;
X elm2->id = elm1Id;
X hp->nodes[elm1->id] = elm1;
X hp->nodes[elm2->id] = elm2;
X}
X
X
X
X#ifdef NOTDEF
X/*
X * Copy KEY and DATA fields of SRC to DEST. ID field is NOT copied.
X */
Xstatic void
X_heap_copy_element(heap_node_p src, heap_node_p dest)
X{
X dest->key = src->key;
X dest->data = src->data;
X}
X#endif /* NOTDEF */
X
X
X/*
X * True if HP needs to be grown in size.
X */
Xstatic int
X_heap_should_grow(heap_p hp)
X{
X if (hp->size <= hp->last)
X return 1;
X return 0;
X}
X
X
X/*
X * Grow HP.
X */
Xstatic void
X_heap_grow(heap_p hp)
X{
X int newSize;
X //heap_node_p * newNodes;
X
X if (hp->size > Threshold)
X newSize = hp->size * SlowRate;
X else
X newSize = hp->size * NormalRate;
X
X hp->nodes = (heap_node_p *)realloc(hp->nodes, newSize * sizeof(heap_node_p));
X //for(i = 0; i < hp->size; i++)
X //newNodes[i] = hp->nodes[i];
X //free(hp->nodes);
X //hp->nodes = newNodes;
X hp->size = newSize;
X}
X
X
X/*
X * True if a node with ID exists in HP.
X */
Xstatic int
X_heap_node_exist(heap_p hp, int id)
X{
X if ((id >= hp->last) || (id < 0) || (hp->nodes[id] == NULL))
X return 0;
X return 1;
X}
X
X/****************************************************************************
X * Printing and debug functions
X ****************************************************************************/
X
X/*
X * Print the heap in element order, id..last.
X */
Xvoid
Xheap_print_inorder(heap_p hp, int id)
X{
X while (id < hp->last) {
X printf("%d\tKey = %.04f\n", id, hp->nodes[id]->key);
X id++;
X }
X}
X
X/*
X * Returns 1 if HP maintians the heap property and 0 otherwise.
X */
Xint
Xverify_heap_property(heap_p hp)
X{
X int i = 0;
X int correct = 1;
X for(i = 0; i < hp->last / 2; i++) {
X correct = 1;
X if (_heap_node_exist(hp, Left(i)))
X if (hp->nodes[i]->key > hp->nodes[Left(i)]->key)
X correct = 0;
X if (_heap_node_exist(hp, Right(i)))
X if (hp->nodes[i]->key > hp->nodes[Right(i)]->key)
X correct = 0;
X if (!correct) {
X printf("verifyHeap: violated at %d", i);
X heap_print_inorder(hp, 0);
X break;
X }
X }
X return correct;
X}
X
X#ifdef MEASURE_HEAP_SKEW
X
X/****************************************************************************
X * Heap skew computation
X ****************************************************************************/
X
Xint
Xcompare_heap_keys(const void * a, const void * b)
X{
X heap_node ** an = (heap_node**)a;
X heap_node ** bn = (heap_node**)b;
X float cmp = (*an)->key - (*bn)->key;
X if (cmp < 0)
X return -1;
X else
X return 1;
X}
X
X/*
X * Compute the heap skew for HEAP, a measure of how out-of-order the
X * elements in the heap are. The skew of a heap node is the difference
X * between its current position in the heap and where it would be if the
X * heap were in sorted order. To compute this we have to sort the heap. At
X * the end if the flag REPLACE is non-zero the heap will be returned in
X * sorted order (with skew == 0). Note: using REPLACE does not help the
X * performance of the heap, so only do this if you really want to have a
X * sorted heap. It is faster not to replace.
X */
Xfloat
Xcalc_heap_skew(heap_p heap, int replace)
X{
X heap_node ** nodes;
X long id, diff, skew = 0;
X #ifdef HEAP_DEBUG_SKEW
X long skewsq = 0;
X #endif /* HEAP_DEBUG_SKEW */
X float norm = 0;
X unsigned long max;
X
X /*
X * Lock the heap to copy it. If replacing it need to keep the heap locked
X * until we are all done.
X */
X mutex_lock(hp->lock);
X
X max = heap_nodes(heap);
X
X /*
X * Copy the heap nodes to a new storage area for offline sorting.
X */
X nodes = (heap_node **)malloc(max * sizeof(heap_node *));
X memcpy(nodes, heap->nodes, max * sizeof(heap_node *));
X
X if (replace == 0) {
X /*
X * Unlock the heap to allow updates from other threads before the sort.
X * This allows other heap operations to proceed concurrently with the
X * heap skew computation on the heap at the time of the call ...
X */
X mutex_unlock(hp->lock);
X }
X
X qsort(nodes, max, sizeof(heap_node *), compare_heap_keys);
X
X for (id=0; id < max; id++) {
X diff = id - nodes[id]->id;
X skew += abs(diff);
X
X #ifdef HEAP_DEBUG_SKEW
X skewsq += diff*diff;
X #ifdef HEAP_DEBUG_ALL
X printf("%d\tKey = %f, diff = %d\n", id, nodes[id]->key, diff);
X #endif /* HEAP_DEBUG */
X #endif /* HEAP_DEBUG_SKEW */
X }
X
X if (replace != 0) {
X /*
X * Replace the original heap with the newly sorted heap and let it
X * continue. Then compute the skew using the copy of the previous heap
X * which we maintain as private data.
X */
X memcpy(heap->nodes, nodes, max * sizeof(heap_node *));
X
X for (id = 0; id < max; id++) {
X /*
X * Fix up all the ID values in the copied nodes.
X */
X heap->nodes[id]->id = id;
X }
X
X mutex_unlock(hp->lock);
X }
X
X /*
X * The skew value is normalized to a range of [0..1]; the distribution
X * appears to be a skewed Gaussian distribution. For random insertions
X * into a heap the normalized skew will be slightly less than 0.5. The
X * maximum value of skew/N^2 (for any value of N) is about 0.39 and is
X * fairly stable.
X */
X norm = skew*2.56/(max*max);
X
X /*
X * Free the nodes array; note this is just an array of pointers, not data!
X */
X free(nodes);
X return norm;
X}
X
X#endif /* MEASURE_HEAP_SKEW */
SHAR_EOF
  : || $echo 'restore of' 'lib/heap.c' 'failed'
fi
rm -fr _sh02595
exit 0
Received on Tue Jul 29 2003 - 13:15:58 MDT

This archive was generated by hypermail pre-2.1.9 : Tue Dec 09 2003 - 16:12:10 MST