|
pfcn.c |
|
|---|---|
|
|
/*
* pfcn.c
*
* ------------------------------------------------------------
* A Kla2 Module
* Copyright (c) 2003, David Clifton
* All Rights Reserved
* http://www.codelode.com
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice, website reference, this permission
* notice, and the disclaimer of warranty below shall be included
* in all copies, derivatives, or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
* -------------------------------------------------------------
*
* This module contains the data and methods necessary
* to create and use prioritized, pre-emptive functions (pfcn's).
*
*/
#include "types.h"
#include "sizes.h"
#include "err.h"
#include "intrpt.h"
#include "pfcn.h"
/*
* A prioritized function (pfcn) is a function with no
* arguments, which returns no value, which is allocated
* by this module (the pfcn module), which has a
* priority relative to other pfcns.
*
* Once it is posted, a pfcn runs to completion unless
* it is interrupted by the posting of a pfcn of a
* higher priority. In that case the higher priority
* pfcn runs to completion and then returns control
* to the one that was running when it was posted.
*
* If the posted pfcn does not preempt the pfcn
* that is currently running, it goes into a prioritized
* wait list to run when it's priority and then its turn
* within that priority comes up.
*
* The prioritized wait list has the useful property that
* only one instance of a registered pfcn my reside in
* the list at a time. Therefore if a pfcn is posted
* several times before it gets around to running,
* it runs only once.
*
* A pfcn that is running does not reside in the priority
* wait list, so it is possible for a pfcn to repost
* itself.
*
* Pfcn's use the 'C' stack, the same as hardware
* interrupts. That is why they are called pfcn's
* and not threads.
*
* Pfcn's posted at the same priority run in
* round-robin fashion.
*
* The priority associated with a pfcn may be an
* integer between 0 (included) and
* SYSMAX_PRIORITIES (excluded). See sizes.h to
* adjust the number of legal priorities.
*
*/
/*
* declarations of private methods of pfcn module
*/
void insertPfcnEntry(s16 pfdex);
void removePfcnEntry(void);
void callPfcns(s16 pfdex,s16 prevpri);
void dummyPfcn(void);
s16 getFreePfcn(void);
/*
* Prioritized function (pfcn) table/list entry struct
*/
typedef struct {
s16 priority; // priority at which this pfcn is registered
s16 status; // unused, inactive, waiting or active
#define PFCN_UNUSED (0) // no pfcn registered for this entry
#define PFCN_INACTIVE (1) // registered, but not posted
#define PFCN_WAITING (2) // posted but not running
#define PFCN_ACTIVE (3) // either running or prempted
void (*pfcn)(void); // pointer to prioritized function (pfcn)
s16 prevPfdex; // index of previous pfcn in list or NIL
s16 nextPfdex; // index of next pfcn in list or NIL
} PFCN;
/* -------------------------------------------------------
* Pfcn module data
* ------------------------------------------------------- */
s16 pfcnWaitList=NIL; // head of pfcn wait list
s16 currPfdex=NIL; // index of currently executing pfcn
s16 currPriority=NIL; // priority of currently executing pfcn
s16 priorityIndex[SYSMAX_PRIORITIES]; // priority index into pfcn wait list
PFCN pfcnTable[SYSMAX_PFCNS]; // table of pfcn entries and home
// of pfcn wait list
/* ------------------------------------------------------
* Public Methods
* ------------------------------------------------------ */
/*
* Pfcn_init
* Initialize the priority index, the prioritized function
* table and other pfcn data.
*/
void Pfcn_init(void)
{
s16 j;
for(j=0;j<SYSMAX_PFCNS;j++) { // initialize prioritized function table
pfcnTable[j].priority=NIL; // and list host
pfcnTable[j].status=PFCN_UNUSED;
pfcnTable[j].pfcn=dummyPfcn;
pfcnTable[j].prevPfdex=NIL;
pfcnTable[j].nextPfdex=NIL;
}
for(j=0;j<SYSMAX_PRIORITIES;j++) { // initialize priority index
priorityIndex[j]=NIL; // to the pfcn wait list
}
pfcnWaitList=NIL; // set head of the pfcn wait list
currPfdex=NIL; // index of currently executing pfcn
currPriority=NIL; // priority of currently executing pfcn
}
/*
* Pfcn_new
* Obtain the index of a pfcn struct, and fill it
* in with the priority and function pointer supplied.
* A bad supplied priority, or failure to find
* a free PFCN struct in the pfcn array will result in
* the system error shutdown function being called.
* Returns an index into the pfcn table
* which can be used to post the pfcn.
*/
s16 Pfcn_new(s16 pri,void (*pfcn)(void))
{
PFCN *pfptr;
s16 pfdex;
bool intsave;
intsave=Intrpt_disable();
pfdex=getFreePfcn();
if(pfdex==NIL) {
Err_shutdown(NO_MORE_PFCNS);
} else {
pfptr=&pfcnTable[pfdex];
pfptr->priority=pri;
pfptr->status=PFCN_INACTIVE;
pfptr->pfcn=pfcn;
pfptr->prevPfdex=NIL;
pfptr->nextPfdex=NIL;
}
Intrpt_restore(intsave);
return pfdex;
}
/*
* Pfcn_post
*
* If the pfcn to post has a higher priority than what
* is running, go ahead and call it, and any other pfcns
* subsequently found on the wait list with a higher priority
* than what is currently running.
* If it has an equal or lower priority than what is
* running, just insert it into the appropriate place in the
* wait list.
*/
void Pfcn_post(s16 pfdex)
{
s16 prevPf,prevPri;
bool intsav;
PFCN *pfptr;
if(pfdex<0 || pfdex >= SYSMAX_PFCNS) {
Err_shutdown(BAD_PFCN_POSTED);
} else {
pfptr=&pfcnTable[pfdex];
intsav=Intrpt_disable();
prevPf=currPfdex;
prevPri=currPriority;
if(pfptr->priority>prevPri) {
callPfcns(pfdex,prevPri); // call one or more pfcns
} else if(pfptr->status != PFCN_WAITING){
insertPfcnEntry(pfdex); // insert into correct location in pfcn list
}
currPfdex=prevPf;
currPriority=prevPri;
Intrpt_restore(intsav);
}
}
/*
* Pfcn_priority
*
* Return the priority of the pfcn entry whose
* pfcnTable index is supplied. If NIL is returned,
* that means no entry has been registered with that
* index.
*/
s16 Pfcn_priority(s16 pfdex)
{
if(pfdex<0 || pfdex>=SYSMAX_PFCNS) {
Err_shutdown(BAD_PFCN_INDEX);
}
return pfcnTable[pfdex].priority;
}
/*
* Pfcn_current
*
* Returns the index of the prioritized function (pfcn)
* which is currently running. If none, returns NIL
*/
s16 Pfcn_current(void)
{
return currPfdex;
}
/* ------------------------------------------------------
* Private Methods
* ------------------------------------------------------ */
/*
* insertPfcnEntry
*
* Insert the priority entry designated by the argument into
* the pfcn wait list in the appropriate position (sorted from
* highest to lowest priority). Update the priority index for
* its priority with the index of the pfdex supplied.
*
* Call this routine only with interrupts disabled
*/
void insertPfcnEntry(s16 pfdex)
{
s16 pfinsert,pridex;
s16 temp;
PFCN *pfptr,*tpfptr;
pfptr=&pfcnTable[pfdex];
/*
* Set the insert point for this pfcn
* (it should be the last pfcn having the
* supplied priority, or one higher if there is
* none with the supplied priority)
* The entire list should remain sorted from
* highest to lowest priority.
*/
pridex=pfptr->priority;
pfinsert=NIL;
while(pfinsert==NIL && pridex
|