File:  [Atari MiNT] / MiNT / src / timeout.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:59:09 2018 UTC (8 years, 1 month ago) by root
Branches: mint, MAIN
CVS tags: mint112, HEAD
MiNT 1.12

/*

Copyright 1990,1991,1992 Eric R. Smith.

Copyright 1992,1993,1994 Atari Corporation.

All rights reserved.

*/



#include "mint.h"



/*

 * We initialize proc_clock to a very large value so that we don't have

 * to worry about unexpected process switches while starting up

 */



short proc_clock = 0x7fff;



/* used by filesystems for time/date stamps; updated once per second */

short timestamp, datestamp;



extern short in_kernel;	/* in main.c */



static void unnapme P_((PROC *));

static TIMEOUT	*newtimeout P_((short));

static void	disposetimeout P_((TIMEOUT *));

static void	inserttimeout P_ ((TIMEOUT *, long));



#define TIMEOUTS	20	/* # of static timeout structs */

#define TIMEOUT_USED	0x01	/* timeout struct is in use */

#define TIMEOUT_STATIC	0x02	/* this is a static timeout */



/* This gets implizitly initialized to zero, thus the flags are

 * set up correctly.

 */

static TIMEOUT timeouts[TIMEOUTS] = { { 0, }, };

TIMEOUT *tlist = NULL;

TIMEOUT *expire_list = NULL;



/* Number of ticks after that an expired timeout is considered to be old

   and disposed automatically.  */

#define TIMEOUT_EXPIRE_LIMIT 400 /* 2 secs */



static TIMEOUT *

newtimeout(fromlist)

	short fromlist;

{

	TIMEOUT *t;

	short i, sr;



	if (!fromlist) {

		t = kmalloc(SIZEOF(TIMEOUT));

		if (t) {

			t->flags = 0;

			t->arg = 0;

			return t;

		}

	}

	sr = spl7();

	for (i = 0; i < TIMEOUTS; ++i) {

		if (!(timeouts[i].flags & TIMEOUT_USED)) {

			timeouts[i].flags |= (TIMEOUT_STATIC|TIMEOUT_USED);

			spl(sr);

			timeouts[i].arg = 0;

			return &timeouts[i];

		}

	}

	spl(sr);

	return 0;

}



static void

disposetimeout(t)

	TIMEOUT *t;

{

	if (t->flags & TIMEOUT_STATIC) t->flags &= ~TIMEOUT_USED;

	else kfree(t);

}



static void

dispose_old_timeouts ()

{

  TIMEOUT *t, **prev, *old;

  long now = *(long *) 0x4ba;

  short sr = spl7 ();



  for (prev = &expire_list, t = *prev; t; prev = &t->next, t = *prev)

    {

      if (t->when < now)

	{

	  /* This and the following timeouts are too old. Throw them away. */

	  *prev = 0;

	  spl (sr);

	  while (t)

	    {

	      old = t;

	      t = t->next;

	      disposetimeout (old);

	    }

	  return;

	}

    }

  spl (sr);

}



static void

inserttimeout(t, delta)

	TIMEOUT *t;

	long delta;

{

	TIMEOUT **prev, *cur;

	short sr = spl7();



	cur = tlist;

	prev = &tlist;

	while (cur) {

		if (cur->when >= delta) {

			cur->when -= delta;

			t->next = cur;

			t->when = delta;

			*prev = t;

			spl(sr);

			return;

		}

		delta -= cur->when;

		prev = &cur->next;

		cur = cur->next;

	}

	assert(delta >= 0);

	t->when = delta;

	t->next = cur;

	*prev = t;

	spl(sr);

}

	

/*

 * addtimeout(long delta, void (*func)()): schedule a timeout for the current

 * process, to take place in "delta" milliseconds. "func" specifies a

 * function to be called at that time; the function is passed as a parameter

 * the process for which the timeout was specified (i.e. the value of

 * curproc at the time addtimeout() was called; note that this is probably

 * *not* the current process when the timeout occurs).

 *

 * NOTE: if kernel memory is low, newtimeout() will try to get a statically

 * allocated timeout struct (fallback method).

 */



TIMEOUT * ARGS_ON_STACK

addtimeout(delta, func)

	long delta;

	void (*func) P_((PROC *));

{

	TIMEOUT *t;

	TIMEOUT **prev;

	short sr;



	/* Try to reuse an already expired timeout that had the

	   same function attached */

	sr = spl7();

	prev = &expire_list;

	for (t = *prev; t != NULL; prev = &t->next, t = *prev)

	  if (t->proc == curproc && t->func == func)

	    {

	      *prev = t->next;

	      break;

	    }



	spl(sr);

	if (t == NULL)

	  t = newtimeout(0);



/* BUG: we should have some fallback mechanism for timeouts when the

   kernel memory is exhausted

 */

	assert(t);



	t->proc = curproc;

	t->func = func;

	inserttimeout(t, delta);

	return t;

}



/*

 * addroottimeout(long delta, void (*)(PROC *), short flags);

 * Same as addtimeout(), except that the timeout is attached to Pid 0 (MiNT).

 * This means the timeout won't be cancelled if the process which was

 * running at the time addroottimeout() was called exits.

 *

 * Currently only bit 0 of `flags' is used. Meaning:

 * Bit 0 set: Call from interrupt (cannot use kmalloc, use statically

 *	allocated `struct timeout' instead).

 * Bit 0 clear: Not called from interrupt, can use kmalloc.

 *

 * Thus addroottimeout() can be called from interrupts (bit 0 of flags set),

 * which makes it *extremly* useful for device drivers.

 * A serial device driver would make an addroottimeout(0, check_keys, 1)

 * if some bytes have arrived.

 * check_keys() is then called at the next context switch, can use all

 * the kernel functions and can do time cosuming jobs.

 */



TIMEOUT * ARGS_ON_STACK

addroottimeout(delta, func, flags)

	long delta;

	void (*func) P_((PROC *));

	short flags;

{

	TIMEOUT *t;

	TIMEOUT **prev;

	short sr;



	/* Try to reuse an already expired timeout that had the

	   same function attached */

	sr = spl7();

	prev = &expire_list;

	for (t = *prev; t != NULL; t = *prev)

	  {

	    if (t->proc == rootproc && t->func == func)

	      {

		*prev = t->next;

		break;

	      }

	    prev = &t->next;

	  }

	spl(sr);



	if (!t)

	  t = newtimeout(flags & 1);



	if (!t) return NULL;

	t->proc = rootproc;

	t->func = func;

	inserttimeout(t, delta);

	return t;

}



/*

 * cancelalltimeouts(): cancels all pending timeouts for the current

 * process

 */



void ARGS_ON_STACK

cancelalltimeouts()

{

	TIMEOUT *cur, **prev, *old;

	long delta;

	short sr = spl7 ();



	cur = tlist;

	prev = &tlist;

	while (cur) {

		if (cur->proc == curproc) {

			delta = cur->when;

			old = cur;

			*prev = cur = cur->next;

			if (cur) cur->when += delta;

			spl(sr);

			disposetimeout(old);

			sr = spl7();

		/* ++kay: just in case an interrupt handler installed a

		 * timeout right after `prev' and before `cur' */

			cur = *prev;

		}

		else {

			prev = &cur->next;

			cur = cur->next;

		}

	}

	prev = &expire_list;

	for (cur = *prev; cur; cur = *prev)

	  {

	    if (cur->proc == curproc)

	      {

		*prev = cur->next;

		spl (sr);

		disposetimeout (cur);

		sr = spl7 ();

	      }

	    else

	      prev = &cur->next;

	  }

	spl (sr);

}



/*

 * Cancel a specific timeout. If the timeout isn't on the list, or isn't

 * for this process, we do nothing; otherwise, we cancel the time out

 * and then free the memory it used. *NOTE*: it's very possible (indeed

 * likely) that "this" was already removed from the list and disposed of

 * by the timeout processing routines, so it's important that we check

 * for it's presence in the list and do absolutely nothing if we don't

 * find it there!

 */



void ARGS_ON_STACK

canceltimeout(this)

	TIMEOUT *this;

{

	TIMEOUT *cur, **prev;

	short sr = spl7();



	/* First look at the list of expired timeouts */

	prev = &expire_list;

	for (cur = *prev; cur; cur = *prev)

	  {

	    if (cur == this && cur->proc == curproc)

	      {

		*prev = cur->next;

		spl (sr);

		disposetimeout (this);

		return;

	      }

	    prev = &cur->next;

	  }



	prev = &tlist;

	for (cur = tlist; cur; cur = cur->next) {

		if (cur == this && cur->proc == curproc) {

			*prev = cur->next;

			if (cur->next) {

				cur->next->when += this->when;

			}

			spl (sr);

			disposetimeout(this);

			return;

		}

		prev = &cur->next;

	}

	spl(sr);

}



void ARGS_ON_STACK

cancelroottimeout(this)

	TIMEOUT *this;

{

	TIMEOUT *cur, **prev;

	short sr = spl7();



	/* First look at the list of expired timeouts */

	prev = &expire_list;

	for (cur = *prev; cur; cur = *prev)

	  {

	    if (cur == this && cur->proc == rootproc)

	      {

		*prev = cur->next;

		spl (sr);

		disposetimeout (this);

		return;

	      }

	    prev = &cur->next;

	  }

	

	prev = &tlist;

	for (cur = tlist; cur; cur = cur->next) {

		if (cur == this && (cur->proc == rootproc)) {

			*prev = cur->next;

			if (cur->next) {

				cur->next->when += this->when;

			}

			spl (sr);

			disposetimeout(this);

			return;

		}

		prev = &cur->next;

	}

	spl(sr);

}



/*

 * timeout: called every 20 ms or so by GEMDOS, this routine

 * is responsible for maintaining process times and such.

 * it should also decrement the "proc_clock" variable, but

 * should *not* take any action when it reaches 0 (the state of the

 * stack is too uncertain, and time is too critical). Instead,

 * a vbl routine checks periodically and if "proc_clock" is 0

 * suspends the current process

 */



volatile int our_clock = 1000;



/* variables for monitoring the keyboard */

extern IOREC_T	*keyrec;	/* keyboard i/o record pointer */

extern short	kintr;		/* keyboard interrupt pending (see intr.s) */



void ARGS_ON_STACK

timeout()

{

	int ms;		/* time between ticks */



	kintr = keyrec->head != keyrec->tail;



	ms = *((short *)0x442L);

	if (proc_clock > 0)

		proc_clock--;



	our_clock -= ms;

	if (tlist) {

		tlist->when -= ms;

	}

}



/*

 * sleep() calls this routine to check on alarms and other sorts

 * of time-outs on every context switch.

 */



void

checkalarms()

{

	extern long searchtime;		/* in dosdir.c */

	PROC *p;

	long delta;

	void (*evnt) P_((PROC *, long arg));

	TIMEOUT *old;

	short sr;

	long arg;



/* do the once per second things */

	while (our_clock < 0) {

		our_clock += 1000;

		timestamp = Tgettime();

		datestamp = Tgetdate();

		searchtime++;

		reset_priorities();

	}



	sr = spl7();

/* see if there are outstanding timeout requests to do */

	while (tlist && ((delta = tlist->when) <= 0)) {

		p = tlist->proc;

/* hack: pass an extra long as arg, those intrested in it will need

 * a cast and have to place it in t->arg themselves but that way

 * everything else still works without change	-nox */

		arg = tlist->arg;

		evnt = (void (*)P_((PROC *, long)))tlist->func;

		old = tlist;

		tlist = tlist->next;

/* if delta < 0, it's possible that the time has come for the next timeout

 * to occur.

 * ++kay: moved this before the timeout fuction is called, in case the

 * timeout function installes a new timeout. */

		if (tlist)

			tlist->when += delta;

		old->next = expire_list;

		old->when = *(long *) 0x4ba + TIMEOUT_EXPIRE_LIMIT;

		expire_list = old;

		spl(sr);

/* ++kay: debug output at spl7 hangs the system, so moved it here */

		TRACE(("doing timeout code for pid %d", p->pid));



	/* call the timeout function */

		(*evnt)(p, arg);

		sr = spl7();

	}

	spl(sr);

	/* Now look at the expired timeouts if some are getting old */

	dispose_old_timeouts ();

}



/*

 * nap(n): nap for n milliseconds. Used in loops where we're waiting for

 * an event. If we expect the event *very* soon, we should use yield

 * instead.

 * NOTE: we may not sleep for exactly n milliseconds; signals can wake

 * us earlier, and the vagaries of process scheduling may cause us to

 * oversleep...

 */



static void

unnapme(p)

	PROC *p;

{

	if (p->wait_q == SELECT_Q && p->wait_cond == (long)nap) {

		short sr = spl7();

		rm_q(SELECT_Q, p);

		add_q(READY_Q, p);

		spl(sr);

		p->wait_cond = 0;

	}

}



void ARGS_ON_STACK 

nap(n)

	unsigned n;

{

	TIMEOUT *t;



	t = addtimeout((long)n, unnapme);

	sleep(SELECT_Q, (long)nap);

	canceltimeout(t);

}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.