This is absched.c in view mode; [Download] [Up]
/* * $Author: cck $ $Date: 88/05/13 09:29:41 $ * $Header: absched.c,v 1.9 88/05/13 09:29:41 cck Rel $ * $Revision: 1.9 $ */ /* * absched.c - Simple Protocol Scheduling Facility * * Provides a very simple non-preemptive "scheduling" facility. * It allows one to: * (a) set timeouts with routines called back on timeouts * (b) call listeners if input is available * Base interface is through "abSleep" (bad name). abSleep * will run for a the time passed returning only if the time has * passed or if one of the above events has occurred * and you asked it to return on any event occurring. * * AppleTalk package for UNIX (4.2 BSD). * * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University * in the City of New York. * * * Edit History: * * June 14, 1986 Schilit Created. * June 18, 1986 CCKim Chuck's handler runs protocol * */ /* needs to do printfs on other than lap_debug */ /* good place to stick the copyright so it shows up in object files */ char Columbia_Copyright[] = "Copyright (c) 1986,1987,1988 by The Trustees of Columbia University in the City of New York"; #include <stdio.h> #include <sys/time.h> #include <sys/types.h> #include <sys/param.h> #include <netinet/in.h> #include <netat/appletalk.h> #include <netat/compat.h> /* to cover difference between bsd systems */ /* * NOTE: it is not important to get the TZ information. It is simply * included because some systems complain if there isn't a valid pointer * there. * */ /* * define localtime_gtod if your system's get time of day call * calls _gettimeofday to get the system time and uses disk based * information to get the time zone information EVERY TIME. * * (e.g. gettimeofday is based on pd_localtime, but does it incorrectly) * * For AUX version 1.0. * */ #ifdef LOCALTIME_GTOD # define gettimeofday(tv, tz) _gettimeofday((tv)) #endif /* * Configuration defines * NOFFS - no ffs function defined, use our own * * Note: need not be ffs as per vax. It may be any routine * that returns bits assuming 1 == 1, 2==2, 4==3, 8==4, etc. * and 0 is none */ /* * structure defs * */ /* timer support */ typedef struct TimerEntry { struct TimerEntry *t_next; /* pointer to next */ struct timeval t_time; /* timeout internal form */ ProcPtr t_fun; /* function to call */ caddr_t t_arg; /* argument for function */ } TimerEntry; #define NILTIMER (TimerEntry *) 0 /* a nil pointer */ /* select on fds */ typedef struct { boolean fl_valid; /* is this item valid? (true/false) */ int (*fl_listener)(); /* listener */ caddr_t fl_addrarg; /* any arguments */ int fl_intarg; } FDLISTENER; /* local defines */ #define MICRO 1000000 /* micro is one million'th, 1mil per sec*/ #define UNITT 250000 /* 1/4 second is tick unit: 250k usec */ /* variables */ private gfd_set fdbitmask; /* file descriptors open... */ private gfd_set *fdbits; private int fdnotinited = 1; /* 0 is not inited */ private int fdmaxdesc = -1; /* mark not inited */ /* list of fd and listeners */ /* NOFILE is the number of file descriptors. If it isn't set */ /* then we use FD_SETSIZE which must be set one way or the other */ /* prefer NOFILE since it can be much smaller */ #ifndef NOFILE # define NOFILE FD_SETSIZE #endif FDLISTENER fdlist[NOFILE]; /* * Tappan: * optimization - holds the current gettimeofday() so lower level routines * can avoid system call overhead - updated when abSleep() is entered and * after every sleep * * cck: optimization for Timeout and other time related support * */ private struct timeval tv_now; /* should be able to pass null for tz if no tz information wanted. */ /* careful: some systems may want structure and may blow up */ #define NO_TZ ((struct timezone *)0) /* routine declarations */ private TimerEntry *tm_alloc(); private void tm_free(); private int i_doTimeout(); #ifdef NOFFS private int ffs(); #endif private int FD_GETBIT(); private void abstoreltime(); private void reltoabstime(); private void apptoabstime(); export int init_fdlistening(); export int fdlistener(); export int fdunlisten(); export int fdlistenread(); export int fdlistensuspend(); export int fdlistenresume(); export void Timeout(); export void AppleTimeout(); export void relTimeout(); export void absTimeout(); export int remTimeout(); export int doTimeout(); export boolean minTimeout(); export int abSleep(); int abSelect(); /* haven't decided whether this should */ /* be exported or not */ /* * FD LISTENER SUPPORT * */ int init_fdlistening() { int i; if (!fdnotinited) /* already inited */ return(NOFILE); fdbits = &fdbitmask; FD_ZERO(fdbits); /* make sure these are cleared */ fdmaxdesc = -1; /* mark init, but not in use */ fdnotinited = FALSE; /* initted */ for (i = 0; i < NOFILE; i++) fdlist[i].fl_valid = FALSE; /* not in use */ return(NOFILE); } /* * install a listener "listener" on file descriptor "fd" * * the listener is called with the file descriptor when INPUT is ready * on the fd. * * returns: 0 okay, -1 problem (fd too large, too small, etc) */ export int fdlistener(fd, listener, addrarg, intarg) int fd; int (*listener)(); caddr_t addrarg; int intarg; { if (fd < 0 || fd >= NOFILE || fdnotinited) return(-1); FD_SET(fd, fdbits); fdmaxdesc = max(fd, fdmaxdesc); fdlist[fd].fl_listener = listener; fdlist[fd].fl_valid = TRUE; fdlist[fd].fl_addrarg = addrarg; fdlist[fd].fl_intarg = intarg; return(0); } /* * close off a listener on file descriptor "fd" * */ export int fdunlisten(fd) int fd; { FDLISTENER *fdi; if (fd < 0 || fd >= NOFILE || fdnotinited) return(-1); fdi = &fdlist[fd]; /* mark end */ if (fdi->fl_valid) { /* this was a valid item */ FD_CLR(fd, fdbits); fdi->fl_valid = FALSE; /* mark off list */ if (fd >= fdmaxdesc) { /* was it at the tail of the list */ /* use >= instead of = above is paranoia */ /* select out boundary case and no fdmaxdesc case (paranoia) */ if (fd == 0 || fdmaxdesc < 0) { fdmaxdesc = -1; return(0); } /* step back one and start from there */ for ( fdmaxdesc--, fdi = &fdlist[fdmaxdesc]; fdmaxdesc >= 0; fdmaxdesc--, fdi--) { if (fdi->fl_valid) break; } /* if none valid, then we come out with fdmaxdesc = -1 */ } } return(0); } /* * run the listener on the file descriptor fd * * returns value returned by listener if there was one * -1 if no listener or fd not valid * */ export int fdlistenread(fd) { FDLISTENER *fdi; if (dbug.db_skd) fprintf(stderr,"fdlistenread call\n"); /* paranoia */ if (fd < 0 || fd >= NOFILE || fdnotinited) return(-1); if (fdlist[fd].fl_valid) { fdi = &fdlist[fd]; if (fdi->fl_listener != NULL) return((*(fdi->fl_listener))(fd, fdi->fl_addrarg, fdi->fl_intarg)); } return(-1); } /* primarily useful outside libraries where we are not event based, but */ /* are more poll based. for instance, you might want the listener for */ /* a terminal to just return that data is available */ /* listensuspend and resume both return 0 on okay, -1 on error */ /* error; bad fd or invalid fd */ /* listensuspend temporarily supsends listening (selecting) on a fd */ export int fdlistensuspend(fd) { if (fd < 0 || fd >= NOFILE || fdnotinited) return(-1); if (fdlist[fd].fl_valid) { FD_CLR(fd, fdbits); /* don't bother updating max */ return(0); } return(-1); } /* listensupend resumes listening (selecting) on a fd */ export int fdlistenresume(fd) { if (fd < 0 || fd >= NOFILE || fdnotinited) return(-1); if (fdlist[fd].fl_valid) { FD_SET(fd, fdbits); /* don't bother updating max */ return(0); } return(-1); } /* * TIMER SUPPORT * * SUPPORTS a timer facility. The clock resolution is in 1/4 second * units (TICKS). * * Timeouts are given as a <routine, routine argument (ra), timeout> * triple. * The combination <routine, ra> should be unique or else remTimeout * must be called for the "unknown" number of times the combination * occurs. */ private TimerEntry TimoutQ = {NILTIMER,{0,0},NILPROC,0}; private TimerEntry FreeTimoutQ = {NILTIMER, {0,0}, NILPROC, 0}; /* these are used primarily for debugging */ int mcalloced = 0; int mcfreesize = 0; /* allocate and dealloc functions for timer entries */ /* keeps chain of allocated pointers */ /* allocate a timer entry with specified fun, arg */ private TimerEntry * tm_alloc(fun,arg) ProcPtr fun; caddr_t arg; { TimerEntry *tu; if (FreeTimoutQ.t_next == NILTIMER) { tu = (TimerEntry *) malloc(sizeof(TimerEntry)); /* new entry */ mcalloced++; } else { mcfreesize--; tu = FreeTimoutQ.t_next; /* get first free */ FreeTimoutQ.t_next = tu->t_next; /* and unlink it */ } tu->t_fun = fun; /* set function to call */ tu->t_arg = arg; /* and arg to pass... */ tu->t_next = NILTIMER; /* make sure it doesn't go anywhere */ return(tu); } /* * link timer entry into the free list * */ private void tm_free(tu) TimerEntry *tu; { mcfreesize++; tu->t_next = FreeTimoutQ.t_next; FreeTimoutQ.t_next = tu; } /* * void * Timeout(ProcPtr fun,caddr_t arg,int t) * * Call "fun" with "arg" after elapsed time "t" has expired. "t" * is in internal tick units of 1/4 seconds. * * This unit conforms to LAP and PAP timeout units. * * FastTimeout is the same as Timeout except it is designed to be * called from called "Timeout" routines. (Basic difference, it * doesn't update the time of day). * * GENERAL WARNING: YOU MUST NOT DELAY FOR A LONG TIME IN THE TIMEOUT * ROUTINES! DOING SO MAY CAUSE PROTOCOL ERRORS * */ export void Timeout(fun,arg,tim) ProcPtr fun; caddr_t arg; int tim; { AppleTimeout(fun, arg, tim, TRUE); } export void AppleTimeout(fun, arg, tim, doupdate) ProcPtr fun; caddr_t arg; int tim; boolean doupdate; { struct timeval tv; apptoabstime(&tv, tim, doupdate); absTimeout(fun, arg, &tv); } export void relTimeout(fun,arg,tv,doupdate) ProcPtr fun; caddr_t arg; struct timeval *tv; boolean doupdate; { struct timeval tv_abs; reltoabstime(tv, &tv_abs, doupdate); absTimeout(fun, arg, &tv_abs); /* send down timeout */ } export void absTimeout(fun,arg,tv) ProcPtr fun; caddr_t arg; struct timeval *tv; { TimerEntry *tu,*tn,*t; tu = tm_alloc(fun, arg); tu->t_time = *tv; /* copy absolute time time */ #ifdef DEBUG if (dbug.db_skd) fprintf(stderr,"TIMEOUT: %x at %d/%d %d\n", fun, tim, tu->t_time.tv_sec, tu->t_time.tv_usec); #endif for (t=(&TimoutQ), tn=TimoutQ.t_next; tn != NILTIMER && (cmptime(&(tu->t_time),&(tn->t_time)) > 0); t = tn, tn=t->t_next) /* NULL */; tu->t_next = t->t_next; /* make sure new entry knows where it is */ t->t_next = tu; /* and link it in the list */ } /* * int * remTimeout(ProcPtr fun, caddr_t arg); * * Given the function and function arg of a pending timeout * remove that timeout from the q. * * Question: should we remove all instances? Should timeout enforce * a single (fun, arg) pair? * * Resolved above by allowing multiple instances. remTimeout will * remove the first. remTimeout is now modified to return TRUE if it * removed something, so just call until false if you need to worry about * it. * */ export int remTimeout(fun,arg) ProcPtr fun; caddr_t arg; { TimerEntry *t,*tn; /* t acts as prev, tn is curr */ for (t=(&TimoutQ), tn=TimoutQ.t_next; (tn != NILTIMER) && /* if current is valid and */ /* either fun or arg mismatches */ ( (tn->t_fun != fun) || (tn->t_arg != arg)); t = tn, tn = t->t_next) /* NULL */; if (tn == NILTIMER) /* find anything? */ return(FALSE); /* no, missing entry! */ t->t_next = tn->t_next; /* unlink ourselves */ tm_free(tn); /* and release timer block */ return(TRUE); } /* * void * doTimeout() * * doTimeout expires entries on the timeout queue. The timeout * function is called with the function argument, the timeout * entry is unlinked from the q and memory is returned. * * doTimeout can be called at any time, if the q is empty, or * no timers have expired then no action is taken. * * doTimeout updates the "current" time because it is designed * be called by external parties. doUnforcedTimeout is the guts * and is only meant be called from within this module. * */ export int doTimeout() { gettimeofday(&tv_now, NO_TZ); /* update, 'cause could be at any point */ return(i_doTimeout()); } /* update time before calling doTimeOut */ private int i_doTimeout() { TimerEntry *t,*tn; int ntimeout = 0; t = &TimoutQ, tn = TimoutQ.t_next; while (tn != NILTIMER && (cmptime(&tn->t_time, &tv_now) <= 0)) { t->t_next = tn->t_next; /* unlink tn */ if (tn->t_fun != NULL) (*tn->t_fun)(tn->t_arg); /* call timeout function */ if (dbug.db_skd) fprintf(stderr, " timeout occurred "); ntimeout++; tm_free(tn); tn = t->t_next; /* t should stay constant since head of list is min time! */ } return(ntimeout); } /* * boolean * minTimeout(struct timeval *mt) * * minTimeout returns the minimum timeout of all entries on the * timeout q. The timer records are ordered earliest first so * this routine only needs to check on the first one. * */ export boolean minTimeout(mt) struct timeval *mt; { TimerEntry *tt; if ((tt = TimoutQ.t_next) == NILTIMER) /* anything on queue? */ return(FALSE); /* nothing... */ *mt = tt->t_time; /* else pass along min time */ return(TRUE); } /* * * external interface to "protocol scheduler" * * */ /* * abSleep(int t, boolean nowait) * * this is the main "protocol event scheduler loop". It waits for * incoming packets or timeout events. * keeps running until timeout "t" if nowait is not set, returns after * the first timeout or incoming packet if nowait is set. * (e.g. returns after first "protocol event scheduled" if nowait is set) * */ export int abSleep(appt,nowait) int appt,nowait; { struct timeval sleept; int eventhappened = 0; /* no events happened */ apptoabstime(&sleept,appt,TRUE); /* find absolute time for sleep */ do { if (abSelect(&sleept, nowait) > 0) { eventhappened++; if (nowait) break; } /* abSelect will have updated tv_now */ } while (cmptime(&sleept,&tv_now) > 0); /* past wait time? */ return(eventhappened); } /* * abSelect(struct timeval *awt, nowait) * * Call with absolute time. Might return before that... * Returns > 0 if event occured. * */ int abSelect(awt,nowait) struct timeval *awt; int nowait; { struct timeval rwt,mt; register int rdy; int fd; gfd_set rdybits; int nevent; /* * Tappan: * optimization: if a timeout is less than the argument sleep time * then we don't have to return after the select() times out. If * a timeout is not less then we don't have to scan the timeout lists. * Note: we must return if a timeout is less than the arg. sleep time * because this may have been an event we were waiting for!!!!! */ /* cck: the above is correct, except we need to know if they are */ /* waiting to drop out after the first event. */ int timeoutless; /* one of the following states: */ #define TL_FALSE FALSE /* timeout is not less than sleep */ #define TL_TRUE 1 /* timeout is less than sleep time */ #define TL_USED 2 /* timeout was run */ if (dbug.db_skd) fprintf(stderr,"abSelect enter... "); nevent = 0; do { timeoutless = TL_FALSE; /* if min timeout on functions and smaller than requested */ if (minTimeout(&mt) && cmptime(&mt,awt) < 0) { abstoreltime(&mt,&rwt, FALSE); /* yes use it */ timeoutless = TL_TRUE; /* and mark it */ } else abstoreltime(awt,&rwt, FALSE); /* use requested */ #ifdef DEBUG if (dbug.db_skd) fprintf(stderr, "%d %d ",rwt.tv_sec, rwt.tv_usec); #endif /* rwt.tv_sec less than 0 means (a) that we are past our welcome here */ /* or that (b) a timeout event is in our past and we should take care */ /* of it before any read calls */ /* in case (a) timeoutless is zero and we drop out almost immediately */ /* in case (b) timeoutless is non-zero and we call doTimeout */ /* to special case (a) here would need about 5 extra lines of code */ if (rwt.tv_sec < 0) { if (dbug.db_skd) fprintf(stderr," negative "); rdy = 0; } else { /* should really sleep if fdmaxdesc == -1 or else we might */ /* go into a tight loop here! Will comprimise by getting */ /* 1 second accuracy with sleep (shouldn't be happening */ /* anyway. If sleep turns out not be widespread, then */ /* .... don't know what to do */ if (fdmaxdesc == -1) { rdy = rwt.tv_sec + (rwt.tv_usec/(2*UNITT))/2; sleep(rdy == 0 ? 1 : rdy); rdy = 0; } else { rdybits = *fdbits; /* update before call to select */ rdy = select(fdmaxdesc+1,&rdybits,0,0,&rwt); /* perform wait... */ } } if (dbug.db_skd) fprintf(stderr,"%d ", rdy); if (rdy > 0) { /* rdy should be # of set file descriptors in the masks */ /* since we only pass it the "read" bits, this loop */ /* should take care of all */ while (rdy--) { if ((fd = FD_GETBIT(&rdybits, fdmaxdesc)) < 0) break; /* paranoia */ FD_CLR(fd, &rdybits); fdlistenread(fd); nevent++; } } else { /* * Tappan: * Optimization: If a packet was waiting don't check the timeout lists - * odds are that the timeout hasn't expired yet, and we'll catch it the * next time through anyway * cck: this should be okay though and things have been * running fine with this, but there are a lot of "nowait" * calls to abSleep... If we modify, take out the "else" on rdy>0 * and check for: timeoutless && (rdy <= 0 || nowait) */ if (timeoutless) { if (dbug.db_skd) fprintf(stderr, "doTimeout..."); /* An internal timeout occured before the user timeout, */ /* and there were no packets available */ gettimeofday(&tv_now, NO_TZ); /* do this to reduce subroutine */ i_doTimeout(); /* call overhead */ nevent++; timeoutless = TL_USED; /* mark we ran the timeout */ } } if (nevent && nowait) { if (dbug.db_skd) fprintf(stderr, "exit to abSleep\n"); return(nevent); } /* cck: we don't want to update the time of day if */ /* the minimum time out was less than the sleep time and doTimeout */ /* was run because the time of day was updated by running doTimeout */ /* timeout routines are supposed to be fairly quick and often update */ /* the time of day themselves */ if (timeoutless != TL_USED) gettimeofday(&tv_now, NO_TZ); } while (timeoutless); if (dbug.db_skd) fprintf(stderr,"exit to abSleep\n"); return(nevent); /* return "event" count */ } #ifdef NOFFS /* Find the First Set bit and return its number. Numbering starts */ /* at one */ private int ffs(pat) register int pat; { register int j; register int i; for (i = 0; i < 8*sizeof(int); i+=8) { /* each byte */ if (pat & 0xff) { /* if byte has bits */ /* do a linear scan */ for (j = 0; j < 8; j++) { if (pat & 0x1) return(i+j+1); pat >>= 1; } } pat >>= 8; /* go to the next byte */ } return(0); /* none */ } #endif /* * Find the first active file descriptor * * really doesn't belong here * */ private int FD_GETBIT(p, maxfd) gfd_set *p; int maxfd; { register int i; register int w; register gfd_mask *fm; int top; top = howmany(maxfd, NFDBITS); /* find length of array */ fm = p->fds_bits; /* point to start of array */ for (w=0,i=0; i < top; i++,fm++) { if ((w=ffs(*fm)) > 0) break; } if (w < 1) return(-1); w += (i*NFDBITS)-1; /* find bit no */ return((w > FD_SETSIZE) ? -1 : w); } /* * abstoreltime(struct timeval *t) * * convert absolute time in t to relative time by subtracting the * current time as returned by gettimeofday. * * novalidtod means that we can't be sure the tod clock is set properly * so update it * */ private void abstoreltime(at,rt, novalidtod) register struct timeval *at; register struct timeval *rt; boolean novalidtod; { if (novalidtod) gettimeofday(&tv_now, NO_TZ); /* update current time */ if (rt != at) *rt = *at; /* copy relative times */ rt->tv_sec -= tv_now.tv_sec; /* add in user elapsed time */ if ((rt->tv_usec -= tv_now.tv_usec) < 0) { /* both seconds and usecs */ --rt->tv_sec; /* yes, so one less second */ rt->tv_usec += MICRO; /* and fix up usecs */ } } /* * reltoabstime(struct timeval *rt, *at) * */ private void reltoabstime(rt, at, novalidtod) register struct timeval *at; register struct timeval *rt; boolean novalidtod; { if (novalidtod) gettimeofday(&tv_now, NO_TZ); /* update current time */ if (at != rt) *at = *rt; /* copy relative to absolute */ at->tv_sec += tv_now.tv_sec; /* add in user elapsed time */ /* do micro seconds */ if ((at->tv_usec += tv_now.tv_usec) >= MICRO) { ++at->tv_sec; /* yes, so one more second */ at->tv_usec -= MICRO; /* and fix up usecs */ } } /* * void * apptoabstime(struct timval *tv,int t) * * Construct actual time of timeout given unit tick 1/4 of a second. * * novalidtod means that we can't be sure the tod clock is set properly * so update it */ private void apptoabstime(tv,t,novalidtod) register struct timeval *tv; register int t; int novalidtod; { if (novalidtod) gettimeofday(&tv_now, NO_TZ); /* update current time */ *tv = tv_now; tv->tv_sec += ticktosec(t); /* seconds till timeout */ tv->tv_usec += (t%4)*UNITT; /* micro seconds... */ if (tv->tv_usec >= MICRO) { /* second or more? */ tv->tv_sec++; tv->tv_usec -= MICRO; /* adjust */ } } /* * user access to scheduler clock * */ getschedulerclock(tv) struct timeval **tv; { *tv = &tv_now; /* return clock */ } updateschedulerclock() { gettimeofday(&tv_now, NO_TZ); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.