/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
/*
	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);
}