home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1991, 1992 Eric R. Smith
- * Copyright 1992 Atari Corporation.
- * All rights reserved.
- */
-
- /*
- * The rendezvous:
- * a little bit of message passing with a lot of synchronization.
- *
- * Pmsg(mode,mboxid,msgptr);
- * short mode;
- * long mboxid;
- * struct { long msg1, msg2; short pid; } *msgptr;
- *
- * 'mode' bits:
- * 0000 read
- * 0001 write
- * 0002 write, then read from mboxid "PDxx" where xx is my pid.
- *
- * 8000 OR with this bit to make the operation non-blocking.
- *
- * The messages are five words long: two longs and a short, in that order.
- * The values of the first two longs are totally up to the processes in
- * question. The value of the short is the PID of the sender. On return
- * from writes, the short is the PID of the process that read your message.
- * On return from reads, it's the PID of the writer.
- *
- * If the 0x8000 bit is set in the mode, and there is not a reader/writer
- * for the mboxid already on the WAIT_Q, this call returns -1.
- *
- * In mode 2, the writer is declaring that it wants to wait for a reply to
- * the message. What happens is that the reader gets put on the ready
- * queue, but the writer is atomically turned into a reader on a mailbox
- * whose mboxid is (0xFFFF0000 .OR. pid). The idea is that this process
- * will sleep until awoken at a later time by the process that read the
- * message. The process reading the original request is guaranteed not to
- * block when writing the reply.
- *
- * There is no provision for a timeout.
-
- The flow is as follows:
-
- if (you're writing)
- if (there's somebody waiting to read)
- (SATISFY THE RENDEZVOUS RIGHT AWAY)
- copy msg from your data space into his proc structure
- wake him up
- if (you're doing a write/read)
- (TURN AROUND AND BLOCK WAITING TO READ)
- write mode 0 to your proc struct and change your mbid
- goto dosleep
- else
- fill in his PID in your data space
- return 0;
- endif
- else
- (YOU HAVE TO BLOCK WAITING FOR A WRITER)
- copy from your data space to your proc structure
- goto dosleep
- endif
- else (you're reading)
- if (there's somebody waiting to write)
- (SATISFY THE RENDEZVOUS RIGHT AWAY)
- copy msg from his proc to your data space
- if (he's doing a write/read)
- (TURN HIM AROUND WAITING FOR A WRITER)
- change his mode from write/read to read, and his mbid
- leave him asleep
- else
- wake him up
- endif
- return 0;
- else
- (YOU HAVE TO BLOCK WAITING FOR A READER)
- write mode 0 to your proc struct
- goto dosleep
- endif
- endif
-
- dosleep:
- if (non-blocking mode) return EWOULDBLOCK;
- do {
- sleep(WAIT_Q, WAIT_MB);
- } until (a rendezvous happens);
- (when you wake up...)
- if (you're reading)
- copy from your proc struct to your data space
- else
- copy the writer's PID from your proc struct to your data space
- endif
- return 0;
-
- * The wait_cond we use is WAIT_MB, and the mbid waited for is another
- * field in the proc structure, along with the two longs and a short you
- * need to remember what somebody else wrote to you, and the mode field for
- * what kind of operation you're doing (read, write, write/read).
- *
- * The beauty of this is that mailboxes don't need to be created or
- * destroyed, and the blocking and turnaround are atomic, and it's a way to
- * do rendezvous and interprocess communication without lots of system
- * calls (Pgetpid, Fwrite, Pause, plus synchronization worries about having
- * the reader get the message and Pkill you before you've Paused).
- *
- * Note: Say PID 33 writes in mode 2 to MBXX and it blocks. Then somebody
- * writes to PD33, and blocks because you're not waiting for it. Then your
- * write is satisfied, so you become a reader on PD33. We should check to
- * see if there are any writers on PD33 and satisfy that rendezvous! But
- * this could go on forever, and it's easier to stop here. So you lose:
- * this situation is timing sensitive. It shouldn't come up anyway.
- */
-
- #include "mint.h"
-
- long ARGS_ON_STACK
- p_msg(mode, mbid, ptr)
- int mode;
- long mbid;
- char *ptr;
- {
- int noblock;
- PROC *p;
-
- TRACELOW(("Pmsg(%d,%lx,%lx)",mode,mbid,ptr));
-
- noblock = (mode & 0x8000);
- mode &= ~0x8000;
-
- if (mode == 0) {
- /* read */
- /* walk the whole process list looking for a writer */
- for (p = proclist; p; p = p->gl_next) {
- if ((p->mb_mode == 1 || p->mb_mode == 2) && p->mb_mbid == mbid) {
- /* this process is trying to write this mbox */
- goto got_rendezvous;
- }
- }
- /* nobody is writing just now */
- goto dosleep;
- }
- else if (mode == 1 || mode == 2) {
- /* write, or write/read */
- /* walk the whole process list looking for a reader */
- for (p = proclist; p; p = p->gl_next) {
- if (p->mb_mode == 0 && p->mb_mbid == mbid) {
- /* this process is reading this mbox */
- goto got_rendezvous;
- }
- }
- /* nobody is reading just now */
- curproc->mb_long1 = *(long *)ptr; /* copy the message */
- curproc->mb_long2 = *(long *)(ptr+4); /* into my proc struct */
- goto dosleep;
- }
- else return EINVFN; /* invalid mode */
-
- /*
- * we get here if we actually have a rendezvous between p and curproc.
- */
-
- got_rendezvous:
- if (!mode) {
- /* curproc is reading */
- *(long *)(ptr) = p->mb_long1; /* copy the message */
- *(long *)(ptr+4) = p->mb_long2; /* from his proc struct */
- *(short *)(ptr+8) = p->pid; /* provide the PID */
-
- if (p->mb_mode == 2) {
- /*
- * The blocked write was in mode 2: writer becomes a reader and
- * stays on WAIT_Q waiting on WAIT_MB.
- */
- p->mb_mbid = 0xFFFF0000L | p->pid;
- p->mb_mode = 0;
- }
- else {
- /* The blocked write was in mode 1: writer wakes up */
- p->mb_writer = curproc->pid; /* tell writer reader's pid */
- p->mb_mode = -1; /* mark rendezvous */
- p->wait_cond = 0; /* not necessary? */
- if (p->wait_q != READY_Q) {
- short sr = spl7();
- /* wake up the writer if it is sleeping */
- rm_q(p->wait_q,p);
- add_q(READY_Q,p);
- spl(sr);
- }
- }
- return 0;
- }
- else {
- /* curproc is writing */
- p->mb_writer = curproc->pid; /* provide the PID */
- p->mb_long1 = *(long *)(ptr); /* copy the message */
- p->mb_long2 = *(long *)(ptr+4);
- p->mb_mode = -1; /* mark rendezvous */
- p->wait_cond = 0; /* not necessary? */
- if (p->wait_q != READY_Q) {
- short sr = spl7();
- /* wake up the reader if it is sleeping */
- rm_q(p->wait_q,p);
- add_q(READY_Q,p);
- spl(sr);
- }
- if (mode == 2) {
- /* now curproc becomes a reader */
- mbid = 0xFFFF0000L | curproc->pid;
- mode = 0;
- goto dosleep;
- }
- else {
- *(short *)(ptr+8) = p->pid; /* tell reader writer's pid */
- return 0;
- }
- }
-
- /*
- * we get here when a read or write should block because there's nobody
- * already waiting at the other end.
- */
-
- dosleep:
- if (noblock) {
- return -1L;
- }
- curproc->mb_mbid = mbid; /* and ID waited for */
- curproc->mb_mode = mode; /* save mode */
-
- /*
- * OK: now we sleep until a rendezvous has occured. The loop is because we
- * may be woken up to deal with signals before the rendezvous. The thing
- * that says the rendezvous actually happened is that mb_mode got changed
- * to -1.
- */
- do {
- sleep(WAIT_Q, WAIT_MB); /* block */
- } while (curproc->mb_mode != -1);
-
- /*
- * When we wake up, we transfer the message from our proc struct
- * into our message pointer space if we were reading, and do
- * nothing if we were writing. The write/read case is taken care
- * of without waking this process up at all.
- *
- * Check curproc->mode because it may not be the mode we started
- * with any more.
- */
- if (mode == 0) {
- *(long *)(ptr) = curproc->mb_long1;
- *(long *)(ptr+4) = curproc->mb_long2;
- }
- /* in any case, copy the 'writer' field because that's the PID */
- /* of the other side of the rendezvous */
- *(short *)(ptr+8) = curproc->mb_writer;
- return 0;
- }
-
- /*
- * more mutex: this time a semaphore.
- *
- * long Psemaphore(short mode, long id, long timeout)
- *
- * MODE ACTION
- * 0 Create and get a semaphore with the given ID.
- * 1 Destroy.
- * 2 Get (blocks until it's available or destroyed, or timeout).
- * 3 Release.
- *
- * RETURNS
- *
- * CODE MEANING
- * 0 OK. Created/obtained/released/destroyed, depending on mode.
- * ERROR You asked for a semaphore that you already own.
- * ERANGE That semaphore doesn't exist (modes 1, 2, 3, 4),
- * or out of slots for new semaphores (0).
- * EACCDN That semaphore exists already, so you can't create it (mode 0),
- * or the semaphore is busy (returned from mode 3 if you lose),
- * or you don't own it (modes 1 and 4).
- *
- * If you create a semaphore you will also own it, so you have to release
- * it with mode 2 before anybody else can get it. Semaphore ID's are magic
- * numbers which the creator should make available to the users. You have
- * to own a semaphore to destroy it. If you block waiting for a semaphore,
- * and then it gets destroyed, you get ERANGE.
- *
- * The timeout argument is ignored except in mode 2. In that mode, 0 means
- * "return immediately" and, as a special case, -1 means "forever."
- * Since it is in fact an unsigned long number of milliseconds, -2 means
- * 49 hours, but here -1 truly means forever.
- *
- */
-
- #define NSEMAPHORES 10
-
- typedef struct sema {
- struct sema *next;
- long id;
- short owner; /* -1 means "available" */
- } SEMA;
-
- static SEMA *semalist;
-
- static void unsemame P_((PROC *));
-
- static void
- unsemame(p)
- PROC *p;
- {
- /* take process P off the WAIT_Q and put it on the READY_Q */
- TRACE(("semaphore sleep ends for pid %d",p->pid));
- if (p->wait_q == WAIT_Q) {
- short sr = spl7();
- rm_q(WAIT_Q,p);
- add_q(READY_Q,p);
- spl(sr);
- }
- p->wait_cond = 0;
- }
-
- long ARGS_ON_STACK
- p_semaphore(mode,id,timeout)
- int mode;
- long id;
- long timeout;
- {
- SEMA *s, **q;
- TIMEOUT *timeout_ptr = NULL;
-
- TRACELOW(("Psemaphore(%d,%lx)",mode,id));
-
- switch (mode) {
- case 0: { /* create */
- for (s = semalist; s; s = s->next) {
- if (s->id == id) {
- DEBUG(("Psemaphore(%d,%lx): already exists",mode,id));
- return EACCDN;
- }
- }
- /* get a new one */
- if ((s = (SEMA *)kmalloc(sizeof(SEMA))) == 0)
- return ENSMEM;
- s->id = id;
- s->owner = curproc->pid;
- s->next = semalist;
- semalist = s;
- return E_OK;
- }
- case 2: { /* get */
- loop:
- for (s = semalist; s; s = s->next) {
- if (s->id == id) {
- /* found your semaphore */
- if (s->owner == curproc->pid) {
- DEBUG(("Psemaphore(%d,%lx): curproc already owns it!",
- mode,id));
- return ERROR;
- }
- if (s->owner == -1) {
- /* it's free; you get it */
- s->owner = curproc->pid;
- if (timeout_ptr) canceltimeout(timeout_ptr);
- return 0;
- }
- else {
- /* not free */
- if (timeout == 0) {
- /* non-blocking mode */
- return EACCDN;
- }
- else {
- if (timeout != -1 && !timeout_ptr) {
- /* schedule a timeout */
- timeout_ptr = addtimeout(timeout,unsemame);
- }
- /* block until it's released, then try again */
- sleep(WAIT_Q,WAIT_SEMA);
- if (curproc->wait_cond != WAIT_SEMA) {
- TRACE(("Psemaphore(%d,%lx) timed out",mode,id));
- return EACCDN;
- }
- goto loop;
- }
- }
- }
- }
- /* no such semaphore (possibly deleted while we were waiting) */
- if (timeout_ptr) canceltimeout(timeout_ptr);
- DEBUG(("Psemaphore(%d,%lx): no such semaphore",mode,id));
- return ERANGE;
- }
- case 3: /* release */
- case 1: { /* destroy */
- /*
- * Style note: this is a handy way to chain down a linked list.
- * q follows along behind s pointing at the "next" field of the
- * previous list element. To start, q points to the semalist
- * variable itself. At all times, then, *q is the place to
- * write s->next when you want to remove an element 's' from
- * the list.
- */
- for (s = *(q = &semalist); s; s = *(q = &s->next)) {
- if (s->id == id) {
- /* found your semaphore */
- if (s->owner != curproc->pid) {
- DEBUG(("Psemaphore(%d,%lx): you don't own it",mode,id));
- return EACCDN;
- }
- else {
- if (mode == 3) {
- s->owner = -1; /* make it free, or */
- } else {
- *q = s->next; /* delete from list */
- kfree(s); /* and free it */
- }
- /* wake up anybody who's waiting for a semaphore */
- wake(WAIT_Q,WAIT_SEMA);
- return E_OK;
- }
- }
- }
- /* no such semaphore */
- DEBUG(("Psemaphore(%d,%lx): no such semaphore",mode,id));
- return ERANGE;
- }
- default: { /* invalid mode argument */
- DEBUG(("Psemaphore(%d,%lx): invalid mode",mode,id));
- return EINVFN;
- }
- }
- }
-
- /*
- * Release all semaphores owned by the process with process id "pid".
- * The semaphores are only released, not destroyed. This function
- * is called from terminate() in dos_mem.c during process termination.
- */
-
- void
- free_semaphores(pid)
- int pid;
- {
- SEMA *s;
- int didsomething = 0; /* did any semaphores get freed? */
-
- for (s = semalist; s; s = s->next) {
- if (s->owner == pid) {
- s->owner = -1; /* mark the semaphore as free */
- didsomething = 1;
- }
- }
- if (didsomething) {
- /* wake up anybody waiting for a semaphore, in case it's one of
- * the ones we just freed
- */
- wake(WAIT_Q, WAIT_SEMA);
- }
- }
-