// /* mlexec.c: select()/longjmp() based task scheduler. Copyright (c) 1996 Eliot W. Dudley. All rights reserved. edudley@servtech.com XIDAR 3388 STATE RT 370 CATO NY 13033 You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the README file. */ /* 1-Wire, DS, DS1820, DS2405, DS2407, DS9097, and MicroLan are trademarks and/or registered trademarks of Dallas Semiconductor Coporation */ //============================================================================= // TIdle(), TMain(), and TPrintf are spawned here, other tasks can be spawned // by feeding commands to TMain() through 'ml.rc' or standard input. // TPrintf() is a scheme to conserve RAM because each task must // have it's own stack pre-allocated and any task that needs to do a *printf // would need another eight K without the separate formatting task. Somebody // might want to run dozens of little task objects and all those 8Ks would // start to add up to something. //============================================================================= //============================================================================= #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <setjmp.h> #include <memory.h> #include <unistd.h> #include <fcntl.h> #include <sys/time.h> #include <sys/types.h> #include <asm/bitops.h> #include "util.h" #include "b.h" #include "mlfnc.h" #include "ml.h" #include "mlexec.h" #include "mlerr.h" //============================================================================= //=External.=================================================================== pEXEC pexec; //============================================================================= //=External static. Would normally be malloc()ed but fatal error reporting==== // needs these these two to report malloc() errors. static EXEC exec; static TCB tcb_main; //============================================================================= //=Prototype.================================================================== static void FncTblCreate(void); static pFNC FncCreate(char *name, VAFNC fnc); static pTCB TaskInit(pTCB pptcb, char *name, int siz); static void StackSet(pTCB ptcb); //============================================================================= //============================================================================= #define BitPriQInsert(idxx, addrr) clear_bit(idxx, addrr) #define BitPriQRemove(idxx, addrr) set_bit(idxx, addrr) #define BitPriQTop(addrr) find_first_zero_bit(addrr, BITPRIQ_BITS) //============================================================================= //============================================================================= //@Man: Tasks object functions. //@{ // /*/ Initializaton and housekeeping preparatory to spawning task objects \Ref{TIdle}, \Ref{TMain}, and \Ref{TPrintf}. Catches fatal exceptions. Source: \URL{../mlexec.c.html#main} */ int main( int argc, char **argv ) { int i; time_t t; char *ptr; char **pstr; char buf[128]; pTCB ptcb; pexec = &exec; ptcb = &tcb_main; TaskInit(ptcb, "main", main_SIZ); StackClr(ptcb); // localtime() and strerror() bruise the stack first // time they're called, get it over with here where // it won't hurt anything. t = time((time_t *) 0); localtime(&t); strerror(errno); pexec->ptcb_cur->call = pexec->ptcb_cur->call_trace; memset(&pexec->bitpriq_rdy, 0xff, sizeof(BITPRIQ)); ENTER(main); if (setjmp(pexec->fatal)) { printf("Task %d: ", pexec->pri_cur); for (pstr = pexec->ptcb_cur->call_trace; *pstr; ++pstr) { printf("%s(): ", *pstr); }; printf("\n Line %d: ", va_arg(pexec->va_alist, int)); printf("%s ", ml_err[va_arg(pexec->va_alist, int)]); if ((ptr = va_arg(pexec->va_alist, char *))) { vsprintf(buf, ptr, pexec->va_alist); puts(buf); } else { puts(""); } exit(1); } PLNew(&pexec->tcb, TASK_MAX, TASK_SIZ, ML_BY_NAME + 1); pexec->tcb.p[ML_BY_CREATE][0] = (pPLMBR)ptcb; PLInsert((pPLMBR)ptcb, &pexec->tcb, 1, NameCmp); PLNew(&pexec->event, EVENT_MAX, EVENT_SIZ, ML_BY_NAME + 1); PLNew(&pexec->fnc, FNC_MAX, FNC_SIZ, ML_BY_NAME + 1); FncTblCreate(); pexec->pbufq = QCreate("", BUF_Q_CNT); for (i = 0; i < BUF_Q_CNT; ++i) { MALLOC(ptr, BUF_SIZ, char); EventPost(pexec->pbufq, ptr); } pexec->pri_min = TASK_MAX; BufDelete(Spawn(strcpy(BufNew(), "TIdle TIdle"))); BufDelete(Spawn(strcpy(BufNew(), "TPrintf TPrintf"))); BufDelete(Spawn(strcpy(BufNew(), "TMain TMain"))); pexec->ptcb_cur = (pTCB)pexec->tcb.p[ML_BY_CREATE][pexec->pri_cur = TASK_MAX - 1]; longjmp(pexec->ptcb_cur->jmp_buf, 1); return(0); // Not really. } //============================================================================= //============================================================================= // /// /*@Memo: When all tasks are waiting on i/o or for time to pass, TIdle() uses select() to wait for something to happen and then wakes up the highest priority task that becomes ready to run. Source: \URL{../mlexec.c.html#TIdle} */ char * TIdle( char *cmd, int pri_ret ) { int i; pTCB ptcb; int delay_usec_sched; int delay_usec_actual; int nfd; fd_set *pfd_set_r; struct timeval tv_select; struct timeval tv_now; struct timeval tv_sleep; if(0 == setjmp((ptcb = TaskCreate(&cmd, TIdle_SIZ))->jmp_buf)) { SpawnRet(pri_ret, ptcb->name); } StackClr(ptcb); ENTER(TIdle); // if (!(pexec->LOG = fopen("ml.out", "a+"))) { if (!(pexec->LOG = stdout)) { Fatal(__LINE__, OPEN_FAILED, "File: %s", "ts.out"); } CALLOC(pfd_set_r, 1, fd_set); for (;;) { delay_usec_sched = 999999; FD_ZERO(pfd_set_r); for (nfd = 0, ptcb = pexec->ptcb_first; ptcb; ptcb = ptcb->ptcb_next) { if ((ptcb->delay_usec) && (ptcb->delay_usec < delay_usec_sched)) { delay_usec_sched = ptcb->delay_usec; } if (ptcb->fd != -1) { nfd = (ptcb->fd > nfd) ? ptcb->fd : nfd; pexec->fd[ptcb->fd] = ptcb->pri; FD_SET(ptcb->fd, pfd_set_r); } } tv_sleep.tv_sec = 0; tv_sleep.tv_usec = delay_usec_sched; gettimeofday(&tv_select, (struct timezone *)0); if (select(++nfd, pfd_set_r, NULL, NULL, &tv_sleep)) { for (i = 0; i < ((FD_CNT + 31) / 32); ++i) { ((int *)pfd_set_r)[i] = ~((int *)pfd_set_r)[i]; } for ( i = find_first_zero_bit(pfd_set_r, FD_CNT); (i < nfd) && (i < FD_CNT); i = find_next_zero_bit(pfd_set_r, 32, i + 1) ) { BitPriQInsert(pexec->fd[i], &pexec->bitpriq_rdy); ((pTCB)pexec->tcb.p[ML_BY_CREATE][pexec->fd[i]])->fd = -1; ((pTCB)pexec->tcb.p[ML_BY_CREATE][pexec->fd[i]])->delay_usec = 0; pexec->fd[i] = 0; } } gettimeofday(&tv_now, (struct timezone *)0); delay_usec_actual = ((tv_now.tv_sec - tv_select.tv_sec) * 1000000) + (tv_now.tv_usec - tv_select.tv_usec); for (nfd = 0, ptcb = pexec->ptcb_first; ptcb; ptcb = ptcb->ptcb_next) { if ( (ptcb->delay_usec) && (0 >= (ptcb->delay_usec -= delay_usec_actual)) ) { ptcb->delay_usec = 0; BitPriQInsert(ptcb->pri, &pexec->bitpriq_rdy); } } ExecSched(); } return(0); // Not really. } //============================================================================= //============================================================================= // /// /*@Memo: TMain parses commands submitted through stdin, after initially running the command script "ml.rc". Source: \URL{../mlexec.c.html#TMain} */ char * TMain( char *cmd, int pri_ret ) { int i; int j; int count; int ret; char *buf; char *cuf; char *ptr; char *fnc_name; VAFNC fnc; pTCB ptcb; pEVENT pq; if(0 == setjmp((ptcb = TaskCreate(&cmd, TMain_SIZ))->jmp_buf)) { pq = QCreate(EVENT_NAME_DEFAULT, 16); ptcb->pmsg = EventCreate(EVENT_NAME_DEFAULT, EVENT_MSG); SpawnRet(pri_ret, ptcb->name); } StackClr(ptcb); ENTER(TMain); buf = BufNew(); cuf = BufNew(); for ( i = 0, count = BUF_SIZ - 1, strcpy(ptr = cuf, "load ml.rc"); ; ) { if ( (fnc_name = Shift(&ptr)) && (fnc = GetFncByName(fnc_name)) ) { ptr = (char *)((fnc)(strcpy(BufNew(), ptr ? ptr : ""))); puts(ptr); BufDelete(ptr); } TaskDelayUSec(300000); for (*(ptr = cuf) = 0; i < count;) { if (ReadYield(0, 0)) { if (0 > (ret = read(0, buf + i, count - i))) { Fatal(__LINE__, READ_FAILED, "stdin"); } i += ret; for (j = 0; j < i; ++j) { if (buf[j] == '\n') { buf[j] = 0; strcpy(cuf, buf); memmove(buf, buf + 1 + j, --i - j); i = i - j; break; } } if (j == i) { continue; } break; } break; } } return(0); // Not really. } //============================================================================= //============================================================================= // /// /*@Memo: Since serveral thousand bytes of stack space must be allocated for the occasional sprintf(), the task TPrintf() is set up to do all output formatting on behalf of any other task. Source: \URL{../mlexec.c.html#TPrintf} */ char * TPrintf( char *cmd, int pri_ret ) { pTCB ptcb; void *obj; char *fmt; VAPRINTF vaprintf; va_list va_args; pEVENT pfrom; pEVENT pq = 0; if (0 == setjmp((ptcb = TaskCreate(&cmd, TPrintf_SIZ))->jmp_buf)) { pq = QCreate(EVENT_NAME_DEFAULT, 128); SpawnRet(pri_ret, ptcb->name); } StackClr(ptcb); ENTER(TPrintf); for (;;) { va_args = (va_list)EventPend(pq, 0); pfrom = va_arg(va_args, pEVENT); vaprintf = va_arg(va_args, VAPRINTF); obj = va_arg(va_args, void *); fmt = va_arg(va_args, char *); EventSched(pfrom, (void *)vaprintf(obj, fmt, va_args)); } return(0); // Not really. } //============================================================================= //@} //End Task object functions. //============================================================================= //============================================================================= //@Man: Exported functions. //@{ //============================================================================= //@Man: Task spawning and event creation. //@{ // /*/ Pull the name of a task object function off the *cmd string and pass the remainder of the *cmd string to the function after setting up a longjmp() context to which the spawned task will jump after initializing. Source: \URL{../mlexec.c.html#Spawn} */ char * Spawn( char *cmd ) { register unsigned long esp asm("esp"); int ret; char *ptr; char *tmp; char *reply; char *tcb_name; VAFNC fnc; int pri_ret; //=Parse a little, check that TCB name isn't in use.======================== for (;;) { reply = strcpy(BufNew(), "1 Spawn"); strcpy(ptr = tmp = BufNew(), cmd); if ('0' == *FncShift(reply, &fnc, &ptr)) { break; } if (!(tcb_name = Shift(&ptr))) { *(strcat(reply, ": Task name is (nil)")) = '0'; break; } if (GetTcbByName(tcb_name)) { *(strcat(strcat(strcat(reply, " "), tcb_name), ": Task already running")) = '0'; break; } BufDelete(reply); BufDelete(tmp); //======================================================================= //=Start over with cmd buf. Already have fnc_name and fnc, just shift.== ptr = cmd; Shift(&ptr); if (0 == (ret = setjmp(pexec->ptcb_cur->jmp_buf))) { pri_ret = pexec->pri_cur; esp = pexec->sp_min; pexec->pri_cur = --pexec->pri_min; (fnc)(ptr, pri_ret); } reply = *(pexec->pbufq->e.pq->bottom + ret); break; //======================================================================= } //========================================================================== //========================================================================== BufDelete(cmd); return(reply); //========================================================================== } //============================================================================= //============================================================================= // /*/ "Return" to the task than spawned us. Source: \URL{../mlexec.c.html#SpawnRet} */ void SpawnRet( int pri_ret, char *msg ) { int ret; ret = pexec->pbufq->e.pq->out - pexec->pbufq->e.pq->bottom; strcat(strcpy(BufNew(), "1 Spawn "), msg); pexec->ptcb_cur = (pTCB)pexec->tcb.p[ML_BY_CREATE][pexec->pri_cur = pri_ret]; longjmp(pexec->ptcb_cur->jmp_buf, ret); } //============================================================================= // /*/ Initialize a newly spawned task and make it ready to run. Source: \URL{../mlexec.c.html#TaskCreate} */ pTCB TaskCreate( char **cmd, int siz ) { char *name; pTCB *pptcb; name = Shift(cmd); if (*(pptcb = (pTCB *)&pexec->tcb.p[ML_BY_CREATE][pexec->pri_cur])) { Fatal(__LINE__, OBJECT_ALREADY_EXISTS, "pri %d: siz: %d", pexec->pri_cur, siz); } CALLOC(*pptcb, 1, TCB); TaskInit(*pptcb, name, siz); PLInsert((pPLMBR)*pptcb, &pexec->tcb, 1, NameCmp); BitPriQInsert(pexec->pri_cur, &pexec->bitpriq_rdy); return(*pptcb); } // /*/ Force some stack space into virtual memory and fill it with a pattern. Source: \URL{../mlexec.c.html#StackClr} */ void StackClr( pTCB ptcb ) { char *ptr; StackSet(ptcb); for (ptr = (char *)ptcb->sp_min; ptr < (char *)&ptr; ptr++) { *ptr = 0x5c; // Pick a number, any number. } } // /*/ For each task object print out initial stack size and the lowset point reached so far. Source: \URL{../mlexec.c.html#StackInfo} */ char * StackInfo( char *cmd ) { char *ptr; pTCB ptcb; for (ptcb = pexec->ptcb_first; ptcb; ptcb = ptcb->ptcb_next) { for ( ptr = (char *)ptcb->sp_min; (*ptr == 0x5c) && (ptr < (char *)ptcb->sp_low); ptr++ ) {;} VAPrintf(0, (VAPRINTF)vfprintf, pexec->LOG ,"%-16.16s %5d %5d %5d\n" ,ptcb->name ,ptcb->sp_siz ,ptcb->sp_low - ptcb->sp_min ,(unsigned long int)ptr - ptcb->sp_min ); } BufDelete(cmd); return(strcpy(BufNew(), "1 StackInfo")); } // /*/ Initialize a semaphore/msg mailbox/queue and give it a name. Source: \URL{../mlexec.c.html#EventCreate} */ pEVENT EventCreate( char *name, int type ) { static char *suffix[] = {"Sem", "Msg", "Q"}; pEVENT *ppevent; char *ptr = ""; STACK_CHECK(); ENTER(EventCreate); ppevent = (pEVENT *)PLGrow(&pexec->event); CALLOC(*ppevent, 1, EVENT); if (name == EVENT_NAME_DEFAULT) { name = pexec->ptcb_cur->name; ptr = suffix[type]; } MALLOC((*ppevent)->name, strlen(name) + strlen(ptr) + 1, char); strcpy((*ppevent)->name, name); strcat((*ppevent)->name, ptr); (*ppevent)->type = type; memset((*ppevent)->bitpriq_wait, 0xff, sizeof(BITPRIQ)); PLInsert(*((pPLMBR *)ppevent), &pexec->event, 1, NameCmp); RETURN(*ppevent); } // /*/ Initialize a queue \Ref{EVENTt}, give it a name. Then allocate and initialize a \Ref{Qt} for it. Source: \URL{../mlexec.c.html#QCreate} */ pEVENT QCreate( char *name, int siz ) { pEVENT pevent; pQ pq; STACK_CHECK(); ENTER(QCreate); pevent = EventCreate(name, EVENT_Q); CALLOC(pq = pevent->e.pq, 1, Q); MALLOC(pq->in = pq->out = pq->bottom, siz, void *); pq->top = pq->bottom + siz; pq->siz = siz; pq->cnt = 0; RETURN(pevent); } //@} //End: Task and event creation. //============================================================================= //============================================================================= //@Man: Task coordination. //@{ // /*/ Wait on a task coordination event. Wait forever with timeout of zero, poll with timeout of -1, or wait timeout micro seconds. Source: \URL{../mlexec.c.html#EventPend} */ void * EventPend( pEVENT pevent, int timeout ) { void *ret; pQ pq = 0; pTCB ptcb; STACK_CHECK(); switch (pevent->type) { case EVENT_SEM: { if (pevent->e.sem-- > 0) { return((void *)1); } break; } case EVENT_MSG: { if ((ret = pevent->e.msg)) { pevent->e.msg = 0; return(ret); } break; } case EVENT_Q: { if ((pq = pevent->e.pq)->cnt) { ret = *pq->out++; --pq->cnt; if (pq->out == pq->top) { pq->out = pq->bottom; } return(ret); } break; } } if (timeout < 0) { return((void *)0); } ptcb = pexec->ptcb_cur; ptcb->status = BLOCKED; ptcb->delay_usec = timeout; BitPriQRemove(ptcb->pri, &pexec->bitpriq_rdy); BitPriQInsert(ptcb->pri, &pevent->bitpriq_wait); ExecSched(); // Timed out. if (BLOCKED == ptcb->status) { BitPriQRemove(ptcb->pri, &pevent->bitpriq_wait); ptcb->status = RDY; return(0); } switch (pevent->type) { case EVENT_SEM: { return((void *)1); } case EVENT_MSG: { ret = pevent->e.msg; pevent->e.msg = 0; return(ret); } case EVENT_Q: { ret = *pq->out++; --pq->cnt; if (pq->out == pq->top) { pq->out = pq->bottom; } return(ret); } } return(0); } // /*/ Post a task coordination object. Source: \URL{../mlexec.c.html#EventPost} */ int EventPost( pEVENT pevent, void *entity ) { pQ pq; STACK_CHECK(); ENTER(EventSched); switch (pevent->type) { case EVENT_SEM: { if (++pevent->e.sem > 0) { if (pevent->e.sem > SEM_MAX) { FATAL(SEMAPHORE_OVERFLOW); } } break; } case EVENT_MSG: { pevent->e.msg = entity; break; } case EVENT_Q: { pq = pevent->e.pq; if(pq->cnt >= pq->siz) { FATAL(QUEUE_OVERFLOW); } ++pq->cnt; *pq->in++ = entity; if (pq->in == pq->top) { pq->in = pq->bottom; } break; } } RETURN(1); } // /*/ Post a task coordination object, then reschedule if that makes another higher priority task ready to run. Source: \URL{../mlexec.c.html#EventSched} */ int EventSched( pEVENT pevent, void *entity ) { int ret; int pri; pTCB ptcb; if ((ret = EventPost(pevent, entity))) { if (BITPRIQ_BITS != (pri = BitPriQTop(&pevent->bitpriq_wait))) { ptcb = (pTCB)pexec->tcb.p[ML_BY_CREATE][pri]; ptcb->delay_usec = 0; ptcb->status = RDY; BitPriQRemove(pri, &pevent->bitpriq_wait); BitPriQInsert(pri, &pexec->bitpriq_rdy); ExecSched(); } } return(ret); } // /*/ Switch to the highest priority task object. Source: \URL{../mlexec.c.html#ExecSched} */ void ExecSched(void ) { int pri_rdy; STACK_CHECK(); if ((pexec->pri_cur) != (pri_rdy = BitPriQTop(&pexec->bitpriq_rdy))) { if (0 == setjmp(pexec->ptcb_cur->jmp_buf)) { pexec->ptcb_cur = (pTCB)pexec->tcb.p[ML_BY_CREATE][pexec->pri_cur = pri_rdy]; longjmp(pexec->ptcb_cur->jmp_buf, 1); } } } // /*/ Get a buffer from the buffer pool. Source: \URL{../mlexec.c.html#BufNew} */ char * BufNew(void) { return((char *)EventPend(pexec->pbufq, 0)); } // /*/ Return a buffer to the buffer pool. Source: \URL{../mlexec.c.html#BufDelete} */ int BufDelete( char *ptr ) { return(EventPost(pexec->pbufq, ptr)); } // /*/ Return semaphore value so \Ref{EVENTt} can remain an incomplete type outside of mlexec.c. Source: \URL{../mlexec.c.html#EventSemCount} */ int EventSemCount( pEVENT pevent ) { return(pevent->e.sem); } // /*/ Sleep at least delay_sec seconds with 10 millisec or so precision. Source: \URL{../mlexec.c.html#TaskDelaySec} */ void TaskDelaySec( int delay_sec ) { while(delay_sec--) { TaskDelayUSec(999999); } } // /*/ Sleep at least delay_usec micro seconds, subject to 10 millisec system tick granularity, etc. Source: \URL{../mlexec.c.html#TaskDelayUSec} */ void TaskDelayUSec( int delay_usec ) { pTCB ptcb; STACK_CHECK(); ptcb = pexec->ptcb_cur; ptcb->delay_usec = delay_usec; BitPriQRemove(ptcb->pri, &pexec->bitpriq_rdy); ExecSched(); } // /*/ Wait at least delay_usec micro seconds for input to appear on the file descriptor, subject to 10 millisec system tick granularity, etc. Not real time, but is real handy. Wait forever if delay_usec is zero. And yield to other tasks in the meantime. Source: \URL{../mlexec.c.html#ReadYield} */ int ReadYield( int fd, int delay_usec ) { int ret; pTCB ptcb; STACK_CHECK(); ptcb = pexec->ptcb_cur; ptcb->delay_usec = delay_usec; ptcb->fd = fd; BitPriQRemove(ptcb->pri, &pexec->bitpriq_rdy); ExecSched(); ret = (-1 == ptcb->fd); ptcb->fd = -1; return(ret); } // /*/ Variable argument event post. Pass address of sender's argument stack to target task and wait for a reply. Source: \URL{../mlexec.c.html#VAPost} */ void * VAPost( pEVENT pto, pEVENT pfrom, ... ) { STACK_CHECK(); EventSched(pto, &pfrom); return(EventPend(pfrom, 0)); } // /*/ Set up an output formatting request for the TPrintf() task object and send if off for him to deal with. Source: \URL{../mlexec.c.html#VAPrintf} */ int VAPrintf( pEVENT pfrom, ... ) { static pEVENT pprintfq; STACK_CHECK(); if (!pprintfq) { if (!(pprintfq = GetEventByName("TPrintfQ"))) { Fatal(__LINE__, GETEVENTBYNAME_FAILED, "TPrintfQ not found"); } } pfrom = pexec->ptcb_cur->pmsg; EventSched(pprintfq, &pfrom); return((int)EventPend(pfrom, 0)); } //@} //End: Task coordination. //============================================================================= //============================================================================= //@Man: Pointer list functions. //@{ // /*/ Allocate and initialize a \Ref{PLt} pointer list. Source: \URL{../mlexec.c.html#PLNew} */ void PLNew( pPL ppl, int max, int siz, int dim ) { int i; ppl->dim = dim; ppl->max = max; ppl->siz = siz; ppl->cnt = 0; for (i = 0; i < dim; ++i) { CALLOC(ppl->p[i], siz, pPLMBR); } } // /*/ Insert into a \Ref{PLt} pointer list. Source: \URL{../mlexec.c.html#PLInsert} */ void PLInsert( pPLMBR pplmbr, pPL ppl, int idx, COMPAREFNC cmp ) { int i; int cnt; cnt = ppl->siz - 1; for (i = 0; i < cnt; ++i) { if (cmp(&pplmbr, &ppl->p[idx][i]) <= 0) { memmove( &ppl->p[idx][i + 1], &ppl->p[idx][i], (cnt - i) * sizeof(pPLMBR)); ppl->p[idx][i] = pplmbr; break; } } } // /*/ Search a \Ref{PLt} pointer list. Source: \URL{../mlexec.c.html#PLSearch} */ pPLMBR * PLSearch( pPLMBR pplmbr, pPL ppl, int idx, COMPAREFNC cmp ) { return( bsearch(&pplmbr, &ppl->p[idx][0], ppl->cnt, sizeof(pPLMBR), cmp)); } // /*/ Search a \Ref{PLt} pointer list by name. Source: \URL{../mlexec.c.html#PLByName} */ pPLMBR * PLByName( char *name, pPL ppl ) { PLMBR plmbr; plmbr.name = name; return(PLSearch(&plmbr, ppl, ML_BY_NAME, NameCmp)); } // /*/ realloc() a \Ref{PLt} pointer list if need be. Source: \URL{../mlexec.c.html#PLGrow} */ pPLMBR * PLGrow( pPL ppl ) { int i; if (!(ppl->cnt < ppl->siz)) { ppl->siz += 16; for (i = 0; i < ppl->dim; ++i) { REALLOC(ppl->p[i], ppl->siz, pPLMBR); } } return(&ppl->p[ML_BY_CREATE][ppl->cnt++]); } // /*/ \Ref{pPLMBR}->name compare funtion for sorting \Ref{PLt} pointer lists. Source: \URL{../mlexec.c.html#NameCmp} */ int NameCmp( const void *p0, const void *p1 ) { pPLMBR *ppplmbr0 = (pPLMBR *)p0; pPLMBR *ppplmbr1 = (pPLMBR *)p1; STACK_CHECK(); if ((!*ppplmbr0) && (!*ppplmbr1)) {return( 0);} if (!*ppplmbr0) {return( 1);} if (!*ppplmbr1) {return(-1);} if((!((*ppplmbr0)->name)) && (!((*ppplmbr1)->name))) {return( 0);} if(!((*ppplmbr0)->name)) {return( 1);} if(!((*ppplmbr1)->name)) {return(-1);} return(strcasecmp((*ppplmbr0)->name, (*ppplmbr1)->name)); } //@} //End: Pointer list functions //============================================================================= //============================================================================= //@Man: Lookup global object by name. //@{ // /*/ Get a pointer to a semaphore/msg/queue \Ref{EVENTt} according to its name string. Source: \URL{../mlexec.c.html#GetEventByName} */ pEVENT GetEventByName( char *event_name ) { pEVENT *ppe; if ((ppe = (pEVENT *)PLByName(event_name, &pexec->event))) { return(*ppe); } return(0); } // /*/ Get a pointer to a task control block \Ref{TCBt} according to its name string. Source: \URL{../mlexec.c.html#GetTCBByName} */ pTCB GetTcbByName(char *task_name) { pTCB *ppt; if ((ppt = (pTCB *)PLByName(task_name, &pexec->tcb))) { return(*ppt); } return(0); } // /*/ Look up function in the executive function table. Return pointer to function \Ref{VAFNC} or NULL. Source: \URL{../mlexec.c.html#GetFncByName} */ VAFNC GetFncByName(char *fnc_name) { pFNC *ppfnc; if ((ppfnc = (pFNC *)PLByName(fnc_name, &pexec->fnc))) { return((*ppfnc)->fnc); } return(0); } //@} //End: Lookup global object by name. //============================================================================= //============================================================================= //@Man: Command string processing. //@{ // /*/ Shift next whitespace delimited token off the front of **pstr. Update *pstr to point past the token returned. Source: \URL{../mlexec.c.html#Shift} */ char * Shift( char **pstr ) { char *ptr; char *delim = " \t\n"; while ((ptr = strsep(pstr, delim))) { if (!(strcspn(ptr, delim))) { continue; } break; } return(ptr); } // /*/ Look up the function named in **pstr, return \Ref{VAFNC} pointer through *pfnc, and append function name to *reply string, if successful. Else update reply string with the reason why. Source: \URL{../mlexec.c.html#FncShift} */ char * FncShift( char *reply, VAFNC *pfnc, char **pstr ) { char *fnc_name; for (;;) { if (!(fnc_name = Shift(pstr))) { *(strcat(reply, ": Function name is (nil)")) = '0'; break; } if (!(*pfnc = GetFncByName(fnc_name))) { *(strcat(strcat(reply, ": Function not found: "), fnc_name)) = '0'; break; } break; } return(reply); } // /*/ Shift a file name off the front of the command string passed in and try to open the file according to mode. Return valid FILE* and file name on success. Concatenate file name on *reply on success, else return error string in *reply. Source: \URL{../mlexec.c.html#FOpen} */ char * FOpen( char *reply, FILE **pFILE, char **pfile_name, char *mode, char **pstr ) { for (;;) { if (!(*pfile_name = Shift(pstr))) { *(strcat(reply, ": File name is (nil)")) = '0'; break; } if (!(*pFILE = fopen(*pfile_name, mode))) { *(strcat(strcat(strcat( reply, " "), *pfile_name), ": fopen() failed")) = '0'; break; } strcat(strcat(reply, " "), *pfile_name); break; } return(reply); } // /*/ Command usage: load FILE_NAME. Read in FILE_NAME and execute commands found therein. Source: \URL{../mlexec.c.html#Load} */ char * Load( char *cmd ) { int i; char *buf; char *cuf; char *ptr; char *reply; char *file_name; FILE *FILE; VAFNC fnc; ENTER(Load); for (;;) { ptr = cmd; reply = strcpy(BufNew(), "1 Load"); if ('0' == *FOpen(reply, &FILE, &file_name, "r", &ptr)) { break; } buf = BufNew(); cuf = BufNew(); for (i = 0; fgets(buf, BUF_SIZ, FILE); ++i) { if ('#' == *buf) { continue; } buf[strlen(ptr = buf) - 1] = 0; strcpy(cuf, buf); if ('0' == *FncShift(reply, &fnc, &ptr)) { VAPrintf(0, (VAPRINTF)vsprintf, reply + strlen(reply) ,": line %d: cmd \"%s\"" ,i + 1 ,cuf ); break; } if ('0' == *(ptr = (fnc)(strcpy(BufNew(), ptr ? ptr : "")))) { VAPrintf(0, (VAPRINTF)vsprintf, reply + strlen(reply) ,": line %d: cmd \"%s\": %s" ,i + 1 ,cuf ,ptr ); BufDelete(ptr); break; } puts(ptr); BufDelete(ptr); } BufDelete(cuf); BufDelete(buf); fclose(FILE); break; } BufDelete(cmd); RETURN(reply); } // /*/ Command usage: post MSG_OR_Q_NAME CMD_STRING. Use MSG_OR_Q_NAME to look up \Ref{pEVENT} by name with \Ref{GetEventByName}. Source: \URL{../mlexec.c.html#Post} */ char * Post( char *cmd ) { char *ptr; char *dst; char *reply; pEVENT pto; ptr = cmd; if ((pto = GetEventByName(dst = Shift(&ptr)))) { reply = VAPost(pto, pexec->ptcb_cur->pmsg, strcpy(BufNew(), ptr)); } else { VAPrintf(0, (VAPRINTF)vsprintf, reply = BufNew(), "0 Post: Destination object %s not found", dst); } BufDelete(cmd); return(reply); } //@} //End: Command string processing. //============================================================================= //============================================================================= //@Man: Application instrumentation. //@{ // /*/ Maintain the task's call trace stack. Source: \URL{../mlexec.c.html#Enter} */ void Enter( unsigned line, char *str ) { *pexec->ptcb_cur->call = str; if (++pexec->ptcb_cur->call >= &pexec->ptcb_cur->call_trace_top) { Fatal(line, CALL_TRACE_OVERFLOW, 0); } } // /*/ longjump() to main() fatal handler. To make Fatal() usable from withing main() itself the statically allocated pexec->va_buf is used so main() can call sprintf() and whatnot without wiping out its own arguments. Source: \URL{../mlexec.c.html#Fatal} */ void Fatal( unsigned line, ... ) { memcpy(pexec->va_buf, (BYTE *)&line, sizeof(pexec->va_buf)); pexec->va_alist = (va_list)pexec->va_buf; longjmp(pexec->fatal, 1); } //@} //End: Application instrumentation. //============================================================================= //@} //End: Exported functions. //============================================================================= //============================================================================= //@Man: Static functions. //@{ // /*/ Initialize a task control block \Ref{TCBt} struct and the global executive context. Give the task its *name and establish a stack of at least siz bytes. Source: \URL{../mlexec.c.html#TaskInit} */ static pTCB TaskInit( pTCB ptcb, char *name, int siz ) { ++pexec->tcb.cnt; pexec->sp_min = (unsigned long)&ptcb - siz; pexec->ptcb_cur = ptcb; ptcb->ptcb_prev = 0; if ((ptcb->ptcb_next = pexec->ptcb_first)) { ptcb->ptcb_next->ptcb_prev = ptcb; } pexec->ptcb_first = ptcb; ptcb->pri = pexec->pri_cur; ptcb->call = ptcb->call_trace; ptcb->fd = -1; ptcb->status = RDY; ptcb->sp_low = (unsigned long)&ptcb; ptcb->sp_min = (unsigned long)&ptcb - siz; ptcb->sp_siz = siz; MALLOC(ptcb->name, strlen(name) + 1, char); strcpy(ptcb->name, name); return(ptcb); } // /*/ Plumb the task's pre-allocated maximum stack depth. Source: \URL{../mlexec.c.html#StackSet} */ static void StackSet( pTCB ptcb ) { if (ptcb->sp_min < ((unsigned long int)&ptcb)) { StackSet(ptcb); } } // /*/ The task scheduler executive spawns task objects and initiates one-shot function calls by looking up generic \Ref{VAFNC} function addresses in the \Ref{pexec}->fnc \Ref{PLt} pointer list table. This table is loaded here by exhausting the entries provided by GetFncTblEntry(); Source: \URL{../mlexec.c.html#FncTblCreate} */ static void FncTblCreate(void) { int i; int cnt; char *name; VAFNC fnc; for (i = 0; GetFncTblEntry(i, &name, &fnc); ++i) { FncCreate(name, fnc); } } // /*/ Create one pexec->fnc table entry. Source: \URL{../mlexec.c.html#FncCreate} */ static pFNC FncCreate( char *name, VAFNC fnc ) { pFNC *ppfnc; STACK_CHECK(); ENTER(FncCreate); ppfnc = (pFNC *)PLGrow(&pexec->fnc); CALLOC(*ppfnc, 1, FNC); (*ppfnc)->name = name; (*ppfnc)->fnc = fnc; PLInsert(*((pPLMBR *)ppfnc), &pexec->fnc, 1, NameCmp); RETURN(*ppfnc); } //@} //End Static functions. //============================================================================= //