/* Copyright (C) 1991, Marcus J. Ranum. All rights reserved. */ /* configure all options BEFORE including system stuff. */ #include "config.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; /* 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)); } void cache_stats () { printf ("cache stats, last %d sec.:\n", (int) (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); }