#!/bin/sh cat >Makefile <<'------ EOF ------' # %M% version %I% %G% # Copyright 1989, National Optical Astronomy Observatories, Tucson, Arizona # # $(VW) comes from environment usually /vw # $(TCP) comes from environment usually /usr/dat/tcp # include directories INCLUDES= -I$(VW)/h -I. -I/usr/include # Programs PGMS= # Object modules OBJS= jobLib.o # Global header files, HDRS=jobLib.h # Documentation files generated DOCS=jobLib.2 # yacc source files YSRCS= # C-source files (not YACC output) CSRCS=jobLib.c # Assembly source files ASRCS= # SCCS'd source files # SPECIAL sources .yacc.sed lint_wrs.c Make.skel SRCS= Makefile $(YSRCS) $(CSRCS) $(ASRCS) $(HDRS) # OTHERS are C-sources, derived from YSRCS (for lint and depend) OTHERS= ########################################################## # implicit make rules. These were necessary so that the documentation # would be derived from the .y file when a .y and .c both were present. # also necessary because of our 'depend' system. .SUFFIXES : .SUFFIXES : .o .y .c .s .1 .2 .3 .4 # convert assembly to object, using the C pre-procssor .s.o : @/bin/rm -f $@ $(ASM_P1) $< >tmp.c $(CC) $(CASFLAGS) tmp.c >tmp.i $(ASM_P2) tmp.i >tmp.s $(AS) -o $@ tmp.s @/bin/rm -f tmp.c tmp.i tmp.s .y.o : @/bin/rm -f $@ yacc $*.y @sed -f $(YACCSED) y.tab.c > y.temp.c $(CC) $(CFLAGS) -c y.temp.c @/bin/mv y.temp.o $@ @/bin/rm -f y.tab.c y.temp.c .y.c : @/bin/rm -f $@ yacc $*.y @sed -f $(YACCSED) y.tab.c > $*.c @/bin/rm -f y.tab.c # Man section 4 target, intended for object level documentation. # No subroutine documentation. .y.4 : mangen -n 4 $*.y @mv mg.out $*.4 # Man section 3 target, subroutine documentation only. # Throw the module level documentation away. .y.3 : mangen -l -n 2 $*.y @/bin/rm -f mg.out -msplit -s .3 lint.out # removes EVERYTHING clean: sccs check sccs clean -/bin/rm -f *.[o1234] temp core a.out cscope.* *.bak # of course it also depends upon the vxWorks header files jobLib.o: jobLib.h jobLib.c ------ EOF ------ ls -l Makefile cat >jobLib.c <<'------ EOF ------' /* jobLib.c - Library module for job objects */ /* LINTLIBRARY jobLib.c version 1.2 8/22/90 National Optical Astronomy Observatories, Tucson, Arizona modification history -------------------- 01b 07dec89 dat - added semaphore and clock support 01a 24may89 dat - fixed up for export to vxWorks archives */ /* The jobLib system is a group of non-dedicated tasks that continuously scan a prioritized list looking for jobs to be executed. A job is a function pointer with up to 4 arguments. Ideally a job should be a short function that does not do an excessive amount of task idling. Jobs can be scheduled by the system clock or a semaphore. A job is a single entity and has only 4 states: inactive, pending, queued, and in-progress. The jobQue routine places a job in the queued condition. If jobQue is called many times while the job is already queued the job will only be executed once. Note that if a job is already in-progress when jobQue is executed, the job will immediately be placed back in the queued condition when execution is completed. (It goes back to the end of its priority waiting list). A pending job is one that is awaiting a system clock time, or waiting for a semaphore to be given. When the event occurs the job is placed into the job que for immediate execution. Job priority levels are relative to a base level priority set during the jobSysInit routine. Job priority level 0 is the highest job priority. It is expected that the number of job priority levels will be small (less than 8). Each priority level has a separate list of pending jobs. The separate lists approach eliminates sorting of jobs by priority. Each separate list acts as a FIFO. The jobSysInit call should only be made once. It establishes the number of processors tasks and the priority lists used by this system. The jobSysInit argument 'slice' will, if non-zero, cause all job processors to run at a single priority level. The jobs will be fetched out of the job queues in priority order, but the processor tasks all run at a single priority level, with round-robin scheduling in effect. If this option is desired, specify slice to be the number of clock ticks per interval. You may want to try running your application both ways and comparing the performance. (Applicable only to the VRTX kernel). */ #include "vxWorks.h" #include "taskLib.h" #include "jobLib.h" #include "semLib.h" /* SEMAPHORES */ typedef long TICKS; /* clock tick units */ IMPORT TICKS tickGet(); /* If you have the VRTX kernel, then you have vrtxTslice() available to implement the time slicing option mentioned in the text. */ /*NOT_DEFINED #define VRTX_KERNEL /* If VRTX kernel is used */ /* The fppReset () function places the floating point co-processor in the idle state. Context switching is speeded up greatly when the fpp is in the idle state (the saved frame size is very small). (Copy of 68020 code is at end of this file) */ /*NOT_DEFINED #define FPP_RESET /* Define if fppReset() available */ #ifdef FPP_RESET IMPORT VOID fppReset(); #endif /* POOL_LIB is an alternate memory allocation/deallocation system optimized for small fixed size blocks. */ /* NOT_DEFINED #define POOL_LIB /* define, if poolLib is available */ /* You must use a corrected version of vxTas () for this library. If you don't have the patch for vxTas already, then use the code supplied. (See code at end of this file). */ IMPORT BOOL vxTas(); /* MUST BE CORRECTED VERSION */ /* vxWorks 4.0.2 is okay, earlier versions are not */ IMPORT char *malloc(); IMPORT free(); /* job Processor task options/limitations */ #define JOB_MAX_LVLS (16) /* max # of priority levels */ #define JOB_MAX_PROC (16) /* max # of processor tasks */ #define JOB_MIN_STACK (4096) /* min/max task stack sizes */ #define JOB_MAX_STACK (32768) #define JOB_MIN_PRI (50) /* min/max base priority level */ #define JOB_MAX_PRI (255) #define JOB_PROC_OPTS (VX_SUPERVISOR_MODE|VX_FP_TASK|VX_DEALLOC_STACK|VX_STDIO) /*Forward Declarations */ VOID jobDaemon (); int jobBasePri; /* vxWorks priority level for job priority 0 */ JOB_LIST *jobListSem; /* jobs waiting on semaphores */ JOB_LIST *jobListDelay; /* jobs waiting on clock */ /* HIDDEN variables */ LOCAL SEM_ID jobDaemonSem = NULL; /* Semaphore for sleeping daemons*/ LOCAL int jobNumLevels = 0; /* # of active levels */ LOCAL JOB_LIST **jobListLevels = NULL; /* pointer to array of jobList ptrs */ LOCAL int jobSlice=0; LOCAL VOID jobClockRoutine(); #ifdef POOL_LIB # include "poolLib.h" LOCAL POOL *jobPool = NULL; #endif /**************************************************************** * * jobSysInit - Create and initialize the jobLib system * * The job system must be initialized before any jobs can be queued. * * Most job system parameters are set as arguments to this routine. * There are limits to the number of priority levels and the number * of processor tasks that can be created (see the source code). * * The time slicing option determines if the processor (daemon) tasks * use taskPrioritySet() to change their priority levels before * beginning job execution. If a positive slice value is given then * time slicing is initiated and all daemon tasks actually run at a * single priority level. The job queues remain prioritized and jobs * are still fetched from the queues in priority order. * * ERRORS: * An error value is returned if any failures are detected during the * process, if any arguments exceed the maximum limits, or if * the system has already been initialized. */ STATUS jobSysInit (levels, procs, size, pri, slice) int levels; /* number of priority levels */ int procs; /* number of processor tasks */ int size; /* stack size for processors */ int pri; /* VxWorks priority for level '0' */ int slice; /* time slicing option, clock ticks */ { int i; jobNumLevels = 0; /* disallow job queing */ if (levels < 1 || levels > JOB_MAX_LVLS || procs < 1 || procs > JOB_MAX_PROC || pri < JOB_MIN_PRI || pri > (JOB_MAX_PRI - levels) || size < JOB_MIN_STACK || size > JOB_MAX_STACK ) /* bad arguments */ return ERROR; if (jobListLevels != NULL || jobDaemonSem != NULL ) /* already initialized */ return ERROR; if ( (jobDaemonSem = semCreate ()) == NULL) return ERROR; semClear (jobDaemonSem); jobListLevels = (JOB_LIST **) malloc( (unsigned)levels*sizeof(JOB_LIST *)); if (jobListLevels == NULL) return ERROR; for (i = 0; i< levels; i++) { jobListLevels[i] = jobListCreate (); if (jobListLevels[i] == NULL) return ERROR; } /* create the semaphore list and the delayed list */ jobListSem = jobListCreate (); jobListDelay = jobListCreate (); if (jobListSem == NULL || jobListDelay == NULL) return ERROR; for (i = 0; i < procs ; i++ ) { /* don't bother with task Names, they will be changed */ if (taskSpawn ("", pri, JOB_PROC_OPTS, size, jobDaemon, 0) == ERROR) return ERROR; } /* allow job queing */ jobNumLevels = levels; jobBasePri = pri; #ifdef VRTX_KERNEL jobSlice = slice; vrtxTslice (slice); #else jobSlice = 0; #endif sysClkConnect (jobClockRoutine, 0); return OK; } /********************************************************************** * * jobDaemon - The main loop for job processor tasks * * This is the main line code for each of the job processor tasks. * Each processor runs continuously scanning the job priority lists * for a job. When a job is located, the processor executes the job * and scans for another job. If there are no jobs at all then the * processor task sleeps on the job semaphore until a new job is added * to the lists. * * PROBLEMS: * We are doing taskPrioritySet to change priority levels often. * I don't know how much of a performance penalty this is costing. * It might be that this is very costly, it may require the * kernel to reshuffle all the tasks and all the lists and a lot more. * If you can, try the time slicing option and compare the performance. * * NOTES: * FppReset() is called between jobs to reduce the overhead of fpp context * save/restore for jobs that don't use floating point math. */ VOID jobDaemon () { int myTaskId = taskIdSelf (); TCBX *myTcbX; myTcbX = taskTcbX (myTaskId); if (myTcbX == NULL) /*HELP: FATAL ERROR*/ return; myTcbX->name = "jobDaemon"; FOREVER { int i; JOB_ID pJob = NULL; int oldLevel; semClear (jobDaemonSem); oldLevel = intLock (); for (i = 0; i < jobNumLevels ; i++) { /* get highest priority job available */ pJob = (JOB_ID) lstGet (&(jobListLevels[i]->jobs)); if (pJob != NULL) { pJob->flags &= ~JOB_QUEUED; break; } } intUnlock (oldLevel); if (pJob != NULL) { /* change to job level priority */ if (jobSlice == 0 && pJob->priority != 0) taskPrioritySet (myTaskId, (int)(pJob->priority + jobBasePri)); myTcbX->name = pJob->name; jobExecute (pJob); #ifdef FPP_RESET /* reset fp co-processor after doing job */ fppReset (); #endif myTcbX->name = "jobDaemon"; /* reset job daemon priority */ if (jobSlice == 0 && pJob->priority != 0) taskPrioritySet (myTaskId, jobBasePri); } else { /* all job lists were empty, just wait for a new one */ myTcbX->name = "idleJob"; semTake (jobDaemonSem); } } } /**************************************************************** * * jobExecute - execute a job * * Execute a job, removing it from any job queues and then disposing of the * job after completion of the user's routine. * * The re-entrancy control mechanism is very simple minded and should be * replaced. * * There is a subtle difference between 'jobQue' and 'jobexecute'. * If you use 'jobQue' while a job is already being executed, then it * will be re-queued after completion. 'jobExecute' will not cause a * job to be re-queued if jobExecute is called while the job is already * in execution. */ VOID jobExecute (pJob) JOB *pJob; { int oldLevel = intLock (); if (pJob->flags & JOB_QUEUED) { lstDelete (&(jobListLevels[pJob->priority]->jobs), &pJob->node); pJob->flags &= ~JOB_QUEUED; } else if (pJob->flags & JOB_SEMAPHORE) { lstDelete (&jobListSem->jobs, &pJob->node); pJob->flags &= ~JOB_SEMAPHORE; } else if (pJob->flags & JOB_DELAYED) { lstDelete (&jobListDelay->jobs, &pJob->node); pJob->flags &= ~JOB_DELAYED; } if (pJob->flags & JOB_BUSY) { /* Job is already being executed, let's exit */ intUnlock (oldLevel); return; } pJob->flags |= JOB_BUSY; intUnlock (oldLevel); pJob->flags &= ~JOB_REPEAT; (pJob->func) (pJob->arg1, pJob->arg2, pJob->arg3, pJob->arg4); /* job disposition */ if (pJob->flags == (JOB_DELETE|JOB_BUSY) ) { #ifdef POOL_LIB poolFreeItem (jobPool, (char *)pJob); #else free ((char *)pJob); #endif } else { pJob->flags &= ~JOB_BUSY; /* was it 'queued' while we were busy ? */ if (pJob->flags & JOB_REPEAT) { jobQue (pJob); } } } /**************************************************************** * * jobCreate - create a new job structure * * A job is a procedure that is run by a jobDaemon task. * It is analagous to a lightweight task. Inactive jobs don't * consume task resources. Jobs are processed by the * jobDaemon tasks, of which there will be several. Those * tasks continually scan the job queues and execute any jobs * that they find. * * A job is a singularity. It is not considered re-entrant (although * good programming practice is to always try for re-entrancy). * If a job needs to be re-scheduled while it is currently * executing, then the scheduling is deferred until processing is * complete, and then it is placed onto the tail of it's priority * list. * * RETURNS: * Returns NULL upon failure. * * VARARGS3 */ JOB_ID jobCreate (name, pri, func, arg1, arg2, arg3, arg4) char *name; /* job title/name */ short pri; /* job priority (highest = 0) */ FUNCPTR func; /* ptr to procedure */ ULONG arg1,arg2; /* procedure arguments*/ ULONG arg3,arg4; { JOB_ID jobId; if (func == NULL || pri >= jobNumLevels || pri < 0 ) return NULL; #ifdef POOL_LIB if (jobPool == NULL) { jobPool = poolCreate ("jobs",sizeof(JOB),128,128); if (jobPool == NULL) { /*HELP: FATAL ERROR */ } } jobId = (JOB_ID) poolGetItem (jobPool); #else jobId = (JOB_ID) malloc (sizeof (JOB)); if (jobId == NULL) { /*HELP: FATAL ERROR */ } #endif if (jobId != NULL) { if (name == NULL) name = "NoJobName"; jobId->name = name; jobId->flags = 0; jobId->priority = pri; jobId->func = func; jobId->arg1 = arg1; jobId->arg2 = arg2; jobId->arg3 = arg3; jobId->arg4 = arg4; jobId->node.next = NULL; } return jobId; } /**************************************************************** * * jobFree - Cancel and release a job structure * * This routine returns a no longer needed job structure back * to the memory manager system. * * RETURNS: * Returns ERROR upon invalid input, OK for all other conditions. */ STATUS jobFree (pJob) JOB_ID pJob; /* job no longer needed */ { if (pJob == NULL) return ERROR; pJob->flags |= JOB_DELETE; if (pJob->flags == JOB_DELETE) #ifdef POOL_LIB poolFreeItem (jobPool, (char *)pJob); #else free ((char *)pJob); #endif return OK; } /**************************************************************** * * jobQue - Queue a job on a jobList * * This routine activates a job by placing it on one of the prioritized * lists of pending jobs. If the job is already in a pending condition * then nothing at all is done. If the job is currently in execution * by one of the jobDaemon processor tasks, then a flag is set that * will cause the jobDaemon to queue the job immediately upon * completion. * * RETURNS: * Returns ERROR if the job system is not initialized., or * if the input job pointer is invalid. * * INTERNAL: * JobQue() has to be compatible with interrupt level processing, * that is why we must use intLock() and intUnlock() when modifying * a jobList. */ STATUS jobQue (pJob) JOB_ID pJob; /* job pointer */ { int pri; JOB_LIST *pJobList; int oldLevel; if (pJob == NULL) return ERROR; pri = pJob->priority; if (pri >= jobNumLevels || pri < 0 ) return ERROR; /* system not initialized, invalid job */ pJobList = jobListLevels[pri]; /* a deleted job CAN BE QUEUED */ oldLevel = intLock (); if (pJob->flags & JOB_SEMAPHORE) { /* remove from semaphore list */ lstDelete (&jobListSem->jobs, &pJob->node); pJob->flags &= ~JOB_SEMAPHORE; } else if (pJob->flags & JOB_DELAYED) { /* remove from delayed list */ lstDelete (&jobListDelay->jobs, &pJob->node); pJob->flags &= ~JOB_DELAYED; } /* turn on the repeat flag, in case job is already busy */ pJob->flags |= JOB_REPEAT; /* do nothing if job is busy or already in priority queue */ if ((pJob->flags & (JOB_QUEUED|JOB_BUSY)) == NULL) { /* put on priority queued list */ lstAdd (&pJobList->jobs, &pJob->node); pJob->flags |= JOB_QUEUED; (VOID) semGive (jobDaemonSem); } intUnlock (oldLevel); return OK; } /**************************************************************** * * jobDelay - schedule a job after a delay * * The specified job is queued for execution after the specified * clock delay has elapsed. * * RETURNS: * Returns ERROR if the job system is not initialized., or * if the input job pointer is invalid. */ STATUS jobDelay (pJob, ticks) JOB_ID pJob; /* job pointer */ TICKS ticks; /* delay interval */ { return jobDelayTill (pJob, tickGet() + ticks); } /**************************************************************** * * jobDelayTill - queue a job at a specified time * * The specified job is queued for execution at a specified clock * time. * * RETURNS: * Returns ERROR if the job system is not initialized., or * if the input job pointer is invalid. */ STATUS jobDelayTill (pJob, time) JOB_ID pJob; /* job pointer */ TICKS time; /* clock tick for queueing */ { int pri, oldLevel; if (pJob == NULL) return ERROR; pri = pJob->priority; if (pri >= jobNumLevels || pri < 0 ) return ERROR; /* system not initialized, invalid job */ /* already time for job */ if ((long)(tickGet() - time) >= 0) return jobQue (pJob); oldLevel = intLock (); /* deleted or queued jobs can't be delayed */ /* a BUSY job can be delayed (re-scheduled) */ if (pJob->flags & (JOB_DELETE|JOB_QUEUED)) { intUnlock (oldLevel); return OK; } if (pJob->flags & JOB_SEMAPHORE) { /* switch from semaphore to delay list */ lstDelete (&jobListSem->jobs, &pJob->node); pJob->flags &= ~JOB_SEMAPHORE; } /* remove from list if already on it */ if (pJob->flags & JOB_DELAYED) lstDelete (&jobListDelay->jobs, &pJob->node); /* put job on the delayed list (ordered list) */ pJob->flags |= JOB_DELAYED; pJob->pendVal = time; /* maintain an ordered list */ { JOB_ID pLast; pLast = jobListLast (jobListDelay); while (pLast && (pLast->pendVal > (ULONG)time)) pLast = jobListPrev (pLast); lstInsert (&jobListDelay->jobs, &pLast->node, &pJob->node); } intUnlock (oldLevel); return OK; } /**************************************************************** * * jobSemGet - que job upon semaphore * * The specified job is queued for execution when the specified * semaphore is available. * * This is not a true semaphore operation since the semaphore is * monitored only during the system clock tick handling routine. * There is no task pending on the semaphore, just constant * monitoring of the semaphore contents. * * If the semaphore is in heavy use by different tasks, and not * just job's, then it is best not to use this routine. Create * a job that does a direct 'semTake' on the semaphore instead. * In that, way the job processor task is directly pending on * the semaphore. Be very careful when using this routine. * * Note also that while the scheduler will 'take' the semaphore * when it is available, there is no automatic 'give' operation * when the job is finished. If this is a resource semaphore then * the job routine should terminate with a 'semGive' function. * * RETURNS: * Returns ERROR if the job system is not initialized., or * if the input job pointer is invalid. */ STATUS jobSemGet (pJob, pSem) JOB_ID pJob; /* job pointer */ SEM_ID pSem; /* semaphore pointer */ { int pri, oldLevel; if (pJob == NULL) return ERROR; pri = pJob->priority; if (pri >= jobNumLevels || pri < 0 ) return ERROR; /* system not initialized, invalid job */ /* semaphore is already available */ if (semClear (pSem) == OK) return jobQue (pJob); /* deleted, queued, and delayed jobs can't wait for semaphores */ /* a BUSY job can be re-scheduled for a semaphore */ if (pJob->flags & (JOB_DELETE|JOB_QUEUED|JOB_DELAYED)) return OK; oldLevel = intLock (); if (pJob->flags & JOB_SEMAPHORE) { /* just change the semaphore pointer */ pJob->pendVal = (ULONG)pSem; } else { /* put job at end of semaphore list */ pJob->pendVal = (ULONG)pSem; lstAdd (&jobListSem->jobs, &pJob->node); pJob->flags |= JOB_SEMAPHORE; } intUnlock (oldLevel); return OK; } /****************************************************************** * * jobClockRoutine - a replacement clock interrupt routine * * This routine is installed into the system as a replacement for * usrClock(). It performs the normal usrClock() functions first then * it scans the delayed job list and the semaphore job list for * jobs to be queued. */ LOCAL VOID jobClockRoutine () { TICKS curTime; JOB_ID pJob; IMPORT VOID usrClock(); /* do the normal system clock activities first */ usrClock (); /* do the delayed jobs first */ curTime = tickGet(); pJob = jobListFirst (jobListDelay); while (pJob && pJob->pendVal <= curTime) { jobQue (pJob); pJob = jobListFirst (jobListDelay); } /* now do the semaphore jobs, */ pJob = jobListFirst (jobListSem); while (pJob) { SEM_ID pSem; JOB_ID pNext; pSem = (SEM_ID) pJob->pendVal; pNext = jobListNext (pJob); if (pSem != NULL && semClear (pSem) == OK) jobQue (pJob); pJob = pNext; } } /**************************************************************** * * jobListCreate - create a jobList structure, returns pointer * * A new jobList structure is created and initialized. */ JOB_LIST *jobListCreate () { JOB_LIST *jobList = (JOB_LIST *) malloc (sizeof(JOB_LIST)); if (jobList != NULL) { if ((jobList->access = semCreate ()) == NULL) { free ( (char *)jobList); return NULL; } semClear (jobList->access); semGive (jobList->access); lstInit ((LIST *)&(jobList->jobs)); } return (jobList); } /**************************************************************** * * jobListFree - destroy a jobList structure * * The jobList should be empty before calling this routine. Any * jobs on the list will not be free'd and will not be recovered. */ VOID jobListFree (pList) JOB_LIST *pList; { if (pList) { if (pList->access) semDelete (pList->access); /* ignore anything that might be on the list */ free ( (char *)pList); } } /**************************************************************** * * jobListProcess - queue the jobs on a job list * * A jobList is scanned. All jobs on the list are queued for execution. */ VOID jobListProcess (pList) JOB_LIST *pList; { if (pList && jobListCount (pList) > 0) { JOB_ID pJob; jobListLock (pList); pJob = jobListFirst (pList); while (pJob != NULL) { jobExecute (pJob); pJob = jobListNext (pJob); } jobListUnlock (pList); } } /***************************************************************** * * jobListAddDelete - add/delete a job to a jobList * */ LOCAL STATUS jobListAddDelete (pList, addFlag, pJob) JOB_LIST *pList; /* list to be updated */ BOOL addFlag; /* TRUE --> add, FALSE --> delete*/ JOB_ID pJob; /* job to be added/deleted */ { STATUS result = ERROR; if (pList != NULL && pJob != NULL) { jobListLock (pList); if (addFlag) lstAdd (&pList->jobs, &pJob->node); else lstDelete (&pList->jobs, &pJob->node); jobListUnlock (pList); result = OK; } return result; } /********************************************************************** * * jobListAdd - add a job onto a jobList * * The specified job is added to the specified jobList. * * SEE ALSO: lstAdd(2) */ STATUS jobListAdd (pList, pJob) JOB_LIST *pList; JOB_ID pJob; { return (jobListAddDelete (pList, TRUE, pJob)); } /********************************************************************** * * jobListDelete - delete a job from a jobList * * The specified job is removed from the specified jobList. * * SEE ALSO: lstDelete(2) */ STATUS jobListDelete (pList, pJob) JOB_LIST *pList; JOB_ID pJob; { return (jobListAddDelete (pList, FALSE, pJob)); } #undef jobListInit /********************************************************************** * * jobListInit - initialize (clear) a jobList * * The specified jobList is initialized. * * SEE ALSO: lstInit(2) */ VOID jobListInit (pList) JOB_LIST *pList; { lstInit (&pList->jobs); } #undef jobListCount /********************************************************************** * * jobListCount - return number of jobs on a jobList * * The number of jobs on a specified jobList is returned as the value * of this function. * * SEE ALSO: lstCount(2) */ int jobListCount (pList) JOB_LIST *pList; { return (lstCount (&pList->jobs)); } #undef jobListLock /********************************************************************** * * jobListLock - lock a jobList (exclusive access) * * The specified job is locked to prevent access by other tasks/jobs. * * SEE ALSO: semTake(2) */ VOID jobListLock (pList) JOB_LIST *pList; { semTake (pList->access); } #undef jobListUnlock /********************************************************************** * * jobListUnlock - release exclusive control of a jobList * * Releases exclusive control of the specified jobList. * * SEE ALSO: semGive(2) */ VOID jobListUnlock (pList) JOB_LIST *pList; { semGive (pList->access); } #undef jobListFirst /********************************************************************** * * jobListFirst - return a pointer to first job on a jobList * * The job pointed to by the return value remains on the specified jobList. * * SEE ALSO: lstFirst(2) */ JOB_ID jobListFirst (pList) JOB_LIST *pList; { return (JOB_ID)(lstFirst (&pList->jobs)); } #undef jobListLast /********************************************************************** * * jobListLast - return pointer to last job on a jobList * * A pointer to the last job on a jobList is returned. The job remains * on the list. * * SEE ALSO: lstLast(2) */ JOB_ID jobListLast (pList) JOB_LIST *pList; { return (JOB_ID)(lstLast (&pList->jobs)); } #undef jobListNext /********************************************************************** * * jobListNext - return pointer to next job * * Given a pointer to a job (from a jobList). This routine returns a * pointer to the next job on the list. A null pointer is returned if * it is the last job on the list. * * SEE ALSO: lstNext(2) */ JOB_ID jobListNext (pJob) JOB_ID pJob; { return (JOB_ID)(lstNext (&pJob->node)); } #undef jobListPrev /********************************************************************** * * jobListPrev - return pointer to previous job on list * * Given a pointer to a job (from a jobList). This routine returns a * pointer to the previous job on the list. A null pointer is returned if * it is the first job on the list. * * SEE ALSO: lstPrev(2) */ JOB_ID jobListPrev (pJob) JOB_ID pJob; { return (JOB_ID)(lstPrevious (&pJob->node)); } #undef jobListGet /********************************************************************** * * jobListGet - get and remove the first job from a jobList * * A pointer to the first job of a jobList is returned. The job is * removed (unlinked) from the list. * * SEE ALSO: lstGet(2) */ JOB_ID jobListGet (pList) JOB_LIST *pList; { return (JOB_ID)(lstGet (&pList->jobs)); } #undef jobListAddAfter /********************************************************************** * * jobListAddAfter - add a job into a jobList at a specified position * * The new job is added to the jobList just following the old job. If * old job is NULL, the new job is inserted at the front of the jobList. * * SEE ALSO: lstInsert(2) */ VOID jobListAddAfter (pList, oldJob, newJob) JOB_LIST *pList; /* list to be updated */ JOB_ID oldJob; /* job already on list */ JOB_ID newJob; /* job to be added, after oldJob */ { lstInsert (&pList->jobs, &oldJob->node, &newJob->node); } #ifdef ASM_LANGUAGE /* SUN ASSEMBLY LANGUAGE */ /***************************************************************** * * fppReset - reset floating point processor * * The floating point processor is placed into the idle state. * This speeds up context switching greatly for tasks that only * use the co-processor occasionally. If the co-processor is in * the idle state, the floating pointer registers and internal * state information are not saved/restored when switching from/to * the current task. */ /* LOCAL VOID fppReset () { } */ .globl _fppReset _fppReset: link a6,#0 frestore L003 clrl d0 unlk a6 rts .data L003: .long 0 /* A 'null' state frame */ /******************************************************************* * * vxTas - Test and Set function (CORRECTED VERSION) * * vxWorks V4.0.1 and earlier had a flawed implementation of vxTas. * From the vxWorks shell type 'l vxTas'. If the branch instruction * is 'BNE' instead of 'BMI' then you have an INCORRECT version. * * You can try patching vxTas from your startup script by adding the * following line: * * .CS * *(vxTas+8) = 0x6b000004 * .CE */ /* LOCAL BOOL vxTas (pChar) char *pChar; /* byte to be tested/set */ .globl _vxTas _vxTas: clrl d0 moveal a7(4),a0 tas (a0) bmi L004 addql #1,d0 L004: rts #endif /* !ASM_LANGUAGE */ ------ EOF ------ ls -l jobLib.c cat >jobLib.h <<'------ EOF ------' /* jobLib.h - header file for 'job' structures */ #ifndef INCjobLib_h #define INCjobLib_h /* jobLib.h version 1.2 8/22/90 Copyright 1989, National Optical Astronomy Observatories, Tucson, Arizona modification history -------------------- 01b 18may90 dat - added 'jobList' system 01a 24may89 dat - fixed up for export */ /* Interface to the jobLib system. It is a system of unassigned tasks doing unrelated work units. SEE ALSO: lstLib.h semLib.h */ #include "lstLib.h" #include "semLib.h" /* bits in the job flag word */ /* YOU MUST USE A CORRECTED VERSION OF vxTas() */ #define JOB_BUSY (0x80) /* MUST BE 0x80 for vxTas () */ #define JOB_REPEAT (0x01) #define JOB_DELETE (0x02) /* delete when done */ #define JOB_QUEUED (0x04) /* job is on a list */ #define JOB_DELAYED (0x08) /* pending on clock */ #define JOB_SEMAPHORE (0x10) /* pending on semaphore */ typedef struct { NODE node; /* active job list */ UTINY flags; /* MUST BE CHAR or UCHAR */ UTINY priority; /* priority level, 0 = highest */ ULONG pendVal; /* pending arg, TICKS or sem ptr */ char *name; /* job Name */ FUNCPTR func; /* procedure ptr */ ULONG arg1; /* procedure arguments */ ULONG arg2; ULONG arg3; ULONG arg4; } JOB; typedef JOB *JOB_ID; IMPORT JOB_ID jobCreate (); /* ( name,pri,func,arg1,arg2,arg3,arg4) */ IMPORT STATUS jobQue (); /* ( pJob ) */ IMPORT STATUS jobFree (); /* ( pJob ) */ IMPORT STATUS jobDelay (); /* ( pJob, ticks ) */ IMPORT STATUS jobDelayTill(); /* ( pJob, ticks ) */ IMPORT STATUS jobSemGet (); /* ( pJob, pSem ) */ IMPORT VOID jobExecute(); /* ( pJob ) */ typedef struct { LIST jobs; SEM_ID access; } JOB_LIST; IMPORT JOB_LIST *jobListCreate (); /* () */ IMPORT VOID jobListFree (); /* (pJobList) */ IMPORT VOID jobListProcess (); /* (pJobList) */ IMPORT STATUS jobListAdd (); /* (pJobList, pNewJob) */ IMPORT STATUS jobListDelete (); /* (pJobList, pOldJob) */ IMPORT VOID jobListInit (); /* (pJobList) */ IMPORT int jobListCount (); /* (pJobList) */ IMPORT VOID jobListLock (); /* (pJobList) */ IMPORT VOID jobListUnlock (); /* (pJobList) */ /* you should lock the list when using these routines */ IMPORT JOB_ID jobListFirst (); /* (pJobList) */ IMPORT JOB_ID jobListLast (); /* (pJobList) */ IMPORT JOB_ID jobListGet (); /* (pJobList) */ IMPORT JOB_ID jobListNext (); /* (pJob) */ IMPORT JOB_ID jobListPrev (); /* (pJob) */ IMPORT VOID jobListAddAfter (); /* (pJobList, pOldJob, pNewJob) */ /* macros for sake of performance (in-line functions) */ #define jobListInit(x) (lstInit(&(x)->jobs)) #define jobListCount(x) (lstCount(&(x)->jobs)) #define jobListLock(x) (semTake((x)->access)) #define jobListUnlock(x) (semGive((x)->access)) /* you should lock the list when using these routines */ #define jobListFirst(x) (JOB_ID)(lstFirst(&(x)->jobs)) #define jobListLast(x) (JOB_ID)(lstLast(&(x)->jobs)) #define jobListGet(x) (JOB_ID)(lstGet(&(x)->jobs)) #define jobListNext(x) (JOB_ID)(lstNext(&(x)->node)) #define jobListPrev(x) (JOB_ID)(lstPrevious(&(x)->node)) #define jobListAddAfter(x,y,z) (lstInsert(&(x)->jobs,&(y)->node,&(z)->node)) #endif ------ EOF ------ ls -l jobLib.h