#define IRIX_DEBUG	0

#if IRIX_DEBUG
#include <sys/rciioctl.h>
#include <sys/rci.h>
extern int rciClockFd;
#endif

/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	monitor
 *
 * DESCRIPTION:	Core code for the RCI tasks monitors. There is one
 *		RCI task monitor on each CPU.
 * 
 *		The RCI monitors are responsible for waking up tasks and
 *      	scheduling them.  The monitor consists essentially of a loop,
 *      	plus a timer AST routine and a signal AST routine. The loop
 *      	checks for the runnability of tasks, and whether they exceed
 *      	timeout conditions. 
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include "../h/cdefs.h"
#include "../h/rciTask.h"
#include "../h/rciMonitor.h"
#include "../h/errorCodes.h"
#include "../h/errorStack.h"

#include "../h/rciConfig.h"
#include "../rci/maskInterrupts.h"
#include "../rci/create.h"
#include "../rci/monitorCom.h"
#include "../rci/monitorDefs.h"

#if IRIX_DEBUG
#include <sys/time.h>
#define TBS 60
typedef struct {
	  struct timeval tv;
	  int blktimeout;
	  int time;
	  char *str;
} Tbent;

int lastschedast_tick;
int tbi = 0;
Tbent tbuf[TBS];
#endif

static TIMER_REQ _timerQueueHead;		/* head of timer queue */
static TIMER_REQ *freeTimerReqs;		/* free requests */
static TIMER_REQ timerReqPool[MAX_TIMER_REQS];	/* timer request buf */
static int insideHandler;			/* inside timer handler */

/* ----- RCI global variables ----- */

int rciCpu = ARBITER_CPU;
int rciTaskId = 0;
RCI_DESC rciTaskTable[MAX_RCI_TASKS];
int rciNumTasks;
unsigned char *rciBus;
unsigned char *rciIOpage;
void *rciMessagePool[MAX_CPUS];
RCI_RBT *rciRbtDescList;	/* list of RBT_DESCs kept on each CPU */

int rciClockNum = 0;		/* No. of the real time clock	*/
float rciClockRes;		/* Resolution of the real time clock */
static int stub;
int *_rciMaskAsts = &stub;	/* masks interrupts when set */
static int zero = 0;
int *_rciAstsPending = &zero;	/* checks if asts are pending */

int (*_rciRestartAstsFxn)();
static monitorRunAst;

extern int rciSimOnly;		/* only` run RCI simulator tasks */

/* ----- Communication variables between planning task and monitor ----- */

extern TASK_SPEC moncomTaskSpec;
extern int moncomClockNum;
extern float moncomClockRes;
extern int moncomClockExport[];
extern int moncomStartTime;
extern int moncomActivateReq[];
extern TASK_ENTRY *residentTaskQueue;

static int doRelease();
static int doStart();

static dummyFunction()
{
	/* dummy function used to invoke AST handler, and consequently
	   all call to monitorRun(), on ARBITER CPU */
}

extern ERROR_STACK_DESC *errorStack;	/* error stack (used by errorMsg()) */

monitorVariableInit()
{
	/* called at program start time by the init routine in rciCreate.c
	   to set up various global variables. 
	 */

	extern int rciAbortFlag;
	extern int rciFatalFlag;

	extern char moncomCmd[];
	extern int moncomClockNum;
	extern int moncomClockExport[];

	rciCpu = ARBITER_CPU;
	rciTaskId = 0;
	rciNumTasks = 0;
	rciClockNum = 0;
	rciAbortFlag = 0;
	rciFatalFlag = 0;
	monitorRunAst = 0;
	rciRbtDescList = 0;

	bzero(moncomCmd, MAX_CPUS*sizeof(char));
	moncomClockNum = 0;
	bzero(moncomClockExport, MAX_CPUS*sizeof(int));
	bzero(rciTaskTable, sizeof(rciTaskTable));

#ifdef SIMONLY_CFG
	rciSimOnly = 1;
#else
	rciSimOnly = 0;
#endif
}

monitorRun (queue)
register TASK_ENTRY *queue;
{
	/* Main monitor polling routine. Called in a loop on the
	   auxiliaries, and either directly by the planning program
	   or on an interrupt on the arbiter. */

	register int taskCouldRun;
	register TASK_ENTRY *task;
	int nextRunTime;

	/* Continue to check each task on 'queue' until every task
	   reports that ii is not ready to run */

	do 
	 { taskCouldRun = 0;
 
	   for (task = queue->next; task != queue; task = task->next)
	    { 
	      RCI_DESC *td = task->td;
	      ERROR_STACK_DESC *savedErrorStack;

	      if (!errorStack)
	       { clearErrors();	/* to init error stack */
	       }
	      savedErrorStack = errorStack;		 

	      errorStack = (ERROR_STACK_DESC*)td->estk;
	      CLEAR_ERRS(errorStack);
	      rciTaskId = td->taskID;
	      if (rciTaskId == 0)
	       { rciPrintf ("MR rciTaskId = 0, td = 0x%x\n", td);
	       }
	      if (taskCheckOut(td))
	       { taskCouldRun = 1;
	       }
	      rciTaskId = 0;
	      errorStack = savedErrorStack;
	    }
	 }
 	while (taskCouldRun);

	/* If we are on the arbiter CPU, and we have some tasks running
	   ON_CLOCK, schedule an AST that will run 'monitorRun()' at
	   the time required to run the first ON_CLOCK task */	   

	if (rciCpu == ARBITER_CPU)
	 { nextRunTime = UNDEF;
	   for (task = queue->next; task != queue; task = task->next)
	    { register RCI_TCB *tcb;
	      tcb = task->td->tcb;
	      if ( (tcb->scheduleDisp == ON_CLOCK) &&
		   (nextRunTime == UNDEF || nextRunTime > tcb->runTime) )
	       { nextRunTime = tcb->runTime;
	       }
	    }
	 }

	if (rciCpu == ARBITER_CPU && nextRunTime != UNDEF)
	 { if (monitorRunAst)
	    { monitorTimerDelete (&monitorRunAst);
	    }
#ifndef LINUX_CFG
	 /* MS Do not need to re-schedule timer in Linux.  Receive
	    periodic beats already.
	 */
	   monitorTimerRequest (&monitorRunAst, nextRunTime, dummyFunction, 1);
#endif
	 }
}


monitorInterruptRun (queue)
register TASK_ENTRY *queue;
{
	/* Just like monitorRun, execpt it is called from an interrupt.
	   We use a separate routine so that we KNOW we are called
	   from interrupt, and can set the 'interruptSet' field in the
	   tcb for each task accordingly.
	*/
	register TASK_ENTRY *task;

	int taskCouldRun;
	int firstTimeThrough = 1;

	/* Continue to check each task on 'queue' until every task
	   reports that ii is not ready to run. The first time
	   around, the 'interruptSet' field is set to 1. */

	do 
	 { taskCouldRun = 0;

	   for (task = queue->next; task != queue; task = task->next)
	    { 
	      RCI_DESC *td = task->td;
	      ERROR_STACK_DESC *savedErrorStack;

	      if (!errorStack)
	       { clearErrors();	/* to init error stack */
	       }
	      savedErrorStack = errorStack;

	      errorStack = (ERROR_STACK_DESC*)td->estk;
	      CLEAR_ERRS(errorStack);
	      rciTaskId = td->taskID;
	      if (rciTaskId == 0)
	       { rciPrintf ("MIR rciTaskId = 0, td = 0x%x\n", td);
	       }

	      if (firstTimeThrough)
	       { td->tcb->interruptSet = 1;
	       }
	      if (taskCheckOut(td))
	       { taskCouldRun = 1;
	       }

	      rciTaskId = 0;
	      errorStack = savedErrorStack;
	    }

	   firstTimeThrough = 0;
	 }
 	while (taskCouldRun);
}

/* The following macro is used to set the block timeout field in the
   task tcb, if and only if block timeouts are enabled */

#define SET_BLK_TIMEOUT(tcb,t) 						\
	if (tcb->maxBlkCount && tcb->interval != UNDEF &&		\
	    rciInKernelMode())						\
	 { tcb->blkTimeout = (t) + tcb->interval * tcb->maxBlkCount;	\
	 }								\
	else								\
	 { tcb->blkTimeout = UNDEF;					\
	 }

rciOnInterrupt (td)
RCI_DESC *td;
{
	/* wakeup function that ensures an RCI task will wakeup once,
	   and only once, when an interrupt (or signal) arrives 
	 */

	if (td->tcb->interruptSet)
	 { td->tcb->interruptSet = 0;
	   return (1);
	 }
	else
	 { return (0);
	 }
}

rciTimeReady (td)
RCI_DESC *td;
{
	/* A wakeup function which can be used for timed tasks. The function
	   task checkOut does not use this, but does the computation in-line
	   for speed. However, a task could specify this as the wakeup 
	   function in the 'rciWakeupOn()' call. 
	 */

	if (td->tcb->runTime <= rciTime())
	 { return (1);
	 }
	else
	 { return (0);
	 }
}

taskCheckOut (td)
RCI_DESC *td;
{
	/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

	   Basic per-task checkout loop, called by 'monitorRun()' only.
	   The basic functions of this loop are:

	   1. Run any 'execute' functions (either the startup function or
	      a function specified by rciExecute().

	   2. If (the task is active)
	       { abort or release the task if necessary;
		 
		 if (the task is ready to run)
		  { set cycle parameters as needed;
		    run the task control function;
		    check for lag timeouts;
		    set up timeout parameters for the next cycle.
		  }
		 else
		  { check for block timeouts;
		  }
	       }
	   3. else if (the task is releasing)
	       { if (the task is ready to run)
		  { call the release function;
		  }
		 else
		  { check for block timeouts;
		  }
	       }

	   The return value of this function is used in the following
	   way:

	   Returning 1 signifies to the polling loop: yes, you should
	   check this task again for runability before exiting.

	   Returning 0 means no, this task is not runnable right now.

	-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--= */

	register RCI_TCB *tcb = td->tcb;

	if (tcb->executeFxn)
	 { tcb->executeRet = (*tcb->executeFxn)(td, tcb->executeArg);
	   tcb->executeFxn = NULLF;
	   if (tcb->abortReq == (rciTaskId + 1))
	    { return (doRelease (td, CONTROL_FXN_ERR));
	    }
	 }
	if (tcb->controlMode == IDLE)
	 { 
	   if (tcb->cmodeReq == SET_ACTIVE_GO || 
	       tcb->cmodeReq == SET_ACTIVE_WAIT)
	    { tcb->cycleCount = 0;
	      if (tcb->startupFxn)
	       { (*tcb->startupFxn)(td, tcb->controlArg);
		 if (tcb->abortReq == (rciTaskId + 1))
		  { return (doRelease (td, STARTUP_FXN_ERR));
		  }
	       }
	      if (tcb->cmodeReq == SET_ACTIVE_GO)
	       { doStart (td, rciTime());
#if IRIX_DEBUG
tbi = 0;
#endif
	       }
	      tcb->cmodeReq = 0;
	      return (1);
	    }
	   else
	    { return (0);
	    }
	 }
	else if (tcb->controlMode == ACTIVE)
	 { int time;

	   time = rciTime();

	   if (tcb->cmodeReq == SET_IDLE)
	    { tcb->cmodeReq = 0;
	      return (doRelease (td, RELEASE_REQ));
	    }
	   if (tcb->abortReq)
	    { tcb->abortReq = 0;
	      return (doRelease (td, EXTERNAL_ABORT));
	    }

	   /* ---- determine if task is runnable ---- */

	   if ((tcb->scheduleDisp == ON_CLOCK &&
		tcb->runTime <= time) ||
	       (tcb->scheduleDisp != ON_CLOCK &&
		tcb->wakeupFxn &&
		(*tcb->wakeupFxn)(tcb->wakeupArg)) )
	    { 
	      /* determine the next task runTime */

	      if (tcb->nrateReq)		/* recompute runTime */
	       { tcb->newRate = 1;
		 tcb->nrateReq = 0;
		 setupRunTime (tcb, tcb->runTime);
	       }
	      if (tcb->interval != UNDEF)
	       { tcb->runTime += tcb->interval;
	       }

	      tcb->cycleCount++;
	      if (tcb->blockReq == 1)
	       { tcb->blockReq = 2;
	       }

#if IRIX_DEBUG
if (tbi < TBS)
 { gettimeofday (&tbuf[tbi].tv, 0);
   tbuf[tbi].time = rciTime();
   tbuf[tbi].str = "task ran";
   tbi++;
 }
#endif
	      if (tcb->controlFxn)
	       { if (rciTaskId == 0)
		  { rciPrintf ("caught it\n");
		    *(int*)1 = 10;
		  }
		 (*tcb->controlFxn)(td, tcb->controlArg);
		 if (tcb->abortReq == (rciTaskId + 1))
		  { return (doRelease (td, CONTROL_FXN_ERR));
		  }
	       }

	      if (tcb->newRate)
	       { tcb->newRate = 0;
	       }
	      if (tcb->blockReq == 2)
	       { tcb->blockReq = 0;
	       }
	      if (tcb->maxLagCount)
	       { if (checkLagTimeout (tcb, time))
		  { return (doRelease (td, LAG_TIMEOUT));
		  }
	       }
	      SET_BLK_TIMEOUT (tcb, time);


	      tcb->lastTime = time;
	      return (1);
	    }
	   else
	    { if (tcb->blkTimeout != UNDEF && tcb->blkTimeout <= time)
	       { 
#if IRIX_DEBUG
int k;
struct timeval tv;
struct timeval rciWallClock();
TIMER_REQ *req;
TIMER_REQ *queue;
RciTimerEvent tev;

#if 0
for (k=0; k<tbi; k++)
 { rciPrintf ("%d.%06d %5d  %s\n", tbuf[k].tv.tv_sec, tbuf[k].tv.tv_usec,
			      tbuf[k].time, tbuf[k].str);
 }
#endif
rciPrintf ("now:\n");
tv = rciWallClock();
rciPrintf ("%d.%6d  %d  %d (%d)\n", tv.tv_sec, tv.tv_usec, 
			       tcb->blkTimeout, time, rciTime());
rciPrintf("timer queue:\n");
		 queue = &_timerQueueHead;
if (queue->next == queue)
		  { rciPrintf("(empty)\n");
		  }
		 else
		  { for (req=queue->next;req!=queue;req=req->next)
		     { rciPrintf("count %d fxn %x arg %d taskId %d handle %x\n",
req->count,(int)req->fxn,req->arg,req->taskId,(int)req->handle);
		     }
		  }
rciPrintf("last requested tick is %d\n",lastschedast_tick);
while (read(rciClockFd,(char *)&tev,sizeof(tev)) == sizeof(tev))
		  { rciPrintf("kernel timer entry req %d tick %d\n",tev.req,tev.tick);
		  }
#endif
		 return (doRelease (td, BLK_TIMEOUT));
	       }
	      else
	       { return (0); /* possibility 1 */
	       }
	    }
	 }
	else if (tcb->controlMode == RELEASING)
	 { 
	   if (tcb->wakeupFxn && (*tcb->wakeupFxn)(tcb->wakeupArg))
	    { doRelease (td, RELEASE_WAITREQ);
	    }
	   else
	    { if (tcb->blkTimeout != UNDEF && tcb->blkTimeout <= rciTime())
	       { doRelease (td, BLK_TIMEOUT);
	       }
	    }
	   return (0);
	 }
	rciAbort (EInternal, "Bogus controlMode %d\n", tcb->controlMode);
	return (0);
}

static doStart(td, time)
RCI_DESC *td;
int time;
{
	register RCI_TCB *tcb = td->tcb;

	tcb->interruptSet = 0;
	tcb->startTime = time;
	tcb->newRate = YES;
	tcb->nrateReq = 0;
	
	setupRunTime (tcb, tcb->startTime);

	if (tcb->scheduleDisp == ON_TRIGGER && tcb->triggerFxn)
	 { int taskId = rciTaskId;
	   rciTaskId = td->taskID;
	   monitorTimerRequest (&tcb->triggerAst, tcb->runTime,
				 tcb->triggerFxn, tcb->triggerArg);
	   rciTaskId = taskId;
	 }
	SET_BLK_TIMEOUT (tcb, tcb->startTime);

	tcb->controlMode = ACTIVE;
}

static doRelease (td, code)
RCI_DESC *td;
int code;
{
	/* called whenever the task is released. It calls the task's
	   release function, and then either resets the control block
	   and sets the task IDLE, or puts the task back 'to sleep' on
	   a wakeup function if that is what the release function
	   requests (the release function can request this only once).

	   Returning 1 signifies to the polling loop: yes, you should
	   check this task again for runability before exiting.

	   Returning 0 means no, this task is not runnable right now.
	*/

	register RCI_TCB *tcb = td->tcb;

	abortIfNecessary (td, code);

	if (tcb->releaseFxn)
	 { (*tcb->releaseFxn)(td, tcb->controlArg, code);
	 }
	taskControlBlockReset (tcb);
	tcb->controlMode = IDLE;
	return (0);
}

setupRunTime (tcb, time)
register RCI_TCB *tcb;
int time;
{
	/* 
	   Computes from scratch the next possible run time for a timed
	   task given the present time ('time'), the start time ('startTime'
	   in the tcb), the interval, and the offset.

	   It also moves the interval and offset data from the 'new' fields
	   in the tcb to their actual fields.
	*/

	int mod, dt;

	tcb->interval = tcb->newInterval;
	tcb->offset = tcb->newOffset;

	if (tcb->interval != UNDEF)
	 { dt = time - tcb->startTime + tcb->interval - tcb->offset;
	   if (mod = (dt % tcb->interval))
	    { tcb->runTime = time + tcb->interval - mod;
	    }
	   else
	    { tcb->runTime = time;
	    }
	 }
	else
	 { tcb->runTime = UNDEF;
	 }
	return (tcb->runTime);
}

monitorSignalHandler()
{
	/* Signal handler. At this time, this routine is only
	   responsible for activating RCI tasks, on a command
	   from the planning task (which issues the signal from
	   the rciStart() routine) */

	register TASK_ENTRY *task;
	int nextRunTime;

	/* responsible for activating RCI tasks */

	nextRunTime = UNDEF;

	for (task = residentTaskQueue->next; task != residentTaskQueue;
					     task = task->next)
	 { RCI_DESC *td = task->td;
	    
	   if (td->tcb->cmodeReq == SET_ACTIVE_SYNC)
	    { doStart (td, moncomStartTime);
#if IRIX_DEBUG
tbi = 0;
#endif
	      td->tcb->cmodeReq = 0;
	    }
	 }

	for (task = residentTaskQueue->next; task != residentTaskQueue;
					     task = task->next)
	 { register RCI_TCB *tcb;
	   tcb = task->td->tcb;
	   if (tcb->scheduleDisp == ON_CLOCK && rciCpu == ARBITER_CPU)
	    { if (nextRunTime == UNDEF || nextRunTime > tcb->runTime)
	       { nextRunTime = tcb->runTime;
	       }
	    }
	 }

	if (rciCpu == ARBITER_CPU && nextRunTime != UNDEF)
	 { if (monitorRunAst)
	    { monitorTimerDelete (&monitorRunAst);
	    }
	   monitorTimerRequest (&monitorRunAst, nextRunTime, dummyFunction, 2);
	 }
}

/* =-=-=-=-=-=-=-=-=-= Monitor Timer Queue Routines -=-=-=-=-=-=-=-=-=-=-= */

monitorTimerInit()
{
	int i;

	/* called once when the monitor is set up by monitorInit().
	   Creates the list of free timer requests and initializes
	   the head of the timer queue (to NULL).
	*/

	_timerQueueHead.next = _timerQueueHead.prev = &_timerQueueHead;

	for (i=0; i<MAX_TIMER_REQS-1; i++)
	 { timerReqPool[i].next = &timerReqPool[i+1];
	 }
	freeTimerReqs = &timerReqPool[0];
	timerReqPool[MAX_TIMER_REQS-1].next = NULL;
	insideHandler = NO;
}

monitorTimerHandler ()
{
	/* AST routine for timer actions. Goes down the timer queue
	   and puts everything whose timeout has expired on a ``fire''
	   list. Then it executes everything on that list.
	*/
	register TIMER_REQ *req;
	register TIMER_REQ *timerQueue = &_timerQueueHead;
	register TIMER_REQ *firingList;

	int count = rciGetClock(rciClockNum);

	/* Don't need to mask interrupts cause they're always masked
	   inside AST's */

	if (timerQueue == timerQueue->next)	/* stray -- queue is empty */
	 { return;
	 }

	for (req = timerQueue->next; req != timerQueue &&
				     req->count <= count; req = req->next)
	   ;

	insideHandler = YES;	/* stop rescheduling of the queue */

	if (req != timerQueue->next)
	 { 
	   firingList = timerQueue->next;	/* set up firing list */
	   req->prev->next = NULL;		/* ... and terminate it */
	   timerQueue->next = req;		/* Reconfigure timer queue */
	   req->prev = timerQueue;
   
	   /* fire the items that have expired */
   
	   while ((req = firingList) != NULL)
	    { ERROR_STACK_DESC *savedErrorStack;
	      int savedTaskId;

	      if (!errorStack)
	       { clearErrors();	/* to init error stack */
	       }
	      savedErrorStack = errorStack;
	      savedTaskId = rciTaskId;

	      *(req->handle) = 0;
	      rciTaskId = req->taskId;
	      errorStack = (ERROR_STACK_DESC*)rciTaskTable[rciTaskId].estk;

	      (*req->fxn)(req->arg);

	      errorStack = savedErrorStack;
	      rciTaskId = savedTaskId;

	      firingList = req->next;
	      req->next = freeTimerReqs;
	      freeTimerReqs = req;
	    }
	 }

/*>>>*/
	if (rciCpu == ARBITER_CPU)
	 { monitorRun (residentTaskQueue);
	 }
/*<<<*/

	insideHandler = NO;

	/* reschedule the timer queue if its not empty */

	if (timerQueue->next != timerQueue)
	 { rciSchedAst (rciClockNum, timerQueue->next->count, 
			   monitorTimerHandler, 0);
	 }
}

monitorTimerRequest (handle, time, fxn, arg)
int *handle;
int time;
int (*fxn)();
int arg;
{
	/* Places a request into the timer queue behind all other requests
	   whose time value is less than or equal to that of this request.
	*/
	register TIMER_REQ *req;
	register TIMER_REQ *timerQueue = &_timerQueueHead;
	TIMER_REQ *newReq;

	int count = (time + (int)rciClockRes - 1) / (int)rciClockRes;	

	MASK_INTERRUPTS;

	for (req = timerQueue->next; req != timerQueue &&
				     req->count <= count; req = req->next)
	   ;
	if ((newReq = freeTimerReqs) == NULL)
	 { rciAbort (EInternal, 
	"monitorTimerRequest() -- no free timer request packets\n");
	   return (-1);
	 }
	freeTimerReqs = newReq->next;

	newReq->next = req;
	newReq->prev = req->prev;
	req->prev->next = newReq;
	req->prev = newReq;

	newReq->count = count;
	newReq->fxn = fxn;
	newReq->arg = arg;
	newReq->taskId = rciTaskId;
	newReq->handle = handle;
	*handle = (int)newReq;

	/* if we are the first item on the queue, reschedule the ast */

	if ((timerQueue->next == newReq) && !insideHandler)
	 { 
#ifndef LINUX_CFG
	   /* MS Linux does not need to re-schedule timer.  Heartbeat
	      function will call as necessary 
	   */
	   rciSchedAst (rciClockNum, count, monitorTimerHandler, 0);
#endif
	 }
	UNMASK_INTERRUPTS;
	return (1);
}

monitorTimerDelete (handle)
int *handle;
{
	/* delete the indicated request from the timer queue */

	register TIMER_REQ *req;
	register TIMER_REQ *timerQueue = &_timerQueueHead;

	MASK_INTERRUPTS;

	if (timerQueue->next == timerQueue)
	 { /* we have a PROBLEM. */
	   rciAbort(EInternal, "monitorTimerDelete(): corrupted queue\n");
	   UNMASK_INTERRUPTS;
	   return;
	 }
	if (req = (TIMER_REQ*)*handle)
	 {
	   req->prev->next = req->next;
	   req->next->prev = req->prev;

	   /* reset clock AST if we deleted the head of the queue */
   
	   if ((req->prev == timerQueue) && !insideHandler)
	    { int count;
	      if (timerQueue->next == timerQueue)
	       { rciSchedAst (rciClockNum, 0, NULLF, 0);
	       }
	      else if ((count = timerQueue->next->count) > req->count)
	       { rciSchedAst (rciClockNum, count, monitorTimerHandler, 0);
	       }
	    }
	   req->next = freeTimerReqs;
	   freeTimerReqs = req;
	   *handle = 0;
	 }
	UNMASK_INTERRUPTS;
}
