/* cache.c */ #include "config.h" /* * This file is part of TeenyMUD II. * Copyright(C) 1993, 1994, 1995 by Jason Downs. * All rights reserved. * * TeenyMUD II is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * TeenyMUD II is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file 'COPYING'); if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * */ #include <stdio.h> #include <sys/types.h> #ifdef HAVE_STRING_H #include <string.h> #else #include <strings.h> #endif /* HAVE_STRING_H */ #include "conf.h" #include "teeny.h" #include "teenydb.h" #include "externs.h" /* new, hashed, cache. */ static struct obj_data **main_cache = NULL; static int cache_locked = 0; static void cache_resize _ANSI_ARGS_((int)); #define cache_hash(x) ((x->objnum) % mudconf.cache_width) /* initialize the cache state. */ void cache_init() { /* set variables */ mudstat.cache_usage = 0; mudstat.cache_hits = 0; mudstat.cache_errors = 0; mudstat.cache_misses = 0; /* set up the hash table. */ main_cache = (struct obj_data **) ty_malloc(sizeof(struct obj_data *) * mudconf.cache_width, "cache_init"); bzero((VOID *)main_cache, sizeof(struct obj_data *) * mudconf.cache_width); } /* grow new buckets. ONLY CALL WHEN THE CACHE IS EMPTY OR DIE. */ static void cache_resize(nbuckets) int nbuckets; { mudconf.cache_width = nbuckets; ty_free((VOID *)main_cache); main_cache = (struct obj_data **) ty_malloc(sizeof(struct obj_data *) * mudconf.cache_width, "cache_resize"); bzero((VOID *)main_cache, sizeof(struct obj_data *) * mudconf.cache_width); } /* "lock" the cache. */ void cache_lock() { cache_locked = 1; } /* "unlock" the cache. */ void cache_unlock() { cache_locked = 0; } /* delete an object from cache. */ void cache_delete(obj) struct obj_data *obj; { register int bucket = cache_hash(obj); /* only thing in this bucket? */ if((obj == main_cache[bucket]) && (main_cache[bucket]->fwd == obj)) { main_cache[bucket] = (struct obj_data *)NULL; } else { if (main_cache[bucket] == obj) main_cache[bucket] = obj->fwd; (obj->back)->fwd = obj->fwd; (obj->fwd)->back = obj->back; } mudstat.cache_usage -= DSC_SIZE(main_index[obj->objnum]); } /* move the obj to the head of it's bucket. */ void cache_touch(obj) struct obj_data *obj; { register int bucket = cache_hash(obj); /* already at the top? */ if(obj != main_cache[bucket]) { (obj->back)->fwd = obj->fwd; (obj->fwd)->back = obj->back; obj->fwd = main_cache[bucket]; obj->back = (main_cache[bucket])->back; ((main_cache[bucket])->back)->fwd = obj; (main_cache[bucket])->back = obj; main_cache[bucket] = obj; } } /* insert an object into the cache. trim if needed. */ void cache_insert(obj) struct obj_data *obj; { register int bucket = cache_hash(obj); /* bucket empty? */ if(main_cache[bucket] == (struct obj_data *)NULL) { main_cache[bucket] = obj->back = obj->fwd = obj; } else { /* insert at top. */ obj->fwd = main_cache[bucket]; obj->back = (main_cache[bucket])->back; ((main_cache[bucket])->back)->fwd = obj; (main_cache[bucket])->back = obj; main_cache[bucket] = obj; } mudstat.cache_usage += DSC_SIZE(main_index[obj->objnum]); cache_trim(); /* trim it. */ } /* trim the cache down to size. */ void cache_trim() { register struct obj_data *obj; register int ret, bucket, bdone, count; /* loop through all of the buckets repeatedly, trimming the last object */ /* in each, until the cache comes under control. */ count = 1; /* infinite loop prevention. */ while(count && (mudstat.cache_usage > mudconf.cache_size)) { for(bucket = 0, count = 0; bucket < mudconf.cache_width; bucket++) { /* if the cache is locked, and the object is dirty, we don't do */ /* anything to it, we wait until the cache is unlocked. */ if (main_cache[bucket] != (struct obj_data *)NULL) { for (obj = (main_cache[bucket])->back, bdone = 0; obj != main_cache[bucket] && !bdone; obj = obj->back) { if (!(DSC_FLAG2(main_index[obj->objnum]) & DIRTY) || !cache_locked) { /* if disk_freeze() fails, keep the object around. */ if (DSC_FLAG2(main_index[obj->objnum]) & DIRTY) ret = disk_freeze(obj); else { /* it's not dirty, simulate a freeze. */ DSC_FLAG2(main_index[obj->objnum]) &= ~IN_MEMORY; free_obj(obj); ret = 0; } if(ret == 0) { (obj->back)->fwd = obj->fwd; (obj->fwd)->back = obj->back; mudstat.cache_usage -= DSC_SIZE(main_index[obj->objnum]); /* done with this bucket, for now. */ bdone++; count++; } } } } } } } /* flush the entire cache to disk. */ void cache_flush() { register struct obj_data *obj, *next; register int bucket; register int count = 0; register int nbuckets = 0; for(bucket = 0; bucket < mudconf.cache_width; bucket++) { if(main_cache[bucket] != (struct obj_data *)NULL) { obj = main_cache[bucket]; do { next = obj->fwd; /* only try to save it if it's dirty. */ if (DSC_FLAG2(main_index[obj->objnum]) & DIRTY) { /* if disk_freeze() failed here, we lose. */ if (disk_freeze(obj) != 0) { DSC_FLAG2(main_index[obj->objnum]) &= ~IN_MEMORY; free_obj(obj); } } else { DSC_FLAG2(main_index[obj->objnum]) &= ~IN_MEMORY; free_obj(obj); } obj = next; count++; } while(obj != main_cache[bucket]); if(count > mudconf.cache_depth) nbuckets++; } } /* reset the cache. */ if(nbuckets) { cache_resize(mudconf.cache_width + nbuckets); } else { bzero((VOID *)main_cache, sizeof(struct obj_data *) * mudconf.cache_width); } mudstat.cache_usage = 0; mudstat.cache_hits = 0; mudstat.cache_misses = 0; }