home *** CD-ROM | disk | FTP | other *** search
/ No Fragments Archive 10: Diskmags / nf_archive_10.iso / MAGS / PURE_B / PBMAG22A.MSA / MINT095S.ZIP / SRC / RENDEZ.C < prev    next >
C/C++ Source or Header  |  1987-04-22  |  14KB  |  457 lines

  1. /* 
  2.  * The rendezvous: 
  3.  * a little bit of message passing with a lot of synchronization.
  4.  *
  5.  * Pmsg(mode,mboxid,msgptr);
  6.  * short mode;
  7.  * long mboxid;
  8.  * struct { long msg1, msg2; short pid; } *msgptr;
  9.  *
  10.  * 'mode' bits:
  11.  *   0000    read
  12.  *   0001    write
  13.  *   0002    write, then read from mboxid "PDxx" where xx is my pid.
  14.  *
  15.  *   8000    OR with this bit to make the operation non-blocking.
  16.  *
  17.  * The messages are five words long: two longs and a short, in that order. 
  18.  * The values  of the first two longs are totally up to the processes in
  19.  * question.  The value of the short is the PID of the sender.  On return
  20.  * from writes, the short is the PID of the process that read your message.
  21.  * On return from reads, it's the PID of the writer.
  22.  *
  23.  * If the 0x8000 bit is set in the mode, and there is not a reader/writer
  24.  * for the mboxid already on the WAIT_Q, this call returns -1.
  25.  *
  26.  * In mode 2, the writer is declaring that it wants to wait for a reply to
  27.  * the message.  What happens is that the reader gets put on the ready
  28.  * queue, but the writer is atomically turned into a reader on a mailbox
  29.  * whose mboxid is (0xFFFF0000 .OR. pid). The idea is that this process
  30.  * will sleep until awoken at a later time by the process that read the
  31.  * message.  The process reading the original request is guaranteed not to
  32.  * block when writing the reply.
  33.  *
  34.  * There is no provision for a timeout.
  35.  
  36. The flow is as follows:
  37.  
  38.     if (you're writing)
  39.     if (there's somebody waiting to read)
  40.                         (SATISFY THE RENDEZVOUS RIGHT AWAY)
  41.         copy msg from your data space into his proc structure
  42.         wake him up
  43.         if (you're doing a write/read)
  44.                     (TURN AROUND AND BLOCK WAITING TO READ)
  45.         write mode 0 to your proc struct and change your mbid
  46.         goto dosleep
  47.         else
  48.         fill in his PID in your data space
  49.         return 0;
  50.         endif
  51.     else
  52.                        (YOU HAVE TO BLOCK WAITING FOR A WRITER)
  53.         copy from your data space to your proc structure
  54.         goto dosleep
  55.     endif
  56.     else (you're reading)
  57.     if (there's somebody waiting to write)
  58.                         (SATISFY THE RENDEZVOUS RIGHT AWAY)
  59.         copy msg from his proc to your data space
  60.         if (he's doing a write/read)
  61.                      (TURN HIM AROUND WAITING FOR A WRITER)
  62.         change his mode from write/read to read, and his mbid
  63.         leave him asleep
  64.         else
  65.         wake him up
  66.         endif
  67.         return 0;
  68.     else
  69.                        (YOU HAVE TO BLOCK WAITING FOR A READER)
  70.         write mode 0 to your proc struct
  71.         goto dosleep
  72.     endif
  73.     endif
  74.  
  75. dosleep:
  76.     if (non-blocking mode) return EWOULDBLOCK;
  77.     do {
  78.         sleep(WAIT_Q, WAIT_MB);
  79.     } until (a rendezvous happens);
  80.     (when you wake up...)
  81.     if (you're reading)
  82.     copy from your proc struct to your data space
  83.     else
  84.     copy the writer's PID from your proc struct to your data space
  85.     endif
  86.     return 0;
  87.  
  88.  * The wait_cond we use is WAIT_MB, and the mbid waited for is another
  89.  * field in the proc structure, along with the two longs and a short you
  90.  * need to remember what somebody else wrote to you, and the mode field for
  91.  * what kind of operation you're doing (read, write, write/read).
  92.  *
  93.  * The beauty of this is that mailboxes don't need to be created or
  94.  * destroyed, and the blocking and turnaround are atomic, and it's a way to
  95.  * do rendezvous and interprocess communication without lots of system
  96.  * calls (Pgetpid, Fwrite, Pause, plus synchronization worries about having
  97.  * the reader get the message and Pkill you before you've Paused).
  98.  *
  99.  * Note: Say PID 33 writes in mode 2 to MBXX and it blocks. Then somebody
  100.  * writes to PD33, and blocks because you're not waiting for it.  Then your
  101.  * write is satisfied, so you become a reader on PD33.  We should check to
  102.  * see if there are any writers on PD33 and satisfy that rendezvous! But
  103.  * this could go on forever, and it's easier to stop here.  So you lose:
  104.  * this situation is timing sensitive.  It shouldn't come up anyway.
  105.  */
  106.  
  107. #include "mint.h"
  108.  
  109. long
  110. p_msg(mode, mbid, ptr)
  111.     int mode;
  112.     long mbid;
  113.     char *ptr;
  114. {
  115.     int noblock;
  116.     PROC *p;
  117.  
  118.     TRACE("Pmsg(%d,%lx,%lx)",mode,mbid,ptr);
  119.  
  120.     noblock = (mode & 0x8000);
  121.     mode &= ~0x8000;
  122.  
  123.     if (mode == 0) {
  124.     /* read */
  125.     /* walk the whole process list looking for a writer */
  126.     for (p = proclist; p; p = p->gl_next) {
  127.         if ((p->mb_mode == 1 || p->mb_mode == 2) && p->mb_mbid == mbid) {
  128.         /* this process is trying to write this mbox */
  129. /*        DEBUG("Pmsg read: found a writer (pid %d)",p->pid); */
  130.         goto got_rendezvous;
  131.         }
  132.     }
  133. /*    DEBUG("Pmsg read: no writers"); */
  134.     /* nobody is writing just now */
  135.     goto dosleep;
  136.     }
  137.     else if (mode == 1 || mode == 2) {
  138.     /* write, or write/read */
  139.     /* walk the whole process list looking for a reader */
  140.     for (p = proclist; p; p = p->gl_next) {
  141.         if (p->mb_mode == 0 && p->mb_mbid == mbid) {
  142.         /* this process is reading this mbox */
  143. /*        DEBUG("Pmsg write: found a reader (pid %d)",p->pid); */
  144.         goto got_rendezvous;
  145.         }
  146.     }
  147.     /* nobody is reading just now */
  148.     curproc->mb_long1 = *(long *)ptr;    /* copy the message */
  149.     curproc->mb_long2 = *(long *)(ptr+4);    /* into my proc struct */
  150. /*    DEBUG("Pmsg write: no readers"); */
  151.     goto dosleep;
  152.     }
  153.     else return EINVFN;            /* invalid mode */
  154.  
  155. /*
  156.  * we get here if we actually have a rendezvous between p and curproc.
  157.  */
  158.  
  159. got_rendezvous:
  160.     if (!mode) {
  161.     /* curproc is reading */
  162.     *(long *)(ptr) = p->mb_long1;        /* copy the message */
  163.     *(long *)(ptr+4) = p->mb_long2;        /* from his proc struct */
  164.     *(short *)(ptr+8) = p->pid;        /* provide the PID */
  165.  
  166.     if (p->mb_mode == 2) {
  167.         /*
  168.          * The blocked write was in mode 2: writer becomes a reader and
  169.          * stays on WAIT_Q waiting on WAIT_MB.
  170.          */
  171. /*        DEBUG("Pmsg: previously-blocked writer becomes reader"); */
  172.         p->mb_mbid = 0xFFFF0000L | p->pid;
  173.         p->mb_mode = 0;
  174.     }
  175.     else {
  176.         /* The blocked write was in mode 1: writer wakes up */
  177. /*        DEBUG("Pmsg: previously-blocked writer put on run queue"); */
  178.         p->mb_writer = curproc->pid;    /* tell writer reader's pid */
  179.         p->mb_mode = -1;            /* mark rendezvous */
  180.         p->wait_cond = 0;            /* not necessary? */
  181.         if (p->wait_q != READY_Q) {
  182.         /* wake up the writer if it is sleeping */
  183.         rm_q(p->wait_q,p);
  184.         add_q(READY_Q,p);
  185.         }
  186.     }
  187.     return 0;
  188.     }
  189.     else {
  190.     /* curproc is writing */
  191.     p->mb_writer = curproc->pid;        /* provide the PID */
  192.     p->mb_long1 = *(long *)(ptr);        /* copy the message */
  193.     p->mb_long2 = *(long *)(ptr+4);
  194. /*     DEBUG("Pmsg: previously-blocked reader put on run queue"); */
  195.     p->mb_mode = -1;            /* mark rendezvous */
  196.     p->wait_cond = 0;            /* not necessary? */
  197.     if (p->wait_q != READY_Q) {
  198.         /* wake up the reader if it is sleeping */
  199.         rm_q(p->wait_q,p);
  200.         add_q(READY_Q,p);
  201.     }
  202.     if (mode == 2) {
  203.         /* now curproc becomes a reader */
  204. /*         DEBUG("Pmsg: curproc (writer) becomes reader & blocks"); */
  205.         mbid = 0xFFFF0000L | curproc->pid;
  206.         mode = 0;
  207.         goto dosleep;
  208.     }
  209.     else {
  210.         *(short *)(ptr+8) = p->pid;        /* tell reader writer's pid */
  211.         return 0;
  212.     }
  213.     }
  214.  
  215. /*
  216.  * we get here when a read or write should block because there's nobody
  217.  * already waiting at the other end.
  218.  */
  219.  
  220. dosleep: 
  221.     if (noblock) {
  222. /*        DEBUG("Pmsg: non-blocking mode: EWOULDBLOCK"); */
  223.         return -1L;
  224.     }
  225. /*    DEBUG("Pmsg: put curproc to sleep."); */
  226.     curproc->mb_mbid = mbid;    /* and ID waited for */
  227.     curproc->mb_mode = mode;    /* save mode */
  228.  
  229. /*
  230.  * OK: now we sleep until a rendezvous has occured. The loop is because we
  231.  * may be woken up to deal with signals before the rendezvous.  The thing
  232.  * that says the rendezvous actually happened is that mb_mode got changed
  233.  * to -1.
  234.  */
  235.     do {
  236.         sleep(WAIT_Q, WAIT_MB);            /* block */
  237.     } while (curproc->mb_mode != -1);
  238. /*
  239.     DEBUG("Pmsg: woke up; mode %d cpmode %d writer %d long1 %lx long2 %lx",
  240.         mode,curproc->mb_mode,curproc->mb_writer,
  241.         curproc->mb_long1, curproc->mb_long2);
  242. */
  243.  
  244.     /*
  245.      * When we wake up, we transfer the message from our proc struct
  246.      * into our message pointer space if we were reading, and do
  247.      * nothing if we were writing.  The write/read case is taken care
  248.      * of without waking this process up at all.
  249.      *
  250.      * Check curproc->mode because it may not be the mode we started
  251.      * with any more.
  252.      */
  253.     if (mode == 0) {
  254.         *(long *)(ptr) = curproc->mb_long1;
  255.         *(long *)(ptr+4) = curproc->mb_long2;
  256.     }
  257.     /* in any case, copy the 'writer' field because that's the PID */
  258.     /* of the other side of the rendezvous */
  259.     *(short *)(ptr+8) = curproc->mb_writer;
  260.     return 0;
  261. }
  262.  
  263. /*
  264.  * more mutex: this time a semaphore.
  265.  *
  266.  * long Psemaphore(short mode, long id, long timeout)
  267.  *
  268.  *    MODE    ACTION
  269.  *      0    Create and get a semaphore with the given ID.
  270.  *      1    Destroy.
  271.  *      2    Get (blocks until it's available or destroyed, or timeout).
  272.  *      3    Release.
  273.  *
  274.  * RETURNS
  275.  *
  276.  *    CODE    MEANING
  277.  *      0    OK.  Created/obtained/released/destroyed, depending on mode.
  278.  *    ERROR    You asked for a semaphore that you already own.
  279.  *    ERANGE    That semaphore doesn't exist (modes 1, 2, 3, 4),
  280.  *        or out of slots for new semaphores (0).
  281.  *    EACCDN    That semaphore exists already, so you can't create it (mode 0),
  282.  *        or the semaphore is busy (returned from mode 3 if you lose),
  283.  *        or you don't own it (modes 1 and 4).
  284.  *
  285.  * If you create a semaphore you will also own it, so you have to release
  286.  * it with mode 2 before anybody else can get it.  Semaphore ID's are magic
  287.  * numbers which the creator should make available to the users. You have
  288.  * to own a semaphore to destroy it.  If you block waiting for a semaphore,
  289.  * and then it gets destroyed, you get ERANGE.
  290.  *
  291.  * The timeout argument is ignored except in mode 2.  In that mode, 0 means
  292.  * "return immediately" and, as a special case, -1 means "forever."
  293.  * Since it is in fact an unsigned long number of milliseconds, -2 means
  294.  * 49 hours, but here -1 truly means forever.
  295.  *
  296.  */
  297.  
  298. #define NSEMAPHORES 10
  299.  
  300. typedef struct sema {
  301.     struct sema *next;
  302.     long id;
  303.     short owner;    /* -1 means "available" */
  304. } SEMA;
  305.  
  306. static SEMA *semalist;
  307.  
  308. static void unsemame P_((PROC *));
  309.  
  310. static void
  311. unsemame(p)
  312. PROC *p;
  313. {
  314.     /* take process P off the WAIT_Q and put it on the READY_Q */
  315.     TRACE("semaphore sleep ends for pid %d",p->pid);
  316.     if (p->wait_q == WAIT_Q) {
  317.         rm_q(WAIT_Q,p);
  318.         add_q(READY_Q,p);
  319.     }
  320.     p->wait_cond = 0;
  321. }
  322.  
  323. long
  324. p_semaphore(mode,id,timeout)
  325. int mode;
  326. long id;
  327. long timeout;
  328. {
  329.     SEMA *s, **q;
  330.     TIMEOUT *timeout_ptr = NULL;
  331.  
  332.     TRACE("Psemaphore(%d,%lx)",mode,id);
  333.  
  334.     switch (mode) {
  335.     case 0: {            /* create */
  336.         for (s = semalist; s; s = s->next) {
  337.         if (s->id == id) {
  338.             DEBUG("Psemaphore(%d,%lx): already exists",mode,id);
  339.             return EACCDN;
  340.         }
  341.         }
  342.         /* get a new one */
  343.         if (!(s = (SEMA *)kmalloc(sizeof(SEMA)))) return ENSMEM;
  344.         s->id = id;
  345.         s->owner = curproc->pid;
  346.         s->next = semalist;
  347.         semalist = s;
  348.         return E_OK;
  349.     }
  350.     case 2: {            /* get */
  351. loop:
  352.         for (s = semalist; s; s = s->next) {
  353.         if (s->id == id) {
  354.             /* found your semaphore */
  355.             if (s->owner == curproc->pid) {
  356.                 DEBUG("Psemaphore(%d,%lx): curproc already owns it!",
  357.                     mode,id);
  358.                 return ERROR;
  359.             }
  360.             if (s->owner == -1) {
  361.             /* it's free; you get it */
  362.             s->owner = curproc->pid;
  363.             if (timeout_ptr) canceltimeout(timeout_ptr);
  364.             return 0;
  365.             }
  366.             else {
  367.             /* not free */
  368.             if (timeout == 0) {
  369.                 /* non-blocking mode */
  370.                 return EACCDN;
  371.             }
  372.             else {
  373.                 if (timeout != -1 && !timeout_ptr) {
  374.                 /* schedule a timeout */
  375.                 timeout_ptr = addtimeout(timeout,unsemame);
  376.                 }
  377.                 /* block until it's released, then try again */
  378.                 sleep(WAIT_Q,WAIT_SEMA);
  379.                 if (curproc->wait_cond != WAIT_SEMA) {
  380.                 TRACE("Psemaphore(%d,%lx) timed out",mode,id);
  381.                 return EACCDN;
  382.                 }
  383.                 goto loop;
  384.             }
  385.             }
  386.         }
  387.         }
  388.         /* no such semaphore (possibly deleted while we were waiting) */
  389.         if (timeout_ptr) canceltimeout(timeout_ptr);
  390.         DEBUG("Psemaphore(%d,%lx): no such semaphore",mode,id);
  391.         return ERANGE;
  392.     }
  393.     case 3:                /* release */
  394.     case 1: {            /* destroy */
  395.         /*
  396.          * Style note: this is a handy way to chain down a linked list.
  397.          * q follows along behind s pointing at the "next" field of the
  398.          * previous list element.  To start, q points to the semalist
  399.          * variable itself.  At all times, then, *q is the place to
  400.          * write s->next when you want to remove an element 's' from
  401.          * the list.
  402.          */
  403.         for (s = *(q = &semalist); s; s = *(q = &s->next)) {
  404.         if (s->id == id) {
  405.             /* found your semaphore */
  406.             if (s->owner != curproc->pid) {
  407.             DEBUG("Psemaphore(%d,%lx): you don't own it",mode,id);
  408.             return EACCDN;
  409.             }
  410.             else {
  411.             if (mode == 3) s->owner = -1;    /* make it free, or */
  412.             else *q = s->next;        /* delete from list */
  413.  
  414.             /* wake up anybody who's waiting for a semaphore */
  415.             wake(WAIT_Q,WAIT_SEMA);
  416.             return E_OK;
  417.             }
  418.         }
  419.         }
  420.         /* no such semaphore */
  421.         DEBUG("Psemaphore(%d,%lx): no such semaphore",mode,id);
  422.         return ERANGE;
  423.     }
  424.     default: {            /* invalid mode argument */
  425.         DEBUG("Psemaphore(%d,%lx): invalid mode",mode,id);
  426.         return EINVFN;
  427.     }
  428.     }
  429. }
  430.  
  431. /*
  432.  * Release all semaphores owned by the process with process id "pid".
  433.  * The semaphores are only released, not destroyed. This function
  434.  * is called from terminate() in dos_mem.c during process termination.
  435.  */
  436.  
  437. void
  438. free_semaphores(pid)
  439.     int pid;
  440. {
  441.     SEMA *s, **q;
  442.     int didsomething = 0;        /* did any semaphores get freed? */
  443.  
  444.     for (s = *(q = &semalist); s; s = *(q = &s->next)) {
  445.         if (s->owner == pid) {
  446.             s->owner = -1;        /* mark the semaphore as free */
  447.             didsomething = 1;
  448.         }
  449.     }
  450.     if (didsomething) {
  451.     /* wake up anybody waiting for a semaphore, in case it's one of
  452.      * the ones we just freed
  453.      */
  454.         wake(WAIT_Q, WAIT_SEMA);
  455.     }
  456. }
  457.