home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / hamradio / s920603.zip / KERNEL.C < prev    next >
C/C++ Source or Header  |  1992-05-14  |  15KB  |  590 lines

  1. /* Non pre-empting synchronization kernel, machine-independent portion
  2.  * Copyright 1992 Phil Karn, KA9Q
  3.  */
  4. #if    defined(PROCLOG) || defined(PROCTRACE)
  5. #include <stdio.h>
  6. #endif
  7. #include <dos.h>
  8. #include <setjmp.h>
  9. #include "global.h"
  10. #include "mbuf.h"
  11. #include "proc.h"
  12. #include "timer.h"
  13. #include "socket.h"
  14. #include "daemon.h"
  15. #include "hardware.h"
  16.  
  17. #ifdef    PROCLOG
  18. FILE *proclog;
  19. FILE *proctrace;
  20. #endif
  21. int Stkchk = 0;
  22. struct proc *Curproc;        /* Currently running process */
  23. struct proc *Rdytab;        /* Processes ready to run (not including curproc) */
  24. struct proc *Waittab[PHASH];    /* Waiting process list */
  25. struct proc *Susptab;        /* Suspended processes */
  26. static struct mbuf *Killq;
  27. struct ksig Ksig;
  28.  
  29. static void addproc __ARGS((struct proc *entry));
  30. static void delproc __ARGS((struct proc *entry));
  31.  
  32. static void _psignal __ARGS((void *event,int n));
  33. static int procsigs __ARGS((void));
  34.  
  35. /* Create a process descriptor for the main function. Must be actually
  36.  * called from the main function, and must be called before any other
  37.  * tasking functions are called!
  38.  *
  39.  * Note that standard I/O is NOT set up here.
  40.  */
  41. struct proc *
  42. mainproc(name)
  43. char *name;
  44. {
  45.     register struct proc *pp;
  46.  
  47.     /* Create process descriptor */
  48.     pp = (struct proc *)callocw(1,sizeof(struct proc));
  49.  
  50.     /* Create name */
  51.     pp->name = strdup(name);
  52. #ifndef    AMIGA
  53.     pp->stksize = 0;
  54. #else
  55.     init_psetup(pp);
  56. #endif
  57.     /* Make current */
  58.     pp->state = READY;
  59.     Curproc = pp;
  60.  
  61. #ifdef    PROCLOG
  62.     proclog = fopen("proclog",APPEND_TEXT);
  63.     proctrace = fopen("proctrace",APPEND_TEXT);
  64. #endif
  65.     return pp;
  66. }
  67. /* Create a new, ready process and return pointer to descriptor.
  68.  * The general registers are not initialized, but optional args are pushed
  69.  * on the stack so they can be seen by a C function.
  70.  */
  71. struct proc *
  72. newproc(name,stksize,pc,iarg,parg1,parg2,freeargs)
  73. char *name;        /* Arbitrary user-assigned name string */
  74. unsigned int stksize;    /* Stack size in words to allocate */
  75. void (*pc)();        /* Initial execution address */
  76. int iarg;        /* Integer argument (argc) */
  77. void *parg1;        /* Generic pointer argument #1 (argv) */
  78. void *parg2;        /* Generic pointer argument #2 (session ptr) */
  79. int freeargs;        /* If set, free arg list on parg1 at termination */
  80. {
  81.     register struct proc *pp;
  82.     int i;
  83.  
  84.     if(Stkchk)
  85.         chkstk();
  86.  
  87.     /* Create process descriptor */
  88.     pp = (struct proc *)callocw(1,sizeof(struct proc));
  89.  
  90.     /* Create name */
  91.     pp->name = strdup(name);
  92.  
  93.     /* Allocate stack */
  94. #ifdef    AMIGA
  95.     stksize += SIGQSIZE0;    /* DOS overhead */
  96. #endif
  97.     pp->stksize = stksize;
  98.     if((pp->stack = (int16 *)malloc(sizeof(int16)*stksize)) == NULL){
  99.         free(pp->name);
  100.         free((char *)pp);
  101.         return NULLPROC;
  102.     }
  103.     /* Initialize stack for high-water check */
  104.     for(i=0;i<stksize;i++)
  105.         pp->stack[i] = STACKPAT;
  106.  
  107.     /* Do machine-dependent initialization of stack */
  108.     psetup(pp,iarg,parg1,parg2,pc);
  109.  
  110.     if(freeargs)
  111.         pp->flags |= P_FREEARGS;
  112.     pp->iarg = iarg;
  113.     pp->parg1 = parg1;
  114.     pp->parg2 = parg2;
  115.     
  116.     /* Inherit creator's input and output sockets */
  117.     pp->input = fdup(stdin);
  118.     pp->output = fdup(stdout);
  119.  
  120.     /* Add to ready process table */
  121.     pp->state = READY;
  122.     addproc(pp);
  123.     return pp;
  124. }
  125.  
  126. /* Free resources allocated to specified process. If a process wants to kill
  127.  * itself, the reaper is called to do the dirty work. This avoids some
  128.  * messy situations that would otherwise occur, like freeing your own stack.
  129.  */
  130. void
  131. killproc(pp)
  132. register struct proc *pp;
  133. {
  134.     char **argv;
  135.  
  136.     if(pp == NULLPROC)
  137.         return;
  138.     /* Don't check the stack here! Will cause infinite recursion if
  139.      * called from a stack error
  140.      */
  141.     if(pp == Curproc)
  142.         killself();    /* Doesn't return */
  143.     fclose(pp->input);
  144.     fclose(pp->output);
  145.  
  146.     /* Stop alarm clock in case it's running */
  147.     stop_timer(&pp->alarm);
  148.  
  149.     /* Alert everyone waiting for this proc to die */
  150.     psignal(pp,0);
  151.  
  152.     /* Remove from appropriate table */
  153.     delproc(pp);
  154.  
  155. #ifdef    PROCLOG
  156.     fprintf(proclog,"id %lx name %s stack %u/%u\n",ptol(pp),
  157.         pp->name,stkutil(pp),pp->stksize);
  158.     fclose(proclog);
  159.     proclog = fopen("proclog",APPEND_TEXT);
  160.     proctrace = fopen("proctrace",APPEND_TEXT);
  161. #endif
  162.     /* Free allocated memory resources */
  163.     if(pp->flags & P_FREEARGS){
  164.         argv = pp->parg1;
  165.         while(pp->iarg-- != 0)
  166.             free(*argv++);
  167.         free(pp->parg1);
  168.     }
  169.     free(pp->name);
  170.     free(pp->stack);
  171.     free((char *)pp);
  172. }
  173. /* Terminate current process by sending a request to the killer process.
  174.  * Automatically called when a process function returns. Does not return.
  175.  */
  176. void
  177. killself()
  178. {
  179.     register struct mbuf *bp;
  180.  
  181.     bp = pushdown(NULLBUF,sizeof(Curproc));
  182.     memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
  183.     enqueue(&Killq,bp);
  184.  
  185.     /* "Wait for me; I will be merciful and quick." */
  186.     for(;;)
  187.         pwait(NULL);
  188. }
  189. /* Process used by processes that want to kill themselves */
  190. void
  191. killer(i,v1,v2)
  192. int i;
  193. void *v1;
  194. void *v2;
  195. {
  196.     struct proc *pp;
  197.     struct mbuf *bp;
  198.  
  199.     for(;;){
  200.         while(Killq == NULLBUF)
  201.             pwait(&Killq);
  202.         bp = dequeue(&Killq);
  203.         pullup(&bp,(char *)&pp,sizeof(pp));
  204.         free_p(bp);
  205.         if(pp != Curproc)    /* We're immortal */
  206.             killproc(pp);
  207.     }                        
  208. }
  209.  
  210. /* Inhibit a process from running */
  211. void
  212. suspend(pp)
  213. struct proc *pp;
  214. {
  215.     if(pp == NULLPROC)
  216.         return;
  217.     if(pp != Curproc)
  218.         delproc(pp);    /* Running process isn't on any list */
  219.     pp->state |= SUSPEND;
  220.     if(pp != Curproc)
  221.         addproc(pp);    /* pwait will do it for us */
  222.     else
  223.         pwait(NULL);
  224. }
  225. /* Restart suspended process */
  226. void
  227. resume(pp)
  228. struct proc *pp;
  229. {
  230.     if(pp == NULLPROC)
  231.         return;
  232.     delproc(pp);    /* Can't be Curproc! */
  233.     pp->state &= ~SUSPEND;
  234.     addproc(pp);
  235. }
  236.  
  237. /* Wakeup waiting process, regardless of event it's waiting for. The process
  238.  * will see a return value of "val" from its pwait() call. Must not be
  239.  * called from an interrupt handler.
  240.  */
  241. void
  242. alert(pp,val)
  243. struct proc *pp;
  244. int val;
  245. {
  246.     if(pp == NULLPROC)
  247.         return;
  248. #ifdef    notdef
  249.     if((pp->state & WAITING) == 0)
  250.         return;
  251. #endif
  252. #ifdef    PROCTRACE
  253.     log(-1,"alert(%lx,%u) [%s]",ptol(pp),val,pp->name);
  254. #endif
  255.     if(pp != Curproc)
  256.         delproc(pp);
  257.     pp->state &= ~WAITING;
  258.     pp->retval = val;
  259.     pp->event = NULL;
  260.     if(pp != Curproc)
  261.         addproc(pp);
  262. }
  263.  
  264. /* Post a wait on a specified event and give up the CPU until it happens. The
  265.  * null event is special: it means "I don't want to block on an event, but let
  266.  * somebody else run for a while". It can also mean that the present process
  267.  * is terminating; in this case the wait never returns.
  268.  *
  269.  * Pwait() returns 0 if the event was signaled; otherwise it returns the
  270.  * arg in an alert() call. Pwait must not be called from interrupt level.
  271.  *
  272.  * Before waiting and after giving up the CPU, pwait() processes the signal
  273.  * queue containing events signaled when interrupts were off. This means
  274.  * the process queues are no longer modified by interrupt handlers,
  275.  * so it is no longer necessary to run with interrupts disabled here. This
  276.  * greatly improves interrupt latencies.
  277.  */
  278. int
  279. pwait(event)
  280. void *event;
  281. {
  282.     register struct proc *oldproc;
  283.     int tmp;
  284.     int i_state;
  285.  
  286.     if(!istate()){
  287.         stktrace();
  288.     }
  289.     Ksig.pwaits++;
  290.     if(intcontext() == 1){
  291.         /* Pwait must not be called from interrupt context */
  292.         Ksig.pwaitints++;
  293.         return 0;
  294.     }
  295.     /* Enable interrupts, after saving the current state.
  296.      * This minimizes interrupt latency since we may have a lot
  297.      * of work to do. This seems safe, since care has been taken
  298.      * here to ensure that signals from interrupt level are not lost, e.g.,
  299.      * if we're waiting on an event, we post it before we scan the
  300.      * signal queue.
  301.      */
  302.     i_state = istate();
  303.     enable();
  304.     if(Stkchk)
  305.         chkstk();
  306.     
  307.     if(event != NULL){
  308.         /* Post a wait for the specified event */
  309.         Curproc->event = event;
  310.         Curproc->state = WAITING;
  311.         addproc(Curproc);    /* Put us on the wait list */
  312.     }
  313.     /* If the signal queue contains a signal for the event that we're
  314.      * waiting for, this will wake us back up
  315.      */
  316.     procsigs();
  317.     if(event == NULL){
  318.         /* We remain runnable */
  319.         if(Rdytab == NULLPROC){
  320.             /* Nothing else is ready, so just return */
  321.             Ksig.pwaitnops++;
  322.             restore(i_state);
  323.             return 0;
  324.         }
  325.         addproc(Curproc); /* Put us on the end of the ready list */
  326.     }
  327.     /* Look for a ready process and run it. If there are none,
  328.      * loop or halt until an interrupt makes something ready.
  329.      */
  330.     while(Rdytab == NULLPROC){
  331.         /* Give system back to upper-level multitasker, if any.
  332.          * Note that this function enables interrupts internally
  333.          * to prevent deadlock, but it restores our state
  334.          * before returning.
  335.          */
  336.         kbint();    /***/
  337.         giveup();
  338.         /* Process signals that occurred during the giveup() */
  339.         procsigs();
  340.     }
  341.     /* Remove first entry from ready list */
  342.     oldproc = Curproc;
  343.     Curproc = Rdytab;
  344.     delproc(Curproc);
  345.  
  346.     /* Now do the context switch.
  347.      * This technique was inspired by Rob, PE1CHL, and is a bit tricky.
  348.      *
  349.      * First save the current process's state. Then if
  350.      * this is still the old process, load the new environment. Since the
  351.      * new task will "think" it's returning from the setjmp() with a return
  352.      * value of 1, the comparison with 0 will bypass the longjmp(), which
  353.      * would otherwise cause an infinite loop.
  354.      */
  355. #ifdef    PROCTRACE
  356.     if(strcmp(oldproc->name,Curproc->name) != 0){
  357.         log(-1,"-> %s(%d)",Curproc->name,!!(Curproc->flags & P_ISTATE));
  358.     }
  359. #endif
  360.     /* Save old state */
  361.     oldproc->flags &= ~P_ISTATE;
  362.     if(i_state)
  363.         oldproc->flags |= P_ISTATE;
  364.     if(setjmp(oldproc->env) == 0){
  365.         /* We're still running in the old task; load new task context.
  366.          * The interrupt state is restored here in case longjmp
  367.          * doesn't do it (e.g., systems other than Turbo-C).
  368.          */
  369.         restore(Curproc->flags & P_ISTATE);
  370.         longjmp(Curproc->env,1);
  371.     }
  372.     /* At this point, we're running in the newly dispatched task */
  373.     tmp = Curproc->retval;
  374.     Curproc->retval = 0;
  375.  
  376.     /* Also restore the true interrupt state here, in case the longjmp
  377.      * DOES restore the interrupt state saved at the time of the setjmp().
  378.      * This is the case with Turbo-C's setjmp/longjmp.
  379.      */
  380.     restore(Curproc->flags & P_ISTATE);
  381.  
  382.     /* If an exception signal was sent and we're prepared, take it */
  383.     if((Curproc->flags & P_SSET) && tmp == Curproc->signo)
  384.         longjmp(Curproc->sig,1);
  385.  
  386.     /* Otherwise return normally to the new task */
  387.     return tmp;
  388. }
  389.  
  390. void
  391. psignal(event,n)
  392. void *event;
  393. int n;
  394. {
  395.     static void *lastevent;
  396.  
  397.     if(istate()){
  398.         /* Interrupts are on, just call _psignal directly after
  399.          * processing the previously queued signals
  400.          */
  401.         procsigs();
  402.         _psignal(event,n);
  403.         return;
  404.     }
  405.     /* Interrupts are off, so quickly queue event */
  406.     Ksig.psigsqueued++;
  407.  
  408.      /* Ignore duplicate signals to protect against a mad device driver
  409.      * overflowing the signal queue
  410.      */
  411.     if(event == lastevent && Ksig.nentries != 0){
  412.         Ksig.dupsigs++;
  413.         return; 
  414.     }
  415.     if(Ksig.nentries == SIGQSIZE){
  416.         /* It's hard to handle this gracefully */
  417.         Ksig.lostsigs++;
  418.         return;
  419.     }
  420.     lastevent = Ksig.wp->event = event;
  421.     Ksig.wp->n = n;
  422.     if(++Ksig.wp >= &Ksig.entry[SIGQSIZE])
  423.         Ksig.wp = Ksig.entry;
  424.     Ksig.nentries++;
  425. }
  426. static int
  427. procsigs()
  428. {
  429.     int cnt = 0;
  430.     int tmp;
  431.  
  432.     for(;;){
  433.         /* Atomic read and decrement of entry count */
  434.         DISABLE();
  435.         tmp = Ksig.nentries;
  436.         if(tmp != 0)
  437.             Ksig.nentries--;
  438.         RESTORE();
  439.         if(tmp == 0)
  440.             break;
  441.         _psignal(Ksig.rp->event,Ksig.rp->n);
  442.         if(++Ksig.rp >= &Ksig.entry[SIGQSIZE])
  443.             Ksig.rp = Ksig.entry;
  444.         cnt++;
  445.     }
  446.     if(cnt > Ksig.maxentries)
  447.         Ksig.maxentries = cnt;    /* Record high water mark */
  448.     return cnt;
  449. }
  450. /* Make ready the first 'n' processes waiting for a given event. The ready
  451.  * processes will see a return value of 0 from pwait().  Note that they don't
  452.  * actually get control until we explicitly give up the CPU ourselves through
  453.  * a pwait(). _psignal is now called from pwait, which is never called at
  454.  * interrupt time, so it is no longer necessary to protect the proc queues
  455.  * against interrupts. This also helps interrupt latencies considerably.
  456.  */
  457. static void
  458. _psignal(event,n)
  459. void *event;    /* Event to signal */
  460. int n;        /* Max number of processes to wake up */
  461. {
  462.     register struct proc *pp;
  463.     struct proc *pnext;
  464.     unsigned int hashval;
  465.     int cnt = 0;
  466.  
  467.     Ksig.psigs++;
  468.     if(Stkchk)
  469.         chkstk();
  470.  
  471.     if(event == NULL){
  472.         Ksig.psignops++;
  473.         return;        /* Null events are invalid */
  474.     }
  475.     /* n == 0 means "signal everybody waiting for this event" */
  476.     if(n == 0)
  477.         n = 65535;
  478.  
  479.     hashval = phash(event);
  480.     for(pp = Waittab[hashval];n != 0 && pp != NULLPROC;pp = pnext){
  481.         pnext = pp->next;
  482.         if(pp->event == event){
  483. #ifdef    PROCTRACE
  484.                 log(-1,"psignal(%lx,%u) wake %lx [%s]",ptol(event),n,
  485.                  ptol(pp),pp->name);
  486. #endif
  487.             delproc(pp);
  488.             pp->state &= ~WAITING;
  489.             pp->retval = 0;
  490.             pp->event = NULL;
  491.             addproc(pp);
  492.             n--;
  493.             cnt++;
  494.         }
  495.     }
  496.     for(pp = Susptab;n != 0 && pp != NULLPROC;pp = pnext){
  497.         pnext = pp->next;
  498.         if(pp->event == event){
  499. #ifdef    PROCTRACE
  500.                 log(-1,"psignal(%lx,%u) wake %lx [%s]",ptol(event),n,
  501.                  ptol(pp),pp->name);
  502. #endif /* PROCTRACE */
  503.             delproc(pp);
  504.             pp->state &= ~WAITING;
  505.             pp->event = 0;
  506.             pp->retval = 0;
  507.             addproc(pp);
  508.             n--;
  509.             cnt++;
  510.         }
  511.     }
  512.     if(cnt == 0)
  513.         Ksig.psignops++;
  514.     else
  515.         Ksig.psigwakes += cnt;
  516. }
  517.  
  518. /* Rename a process */
  519. void
  520. chname(pp,newname)
  521. struct proc *pp;
  522. char *newname;
  523. {
  524.     free(pp->name);
  525.     pp->name = strdup(newname);
  526. }
  527. /* Remove a process entry from the appropriate table */
  528. static void
  529. delproc(entry)
  530. register struct proc *entry;    /* Pointer to entry */
  531. {
  532.     if(entry == NULLPROC)
  533.         return;
  534.  
  535.     if(entry->next != NULLPROC)
  536.         entry->next->prev = entry->prev;
  537.     if(entry->prev != NULLPROC){
  538.         entry->prev->next = entry->next;
  539.     } else {
  540.         switch(entry->state){
  541.         case READY:
  542.             Rdytab = entry->next;
  543.             break;
  544.         case WAITING:
  545.             Waittab[phash(entry->event)] = entry->next;
  546.             break;
  547.         case SUSPEND:
  548.         case SUSPEND|WAITING:
  549.             Susptab = entry->next;
  550.             break;
  551.         }
  552.     }
  553. }
  554. /* Append proc entry to end of appropriate list */
  555. static void
  556. addproc(entry)
  557. register struct proc *entry;    /* Pointer to entry */
  558. {
  559.     register struct proc *pp;
  560.     struct proc **head;
  561.  
  562.     if(entry == NULLPROC)
  563.         return;
  564.  
  565.     switch(entry->state){
  566.     case READY:
  567.         head = &Rdytab;
  568.         break;
  569.     case WAITING:
  570.         head = &Waittab[phash(entry->event)];
  571.         break;
  572.     case SUSPEND:
  573.     case SUSPEND|WAITING:
  574.         head = &Susptab;
  575.         break;
  576.     }
  577.     entry->next = NULLPROC;
  578.     if(*head == NULLPROC){
  579.         /* Empty list, stick at beginning */
  580.         entry->prev = NULLPROC;
  581.         *head = entry;
  582.     } else {
  583.         /* Find last entry on list */
  584.         for(pp = *head;pp->next != NULLPROC;pp = pp->next)
  585.             ;
  586.         pp->next = entry;
  587.         entry->prev = pp;
  588.     }
  589. }
  590.