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 >
Wrap
C/C++ Source or Header
|
1987-04-22
|
14KB
|
457 lines
/*
* 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
p_msg(mode, mbid, ptr)
int mode;
long mbid;
char *ptr;
{
int noblock;
PROC *p;
TRACE("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 */
/* DEBUG("Pmsg read: found a writer (pid %d)",p->pid); */
goto got_rendezvous;
}
}
/* DEBUG("Pmsg read: no writers"); */
/* 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 */
/* DEBUG("Pmsg write: found a reader (pid %d)",p->pid); */
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 */
/* DEBUG("Pmsg write: no readers"); */
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.
*/
/* DEBUG("Pmsg: previously-blocked writer becomes reader"); */
p->mb_mbid = 0xFFFF0000L | p->pid;
p->mb_mode = 0;
}
else {
/* The blocked write was in mode 1: writer wakes up */
/* DEBUG("Pmsg: previously-blocked writer put on run queue"); */
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) {
/* wake up the writer if it is sleeping */
rm_q(p->wait_q,p);
add_q(READY_Q,p);
}
}
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);
/* DEBUG("Pmsg: previously-blocked reader put on run queue"); */
p->mb_mode = -1; /* mark rendezvous */
p->wait_cond = 0; /* not necessary? */
if (p->wait_q != READY_Q) {
/* wake up the reader if it is sleeping */
rm_q(p->wait_q,p);
add_q(READY_Q,p);
}
if (mode == 2) {
/* now curproc becomes a reader */
/* DEBUG("Pmsg: curproc (writer) becomes reader & blocks"); */
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) {
/* DEBUG("Pmsg: non-blocking mode: EWOULDBLOCK"); */
return -1L;
}
/* DEBUG("Pmsg: put curproc to sleep."); */
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);
/*
DEBUG("Pmsg: woke up; mode %d cpmode %d writer %d long1 %lx long2 %lx",
mode,curproc->mb_mode,curproc->mb_writer,
curproc->mb_long1, curproc->mb_long2);
*/
/*
* 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) {
rm_q(WAIT_Q,p);
add_q(READY_Q,p);
}
p->wait_cond = 0;
}
long
p_semaphore(mode,id,timeout)
int mode;
long id;
long timeout;
{
SEMA *s, **q;
TIMEOUT *timeout_ptr = NULL;
TRACE("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)))) 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 */
/* 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, **q;
int didsomething = 0; /* did any semaphores get freed? */
for (s = *(q = &semalist); s; s = *(q = &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);
}
}