/*
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);
}