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