/* Copyright (C) 1991, Marcus J. Ranum. All rights reserved. */ #ifndef lint static char RCSid[] = "$Header: /homes/hawkwind/u0/cks/src/MUD/umud/DB/RCS/dbchk_cache.c,v 1.0 91/08/15 23:11:45 cks Exp Locker: cks $"; #endif /* configure all options BEFORE including system stuff. */ #include "config.h" #ifdef NOSYSTYPES_H #include <types.h> #else #include <sys/types.h> #endif #include <stdio.h> #include <assert.h> #include "mud.h" /* This is by far the most complex and kinky code in UnterMUD. You should never need to mess with anything in here - if you value your sanity. */ typedef struct cache { char *onm; Obj *op; int flg; struct cache *nxt; struct cache *prv; } Cache; /* flag states */ #define C_NOFLG 00 #define C_DIRTY 01 #define C_DEAD 02 typedef struct { Cache *ahead; Cache *ohead; Cache *atail; Cache *otail; } CacheLst; /* initial settings for cache sizes */ static int cwidth = CACHE_WIDTH; static int cdepth = CACHE_DEPTH; /* ntbfw - main cache pointer and list of things to kill off */ static CacheLst *sys_c; static int cache_initted = 0; /* cache stats gathering stuff. you don't like it? comment it out */ static time_t cs_ltime; static int cs_writes = 0; /* total writes */ static int cs_reads = 0; /* total reads */ static int cs_dbreads = 0; /* total read-throughs */ static int cs_dbwrites = 0; /* total write-throughs */ static int cs_dels = 0; /* total deletes */ static int cs_checks = 0; /* total checks */ static int cs_rhits = 0; /* total reads filled from cache */ static int cs_ahits = 0; /* total reads filled active cache */ static int cs_whits = 0; /* total writes to dirty cache */ static int cs_fails = 0; /* attempts to grab nonexistent */ static int cs_resets = 0; /* total cache resets */ static int cs_syncs = 0; /* total cache syncs */ static int cs_objects = 0; /* total cache size */ int cache_init() { int x; int y; Cache *np; static char *ncmsg = "cache_init: cannot allocate cache: "; if(cache_initted || sys_c != (CacheLst *)0) return(0); sys_c = (CacheLst *)malloc((unsigned)cwidth * sizeof(CacheLst)); if(sys_c == (CacheLst *)0) { fprintf(stderr, "dbchk: cache malloc failure\n"); return(-1); } for(x = 0; x < cwidth; x++) { sys_c[x].ahead = sys_c[x].ohead = (Cache *)0; sys_c[x].atail = sys_c[x].otail = (Cache *)0; for(y = 0; y < cdepth; y++) { np = (Cache *)malloc(sizeof(Cache)); if(np == (Cache *)0) { fprintf(stderr, "%s\n", ncmsg); return(-1); } if((np->nxt = sys_c[x].ohead) != (Cache *)0) np->nxt->prv = np; else sys_c[x].otail = np; np->prv = (Cache *)0; np->flg = C_NOFLG; np->onm = (char *)0; np->op = (Obj *)0; sys_c[x].ohead = np; cs_objects++; } } /* mark caching system live */ cache_initted++; time(&cs_ltime); return(0); } void cache_reset() { int x; if(!cache_initted) return; /* unchain and rechain each active list at head of old chain */ for(x = 0; x < cwidth; x++) { if(sys_c[x].ahead != (Cache *)0) { assert(sys_c[x].otail || !sys_c[x].ohead); sys_c[x].atail->nxt = sys_c[x].ohead; if(sys_c[x].ohead != (Cache *)0) sys_c[x].ohead->prv = sys_c[x].atail; sys_c[x].ohead = sys_c[x].ahead; if (sys_c[x].otail == (Cache *)0) sys_c[x].otail = sys_c[x].atail; sys_c[x].ahead = (Cache *)0; sys_c[x].atail = (Cache *)0; } } cs_resets++; } /* search the cache for an object, and if it is not found, thaw it. this code is probably a little bigger than it needs be because I had fun and unrolled all the pointer juggling inline. */ Obj * cache_get(nam) char *nam; { Cache *cp; int hv = 0; Obj *ret; static char *nmmesg = "cache_get: cannot allocate memory "; /* firewall */ if(nam == (char *)0 || *nam == '\0' || !cache_initted) { #ifdef CACHE_VERBOSE logf("cache_get: NULL object name - programmer error\n",(char *)0); #endif abort(); return((Obj *)0); } #ifdef CACHE_DEBUG printf("get %s\n",nam); #endif /* is some fool trying to get "nowhere"? */ if(!strcmp(nam,"nowhere")) return((Obj *)0); cs_reads++; hv = objid_hash(nam,cwidth); /* search active chain first */ for(cp = sys_c[hv].ahead; cp != (Cache *)0; cp = cp->nxt) { if(cp->onm != (char *)0 && !(cp->flg & C_DEAD) && !strcmp(cp->onm,nam)) { cs_rhits++; cs_ahits++; #ifdef CACHE_DEBUG printf("return %s active cache %d\n",cp->onm,cp->op); #endif return(cp->op); } } /* search in-active chain second */ for(cp = sys_c[hv].ohead; cp != (Cache *)0; cp = cp->nxt) { if(cp->onm != (char *)0 && !(cp->flg & C_DEAD) && !strcmp(cp->onm,nam)) { /* dechain from in-active chain */ if(cp->nxt == (Cache *)0) sys_c[hv].otail = cp->prv; else cp->nxt->prv = cp->prv; if(cp->prv == (Cache *)0) sys_c[hv].ohead = cp->nxt; else cp->prv->nxt = cp->nxt; /* insert at head of active chain */ cp->nxt = sys_c[hv].ahead; if(sys_c[hv].ahead == (Cache *)0) sys_c[hv].atail = cp; cp->prv = (Cache *)0; if(cp->nxt != (Cache *)0) cp->nxt->prv = cp; sys_c[hv].ahead = cp; /* done */ cs_rhits++; #ifdef CACHE_DEBUG printf("return %s old cache %d\n",cp->onm,cp->op); #endif return(cp->op); } } /* DARN IT - at this point we have a certified, type-A cache miss */ /* thaw the object from wherever. */ if((ret = DB_GET(nam)) == (Obj *)0) { cs_fails++; #ifdef CACHE_DEBUG printf("%s not in db\n",nam); #endif return((Obj *)0); } cs_dbreads++; /* if there are no old cache object holders left, allocate one */ if(sys_c[hv].otail == (Cache *)0) { int xnm; assert(sys_c[hv].ohead == (Cache *)0); if((cp = (Cache *)malloc(sizeof(Cache))) == (Cache *)0) { fprintf(stderr, "dbchk: no memory for new cache struct\n"); abort(); } xnm = strlen(nam) + 1; if((cp->onm = (char *)malloc((unsigned)xnm)) == (char *)0) { fprintf(stderr, "dbchk: no memory for new cache name\n"); abort(); } /* sanity chex */ if(xnm > MAXOID) fprintf(stderr, "dbchk: cache_get: name %s too long!\n", nam); (void)strcpy(cp->onm,nam); cp->flg = C_NOFLG; /* linkit at head of active chain */ cp->nxt = sys_c[hv].ahead; if(sys_c[hv].ahead == (Cache *)0) sys_c[hv].atail = cp; cp->prv = (Cache *)0; if(cp->nxt != (Cache *)0) cp->nxt->prv = cp; sys_c[hv].ahead = cp; cs_objects++; #ifdef CACHE_DEBUG printf("return %s loaded into cache %d\n",cp->onm,cp->op); #endif return(cp->op = ret); } /* unlink old cache chain tail */ cp = sys_c[hv].otail; if(cp->prv != (Cache *)0) { sys_c[hv].otail = cp->prv; cp->prv->nxt = cp->nxt; } else /* took last one */ sys_c[hv].ohead = sys_c[hv].otail = (Cache *)0; /* if there is a dirty object still in memory, write it */ if((cp->flg & C_DIRTY) && cp->onm != (char *)0 && cp->op != (Obj *)0) { #ifdef CACHE_DEBUG printf("clean %s from cache %d\n",cp->onm,cp->op); #endif fprintf(stderr, "dbchk: dirty object in cache to be flushed.\n"); abort(); cs_dbwrites++; } /* free object's data */ if(cp->op != (Obj *)0) objfree(cp->op); cp->op = ret; /* free old name */ if(cp->onm != (char *)0) deferfree((mall_t)cp->onm); /* if this fails, boy are we in trouble! */ if((cp->onm = (char *)malloc((unsigned)strlen(nam) + 1)) == (char *)0) { fprintf(stderr, "dbchk: death point 1\n"); abort(); } /* sanity chex */ if(strlen(nam) + 1 > MAXOID) fprintf(stderr, "dbchk: cache_get: name %s too long!\n", nam); (void)strcpy(cp->onm,nam); cp->flg = C_NOFLG; /* relink at head of active chain */ cp->nxt = sys_c[hv].ahead; if(sys_c[hv].ahead == (Cache *)0) sys_c[hv].atail = cp; cp->prv = (Cache *)0; if(cp->nxt != (Cache *)0) cp->nxt->prv = cp; sys_c[hv].ahead = cp; #ifdef CACHE_DEBUG printf("return %s loaded into cache %d\n",cp->onm,cp->op); #endif return(ret); } int cache_sync() { int x; Cache *cp; cs_syncs++; if(!cache_initted) return(1); for(x = 0; x < cwidth; x++) { for(cp = sys_c[x].ahead; cp != (Cache *)0; cp = cp->nxt) { if(cp->flg & C_DIRTY) { fprintf(stderr, "dbchk: panic: dirty cache\n"); abort(); } } for(cp = sys_c[x].ohead; cp != (Cache *)0; cp = cp->nxt) { if(cp->flg & C_DIRTY) { fprintf(stderr, "dbchk: panic: dirty cache\n"); abort(); } } } return(0); } /* probe the cache and the database (if needed) for the existence of an object. return nonzero if the object is in cache or database */ int cache_check(nam) char *nam; { Cache *cp; int hv = 0; if(nam == (char *)0 || !cache_initted) return(0); cs_checks++; hv = objid_hash(nam,cwidth); for(cp = sys_c[hv].ahead; cp != (Cache *)0; cp = cp->nxt) if(cp->onm != (char *)0 && !(cp->flg & C_DEAD) && !strcmp(cp->onm,nam)) return(1); for(cp = sys_c[hv].ohead; cp != (Cache *)0; cp = cp->nxt) if(cp->onm != (char *)0 && !(cp->flg & C_DEAD) && !strcmp(cp->onm,nam)) return(1); /* no ? */ return(DB_CHECK(nam)); } cache_stats() { printf("cache stats, last %d sec.:\n",time(0) - cs_ltime); printf("writes=%d reads=%d\n",cs_writes,cs_reads); printf("dbwrites=%d dbreads=%d\n",cs_dbwrites,cs_dbreads); printf("read hits=%d active hits=%d\n",cs_rhits,cs_ahits); printf("write hits to dirty cache=%d\n",cs_whits); printf("deletes=%d checks=%d\n",cs_dels,cs_checks); printf("resets=%d syncs=%d objects=%d\n", cs_resets,cs_syncs,cs_objects); }