|
|
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.