|
|
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:
217: long
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:
235: TRACE("Pmsg(%d,%lx,%lx)",mode,mbid,ptr);
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: /* DEBUG("Pmsg read: found a writer (pid %d)",p->pid); */
258:
259: goto got_rendezvous;
260:
261: }
262:
263: }
264:
265: /* DEBUG("Pmsg read: no writers"); */
266:
267: /* nobody is writing just now */
268:
269: goto dosleep;
270:
271: }
272:
273: else if (mode == 1 || mode == 2) {
274:
275: /* write, or write/read */
276:
277: /* walk the whole process list looking for a reader */
278:
279: for (p = proclist; p; p = p->gl_next) {
280:
281: if (p->mb_mode == 0 && p->mb_mbid == mbid) {
282:
283: /* this process is reading this mbox */
284:
285: /* DEBUG("Pmsg write: found a reader (pid %d)",p->pid); */
286:
287: goto got_rendezvous;
288:
289: }
290:
291: }
292:
293: /* nobody is reading just now */
294:
295: curproc->mb_long1 = *(long *)ptr; /* copy the message */
296:
297: curproc->mb_long2 = *(long *)(ptr+4); /* into my proc struct */
298:
299: /* DEBUG("Pmsg write: no readers"); */
300:
301: goto dosleep;
302:
303: }
304:
305: else return EINVFN; /* invalid mode */
306:
307:
308:
309: /*
310:
311: * we get here if we actually have a rendezvous between p and curproc.
312:
313: */
314:
315:
316:
317: got_rendezvous:
318:
319: if (!mode) {
320:
321: /* curproc is reading */
322:
323: *(long *)(ptr) = p->mb_long1; /* copy the message */
324:
325: *(long *)(ptr+4) = p->mb_long2; /* from his proc struct */
326:
327: *(short *)(ptr+8) = p->pid; /* provide the PID */
328:
329:
330:
331: if (p->mb_mode == 2) {
332:
333: /*
334:
335: * The blocked write was in mode 2: writer becomes a reader and
336:
337: * stays on WAIT_Q waiting on WAIT_MB.
338:
339: */
340:
341: /* DEBUG("Pmsg: previously-blocked writer becomes reader"); */
342:
343: p->mb_mbid = 0xFFFF0000L | p->pid;
344:
345: p->mb_mode = 0;
346:
347: }
348:
349: else {
350:
351: /* The blocked write was in mode 1: writer wakes up */
352:
353: /* DEBUG("Pmsg: previously-blocked writer put on run queue"); */
354:
355: p->mb_writer = curproc->pid; /* tell writer reader's pid */
356:
357: p->mb_mode = -1; /* mark rendezvous */
358:
359: p->wait_cond = 0; /* not necessary? */
360:
361: if (p->wait_q != READY_Q) {
362:
363: /* wake up the writer if it is sleeping */
364:
365: rm_q(p->wait_q,p);
366:
367: add_q(READY_Q,p);
368:
369: }
370:
371: }
372:
373: return 0;
374:
375: }
376:
377: else {
378:
379: /* curproc is writing */
380:
381: p->mb_writer = curproc->pid; /* provide the PID */
382:
383: p->mb_long1 = *(long *)(ptr); /* copy the message */
384:
385: p->mb_long2 = *(long *)(ptr+4);
386:
387: /* DEBUG("Pmsg: previously-blocked reader put on run queue"); */
388:
389: p->mb_mode = -1; /* mark rendezvous */
390:
391: p->wait_cond = 0; /* not necessary? */
392:
393: if (p->wait_q != READY_Q) {
394:
395: /* wake up the reader if it is sleeping */
396:
397: rm_q(p->wait_q,p);
398:
399: add_q(READY_Q,p);
400:
401: }
402:
403: if (mode == 2) {
404:
405: /* now curproc becomes a reader */
406:
407: /* DEBUG("Pmsg: curproc (writer) becomes reader & blocks"); */
408:
409: mbid = 0xFFFF0000L | curproc->pid;
410:
411: mode = 0;
412:
413: goto dosleep;
414:
415: }
416:
417: else {
418:
419: *(short *)(ptr+8) = p->pid; /* tell reader writer's pid */
420:
421: return 0;
422:
423: }
424:
425: }
426:
427:
428:
429: /*
430:
431: * we get here when a read or write should block because there's nobody
432:
433: * already waiting at the other end.
434:
435: */
436:
437:
438:
439: dosleep:
440:
441: if (noblock) {
442:
443: /* DEBUG("Pmsg: non-blocking mode: EWOULDBLOCK"); */
444:
445: return -1L;
446:
447: }
448:
449: /* DEBUG("Pmsg: put curproc to sleep."); */
450:
451: curproc->mb_mbid = mbid; /* and ID waited for */
452:
453: curproc->mb_mode = mode; /* save mode */
454:
455:
456:
457: /*
458:
459: * OK: now we sleep until a rendezvous has occured. The loop is because we
460:
461: * may be woken up to deal with signals before the rendezvous. The thing
462:
463: * that says the rendezvous actually happened is that mb_mode got changed
464:
465: * to -1.
466:
467: */
468:
469: do {
470:
471: sleep(WAIT_Q, WAIT_MB); /* block */
472:
473: } while (curproc->mb_mode != -1);
474:
475: /*
476:
477: DEBUG("Pmsg: woke up; mode %d cpmode %d writer %d long1 %lx long2 %lx",
478:
479: mode,curproc->mb_mode,curproc->mb_writer,
480:
481: curproc->mb_long1, curproc->mb_long2);
482:
483: */
484:
485:
486:
487: /*
488:
489: * When we wake up, we transfer the message from our proc struct
490:
491: * into our message pointer space if we were reading, and do
492:
493: * nothing if we were writing. The write/read case is taken care
494:
495: * of without waking this process up at all.
496:
497: *
498:
499: * Check curproc->mode because it may not be the mode we started
500:
501: * with any more.
502:
503: */
504:
505: if (mode == 0) {
506:
507: *(long *)(ptr) = curproc->mb_long1;
508:
509: *(long *)(ptr+4) = curproc->mb_long2;
510:
511: }
512:
513: /* in any case, copy the 'writer' field because that's the PID */
514:
515: /* of the other side of the rendezvous */
516:
517: *(short *)(ptr+8) = curproc->mb_writer;
518:
519: return 0;
520:
521: }
522:
523:
524:
525: /*
526:
527: * more mutex: this time a semaphore.
528:
529: *
530:
531: * long Psemaphore(short mode, long id, long timeout)
532:
533: *
534:
535: * MODE ACTION
536:
537: * 0 Create and get a semaphore with the given ID.
538:
539: * 1 Destroy.
540:
541: * 2 Get (blocks until it's available or destroyed, or timeout).
542:
543: * 3 Release.
544:
545: *
546:
547: * RETURNS
548:
549: *
550:
551: * CODE MEANING
552:
553: * 0 OK. Created/obtained/released/destroyed, depending on mode.
554:
555: * ERROR You asked for a semaphore that you already own.
556:
557: * ERANGE That semaphore doesn't exist (modes 1, 2, 3, 4),
558:
559: * or out of slots for new semaphores (0).
560:
561: * EACCDN That semaphore exists already, so you can't create it (mode 0),
562:
563: * or the semaphore is busy (returned from mode 3 if you lose),
564:
565: * or you don't own it (modes 1 and 4).
566:
567: *
568:
569: * If you create a semaphore you will also own it, so you have to release
570:
571: * it with mode 2 before anybody else can get it. Semaphore ID's are magic
572:
573: * numbers which the creator should make available to the users. You have
574:
575: * to own a semaphore to destroy it. If you block waiting for a semaphore,
576:
577: * and then it gets destroyed, you get ERANGE.
578:
579: *
580:
581: * The timeout argument is ignored except in mode 2. In that mode, 0 means
582:
583: * "return immediately" and, as a special case, -1 means "forever."
584:
585: * Since it is in fact an unsigned long number of milliseconds, -2 means
586:
587: * 49 hours, but here -1 truly means forever.
588:
589: *
590:
591: */
592:
593:
594:
595: #define NSEMAPHORES 10
596:
597:
598:
599: typedef struct sema {
600:
601: struct sema *next;
602:
603: long id;
604:
605: short owner; /* -1 means "available" */
606:
607: } SEMA;
608:
609:
610:
611: static SEMA *semalist;
612:
613:
614:
615: static void unsemame P_((PROC *));
616:
617:
618:
619: static void
620:
621: unsemame(p)
622:
623: PROC *p;
624:
625: {
626:
627: /* take process P off the WAIT_Q and put it on the READY_Q */
628:
629: TRACE("semaphore sleep ends for pid %d",p->pid);
630:
631: if (p->wait_q == WAIT_Q) {
632:
633: rm_q(WAIT_Q,p);
634:
635: add_q(READY_Q,p);
636:
637: }
638:
639: p->wait_cond = 0;
640:
641: }
642:
643:
644:
645: long
646:
647: p_semaphore(mode,id,timeout)
648:
649: int mode;
650:
651: long id;
652:
653: long timeout;
654:
655: {
656:
657: SEMA *s, **q;
658:
659: TIMEOUT *timeout_ptr = NULL;
660:
661:
662:
663: TRACE("Psemaphore(%d,%lx)",mode,id);
664:
665:
666:
667: switch (mode) {
668:
669: case 0: { /* create */
670:
671: for (s = semalist; s; s = s->next) {
672:
673: if (s->id == id) {
674:
675: DEBUG("Psemaphore(%d,%lx): already exists",mode,id);
676:
677: return EACCDN;
678:
679: }
680:
681: }
682:
683: /* get a new one */
684:
685: if (!(s = (SEMA *)kmalloc(sizeof(SEMA)))) return ENSMEM;
686:
687: s->id = id;
688:
689: s->owner = curproc->pid;
690:
691: s->next = semalist;
692:
693: semalist = s;
694:
695: return E_OK;
696:
697: }
698:
699: case 2: { /* get */
700:
701: loop:
702:
703: for (s = semalist; s; s = s->next) {
704:
705: if (s->id == id) {
706:
707: /* found your semaphore */
708:
709: if (s->owner == curproc->pid) {
710:
711: DEBUG("Psemaphore(%d,%lx): curproc already owns it!",
712:
713: mode,id);
714:
715: return ERROR;
716:
717: }
718:
719: if (s->owner == -1) {
720:
721: /* it's free; you get it */
722:
723: s->owner = curproc->pid;
724:
725: if (timeout_ptr) canceltimeout(timeout_ptr);
726:
727: return 0;
728:
729: }
730:
731: else {
732:
733: /* not free */
734:
735: if (timeout == 0) {
736:
737: /* non-blocking mode */
738:
739: return EACCDN;
740:
741: }
742:
743: else {
744:
745: if (timeout != -1 && !timeout_ptr) {
746:
747: /* schedule a timeout */
748:
749: timeout_ptr = addtimeout(timeout,unsemame);
750:
751: }
752:
753: /* block until it's released, then try again */
754:
755: sleep(WAIT_Q,WAIT_SEMA);
756:
757: if (curproc->wait_cond != WAIT_SEMA) {
758:
759: TRACE("Psemaphore(%d,%lx) timed out",mode,id);
760:
761: return EACCDN;
762:
763: }
764:
765: goto loop;
766:
767: }
768:
769: }
770:
771: }
772:
773: }
774:
775: /* no such semaphore (possibly deleted while we were waiting) */
776:
777: if (timeout_ptr) canceltimeout(timeout_ptr);
778:
779: DEBUG("Psemaphore(%d,%lx): no such semaphore",mode,id);
780:
781: return ERANGE;
782:
783: }
784:
785: case 3: /* release */
786:
787: case 1: { /* destroy */
788:
789: /*
790:
791: * Style note: this is a handy way to chain down a linked list.
792:
793: * q follows along behind s pointing at the "next" field of the
794:
795: * previous list element. To start, q points to the semalist
796:
797: * variable itself. At all times, then, *q is the place to
798:
799: * write s->next when you want to remove an element 's' from
800:
801: * the list.
802:
803: */
804:
805: for (s = *(q = &semalist); s; s = *(q = &s->next)) {
806:
807: if (s->id == id) {
808:
809: /* found your semaphore */
810:
811: if (s->owner != curproc->pid) {
812:
813: DEBUG("Psemaphore(%d,%lx): you don't own it",mode,id);
814:
815: return EACCDN;
816:
817: }
818:
819: else {
820:
821: if (mode == 3) s->owner = -1; /* make it free, or */
822:
823: else *q = s->next; /* delete from list */
824:
825:
826:
827: /* wake up anybody who's waiting for a semaphore */
828:
829: wake(WAIT_Q,WAIT_SEMA);
830:
831: return E_OK;
832:
833: }
834:
835: }
836:
837: }
838:
839: /* no such semaphore */
840:
841: DEBUG("Psemaphore(%d,%lx): no such semaphore",mode,id);
842:
843: return ERANGE;
844:
845: }
846:
847: default: { /* invalid mode argument */
848:
849: DEBUG("Psemaphore(%d,%lx): invalid mode",mode,id);
850:
851: return EINVFN;
852:
853: }
854:
855: }
856:
857: }
858:
859:
860:
861: /*
862:
863: * Release all semaphores owned by the process with process id "pid".
864:
865: * The semaphores are only released, not destroyed. This function
866:
867: * is called from terminate() in dos_mem.c during process termination.
868:
869: */
870:
871:
872:
873: void
874:
875: free_semaphores(pid)
876:
877: int pid;
878:
879: {
880:
881: SEMA *s, **q;
882:
883: int didsomething = 0; /* did any semaphores get freed? */
884:
885:
886:
887: for (s = *(q = &semalist); s; s = *(q = &s->next)) {
888:
889: if (s->owner == pid) {
890:
891: s->owner = -1; /* mark the semaphore as free */
892:
893: didsomething = 1;
894:
895: }
896:
897: }
898:
899: if (didsomething) {
900:
901: /* wake up anybody waiting for a semaphore, in case it's one of
902:
903: * the ones we just freed
904:
905: */
906:
907: wake(WAIT_Q, WAIT_SEMA);
908:
909: }
910:
911: }
912:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.