/* File: objcache.c * Author: Jean Thierry-Mieg (mieg@mrc-lmb.cam.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1991 * ------------------------------------------------------------------- * Acedb 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. * * This program 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: * Exported functions: * HISTORY: * Last edited: Nov 17 16:18 2000 (edgrif) * * Nov 17 15:19 2000 (edgrif): Add cacheUnlock function, needed in case * where cache entry has been locked for update but is then not * altered and needs to be unlocked prior to destruction of entry. * * Sep 15 15:57 1998 (edgrif): Fix missing ';' before chronoReturn in cacheKOadd * * -----NO USER RECORDED FOR BELOW CHANGE--------- * - resolved WIN32 cache crash by fix at c.a. line 521 (see comment) * * Jul 12 12:25 1996 (srk) * * Nov 6 18:26 1991 (mieg): added READING_MODELS protection * Created: Wed Nov 6 18:25:34 1991 (mieg) * CVS info: $Id: objcache.c,v 1.33 2000/11/17 16:47:08 edgrif Exp $ *------------------------------------------------------------------- */ /***************************************************************/ /** File objcache.c : **/ /** Handles the secondary cache of the ACeDB program. **/ /***************************************************************/ /***************************************************************/ /* */ /* ? routines are public : */ /* create/destroy, update/save, acedump */ /* Get/Release, Lock/Unlock, ForceFree */ /* The secondary cache is a linked list of up to */ /* MAXKNOWNOBJ unpinned objects. */ /* */ /* The cache has two piles controlled by Get and Lock/Unlock */ /* 1 : the "knownCache", a LIFO stack, on top of which each */ /* cacheEntry is put when "get" (implying a load the first time*/ /* or a pop, or a new). These cacheEntries are lost, LIFO way, */ /* when the cache is full, automatically. */ /* 2 : the "Locked CacheEntries". For counting purpose */ /* This pile is controlled explicitely by Lock/Unlock */ /* cacheEntrySave saves one marked cacheEntry to disk. */ /* */ /* cacheStatus gives the status. */ /* */ /* The Primary Cache handling is done in the separate file */ /* blocksub.c invoked only by the static routines load/unload. */ /* */ /* R.Durbin & J.Thierry-Mieg. */ /* last modified 7/3/1991 by JTM. */ /* */ /***************************************************************/ #include #include #include /* our public header. */ #include #include #include /* for q->cache */ #include #include #include /*****************************************************************/ static CACHE cacheNew(KEY key) ; static CACHE cacheCopy(CACHE cache); static void cacheDestruct(CACHE v); static void cachePop(CACHE v); static void cacheKOadd(CACHE v); static void cacheKOremove(CACHE v); static void cacheLOadd(CACHE v); static void cacheLOremove(CACHE v); static CACHE cacheAlloc (void) ; /* self managed calloc */ static void cacheFree (CACHE p); #ifndef READONLY static int cacheStore(CACHE v, BOOL doCopy, BOOL dowait) ; #endif /*****************************************************************/ /* Use this macro to check that a cache pointer passed into a routine is */ /* non-NULL and points to a valid cache block. */ /* */ #define CHECK_CACHE_PTR(FUNCNAME, CACHE_PTR) \ if (!(CACHE_PTR) || (CACHE_PTR)->magic != CACHEMAGIC) \ messcrash("%s received a %s pointer", \ (FUNCNAME), \ (CACHE_PTR) ? "bad cache" : "NULL") /********************* globals ***********************************/ /*number of cacheEntries, defined in blocksub*/ extern int MAXKNOWNCACHE; /* Used in _lots_ of places.... */ BOOL READING_MODELS = FALSE ; /********************* locals ***********************************/ static CACHE knownCacheTop = NULL , knownCacheEnd = NULL , lockedCacheTop = NULL ; static int nKnownCache = 0; static int nAllocatedCache = 0 ; /* useful for debug */ static int ncache1read = 0, ncache1written = 0 , ncache2served = 0, ncache2saved = 0 ; static KEYSET knownKeySet = 0 ; static Stack freeStack = 0 ; /* #define TESTCACHE_to_test */ /********************** PUBLIC *********************************/ #ifndef ACEDB5 void bsTreeArrayFlush(void) { return ; } #endif /* General utility routines */ void cacheMark(CACHE cache) { CHECK_CACHE_PTR("cacheMark", cache) ; if (cache->lock == 0) messcrash("cacheMark called on cache that is not locked.") ; if (cache->lock > 0) cache->lock=2 ; return ; } /***********************/ BOOL isCacheModified(CACHE cache) { CHECK_CACHE_PTR("isCacheModified", cache) ; return cache->lock == 2 ; } /**********************/ BOOL isCacheLocked(CACHE cache) { CHECK_CACHE_PTR("isCacheLocked", cache) ; return cache->lock > 0 ? TRUE : FALSE ; } #ifndef READONLY /**********************/ /* The purpose of this function is to allow cache to be unlocked in a */ /* special set of circumstances: */ /* sequence is: bsUpdate() */ /* object is not changed for some reason */ /* bsDestroy() */ /* at this point, the cache entry for the object is still */ /* locked from the bsUpdate and so if the caller trys */ /* to cacheKill() the cache entry the program crashes. */ /* */ /* This function allows the caller to unlock the cache entry for the object */ /* after the bsDestroy() so that the cacheKill() will work. */ /* */ /* Because this function is potentially dangerous we check lock and refCount.*/ /* */ void cacheUnlock(CACHE cache) { CHECK_CACHE_PTR("cacheUnlock", cache) ; if (cache->lock == 0) messcrash("Attempt to unlock cache that is already unlocked") ; if (cache->refCount != 0) messcrash("Attempt to unlock cache that is still in use, cache->refCount = %d", cache->refCount) ; #ifdef ED_G_NEVER_INCLUDE_THIS_CODE /* I'm not very happy about not including this test, but it appears that */ /* an object can be locked with no one referring to it, but it has been */ /* modified...maybe this is ok...not sure... */ if (cache->hasBeenModified) messcrash("Attempt to unlock cache that although not in use or locked has been modified") ; #endif /* ED_G_NEVER_INCLUDE_THIS_CODE */ /* In fear and loathing I unlock the cache entry and will never again */ /* sleep peacefully in my bed. */ cache->lock = 0 ; return ; } int cacheSaveAll(void) { CACHE v = knownCacheEnd ; int n = 0 ; chrono("cacheSaveAll") ; while(v) { if(v->hasBeenModified) { cacheStore(v,TRUE,TRUE) ; n++ ; } v = v->up ; if (n >= 128) { n = 0 ; bsTreeArrayFlush () ; } } bsTreeArrayFlush () ; chronoReturn(); return(0); } #endif /************************************************************************/ /* To be called only by session manager */ /* void cacheClear(void) { CACHE v, w ;; v = knownCacheTop; while(v) { if (v->x) switch(v->type) { case 'B' : bsTreePrune(v->x) ; break ; default: messcrash("cacheDestruct received unknown type") ; } w = v ; v = v->next ; cacheFree(w); } v = lockedCacheTop; while(v) { if (v->x) switch(v->type) { case 'B' : bsTreePrune(v->x) ; break ; default: messcrash("cacheDestruct received unknown type") ; } w = v ; v = v->next ; cacheFree(w); } knownCacheTop = lockedCacheTop = knownCacheEnd = 0 ; } */ /************************************************************************/ /* Gives the cache status*/ /* Public; Calls nothing*/ int cacheStatus (int *used, int *locked, int*known, int *modified, int *nc2rp, int *nc2wp, int *nccrp, int *nccwp) { register CACHE v; register int i, l, m; i = l = m = 0; v = knownCacheTop; while(v) { if (v->refCount) l++ ; else i++; if(v->hasBeenModified) m++ ; v = v->next; } v = lockedCacheTop; while(v) { l++ ; if(v->hasBeenModified) m++ ; v=v->next; } *used = nAllocatedCache ; *known = i ; *locked = l ; *modified = m ; *nc2rp = ncache2served ; *nc2wp = ncache2saved ; *nccrp = ncache1read ; *nccwp = ncache1written ; return TRUE; } /************************************************************************/ /* Public; but the user must never destroy it */ KEYSET cacheKeySet (void) { register CACHE v ; register int i ; /* it may be slower to keysetinsert remove all the time if (!knownKeySet) knownKeySet = keySetCreate() ; return knownKeySet ; */ knownKeySet = arrayReCreate (knownKeySet, nAllocatedCache, KEY) ; i = 0 ; v = knownCacheTop; while(v) { keySet(knownKeySet, i++) = v->key ; v = v->next; } keySetSort(knownKeySet) ; keySetCompress (knownKeySet) ; return knownKeySet ; } /************************************************************************/ /* Public ; Called by cacheCreate and cacheUpdate */ /* Calls buCopy */ static CACHE cacheCopy(CACHE cache) { CACHE copy ; chrono("cacheCopy") ; CHECK_CACHE_PTR("cacheCopy", cache) ; copy = cacheAlloc(); *copy = *cache; /* everything but those relevant to this package */ switch(cache->type) { case 'A' : copy->x = (CACHE_HANDLE) arrayCopy((Array)cache->x) ; break ; case 'B' : copy->x = bsTreeCopy(cache->x); break ; default : messcrash("cacheCopy received unknown type") ; } chronoReturn(); return copy; } /**************************************************************/ /* to get a cache address in main memory, with read only status*/ /* getting it if necessary from the primary cache disk */ /* the system is allowed to unload when refCount == 0 */ /* Called by bsCreate and cacheUpdate */ /* Calls either cachePop (if k is already in memory)*/ /* or cacheNew */ /* or cacheCopy if the cacheEntry is locked but not yet modified */ CACHE cacheCreate(KEY k, CACHE_HANDLE *bsp, BOOL newCopy) { LEXP q=KEY2LEX(k); CACHE cache ; chrono("cacheCreate") ; ncache2served++ ; #ifdef TESTCACHE messout("Hello from cacheCreate(%s)",name(k)); #endif if(!q) messcrash("Error : cacheCreate called with unknown key"); if ((cache = q->cache)) switch(cache->lock) { case 0 : /* already in memory*/ if (!newCopy) { (cache->refCount)++; cachePop(cache); *bsp = cache->x ; chronoReturn() ; return cache ; } /* else fall thru on case 1 */ case 1 : /* accessed read write else where, make a copy */ cache = cacheCopy(cache); break; default: /* already modified, grab it again from disk */ cache = 0 ; break; } /* cache->hasBeenModified is set to 0 by cacheNew */ /* otherwise is stays as is */ if (!cache) cache = cacheNew(k) ; if (cache->key != k) messcrash("Cache mismatch %s:%s != %s:%s", className(k), name(k), className(cache->key), name(cache->key) ) ; if (cache) { cache->refCount = 1; if (!newCopy) { cacheKOadd(cache); cache->lock = 0; q=KEY2LEX(k); /* possible relocation */ q->cache = cache ; /* for future access */ } else { cacheLOadd(cache); cache->lock = -1 ; } *bsp = cache->x ; } else *bsp = 0 ; chronoReturn(); return cache ; } /**************************************************************/ /* new : to get a new cache address in cache, */ /* Static; called by cacheCreate; Calls cacheAlloc*/ static CACHE cacheNew(KEY key) { CACHE cache = cacheAlloc(); chrono("cacheNew") ; if (cache->key) messcrash("cacheNew: double use of %s", name(cache->key)) ; cache->key = key; cache->magic = CACHEMAGIC ; cache->type = pickType(key) ; cache->hasBeenModified = 0; switch (cache->type) { case 'A' : messcrash("cacheNew received Array type, read a.h") ; case 'B' : ncache1read++ ; #ifdef ACEDB5 cache->x = bsTreeArrayGet(key) ; /* cannot fail */ #else cache->x = bsTreeGet(key) ; /* cannot fail */ #endif break ; default : messcrash("cacheNew received unknown type") ; } chronoReturn() ; return cache ; } /************************************************************************/ /* Release : complementary to Get */ void cacheDestroy (CACHE v) { LEXP q ; CACHE presentCache ; chrono("cacheDestroy") ; CHECK_CACHE_PTR("cacheDestroy", v) ; q = KEY2LEX(v->key) ; presentCache = q->cache ; if(!(v->refCount) --) messerror("Unbalanced Release of cacheEntry %s", name(v->key)); switch (v->lock) { case 0 : /* This cacheEntry was opened read only */ break ; case 1 : /* This cacheEntry has not been modified, thus we can still use it next time */ lexunlock(v->key); cacheLOremove(v); cacheKOadd(v); break ; case 2 : /* We do want to lose the modifications */ lexunlock(v->key); /* fall thru on case -1 */ case -1: cacheLOremove(v); cacheDestruct(v) ; chronoReturn(); return ; } if (presentCache && presentCache != v && /* There is a newer version around */ !v->refCount) /* v is not beeing used */ /* mieg: april 96 I use to have: && !v->hasBeenModified) If it has, it is likely to become lame duck when * presentCache will be saved, * so it is better not to save it now but it seems that in the alias case oldCacche was lost prior to the cacheSave call, hence one could have still a modified flag around the present obj. hence this block remained around and was saved to disk after the key had been aliased */ { cacheKOremove(v); cacheDestruct (v) ; } chronoReturn(); return; } void cacheClone(CACHE source, CACHE dest, CACHE_HANDLE *bsp) { bsTreePrune(dest->x); /* remove old data */ *bsp = dest->x = bsTreeCopy(source->x); } /***********************************************************************/ /* Locks a cache, it must then be unlocked or ForceDestroy explicitely*/ /* before the memory can be recovered*/ /* Public; Calls cacheCreate */ CACHE cacheUpdate(KEY k, CACHE_HANDLE *bsp, BOOL inXref) { unsigned char cc = lexGetStatus (k) ; LEXP q = KEY2LEX(k); CACHE cache ; chrono("cacheUpdate") ; if (cc & LOCKSTATUS) { messout ("Sorry, cacheEntry %s is already locked", name (k)) ; chronoReturn(); return NULL; } lexlock (k) ; if ((cache = q->cache )) /* already in memory*/ { if (!cache->refCount && (!cache->hasBeenModified || inXref)) /* unused and untouched */ /* Except during doXref where I am garanteed that I will not edit then bsDestroy. * The problem is that the sequence bsUpdate/cacheMark/bsDestroy would destroy * the earlier modif of an ealier update/save sequence. * Obviously, if cache is in use, you need to work on a copy. */ cacheKOremove(cache); else cache = cacheCopy(cache); } else { cache = cacheNew(k) ; /* Important fix by rbrusk: 28/12/96: Need to reset q here for times when cacheNew(k) indirectly results in a dangling q pointer reference from Lexi2 arrayExtend() */ q = KEY2LEX(k) ; q->cache = cache ; } /* lex2Check() ; */ if (cache->key != k) messcrash("Cache mismatch %s:%s != %s:%s", className(k), name(k), className(cache->key), name(cache->key) ) ; if (cache) { cache->refCount = 1; cacheLOadd(cache); cache->lock = 1; /* locking */ *bsp = cache->x ; } ncache2served++ ; chronoReturn(); return cache ; } /************************************************************************/ /* unlock : to register the recent modifications */ void cacheCheck(KEY key, CACHE v) { if (v->key != key) messcrash("Cache mismatch %s:%s != %s:%s", className(key), name(key), className(v->key), name(v->key) ) ; return ; } /************************************************************************/ void cacheSave(CACHE v) { LEXP q ; CACHE oldCache ; chrono("cacheSave") ; CHECK_CACHE_PTR("cacheSave", v) ; q = KEY2LEX(v->key) ; oldCache = q->cache ; if (v->lock == 2) ncache2saved++ ; #ifdef TESTCACHE messout("Hello from cacheSave(%s)",name(v->key)); #endif if(v->lock <= 0) messcrash("Attempt to save an unlocked cacheEntry %s:%s", className(v->key),name(v->key)) ; lexunlock(v->key); cacheLOremove(v); /* mieg: apr-96: flag as lame before KOadd */ if (oldCache && oldCache != v) /* Lame duck, not to be saved */ { oldCache->hasBeenModified = 0 ; if (!oldCache->refCount) { cacheKOremove (oldCache) ; cacheDestruct (oldCache) ; } } q = KEY2LEX(v->key) ; /* possible relocation */ q->cache = v ; /* for future access, including inside KOadd */ cacheKOadd(v); if (v->lock == 2) (v->hasBeenModified)++ ; v->lock = 0; if(!(v->refCount) --) messerror("Unbalanced Release of cacheEntry %s", name(v->key)); chronoReturn(); return; } /**************************************************************/ /* to copy a cacheEntry into the block cache */ /* Static, called by cachefree */ /* calls ..Store */ /* Note i abandon the forced write system available inside block sub */ #ifndef READONLY static int cacheStore(CACHE v, BOOL doCopy, BOOL dowait) { chrono("cacheStore") ; CHECK_CACHE_PTR("cacheStore", v) ; #ifdef TESTCACHE messout("Hello from cacheStore"); #endif if(v->hasBeenModified && (v == KEY2LEX(v->key)->cache)) switch(v->type) { case 'A' : messcrash("cacheStore received A class obj") ; break ; #ifndef READONLY case 'B' : ncache1written++ ; #ifdef ACEDB5 bsTreeArrayStore(v->x, dowait) ; /* non destructive operation */ if (!doCopy) bsTreePrune(v->x); #else if (doCopy) bsTreeStore(bsTreeCopy(v->x)) ; else bsTreeStore(v->x); /* Destructive operation */ #endif break ; #endif default : messcrash("cacheStore received unknown type") ; } v->hasBeenModified = FALSE ; chronoReturn() ; return(0); } #endif /**************************************************************/ static void cacheDestruct(CACHE v) { LEXP q ; /* KEY dummy = v->key ; for debugging */ chrono("cacheDestruct") ; CHECK_CACHE_PTR("cacheDestruct", v) ; q = KEY2LEX(v->key); /* lex2Check() ; */ if (q->cache == v) { q->cache = 0 ; lex2clear(v->key) ; } if (v) { if (v->x) switch(v->type) { /* case 'A' : arrayDestroy(v->x) ; break ; */ case 'B' : bsTreePrune(v->x) ; break ; default: messcrash("cacheDestruct received unknown type") ; } cacheFree(v); } chronoReturn(); return; } /**************************************************************/ /* Called to explicitly get rid of the cache2 ref for an object that has */ /* been destroyed. This shouldn't be called until the all BS objects that */ /* reference the cache entry have been properly cleaned up. */ /* */ void cacheKill (KEY key) { LEXP q = KEY2LEX(key); CACHE v = q ? q->cache : 0 ; chrono("cacheKill") ; if (v) { if (v->refCount || v->lock) messcrash ("Illicit call to cacheKill") ; v->hasBeenModified = 0 ; cacheKOremove(v) ; cacheDestruct (v) ; } chronoReturn(); return; } /**************************************************************/ /**************************************************************/ /* Linked Lists manipulations */ /************************************************************************/ /*pops a cache to the top of the knownCache line*/ /* Static; called by cacheCreate; Calls nothing*/ static void cachePop(CACHE v) { chrono("cachePop") ; CHECK_CACHE_PTR("cachePop", v) ; cacheKOremove(v) ; cacheKOadd(v) ; chronoReturn(); return; } /*********************************************************************/ /* to add an cacheEntry to the Known CacheEntry List */ /* Static; called by cacheCreate and cacheSave */ static void cacheKOadd(CACHE v) { CACHE w ; int nBSused, nAa, bsmem, pass = 0, nn ; extern int BS_Cache_limit ; #ifdef TESTCACHE messout("Hello from cacheKOadd(%s)",name(v->key)); #endif chrono("cacheKOadd") ; CHECK_CACHE_PTR("cacheKOadd", v) ; nKnownCache++; switch(v->type) { case 'A' : messcrash("cacheKOadd received A type") ; break ; case 'B' : break ; default : messcrash("cacheKOadd received unknown type") ; } w = knownCacheTop; knownCacheTop=v; if (!knownCacheEnd) knownCacheEnd = v; if (w) w->up = v ; v->up = NULL; v->next = w ; w = knownCacheEnd ; if (!BS_Cache_limit) messcrash("BS_cache_limit not set") ; lao: nn = 1 ; w = knownCacheEnd ; while (w) { if (pass != 1) { BSstatus (&nBSused,&nAa,&bsmem) ; if (nBSused <= BS_Cache_limit) break ; } v = w; w = w->up; if (!w) switch (pass) { case 0: pass++ ; goto lao ; break ; case 1: bsTreeArrayFlush() ; pass++ ; goto lao ; break ; case 2: if (nKnownCache < 10) /* this means there are some very big obj around, rather than a memory leak */ { BS_Cache_limit *= 2 ; goto lao ; } else messcrash ("Cache 2 is full, %d objetcs are locked\n balance bsCreate/destroy\n%s\n%s %s:%s %s", nKnownCache, " or increase the limits in wspec/cachesize.wrm", " for example ", className(knownCacheEnd->key), name(knownCacheEnd->key), "is locked ") ; break ; } if (nn > 128 || 4*nn > nKnownCache) switch (pass) { case 0: pass++ ; goto lao ; break ; case 1: /* during pass 1, systematically store flagged obj of last quarter */ bsTreeArrayFlush() ; pass++ ; goto lao ; break ; case 2: break ; } if (!v->refCount && pass == 0 && v->hasBeenModified) { nn++ ; continue ; } if (!v->refCount) { cacheKOremove(v); if(v->hasBeenModified) switch (pass) { case 0: break ; #ifndef READONLY case 1: /* during pass 1, systematically store flagged obj of last quarter */ nn++ ; /* fall thru */ case 2: cacheStore(v, FALSE, TRUE) ; v->x = 0; break ; #endif } cacheDestruct(v); } } bsTreeArrayFlush() ; chronoReturn() ; } /*********************************************************************/ /* to remove an cacheEntry to the Known CacheEntry List */ /* Static; called by cacheUpdate and cacheNew */ /* Calls nothing. */ static void cacheKOremove(CACHE v) { CACHE u, w; chrono("cacheKOremove") ; CHECK_CACHE_PTR("cacheKOremove", v) ; #ifdef TESTCACHE messout("Hello from cacheKOremove(%s)",name(v->key)); #endif /* if (knownKeySet) keySetRemove(knownKeySet, v->key) ; */ nKnownCache--; u=v->up; w=v->next; if (knownCacheTop == v) knownCacheTop = w ; if(knownCacheEnd == v) knownCacheEnd = u ; if(u) u->next = w; if (w) w->up = u; v->up = v->next = NULL; chronoReturn() ; } /*********************************************************************/ /* to add an cacheEntry to the Locked CacheEntry List */ /* Static; called by cacheSave and cacheForceFree */ /* Calls nothing. */ static void cacheLOadd(CACHE v) { CACHE w ; chrono("cacheLOadd") ; CHECK_CACHE_PTR("cacheLOadd", v) ; #ifdef TESTCACHE messout("Hello from cacheLOadd(%s)",name(v->key)); #endif w= lockedCacheTop ; lockedCacheTop = v ; if (w) w->up = v ; v->up = NULL; v->next = w ; chronoReturn() ; } /*********************************************************************/ /* to add an cacheEntry to the Locked CacheEntry List */ /* Static; called by cacheSave and cacheDestroy */ /* Calls nothing. */ static void cacheLOremove(CACHE v) { CACHE u, w ; chrono("cacheLOremove") ; CHECK_CACHE_PTR("cacheLOremove", v) ; #ifdef TESTCACHE messout("Hello from cacheLOremove(%s)",name(v->key)); #endif u=v->up; w=v->next; if (lockedCacheTop == v) lockedCacheTop = w ; if(u) u->next = w; if (w) w->up = u; v->up = v->next = NULL; chronoReturn() ; } /**************************************************************/ /**************************************************************/ static CACHE cacheAlloc (void) /* self managed calloc */ { static int blocSize = 256 ; CACHE p ; int i = -12 ; chrono("cacheAlloc") ; if (!freeStack) freeStack = stackCreate (4*blocSize) ; if (stackEmpty (freeStack)) { p = (CACHE) messalloc (blocSize * SIZE_OF_CACHE) ; for (i = blocSize ; i-- ; ++p) push (freeStack,p,CACHE) ; /* printf ("Adding %d to CACHE free list\n",blocSize) ; */ nAllocatedCache += blocSize ; blocSize *= 2 ; } p = pop (freeStack,CACHE) ; p->magic = CACHEMAGIC ; chronoReturn() ; return p ; } /***********************/ static void cacheFree (CACHE p) { chrono("cacheFree") ; CHECK_CACHE_PTR("cacheFree", p) ; memset (p, 0, SIZE_OF_CACHE) ; push (freeStack,p,CACHE) ; if (p->refCount) invokeDebugger() ; chronoReturn() ; /* lex2Check () ; */ } /***********************/ /* functions private to the cache package, that allow objcachedisp.c access to cache system variables within this file */ CACHE cacheGetKnownTop (void) { return knownCacheTop; } CACHE cacheGetLockedTop (void) { return lockedCacheTop; } /**************************************************************/ /**************************************************************/