/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* cache.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */

#include "conf.h"
#include "teeny.h"
#include "teenydb.h"
#include "externs.h"

/* new, hashed, cache. */

static struct obj_data **main_cache = NULL;
static int cache_locked = 0;

static void cache_resize _ANSI_ARGS_((int));

#define cache_hash(x)	((x->objnum) % mudconf.cache_width)

/* initialize the cache state. */
void cache_init()
{
  /* set variables */
  mudstat.cache_usage = 0;
  mudstat.cache_hits = 0;
  mudstat.cache_errors = 0;
  mudstat.cache_misses = 0;

  /* set up the hash table. */
  main_cache = (struct obj_data **)
  	ty_malloc(sizeof(struct obj_data *) * mudconf.cache_width,
		  "cache_init");
  bzero((VOID *)main_cache, sizeof(struct obj_data *) * mudconf.cache_width);
}

/* grow new buckets. ONLY CALL WHEN THE CACHE IS EMPTY OR DIE. */
static void cache_resize(nbuckets)
    int nbuckets;
{
  mudconf.cache_width = nbuckets;

  ty_free((VOID *)main_cache);
  main_cache = (struct obj_data **)
  	ty_malloc(sizeof(struct obj_data *) * mudconf.cache_width,
		  "cache_resize");
  bzero((VOID *)main_cache, sizeof(struct obj_data *) * mudconf.cache_width);
}

/* "lock" the cache. */
void cache_lock()
{
  cache_locked = 1;
}

/* "unlock" the cache. */
void cache_unlock()
{
  cache_locked = 0;
}

/* delete an object from cache. */
void cache_delete(obj)
    struct obj_data *obj;
{
  register int bucket = cache_hash(obj);

  /* only thing in this bucket? */
  if((obj == main_cache[bucket]) && (main_cache[bucket]->fwd == obj)) {
    main_cache[bucket] = (struct obj_data *)NULL;
  } else {
    if (main_cache[bucket] == obj)
      main_cache[bucket] = obj->fwd;
    (obj->back)->fwd = obj->fwd;
    (obj->fwd)->back = obj->back;
  }

  mudstat.cache_usage -= DSC_SIZE(main_index[obj->objnum]);
}

/* move the obj to the head of it's bucket. */
void cache_touch(obj)
    struct obj_data *obj;
{
  register int bucket = cache_hash(obj);

  /* already at the top? */
  if(obj != main_cache[bucket]) {
    (obj->back)->fwd = obj->fwd;
    (obj->fwd)->back = obj->back;

    obj->fwd = main_cache[bucket];
    obj->back = (main_cache[bucket])->back;
    ((main_cache[bucket])->back)->fwd = obj;
    (main_cache[bucket])->back = obj;

    main_cache[bucket] = obj;
  }
}

/* insert an object into the cache. trim if needed. */
void cache_insert(obj)
    struct obj_data *obj;
{
  register int bucket = cache_hash(obj);

  /* bucket empty? */
  if(main_cache[bucket] == (struct obj_data *)NULL) {
    main_cache[bucket] = obj->back = obj->fwd = obj;
  } else {
    /* insert at top. */
    obj->fwd = main_cache[bucket];
    obj->back = (main_cache[bucket])->back;
    ((main_cache[bucket])->back)->fwd = obj;
    (main_cache[bucket])->back = obj;
    main_cache[bucket] = obj;
  }

  mudstat.cache_usage += DSC_SIZE(main_index[obj->objnum]);
  cache_trim();		/* trim it. */
}

/* trim the cache down to size. */
void cache_trim()
{
  register struct obj_data *obj;
  register int ret, bucket, bdone, count;

  /* loop through all of the buckets repeatedly, trimming the last object */
  /* in each, until the cache comes under control. */

  count = 1;	/* infinite loop prevention. */
  while(count && (mudstat.cache_usage > mudconf.cache_size)) {
    for(bucket = 0, count = 0; bucket < mudconf.cache_width; bucket++) {
      /* if the cache is locked, and the object is dirty, we don't do */
      /* anything to it, we wait until the cache is unlocked. */

      if (main_cache[bucket] != (struct obj_data *)NULL) {
        for (obj = (main_cache[bucket])->back, bdone = 0;
	     obj != main_cache[bucket] && !bdone; obj = obj->back) {
	  if (!(DSC_FLAG2(main_index[obj->objnum]) & DIRTY) || !cache_locked) {

	    /* if disk_freeze() fails, keep the object around. */
	    if (DSC_FLAG2(main_index[obj->objnum]) & DIRTY)
	      ret = disk_freeze(obj);
	    else {
	      /* it's not dirty, simulate a freeze. */
	      DSC_FLAG2(main_index[obj->objnum]) &= ~IN_MEMORY;
	      free_obj(obj);
	      ret = 0;
	    }
	    if(ret == 0) {
	      (obj->back)->fwd = obj->fwd;
	      (obj->fwd)->back = obj->back;

	      mudstat.cache_usage -= DSC_SIZE(main_index[obj->objnum]);
	      /* done with this bucket, for now. */
	      bdone++;
	      count++;
	    }
	  }
	}
      }
    }
  }
}

/* flush the entire cache to disk. */
void cache_flush()
{
  register struct obj_data *obj, *next;
  register int bucket;
  register int count = 0;
  register int nbuckets = 0;

  for(bucket = 0; bucket < mudconf.cache_width; bucket++) {
    if(main_cache[bucket] != (struct obj_data *)NULL) {
      obj = main_cache[bucket];
      do {
	next = obj->fwd;

	/* only try to save it if it's dirty. */
	if (DSC_FLAG2(main_index[obj->objnum]) & DIRTY) {
	  /* if disk_freeze() failed here, we lose. */
	  if (disk_freeze(obj) != 0) {
	    DSC_FLAG2(main_index[obj->objnum]) &= ~IN_MEMORY;
	    free_obj(obj);
	  }
	} else {
	  DSC_FLAG2(main_index[obj->objnum]) &= ~IN_MEMORY;
	  free_obj(obj);
	}
	obj = next;
	count++;
      } while(obj != main_cache[bucket]);

      if(count > mudconf.cache_depth)
        nbuckets++;
    }
  }

  /* reset the cache. */
  if(nbuckets) {
    cache_resize(mudconf.cache_width + nbuckets);
  } else {
    bzero((VOID *)main_cache, sizeof(struct obj_data *) * mudconf.cache_width);
  }
  mudstat.cache_usage = 0;
  mudstat.cache_hits = 0;
  mudstat.cache_misses = 0;
}