|
|
1.1 root 1: /*
2:
3: * The rendezvous:
4:
5: * a little bit of message passing with a lot of synchronization.
6:
7: *
8:
9: * Pmsg(mode,mboxid,msgptr);
10:
11: * short mode;
12:
13: * long mboxid;
14:
15: * struct { long msg1, msg2; short pid; } *msgptr;
16:
17: *
18:
19: * 'mode' bits:
20:
21: * 0000 read
22:
23: * 0001 write
24:
25: * 0002 write, then read from mboxid "PDxx" where xx is my pid.
26:
27: *
28:
29: * 8000 OR with this bit to make the operation non-blocking.
30:
31: *
32:
33: * The messages are five words long: two longs and a short, in that order.
34:
35: * The values of the first two longs are totally up to the processes in
36:
37: * question. The value of the short is the PID of the sender. On return
38:
39: * from writes, the short is the PID of the process that read your message.
40:
41: * On return from reads, it's the PID of the writer.
42:
43: *
44:
45: * If the 0x8000 bit is set in the mode, and there is not a reader/writer
46:
47: * for the mboxid already on the WAIT_Q, this call returns -1.
48:
49: *
50:
51: * In mode 2, the writer is declaring that it wants to wait for a reply to
52:
53: * the message. What happens is that the reader gets put on the ready
54:
55: * queue, but the writer is atomically turned into a reader on a mailbox
56:
57: * whose mboxid is (0xFFFF0000 .OR. pid). The idea is that this process
58:
59: * will sleep until awoken at a later time by the process that read the
60:
61: * message. The process reading the original request is guaranteed not to
62:
63: * block when writing the reply.
64:
65: *
66:
67: * There is no provision for a timeout.
68:
69:
70:
71: The flow is as follows:
72:
73:
74:
75: if (you're writing)
76:
77: if (there's somebody waiting to read)
78:
79: (SATISFY THE RENDEZVOUS RIGHT AWAY)
80:
81: copy msg from your data space into his proc structure
82:
83: wake him up
84:
85: if (you're doing a write/read)
86:
87: (TURN AROUND AND BLOCK WAITING TO READ)
88:
89: write mode 0 to your proc struct and change your mbid
90:
91: goto dosleep
92:
93: else
94:
95: fill in his PID in your data space
96:
97: return 0;
98:
99: endif
100:
101: else
102:
103: (YOU HAVE TO BLOCK WAITING FOR A WRITER)
104:
105: copy from your data space to your proc structure
106:
107: goto dosleep
108:
109: endif
110:
111: else (you're reading)
112:
113: if (there's somebody waiting to write)
114:
115: (SATISFY THE RENDEZVOUS RIGHT AWAY)
116:
117: copy msg from his proc to your data space
118:
119: if (he's doing a write/read)
120:
121: (TURN HIM AROUND WAITING FOR A WRITER)
122:
123: change his mode from write/read to read, and his mbid
124:
125: leave him asleep
126:
127: else
128:
129: wake him up
130:
131: endif
132:
133: return 0;
134:
135: else
136:
137: (YOU HAVE TO BLOCK WAITING FOR A READER)
138:
139: write mode 0 to your proc struct
140:
141: goto dosleep
142:
143: endif
144:
145: endif
146:
147:
148:
149: dosleep:
150:
151: if (non-blocking mode) return EWOULDBLOCK;
152:
153: do {
154:
155: sleep(WAIT_Q, WAIT_MB);
156:
157: } until (a rendezvous happens);
158:
159: (when you wake up...)
160:
161: if (you're reading)
162:
163: copy from your proc struct to your data space
164:
165: else
166:
167: copy the writer's PID from your proc struct to your data space
168:
169: endif
170:
171: return 0;
172:
173:
174:
175: * The wait_cond we use is WAIT_MB, and the mbid waited for is another
176:
177: * field in the proc structure, along with the two longs and a short you
178:
179: * need to remember what somebody else wrote to you, and the mode field for
180:
181: * what kind of operation you're doing (read, write, write/read).
182:
183: *
184:
185: * The beauty of this is that mailboxes don't need to be created or
186:
187: * destroyed, and the blocking and turnaround are atomic, and it's a way to
188:
189: * do rendezvous and interprocess communication without lots of system
190:
191: * calls (Pgetpid, Fwrite, Pause, plus synchronization worries about having
192:
193: * the reader get the message and Pkill you before you've Paused).
194:
195: *
196:
197: * Note: Say PID 33 writes in mode 2 to MBXX and it blocks. Then somebody
198:
199: * writes to PD33, and blocks because you're not waiting for it. Then your
200:
201: * write is satisfied, so you become a reader on PD33. We should check to
202:
203: * see if there are any writers on PD33 and satisfy that rendezvous! But
204:
205: * this could go on forever, and it's easier to stop here. So you lose:
206:
207: * this situation is timing sensitive. It shouldn't come up anyway.
208:
209: */
210:
211:
212:
213: #include "mint.h"
214:
215:
216:
1.1.1.2 ! root 217: long ARGS_ON_STACK
1.1 root 218:
219: p_msg(mode, mbid, ptr)
220:
221: int mode;
222:
223: long mbid;
224:
225: char *ptr;
226:
227: {
228:
229: int noblock;
230:
231: PROC *p;
232:
233:
234:
1.1.1.2 ! root 235: TRACE(("Pmsg(%d,%lx,%lx)",mode,mbid,ptr));
1.1 root 236:
237:
238:
239: noblock = (mode & 0x8000);
240:
241: mode &= ~0x8000;
242:
243:
244:
245: if (mode == 0) {
246:
247: /* read */
248:
249: /* walk the whole process list looking for a writer */
250:
251: for (p = proclist; p; p = p->gl_next) {
252:
253: if ((p->mb_mode == 1 || p->mb_mode == 2) && p->mb_mbid == mbid) {
254:
255: /* this process is trying to write this mbox */
256:
257: goto got_rendezvous;
258:
259: }
260:
261: }
262:
263: /* nobody is writing just now */
264:
265: goto dosleep;
266:
267: }
268:
269: else if (mode == 1 || mode == 2) {
270:
271: /* write, or write/read */
272:
273: /* walk the whole process list looking for a reader */
274:
275: for (p = proclist; p; p = p->gl_next) {
276:
277: if (p->mb_mode == 0 && p->mb_mbid == mbid) {
278:
279: /* this process is reading this mbox */
280:
281: goto got_rendezvous;
282:
283: }
284:
285: }
286:
287: /* nobody is reading just now */
288:
289: curproc->mb_long1 = *(long *)ptr; /* copy the message */
290:
291: curproc->mb_long2 = *(long *)(ptr+4); /* into my proc struct */
292:
293: goto dosleep;
294:
295: }
296:
297: else return EINVFN; /* invalid mode */
298:
299:
300:
301: /*
302:
303: * we get here if we actually have a rendezvous between p and curproc.
304:
305: */
306:
307:
308:
309: got_rendezvous:
310:
311: if (!mode) {
312:
313: /* curproc is reading */
314:
315: *(long *)(ptr) = p->mb_long1; /* copy the message */
316:
317: *(long *)(ptr+4) = p->mb_long2; /* from his proc struct */
318:
319: *(short *)(ptr+8) = p->pid; /* provide the PID */
320:
321:
322:
323: if (p->mb_mode == 2) {
324:
325: /*
326:
327: * The blocked write was in mode 2: writer becomes a reader and
328:
329: * stays on WAIT_Q waiting on WAIT_MB.
330:
331: */
332:
333: p->mb_mbid = 0xFFFF0000L | p->pid;
334:
335: p->mb_mode = 0;
336:
337: }
338:
339: else {
340:
341: /* The blocked write was in mode 1: writer wakes up */
342:
343: p->mb_writer = curproc->pid; /* tell writer reader's pid */
344:
345: p->mb_mode = -1; /* mark rendezvous */
346:
347: p->wait_cond = 0; /* not necessary? */
348:
349: if (p->wait_q != READY_Q) {
350:
351: /* wake up the writer if it is sleeping */
352:
353: rm_q(p->wait_q,p);
354:
355: add_q(READY_Q,p);
356:
357: }
358:
359: }
360:
361: return 0;
362:
363: }
364:
365: else {
366:
367: /* curproc is writing */
368:
369: p->mb_writer = curproc->pid; /* provide the PID */
370:
371: p->mb_long1 = *(long *)(ptr); /* copy the message */
372:
373: p->mb_long2 = *(long *)(ptr+4);
374:
375: p->mb_mode = -1; /* mark rendezvous */
376:
377: p->wait_cond = 0; /* not necessary? */
378:
379: if (p->wait_q != READY_Q) {
380:
381: /* wake up the reader if it is sleeping */
382:
383: rm_q(p->wait_q,p);
384:
385: add_q(READY_Q,p);
386:
387: }
388:
389: if (mode == 2) {
390:
391: /* now curproc becomes a reader */
392:
393: mbid = 0xFFFF0000L | curproc->pid;
394:
395: mode = 0;
396:
397: goto dosleep;
398:
399: }
400:
401: else {
402:
403: *(short *)(ptr+8) = p->pid; /* tell reader writer's pid */
404:
405: return 0;
406:
407: }
408:
409: }
410:
411:
412:
413: /*
414:
415: * we get here when a read or write should block because there's nobody
416:
417: * already waiting at the other end.
418:
419: */
420:
421:
422:
423: dosleep:
424:
425: if (noblock) {
426:
427: return -1L;
428:
429: }
430:
431: curproc->mb_mbid = mbid; /* and ID waited for */
432:
433: curproc->mb_mode = mode; /* save mode */
434:
435:
436:
437: /*
438:
439: * OK: now we sleep until a rendezvous has occured. The loop is because we
440:
441: * may be woken up to deal with signals before the rendezvous. The thing
442:
443: * that says the rendezvous actually happened is that mb_mode got changed
444:
445: * to -1.
446:
447: */
448:
449: do {
450:
451: sleep(WAIT_Q, WAIT_MB); /* block */
452:
453: } while (curproc->mb_mode != -1);
454:
455:
456:
457: /*
458:
459: * When we wake up, we transfer the message from our proc struct
460:
461: * into our message pointer space if we were reading, and do
462:
463: * nothing if we were writing. The write/read case is taken care
464:
465: * of without waking this process up at all.
466:
467: *
468:
469: * Check curproc->mode because it may not be the mode we started
470:
471: * with any more.
472:
473: */
474:
475: if (mode == 0) {
476:
477: *(long *)(ptr) = curproc->mb_long1;
478:
479: *(long *)(ptr+4) = curproc->mb_long2;
480:
481: }
482:
483: /* in any case, copy the 'writer' field because that's the PID */
484:
485: /* of the other side of the rendezvous */
486:
487: *(short *)(ptr+8) = curproc->mb_writer;
488:
489: return 0;
490:
491: }
492:
493:
494:
495: /*
496:
497: * more mutex: this time a semaphore.
498:
499: *
500:
501: * long Psemaphore(short mode, long id, long timeout)
502:
503: *
504:
505: * MODE ACTION
506:
507: * 0 Create and get a semaphore with the given ID.
508:
509: * 1 Destroy.
510:
511: * 2 Get (blocks until it's available or destroyed, or timeout).
512:
513: * 3 Release.
514:
515: *
516:
517: * RETURNS
518:
519: *
520:
521: * CODE MEANING
522:
523: * 0 OK. Created/obtained/released/destroyed, depending on mode.
524:
525: * ERROR You asked for a semaphore that you already own.
526:
527: * ERANGE That semaphore doesn't exist (modes 1, 2, 3, 4),
528:
529: * or out of slots for new semaphores (0).
530:
531: * EACCDN That semaphore exists already, so you can't create it (mode 0),
532:
533: * or the semaphore is busy (returned from mode 3 if you lose),
534:
535: * or you don't own it (modes 1 and 4).
536:
537: *
538:
539: * If you create a semaphore you will also own it, so you have to release
540:
541: * it with mode 2 before anybody else can get it. Semaphore ID's are magic
542:
543: * numbers which the creator should make available to the users. You have
544:
545: * to own a semaphore to destroy it. If you block waiting for a semaphore,
546:
547: * and then it gets destroyed, you get ERANGE.
548:
549: *
550:
551: * The timeout argument is ignored except in mode 2. In that mode, 0 means
552:
553: * "return immediately" and, as a special case, -1 means "forever."
554:
555: * Since it is in fact an unsigned long number of milliseconds, -2 means
556:
557: * 49 hours, but here -1 truly means forever.
558:
559: *
560:
561: */
562:
563:
564:
565: #define NSEMAPHORES 10
566:
567:
568:
569: typedef struct sema {
570:
571: struct sema *next;
572:
573: long id;
574:
575: short owner; /* -1 means "available" */
576:
577: } SEMA;
578:
579:
580:
581: static SEMA *semalist;
582:
583:
584:
585: static void unsemame P_((PROC *));
586:
587:
588:
589: static void
590:
591: unsemame(p)
592:
593: PROC *p;
594:
595: {
596:
597: /* take process P off the WAIT_Q and put it on the READY_Q */
598:
1.1.1.2 ! root 599: TRACE(("semaphore sleep ends for pid %d",p->pid));
1.1 root 600:
601: if (p->wait_q == WAIT_Q) {
602:
603: rm_q(WAIT_Q,p);
604:
605: add_q(READY_Q,p);
606:
607: }
608:
609: p->wait_cond = 0;
610:
611: }
612:
613:
614:
1.1.1.2 ! root 615: long ARGS_ON_STACK
1.1 root 616:
617: p_semaphore(mode,id,timeout)
618:
619: int mode;
620:
621: long id;
622:
623: long timeout;
624:
625: {
626:
627: SEMA *s, **q;
628:
629: TIMEOUT *timeout_ptr = NULL;
630:
631:
632:
1.1.1.2 ! root 633: TRACE(("Psemaphore(%d,%lx)",mode,id));
1.1 root 634:
635:
636:
637: switch (mode) {
638:
639: case 0: { /* create */
640:
641: for (s = semalist; s; s = s->next) {
642:
643: if (s->id == id) {
644:
1.1.1.2 ! root 645: DEBUG(("Psemaphore(%d,%lx): already exists",mode,id));
1.1 root 646:
647: return EACCDN;
648:
649: }
650:
651: }
652:
653: /* get a new one */
654:
1.1.1.2 ! root 655: if ((s = (SEMA *)kmalloc(sizeof(SEMA))) == 0)
! 656:
! 657: return ENSMEM;
1.1 root 658:
659: s->id = id;
660:
661: s->owner = curproc->pid;
662:
663: s->next = semalist;
664:
665: semalist = s;
666:
667: return E_OK;
668:
669: }
670:
671: case 2: { /* get */
672:
673: loop:
674:
675: for (s = semalist; s; s = s->next) {
676:
677: if (s->id == id) {
678:
679: /* found your semaphore */
680:
681: if (s->owner == curproc->pid) {
682:
1.1.1.2 ! root 683: DEBUG(("Psemaphore(%d,%lx): curproc already owns it!",
1.1 root 684:
1.1.1.2 ! root 685: mode,id));
1.1 root 686:
687: return ERROR;
688:
689: }
690:
691: if (s->owner == -1) {
692:
693: /* it's free; you get it */
694:
695: s->owner = curproc->pid;
696:
697: if (timeout_ptr) canceltimeout(timeout_ptr);
698:
699: return 0;
700:
701: }
702:
703: else {
704:
705: /* not free */
706:
707: if (timeout == 0) {
708:
709: /* non-blocking mode */
710:
711: return EACCDN;
712:
713: }
714:
715: else {
716:
717: if (timeout != -1 && !timeout_ptr) {
718:
719: /* schedule a timeout */
720:
721: timeout_ptr = addtimeout(timeout,unsemame);
722:
723: }
724:
725: /* block until it's released, then try again */
726:
727: sleep(WAIT_Q,WAIT_SEMA);
728:
729: if (curproc->wait_cond != WAIT_SEMA) {
730:
1.1.1.2 ! root 731: TRACE(("Psemaphore(%d,%lx) timed out",mode,id));
1.1 root 732:
733: return EACCDN;
734:
735: }
736:
737: goto loop;
738:
739: }
740:
741: }
742:
743: }
744:
745: }
746:
747: /* no such semaphore (possibly deleted while we were waiting) */
748:
749: if (timeout_ptr) canceltimeout(timeout_ptr);
750:
1.1.1.2 ! root 751: DEBUG(("Psemaphore(%d,%lx): no such semaphore",mode,id));
1.1 root 752:
753: return ERANGE;
754:
755: }
756:
757: case 3: /* release */
758:
759: case 1: { /* destroy */
760:
761: /*
762:
763: * Style note: this is a handy way to chain down a linked list.
764:
765: * q follows along behind s pointing at the "next" field of the
766:
767: * previous list element. To start, q points to the semalist
768:
769: * variable itself. At all times, then, *q is the place to
770:
771: * write s->next when you want to remove an element 's' from
772:
773: * the list.
774:
775: */
776:
777: for (s = *(q = &semalist); s; s = *(q = &s->next)) {
778:
779: if (s->id == id) {
780:
781: /* found your semaphore */
782:
783: if (s->owner != curproc->pid) {
784:
1.1.1.2 ! root 785: DEBUG(("Psemaphore(%d,%lx): you don't own it",mode,id));
1.1 root 786:
787: return EACCDN;
788:
789: }
790:
791: else {
792:
793: if (mode == 3) s->owner = -1; /* make it free, or */
794:
795: else *q = s->next; /* delete from list */
796:
797:
798:
799: /* wake up anybody who's waiting for a semaphore */
800:
801: wake(WAIT_Q,WAIT_SEMA);
802:
803: return E_OK;
804:
805: }
806:
807: }
808:
809: }
810:
811: /* no such semaphore */
812:
1.1.1.2 ! root 813: DEBUG(("Psemaphore(%d,%lx): no such semaphore",mode,id));
1.1 root 814:
815: return ERANGE;
816:
817: }
818:
819: default: { /* invalid mode argument */
820:
1.1.1.2 ! root 821: DEBUG(("Psemaphore(%d,%lx): invalid mode",mode,id));
1.1 root 822:
823: return EINVFN;
824:
825: }
826:
827: }
828:
829: }
830:
831:
832:
833: /*
834:
835: * Release all semaphores owned by the process with process id "pid".
836:
837: * The semaphores are only released, not destroyed. This function
838:
839: * is called from terminate() in dos_mem.c during process termination.
840:
841: */
842:
843:
844:
845: void
846:
847: free_semaphores(pid)
848:
849: int pid;
850:
851: {
852:
1.1.1.2 ! root 853: SEMA *s;
1.1 root 854:
855: int didsomething = 0; /* did any semaphores get freed? */
856:
857:
858:
1.1.1.2 ! root 859: for (s = semalist; s; s = s->next) {
1.1 root 860:
861: if (s->owner == pid) {
862:
863: s->owner = -1; /* mark the semaphore as free */
864:
865: didsomething = 1;
866:
867: }
868:
869: }
870:
871: if (didsomething) {
872:
873: /* wake up anybody waiting for a semaphore, in case it's one of
874:
875: * the ones we just freed
876:
877: */
878:
879: wake(WAIT_Q, WAIT_SEMA);
880:
881: }
882:
883: }
884:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.