[comp.unix.microport] Interval timer for SV/AT and SV/386!

dave@micropen (David F. Carlson) (06/21/88)

This is a published driver for a System 5 interval timer.  It works.
Think about it:  almost realtime!  You can easily code a fsleep()
that will wakeup in less than 1 second.  I find minimum reliable
interval to be about 2/10ths of a second on 20MHz 386-- your mileage
may vary.  The trick is that the System 5 scheduler gives priority to
processes with pending signals.  This timer forces a pre-determined
signal on a presumably sleeping process (which raises its priority
even more).  It is almost assured (on a non-thrashing machine) that
the process waiting for the interval timer will be the next to execute:
thanks to our friend the UNIX scheduler.

It is not mine however, I have made changes to it so that it runs
under SV/AT and SV/386.  These mods are nasty as they affect the
proc structure of the running process:  something I am loath to do.
The driver is available in un-altered-for-Microport form from comp.unix.sources,
but I though it of wide enough interest to post it here.  Sorry its not
in shar format but it is only 300 lines.  Oh well...

Since this is not my driver but merely a port and redistribution,
I may not answer mail concerning problems.  Know that it does work
as I used it to right a little thread runner.  Enjoy!

------ cut here for ft.c --------

/*  ft.c     08/15/81 */

#include <sys/ft.h>
#if NFT > 0
/*
 *  Fast Timer -- a pseudo-device that behaves like an interval timer.
 *
 *  Note because of exclusive-open it can also be used as lock files.
 */

#include <types.h>
#include <param.h>
#include <systm.h>
#include <tty.h>
#include <dir.h>
#include <signal.h>
#include <errno.h>
#include <user.h>
#include <immu.h>
#include <region.h>
#include <proc.h>
#include <map.h>
/* not for microport #include <pte.h> */
#include <conf.h>

/*
 * per-minor-device state table
 *
 * Invariants:
 *      - device is open iff ftt_procp != 0
 *      - timeout in progress iff ftt_state != AVAILABLE
 */

struct fttab {

    struct proc *ftt_procp; /* proctab entry and process id of controlling  */
    short ftt_pid;          /*   process.                                   */

    short ftt_state;        /* AVAILABLE, CANCELLED, or signal number       */

} fttab[NFT];

#define AVAILABLE   (0)
#define CANCELLED  (-1)

extern hz;  /* kernal parm */
#define MAXTIMEREQ (60*hz)  /* 1 minute */
#define minor(x)	(0x0ff & (x))		/* low char for this driver */

/*
 * open 
 *
 * exclusive use only
 */

/*ARGSUSED*/
ftopen(d)
{
register unsigned dminor;
register struct fttab *ftp;


    dminor = minor(d);

    if( dminor >= NFT ) {
		u.u_error = ENXIO;
		return;
    }
    ftp = &fttab[dminor];
    if( (ftp->ftt_procp != (struct proc *)0)
      || (ftp->ftt_state != AVAILABLE) ) {
        u.u_error = EBUSY;
        return;
    }
    ftp->ftt_procp = u.u_procp;
    ftp->ftt_pid = u.u_procp->p_pid;
}

/*
 * close
 */

ftclose(d)
{
register unsigned dminor;
register struct fttab *ftp;

    dminor = minor(d);
#   ifdef PARANOIA
        if( dminor >= NFT ) {
            printf("ERR: ftclose\n");
            u.u_error = ENXIO;
            return;
        }
#   endif
    ftp = &fttab[ dminor ];
    ftp->ftt_procp = (struct proc *) 0;
    ftp->ftt_pid = 0;

}

/*
 * dummy read/write routines
 */

/*ARGSUSED*/
ftread(d)
{
    u.u_error = ENXIO;
}

/*ARGSUSED*/
ftwrite(d)
{
    u.u_error = ENXIO;
}

/*
 * ioctl
 *
 * all real work is done here using
 *
 *  FTIOCSET    request a software interrupt after specified number
 *              of clock ticks.
 *  FTIOCCANCEL cancel an outstanding request.
 */

/*ARGSUSED*/
ftioctl(dev, cmd, addr, flag)
caddr_t addr;
dev_t dev;
{
void fttimeout();
register unsigned dminor;
register struct fttab *ftp;

    dminor = minor(dev);
    if( dminor >= NFT ) {
        printf("ERR: ftioctl!\n");
        u.u_error = EINVAL;
    }

    ftp = &fttab[ dminor ];

    /* Make sure invoking process owns the device.  */
    /* This can only fail if call is from a child   */
    /*  forked off after opening the ft device.     */
        if( (u.u_procp != ftp->ftt_procp)
          || (u.u_procp->p_pid != ftp->ftt_pid) ) {
            u.u_error = ENXIO;
            return;
        }

    /* interpret command */

        switch( cmd ) {

        case FTIOCSET:
            { struct ftsetarg request;

                /* get request, make sure it's reasonable */
                    if( copyin(addr,(caddr_t)&request, sizeof request) ) {
                        u.u_error = EFAULT;
                        return;
                    }
                    if( (request.sigreq <= 0) || (request.sigreq > NSIG) 
                      || (request.timereq > (MAXTIMEREQ)) ) {
                        u.u_error = EINVAL;
                        return;
                    }
                /* schedule the interrupt */
                    if( ftp->ftt_state != AVAILABLE ) {
                        u.u_error = EBUSY;
                        return;
                    }
                    ftp->ftt_state = request.sigreq;
                    timeout( fttimeout, (caddr_t) ftp, request.timereq );
            }
            break;

        case FTIOCCANCEL:
            (void) spl5();
            if( ftp->ftt_state != AVAILABLE )
                ftp->ftt_state = CANCELLED;
            (void) spl0();
            break;

        default:
            u.u_error = ENOTTY;
            break;

        }
}

/*
 * fttimeout
 *
 * called by system timeout
 *
 * verify that requesting process is still around
 * verify that request hasn't been cancelled
 * send the signal
 */
void
fttimeout(ftp)
register struct fttab *ftp;
{
register struct proc *pp;
register int prevstate;

    prevstate = ftp->ftt_state;
    ftp->ftt_state = AVAILABLE;

    if( (prevstate == CANCELLED)
      || ((pp = ftp->ftt_procp) == ((struct proc *)0))
      || (pp->p_pid != ftp->ftt_pid) )
        return;

#   ifdef PARANOIA
        if( (prevstate < 1) || (prevstate >= NSIG) ) {
            printf("ERR: fttimeout %d\n",prevstate);
            return;
        }
#   endif

    psignal( pp, prevstate );

}
#endif
--------end of ft.c --------
-------- cut here for ft.h --------

#define NFT (8)  /* 8 devices */
#define FTIOCSET	(('T'<<8)|0)
#define FTIOCCANCEL	(('T'<<8)|1)

#define FTBASENAME	"/dev/ft/ft0"

struct ftsetarg	{
	unsigned short timereq;  /* in mS < 1  */
	short sigreq;
};
-------- end of ft.h ---------
-------- cut here for config --------
* 1 "rt/config"

*	Micropen interval timer driver

character(25)

prefix = ft

functions = read, write, ioctl, open, close
-------- end of config ---------
-------- start of fttest.c ---------
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/ft.h>


int count = 0;
int up_count(), stop();
int fd;
int done = 1;
extern errno;
struct ftsetarg ftarg;

main(argc, argv)
int argc;char **argv;
{

fd = open ("/dev/ft",O_RDONLY);

if (argv[1] != NULL)	{
	sscanf(argv[1],"%d",&ftarg.timereq);
	fprintf(stderr,"Time req=%d.\n",ftarg.timereq);
}
else
	ftarg.timereq = 2;  /* in mS */
ftarg.sigreq = SIGUSR1; 

signal(SIGALRM, stop);
signal(SIGUSR1, up_count);
ioctl(fd, FTIOCSET, &ftarg);

alarm(10);

while ((pause() == -1) && done && (errno == EINTR));
signal(SIGUSR1, SIG_IGN);

fprintf(stderr,"ft: count=%d.\n", count);
}

up_count()
	{
	signal(SIGUSR1, up_count);
	count++;
	ioctl(fd, FTIOCSET, &ftarg);
}

stop()
	{
	done = 0;
	ioctl(fd, FTIOCCANCEL, 0);
}
--------  end of fttest.c ------------
-- 
David F. Carlson, Micropen, Inc.
...!{ames|harvard|rutgers|topaz|...}!rochester!ur-valhalla!micropen!dave

"The faster I go, the behinder I get." --Lewis Carroll