home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ARM Club 3
/
TheARMClub_PDCD3.iso
/
hensa
/
graphics
/
a090_1
/
c
/
cache
next >
Wrap
Text File
|
1992-04-18
|
10KB
|
372 lines
/**** 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