412 lines
14 KiB
C

/* Cache.c v1.2 Caching control routines */
/*
This is part of ODS2 written by Paul Nankervis,
email address: Paulnank@au1.ibm.com
ODS2 is distributed freely for all members of the
VMS community to use. However all derived works
must maintain comments in their source to acknowledge
the contibution of the original author.
*/
/* Caching seems to have a big impact on performance!!!
This version based on number of objects - probably
should change it to use space occupied by objects.
Currently use vanilla binary trees - could either
use hashing or balanced trees for better performance. */
/* The theory is that all cachable objects share a common
cache pool. Any object with a reference count of zero
is a candidate for destruction. All cacheable objects
have a 'struct CACHE' as the first item of the object
so that cache pointers and object pointers are interchangeable.
All cache objects are also part of a binary tree so that
they can be located. There are many instances of these
binary trees: for files on a volume, for chunks (segments)
of files, etc. Also each object can have an object manager:
a routine to handle special object deletion requirements
(for example to remove all file chunks before removing a
file), or to flush objects (write modified chunks to disk). */
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "ssdef.h"
#include "cache.h"
#define DEBUG on
/* Set limits for number of unaccessed cache entries... */
#define CACHELIM 64
#define CACHEGOAL 25
int cachesearches = 0;
int cachecreated = 0;
int cachedeleted = 0;
int cachepeak = 0;
int cachecount = 0;
int cachefreecount = 0;
struct CACHE cachelist = {NULL,NULL,NULL,&cachelist,&cachelist,NULL,0,0,1};
/* cacheshow - to print cache statistics */
void cacheshow(void)
{
printf("CACHESHOW Searches: %d Created: %d Peak: %d Count: %d Free: %d\n",
cachesearches,cachecreated,cachepeak,cachecount,cachefreecount);
}
void cachedump(void)
{
register struct CACHE *cacheobj = cachelist.lstcache;
printf("%8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s cachelist\n","Object",
"Parent","Left","Right","Value","Status","Count");
while (cacheobj != &cachelist) {
printf("%8p %8p %8p %8p %8x %8x %8d\n",
cacheobj,cacheobj->parent,cacheobj->left,cacheobj->right,
cacheobj->keyval,cacheobj->status,cacheobj->refcount);
cacheobj = cacheobj->lstcache;
}
}
/* cacheprint - debugging tool to print out a cache subtree... */
void cacheprint(struct CACHE *cacheobj,int level)
{
if (cacheobj != NULL) {
int i;
cacheprint(cacheobj->left,level + 1);
for (i = 0; i < level; i++) printf(" ");
printf("%8p %8x %8x %d\n",
cacheobj,cacheobj->keyval,cacheobj->status,cacheobj->refcount);
cacheprint(cacheobj->right,level + 1);
}
}
/* cacherefcount - compute reference count for cache subtree... */
int cacherefcount(struct CACHE *cacheobj)
{
register int refcount = 0;
if (cacheobj != NULL) {
if (cacheobj->left != NULL) refcount += cacherefcount(cacheobj->left);
if (cacheobj->right != NULL) refcount += cacherefcount(cacheobj->right);
refcount += cacheobj->refcount;
}
return refcount;
}
/* cachefree - blow away item from cache - allow item to select a proxy (!)
and adjust 'the tree' containing the item. */
int freeactive = 0; /* In case object managers re-call cachefree... :-( */
struct CACHE *cachefree(struct CACHE *cacheobj)
{
if (cacheobj->refcount != 0) {
char str[100];
printf("cachelist, object to delete still inuse?\n");
printf("%8p %8p %8p %8p %8x %8x %8d\n",
cacheobj,cacheobj->parent,cacheobj->left,cacheobj->right,
cacheobj->keyval,cacheobj->status,cacheobj->refcount);
printf("Type RETURN: ");
fgets(str, sizeof(str)-1, stdin);
return NULL;
} else {
register struct CACHE *proxyobj;
while (cacheobj->objmanager != NULL) {
freeactive = 1;
proxyobj = (*cacheobj->objmanager) (cacheobj);
freeactive = 0;
if (proxyobj == NULL) return NULL;
if (proxyobj == cacheobj) break;
cacheobj = proxyobj;
}
if ((cacheobj->status & CACHE_MODIFIED) && cacheobj->objmanager == NULL)
printf("cachelist No manager to write modified cache\n");
cacheobj->lstcache->nxtcache = cacheobj->nxtcache;
cacheobj->nxtcache->lstcache = cacheobj->lstcache;
if (cacheobj->parent != NULL) { /* Check if in tree... */
if (cacheobj->left == NULL) {
if (cacheobj->right == NULL) {
*(cacheobj->parent) = NULL;
} else {
cacheobj->right->parent = cacheobj->parent;
*(cacheobj->parent) = cacheobj->right;
}
} else {
if (cacheobj->right == NULL) {
cacheobj->left->parent = cacheobj->parent;
*(cacheobj->parent) = cacheobj->left;
} else {
register struct CACHE *leftpath = cacheobj->left;
register struct CACHE *rightpath = cacheobj->right;
while (1) {
if (leftpath->right == NULL) {
leftpath->right = cacheobj->right;
cacheobj->right->parent = &leftpath->right;
cacheobj->left->parent = cacheobj->parent;
*(cacheobj->parent) = cacheobj->left;
break;
} else {
if (rightpath->left == NULL) {
rightpath->left = cacheobj->left;
cacheobj->left->parent = &rightpath->left;
cacheobj->right->parent = cacheobj->parent;
*(cacheobj->parent) = cacheobj->right;
break;
} else {
leftpath = leftpath->right;
rightpath = rightpath->left;
}
}
}
}
}
}
if (--cachecount < 0) printf("cachelist, cache current too small\n");
cachefreecount--;
cachedeleted--;
#ifdef DEBUG
cacheobj->parent = NULL;
cacheobj->left = NULL;
cacheobj->right = NULL;
cacheobj->nxtcache = NULL;
cacheobj->lstcache = NULL;
cacheobj->objmanager = NULL;
cacheobj->keyval = 0;
cacheobj->status = 0;
cacheobj->refcount = 0;
#endif
free(cacheobj);
return cacheobj;
}
}
/* cachepurge - trim size of free list */
void cachepurge()
{
register struct CACHE *cacheobj = cachelist.lstcache;
while (cachefreecount > CACHEGOAL && cacheobj != &cachelist) {
register struct CACHE *lastobj = cacheobj->lstcache;
#ifdef DEBUG
if (cacheobj->lstcache->nxtcache != cacheobj ||
cacheobj->nxtcache->lstcache != cacheobj ||
*(cacheobj->parent) != cacheobj) {
printf("CACHE pointers in bad shape! %p %p %p - %p\n",
cacheobj->lstcache->nxtcache,cacheobj,cacheobj->nxtcache->lstcache,*(cacheobj->parent));
}
#endif
if (cacheobj->refcount == 0) {
if (cachefree(cacheobj) != lastobj) cacheobj = lastobj;
} else {
cacheobj = lastobj;
}
}
}
/* cacheflush - flush modified entries in cache */
void cacheflush()
{
register struct CACHE *cacheobj = cachelist.lstcache;
while (cacheobj != &cachelist) {
if (cacheobj->status & CACHE_MODIFIED) {
if (cacheobj->objmanager != NULL) {
(*cacheobj->objmanager) (cacheobj);
} else {
printf("CACHEFLUSH No manager to write modified cache\n");
}
}
cacheobj = cacheobj->lstcache;
}
}
/* cachedelete - delete an object from cache */
struct CACHE *cachedelete(struct CACHE *cacheobj)
{
if (cacheobj != NULL) {
register struct CACHE *cachedel;
do {
cachedel = cachefree(cacheobj);
} while (cachedel != NULL && cachedel != cacheobj);
}
return cacheobj;
}
/* cachedeltree: delete cache subtree */
void cachedeltree(struct CACHE *cacheobj)
{
if (cacheobj != NULL) {
if (cacheobj->left != NULL) cachedeltree(cacheobj->left);
if (cacheobj->right != NULL) cachedeltree(cacheobj->right);
if (cacheobj->refcount == 0) cachedelete(cacheobj);
}
}
/* cacheuntouch - decrement object reference count */
unsigned cacheuntouch(struct CACHE *cacheobj,unsigned reuse,unsigned modflg)
{
if (cacheobj->refcount < 1) {
char str[100];
printf("%8p %8p %8p %8p %8x %8x %8d\n",
cacheobj,cacheobj->parent,cacheobj->left,cacheobj->right,
cacheobj->keyval,cacheobj->status,cacheobj->refcount);
printf("CACHE untouch TOO FAR!\n");
printf("Type RETURN: ");
fgets(str, sizeof(str), stdin);
return 64;
} else {
if (modflg) {
if ((cacheobj->status & CACHE_WRITE) == 0) return SS$_WRITLCK;
cacheobj->status |= CACHE_MODIFIED;
}
if (--cacheobj->refcount == 0) {
/* Move to new list position... */
if (reuse == 0 && cacheobj != cachelist.lstcache) {
cacheobj->lstcache->nxtcache = cacheobj->nxtcache;
cacheobj->nxtcache->lstcache = cacheobj->lstcache;
cacheobj->nxtcache = &cachelist;
cacheobj->lstcache = cachelist.lstcache;
cachelist.lstcache->nxtcache = cacheobj;
cachelist.lstcache = cacheobj;
}
cacheobj->status &= ~CACHE_WRITE;
if (++cachefreecount > CACHELIM && freeactive == 0) cachepurge();
}
}
return 1;
}
/* cachetouch - add one to reference count */
void cachetouch(struct CACHE *cacheobj)
{
if (cacheobj->refcount++ < 1) {
#ifdef DEBUG
if (cacheobj->lstcache->nxtcache != cacheobj ||
cacheobj->nxtcache->lstcache != cacheobj ||
*(cacheobj->parent) != cacheobj) {
printf("CACHE pointers in bad shape! %p %p %p - %p\n",
cacheobj->lstcache->nxtcache,cacheobj,cacheobj->nxtcache->lstcache,*(cacheobj->parent));
}
#endif
cachefreecount--;
}
/* Move object to head of list... */
if (cacheobj != cachelist.nxtcache) {
cacheobj->lstcache->nxtcache = cacheobj->nxtcache;
cacheobj->nxtcache->lstcache = cacheobj->lstcache;
cacheobj->lstcache = &cachelist;
cacheobj->nxtcache = cachelist.nxtcache;
cachelist.nxtcache->lstcache = cacheobj;
cachelist.nxtcache = cacheobj;
}
}
/* cachesearch - to find or create cache entries...
The grand plan here was to use a hash code as a quick key
and call a compare function for duplicates. So far no data
type actually works like this - they either have a unique binary
key, or all records rely on the compare function - sigh!
Never mind, the potential is there! :-) */
void *cachesearch(void **root,unsigned keyval,unsigned keylen,void *key,
int (*cmpfunc) (unsigned keylen,void *key,void *node),
unsigned *createsize)
{
register struct CACHE *parentobj = NULL;
register struct CACHE *cacheobj,**parent = (struct CACHE **) root;
cachesearches++;
while ((cacheobj = *parent) != NULL) {
parentobj = cacheobj;
if (cacheobj->keyval == keyval) {
register int cmp = 0;
if (cmpfunc != NULL)
cmp = (*cmpfunc) (keylen,key,(void *) cacheobj);
if (cmp == 0) {
cachetouch(cacheobj);
return cacheobj;
} else {
if (cmp < 0) {
parent = &cacheobj->left;
} else {
parent = &cacheobj->right;
}
}
} else {
if (cacheobj->keyval < keyval) {
parent = &cacheobj->left;
} else {
parent = &cacheobj->right;
}
}
}
if (*createsize > sizeof(struct CACHE)) {
cacheobj = (struct CACHE *) malloc(*createsize);
if (cacheobj != NULL) {
cacheobj->parent = parent;
cacheobj->left = NULL;
cacheobj->right = NULL;
cacheobj->objmanager = NULL;
cacheobj->keyval = keyval;
cacheobj->status = 0;
cacheobj->refcount = 1;
*parent = cacheobj;
*createsize = 0;
cachecreated++;
/* Add to cache list... */
cacheobj->lstcache = &cachelist;
cacheobj->nxtcache = cachelist.nxtcache;
cachelist.nxtcache->lstcache = cacheobj;
cachelist.nxtcache = cacheobj;
if (parentobj != NULL) {
/* Attempt to mix up the tree a little... */
if (parentobj->left == NULL) {
*parentobj->parent = cacheobj;
cacheobj->parent = parentobj->parent;
parentobj->parent = &cacheobj->left;
cacheobj->left = parentobj;
parentobj->right = NULL;
} else {
if (parentobj->right == NULL) {
*parentobj->parent = cacheobj;
cacheobj->parent = parentobj->parent;
parentobj->parent = &cacheobj->right;
cacheobj->right = parentobj;
parentobj->left = NULL;
}
}
}
if (cachecount++ >= cachepeak) cachepeak = cachecount;
}
}
return cacheobj;
}