home *** CD-ROM | disk | FTP | other *** search
- /**** cache.c ****/
- /* By Paul Field
- * See !ReadMe file for distribution/modification restrictions
- */
-
- #include "cache.h"
- #include <assert.h>
- #include <stdlib.h>
-
- /* Turn on/off cache state change messages */
- #define CACHE_DEBUG FALSE
-
- /* I may move the list and queue routines into their own modules */
- /* It's just that they should be in some sort of data type library */
- /* rather than the euclid library */
-
- /***** Simple queue routines *****/
-
- typedef struct item item;
- struct item
- { item *next;
- };
-
- typedef struct queue
- { item *front;
- item *rear;
- }queue;
-
- static void queue_empty(queue *q)
- { q->front = NULL;
- }
-
- static BOOL queue_isempty(queue *q)
- { return(q->front == NULL);
- }
-
- static void queue_addvalue(queue *q, item *toadd)
- { if (queue_isempty(q))
- { q->front = toadd;
- }
- else
- { q->rear->next = toadd;
- }
- q->rear = toadd;
- toadd->next = NULL;
- }
-
- static item *queue_removevalue(queue *q)
- { item *removed;
-
- assert(!queue_isempty(q));
- removed = q->front;
- q->front = removed->next;
- return(removed);
- }
-
- static item *queue_findandremovevalue(queue *q, BOOL (*match)(item*, void*),void *handle)
- { item **itempp;
-
- for (itempp = &q->front; *itempp != NULL; itempp = &(*itempp)->next)
- { if (match(*itempp, handle))
- { item *removed;
-
- removed = *itempp;
- *itempp = removed->next;
- return(removed);
- }
- }
- return(NULL);
- }
-
- /***** Simple list routines *****/
-
- typedef item *list;
-
- static list list_empty(void)
- { return(NULL);
- }
-
- static BOOL list_isempty(list l)
- { return(l == NULL);
- }
-
- static item *list_head(list l)
- { return(l);
- }
-
- static list list_tail(list l)
- { return(l->next);
- }
-
- static void list_cons(list *l, item *tocons)
- { tocons->next = *l;
- *l = tocons;
- }
-
-
- static void list_remove2(list l, item *toremove, list *last)
- { if (!list_isempty(l))
- { if (list_head(l) == toremove)
- { *last = list_tail(l);
- }
- else
- { list_remove2(list_tail(l), toremove, &list_head(l)->next);
- }
- }
- }
-
- static void list_remove(list *l, item *toremove)
- { list_remove2(*l, toremove, l);
- }
-
-
- /***** The cache routines *****/
-
- #if CACHE_DEBUG
- static void cache_cwfreport(void *cacheaddr, BOOL active);
- static void cache_cfreport(void *cacheaddr, BOOL active);
- static void cache_crpreport(void *cacheaddr, cache_processstart, void *handle);
- static void cache_crapreport(void *cacheaddr, void *handle);
- #else
- # define cache_cwfreport(ca,a)
- # define cache_cfreport(ca,a)
- # define cache_crpreport(ca,p,h)
- # define cache_crapreport(ca,h)
- #endif
-
-
- typedef struct pendingprocess
- { void *next;
- cache_processstart fn;
- void *handle;
- }pendingprocess;
-
- typedef struct cache
- { void *next;
- void *cacheaddr;
- queue pendingprocesses;
- }cache;
-
- static list activecaches = NULL;
- /* List of active caches with pending processes. */
- /* A binary tree or hash table might be faster but */
- /* I can't see applications using more than a few */
- /* caches at one time - so lists will be fast enough */
-
-
- /**** Goodness Me. First abstract data types and now recursion. ****/
- /**** With all this good programming practice, it might actually work. ****/
-
- static cache *cache_find2(list remaining, void *cacheaddr)
- { if (list_isempty(remaining))
- { return(NULL);
- }
- else
- { cache *head;
-
- head = (cache *)list_head(remaining);
- if (head->cacheaddr == cacheaddr)
- { return(head);
- }
- else
- { return(cache_find2(list_tail(remaining), cacheaddr));
- }
- }
- }
-
- static cache *cache_find(void *cacheaddr)
- { return(cache_find2(activecaches, cacheaddr));
- }
-
-
-
- BOOL cache_callwhenfree(void *cacheaddr, cache_processstart fn, void *handle)
- { cache *thecache;
-
- thecache = cache_find(cacheaddr);
- if (!thecache)
- { cache *newcache;
-
- /**** If the cache is not active, add it to the ****/
- /**** active list and start the process immediately ****/
- if ((newcache = malloc(sizeof(cache))) != NULL)
- { newcache->cacheaddr = cacheaddr;
- queue_empty(&newcache->pendingprocesses);
- list_cons(&activecaches, (item *)newcache);
- cache_cwfreport(cacheaddr,FALSE);
- fn(cacheaddr, handle);
- return(TRUE);
- }
- }
- else
- { pendingprocess *newprocess;
-
- /**** If the cache is active, add the process to the pending queue ****/
- if ((newprocess = malloc(sizeof(pendingprocess))) != NULL)
- { newprocess->fn = fn;
- newprocess->handle = handle;
-
- queue_addvalue(&thecache->pendingprocesses, (item *)newprocess);
- cache_cwfreport(cacheaddr,TRUE);
- return(TRUE);
- }
- }
- return(FALSE);
- }
-
-
- void cache_finished(void *cacheaddr)
- { cache *thecache;
-
- thecache = cache_find(cacheaddr);
- assert(thecache != NULL); /* cache_finish either called from an unregistered */
- /* process or called more than once from a */
- /* registered process. */
-
- /**** If there are no pending processes then remove cache from ****/
- /**** the active list otherwise remove a process and run it ****/
- if (queue_isempty(&thecache->pendingprocesses))
- { list_remove(&activecaches, (item *)thecache);
- free(thecache);
- cache_cfreport(cacheaddr,FALSE);
- }
- else
- { pendingprocess *process;
-
- process = (pendingprocess *)queue_removevalue(&thecache->pendingprocesses);
- cache_cfreport(cacheaddr,TRUE);
- process->fn(cacheaddr, process->handle);
- free(process);
- }
- }
-
- static BOOL match(item *iprocess, void *vpinfo)
- { pendingprocess *process = (pendingprocess *)iprocess;
- pendingprocess *pinfo = vpinfo;
-
- return(process->fn == pinfo->fn && process->handle == pinfo->handle);
- }
-
- void cache_removeprocess(void *cacheaddr, cache_processstart fn, void *handle)
- { cache *thecache;
-
- if ((thecache = cache_find(cacheaddr)) != NULL)
- { item *process;
- pendingprocess pinfo;
-
- pinfo.fn = fn;
- pinfo.handle = handle;
- /* A bit inefficient but won't make much difference unless */
- /* you have a lot of processes queued up on the cache */
- while((process = queue_findandremovevalue(&thecache->pendingprocesses,
- match, &pinfo)) != NULL)
- { free(process);
- }
- }
- cache_crpreport(cacheaddr,fn,handle);
- }
-
-
- static BOOL match2(item *iprocess, void *handle)
- { pendingprocess *process = (pendingprocess *)iprocess;
-
- return(process->handle == handle);
- }
-
- void cache_removeallprocesses(void *cacheaddr, void *handle)
- { cache *thecache;
-
- if ((thecache = cache_find(cacheaddr)) != NULL)
- { item *process;
-
- while((process = queue_findandremovevalue(&thecache->pendingprocesses,
- match2, handle)) != NULL)
- { free(process);
- }
- }
- cache_crapreport(cacheaddr,handle);
- }
-
-
- BOOL cache_inuse(void *cache)
- { return(cache_find(cache) != NULL);
- }
-
- void *cache_address(euclid_header *structure)
- { return(structure->cache ? structure->cache : structure);
- }
-
-
-
-
- /***** Debugging routines *****/
- /* Loads of horrid messing about with files because !DDT seems to stop
- * redirection of output. It also tended to leave the output file empty
- * which is why I close it after every message and then reopen it.
- */
-
- #if CACHE_DEBUG
-
- #include <stdio.h>
- #include "bbc.h"
-
- static FILE *output = NULL;
-
- static void cache_displayall(void)
- { list caches;
-
- fputs(" Here are the active caches :", output);
- fputc('\n',output);
- for (caches = activecaches; !list_isempty(caches); caches=list_tail(caches))
- { cache *thecache = (cache *)list_head(caches);
- list processes;
-
- fprintf(output, " cacheaddr = %p \n",thecache->cacheaddr);
- fprintf(output, " pending = ");
- /* I now access the queue directly and treat its contents as a list */
- /* This is VERY BAD programming practice, I'm only doing it because */
- /* this is meant to be a debugging routine and I need to write it quickly */
- for (processes = (list)thecache->pendingprocesses.front; !list_isempty(processes);
- processes = list_tail(processes))
- { pendingprocess *p;
-
- p = (pendingprocess *)list_head(processes);
- fprintf(output, "%p(%p), ", p->fn, p->handle);
- }
- fputs(" ",output);
- fputc('\n',output);
- }
- fputs("*****************",output);
- fputc('\n',output);
- if (output != stdout)
- { fclose(output);
- }
- }
-
- static void cache_reportoutput(void)
- {/*output = fopen("<Example$Dir>.Cachedata","a");*/
- output=stdout;
- bbc_vdu(4); bbc_vdu(26);
- }
-
-
- static void cache_cwfreport(void *cacheaddr, BOOL active)
- { cache_reportoutput();
- fprintf(output, "cache_callwhenfree(%p,...) - cache %sactive. \n",
- cacheaddr,active?"":"not ");
- cache_displayall();
- }
-
- static void cache_cfreport(void *cacheaddr, BOOL active)
- { cache_reportoutput();
- fprintf(output, "cache_finished(%p) - cache %s active. \n",cacheaddr,
- active?"still":"no longer");
- cache_displayall();
- }
-
- static void cache_crpreport(void *cacheaddr, cache_processstart fn, void *handle)
- { cache_reportoutput();
- fprintf(output, "cache_removeprocess(%p,%p,%p) \n", cacheaddr, fn, handle);
- cache_displayall();
- }
-
- /* cache_removeallprocesses has an unfortunate acronym
- * - but at least I'm being consistant */
- static void cache_crapreport(void *cacheaddr, void *handle)
- { cache_reportoutput();
- fprintf(output, "cache_removeallprocesses(%p,%p) \n", cacheaddr, handle);
- cache_displayall();
- }
- #endif
-