home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol1 / dos / reentrant_code < prev    next >
Text File  |  1990-01-25  |  24KB  |  757 lines

  1. (c)  Copyright 1989 Commodore-Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice, and 
  3. is provided "as is" without warranty of any kind, either express or implied.  
  4. The entire risk as to the use of this information is assumed by the user.
  5.  
  6.  
  7.  
  8. Creating Multiple Processes With Re-entrant Code
  9.  
  10. by Michael Sinz
  11.  
  12. With the Amiga and its multitasking Exec, a whole new class of
  13. programming solutions is available for your applications. The
  14. Amiga allows you to divide up your program into multiple
  15. processes that do their work independently and asynchronously.
  16. For example, a spread-sheet program could have a background
  17. process which performs recalculation of the cells.  This feature
  18. would allow the application to utilize the time between
  19. keystrokes to pre-calculate the value of the cell that is being
  20. worked or other cells -- with minimal code and none of the fancy
  21. tricks that would be required to accomplish the same thing on
  22. other systems.
  23.  
  24. Of course, the Amiga's multitasking abilities also allow you to
  25. easily build "multi- window" type applications, too.  This
  26. includes products such as word processors that let you edit more
  27. than one document at a time,  spread-sheets that let you have
  28. more than one sheet open at a time, etc.  
  29.  
  30. For instance, a multi-window word processor can be written as a
  31. basic "single" document handler.  By making the application
  32. re-entrant (i.e., no global or static variables) multiple
  33. documents can be opened by  simply adding a new process for each
  34. context, or window, required.  This would give your application
  35. the ability to start a document printing while the user works
  36. onsome other document in a different window.
  37.  
  38. The example program listed below shows you how this can be done.
  39. The example will open a window with the standard system gadgets
  40. and a gadget that displays the task address.  If you click on the
  41. gadget that  displays the task address, another window will open
  42. with the same program running.  This one is a new process but no
  43. new code has been loaded!
  44.  
  45. To accomplish this feat, the program will check to see if there
  46. is a copy of itself already running whenever it is started.  If
  47. there is, then the program signals the running code telling it to
  48. start the new copy, and then exits. In this way, multiple copies
  49. run using only one instance of the code.
  50.  
  51. The program listing is rather long.  To help you understand it,
  52. here is a list of all the functions in the program.  There are
  53. eight in all:
  54.  
  55. o  MyOpenWindow()        o  Do_More()
  56. o  MyCloseWindow()        o  MyCreateProc()
  57. o  Do_Example()            o  StartMain()
  58. o  myMain()            o  main()
  59.  
  60. The program starts up a copy of itself with the function
  61. StartMain() and then waits for messages from the Do_More()
  62. function.  If a message arrives from Do_More(), then either the
  63. user started another copy of the program from the CLI or the user
  64. clicked on the task address gadget.  In either case, the function
  65. MyCreateProc() is called to restart the program without loading
  66. up any new code.
  67.  
  68. The functions myMain(),  Do_Example(),  MyOpenWindow() and
  69. MyCloseWindow() just serve as a test case here - you could drop
  70. in code of your own instead.  Don't forget, your code must be
  71. re-entrant.  Variables which are not dynamically allocated at
  72. run-time are not allowed.
  73.  
  74. The functions Do_More(),  StartMain() and MyCreateProc() are the
  75. ones that do the multi-processing work.  Do_More() sends the
  76. signal that the user wants to start the code another time.
  77. StartMain() waits for the signal and, if received, it calls the
  78. function MyCreateProc().  StartMain() is also responsible for
  79. keeping track of how many processes the user has started - it
  80. will not exit until all the processes are finished.  The
  81. MyCreateProc() function actually does the work of beginning a new
  82. process using the same re-entrant code.
  83.  
  84. Note that the program runs another copy of itself as a new
  85. process, not just as a task, so you can make calls to dos.library
  86. in your code.  If the program only restarted itself as a
  87. task,then the using dos.library routines (such as printf() )
  88. would cause a system crash.
  89.  
  90.  
  91. /*
  92.  * Example of how to start multiple processes from within the
  93.  * same executable...
  94.  *
  95.  * This example assumes ANSI C and has been written and tested
  96.  * with Lattice C 5.02 and 5.04...
  97.  */
  98.  
  99. /*
  100.  * MakeFile used to make MultiProc...
  101.  *
  102.  
  103. #
  104. # MakeFile for MultiProc
  105. #
  106.  
  107. CFLAGS= -b1 -cfirstq -ms0 -rr1 -v -w
  108.  
  109. OBJS=    MultiProc.o
  110.  
  111. LIBS=    LIB:lcsr.lib
  112.  
  113. .c.o:
  114.     @LC $(CFLAGS) $*
  115.  
  116. MultiProc: $(OBJS)
  117.     @BLink FROM LIB:c.o $(OBJS) TO MultiProc LIB $(LIBS) SMALLDATA SMALLCODE
  118.  
  119.  *
  120.  *
  121.  */
  122.  
  123. #include <exec/types.h>
  124. #include <exec/memory.h>
  125. #include <intuition/intuition.h>
  126. #include <libraries/dos.h>
  127. #include <libraries/dosextens.h>
  128. #include <graphics/gfx.h>
  129. #include <graphics/regions.h>
  130. #include <graphics/rastport.h>
  131. #include <devices/timer.h>
  132.  
  133. #include <proto/all.h>
  134.  
  135. #include <string.h>
  136. #include <stdio.h>
  137.  
  138. /* Lattice method of removing CTRL-C checking... */
  139. int CXBRK(VOID) { return(0); }
  140.  
  141. /*
  142.  * These library bases are shared...
  143.  *
  144.  * Note that you can not share the IEEE math libraries as they
  145.  * need to set up some state information on a per-task basis.
  146.  */
  147. extern struct IntuitionBase *IntuitionBase;
  148. extern struct GfxBase       *GfxBase;
  149.  
  150. /*
  151.  * Structure that is a fake SegList for the code.  It will contain
  152.  * a single JMP instruction to the code we wish to start.
  153.  */
  154. struct CodeHdr
  155. {
  156.     ULONG SegSize;
  157.     ULONG NextSeg;
  158.     UWORD JumpInstr;
  159.     APTR  Function;
  160. };
  161.  
  162. /*
  163.  * Convert a normal pointer (CPTR or APTR) into a BCPL pointer (BPTR)
  164.  */
  165. #define TO_BPTR(x) ((BPTR)(((ULONG)(x)) >> 2))
  166.  
  167. /*
  168.  * My message type for sending message between my processes and
  169.  * the main process...
  170.  */
  171. struct MyMSG
  172. {
  173. struct Message msg;
  174.        BPTR    lock;        /* A directory lock for the new process */
  175.        APTR    UserData;    /* Data that is passed to the process   */
  176.        SHORT   type;        /* My type recognition... */
  177. };
  178.  
  179. #define MSG_TYPE_NEW 1
  180. #define MSG_TYPE_DIE 2
  181.  
  182. /*
  183.  * Constant task and port names...  These should be unique for each
  184.  * of your applications...
  185.  */
  186. static char MyMessagePort[]="Sample Multi-Proc Port";
  187. static char MyProcessName[]="Sample Process Name";
  188.  
  189. /*
  190.  * This function will return TRUE if it was able to send a message
  191.  * to start another task...
  192.  *
  193.  * The lock should be the default directory you wish the new process
  194.  * to have.  If it is NULL, it will get the starting directory...
  195.  * The lock will be DupLock()ed and the copy will be sent to the
  196.  * new process.  So, the lock you pass to this function can be
  197.  * UnLock()ed after you call it.
  198.  *
  199.  * UserData is a 32-bit value that can be whatever you define...
  200.  */
  201. SHORT Do_More(BPTR lock,APTR UserData)
  202. {
  203. register struct MyMSG   *msg;
  204. register struct MsgPort *port;
  205. register struct MsgPort *MyPort;
  206. register        SHORT   flag=FALSE;
  207.  
  208.     if (MyPort=CreatePort(NULL,NULL))
  209.     {
  210.         if (msg=AllocMem(sizeof(struct MyMSG),MEMF_PUBLIC|MEMF_CLEAR))
  211.         {
  212.             msg->msg.mn_ReplyPort=MyPort;
  213.             msg->msg.mn_Length=sizeof(struct MyMSG);
  214.             msg->UserData=UserData;
  215.             msg->lock=lock;
  216.             msg->type=MSG_TYPE_NEW;
  217.  
  218.             /*
  219.              * Once we find the port, we don't want it to go away
  220.              * so we prevent anyone else from running...
  221.              *
  222.              * Since this only happens during the starting of a process
  223.              * this is a low overhead Forbid()
  224.              */
  225.             Forbid();
  226.             if (port=FindPort(MyMessagePort)) /* Ok, send the message... */
  227.             {
  228.                 PutMsg(port,(struct Message *)msg);
  229.                 WaitPort(MyPort);
  230.                 GetMsg(MyPort);
  231.                 flag=TRUE;
  232.             }
  233.             Permit();
  234.             FreeMem(msg,(ULONG)(msg->msg.mn_Length));
  235.         }
  236.         DeletePort(MyPort);
  237.     }
  238.     return(flag);
  239. }
  240.  
  241. /**********************************************************************/
  242.  
  243. /*
  244.  * Simple example program that opens a window with a gadget
  245.  * If the gadget is pressed, it sends the signal to start another
  246.  * copy of the program.  If the close gadget is pressed, the window
  247.  * shuts down.
  248.  *
  249.  * Note that any code that is to be multi-started from within the
  250.  * the same code MUST be re-entrant.  That means that global and
  251.  * static values *MUST* be constants and not be changed...
  252.  *
  253.  * The text in the gadget displays the process ID (the process
  254.  * structure pointer as found by FindTask(NULL)
  255.  */
  256.  
  257. #define MORE_GADGET 1
  258.  
  259. /*
  260.  * This is the stack size...  Since you are a process and may do
  261.  * AmigaDOS calls, the ABSOLUTE minimum would be around 2000.
  262.  * A good recommended minimum is 4000...
  263.  */
  264. #define EXAMPLE_STACK 4000L
  265.  
  266. /*
  267.  * This is the priority that the started code will run at...
  268.  */
  269. #define EXAMPLE_PRI 0
  270.  
  271. /*
  272.  * Some global strings...
  273.  */
  274. static char WindowTitle[10]="MultiProc";
  275.  
  276. static char fontnam[11]="topaz.font";
  277. static struct TextAttr TOPAZ80={fontnam,TOPAZ_EIGHTY,0,0};
  278.  
  279. struct MyGadget
  280. {
  281. struct Gadget    Gadget;
  282. struct IntuiText IntuiText;
  283. struct Image     Image;
  284. };
  285.  
  286. /*
  287.  * Note that to become reentrant we must allocate the gadget
  288.  * structures rather than use global structures since gadgets
  289.  * are data elements that are modified as they are used by
  290.  * Intuition.  Items such as the actual text strings and border
  291.  * vectors and image data can be shared as they are read-only
  292.  */
  293. struct Window *MyOpenWindow(char *MyID)
  294. {
  295. register struct Window    *w=NULL;
  296. register struct MyGadget  *gad;
  297.          struct NewWindow nw;
  298.  
  299.     if (gad=AllocMem(sizeof(struct MyGadget),MEMF_PUBLIC|MEMF_CLEAR))
  300.     {   
  301.         nw.LeftEdge=0;
  302.         nw.TopEdge=22;
  303.         nw.Width=200;
  304.         nw.Height=50;
  305.         nw.DetailPen=3;
  306.         nw.BlockPen=1;
  307.         nw.IDCMPFlags=CLOSEWINDOW|GADGETUP;
  308.         nw.Flags=WINDOWCLOSE|WINDOWDRAG|WINDOWDEPTH|SIMPLE_REFRESH|NOCAREREFRESH;
  309.         nw.FirstGadget=&(gad->Gadget);
  310.         nw.CheckMark=NULL;
  311.         nw.Title=WindowTitle;
  312.         nw.Screen=NULL;
  313.         nw.BitMap=NULL;
  314.         nw.Type=WBENCHSCREEN;
  315.  
  316.         gad->Gadget.LeftEdge=4;
  317.         gad->Gadget.TopEdge=11;
  318.         gad->Gadget.Width=nw.Width-8;
  319.         gad->Gadget.Height=nw.Height-13;
  320.         gad->Gadget.Flags=GADGHCOMP|GADGIMAGE;
  321.         gad->Gadget.Activation=RELVERIFY;
  322.         gad->Gadget.GadgetType=BOOLGADGET;
  323.         gad->Gadget.GadgetRender=(APTR)&(gad->Image);
  324.         gad->Gadget.GadgetText=&(gad->IntuiText);
  325.         gad->Gadget.GadgetID=MORE_GADGET;
  326.  
  327.         gad->IntuiText.TopEdge=(gad->Gadget.Height - 8) >> 1;
  328.         gad->IntuiText.FrontPen=0;
  329.         gad->IntuiText.DrawMode=JAM1;
  330.         gad->IntuiText.ITextFont=&TOPAZ80;
  331.         gad->IntuiText.IText=MyID;
  332.         gad->IntuiText.LeftEdge=(gad->Gadget.Width-
  333.                              IntuiTextLength(&(gad->IntuiText))) >> 1;
  334.  
  335.         gad->Image.Width=gad->Gadget.Width;
  336.         gad->Image.Height=gad->Gadget.Height;
  337.         gad->Image.PlaneOnOff=3;
  338.  
  339.         /*
  340.          * So, if the window opens, we use the UserData field to
  341.          * keep track of the memory we allocated for the gadget
  342.          * and other related items...
  343.          */
  344.         if (w=OpenWindow(&nw)) w->UserData=(UBYTE *)gad;
  345.         else FreeMem(gad,sizeof(struct MyGadget));
  346.     }
  347.     return(w);
  348. }
  349.  
  350. /*
  351.  * We need to not only close the window but also free the memory
  352.  * that we had allocated.  Since the UserData field of the window
  353.  * structure points to this data we just need to extract that pointer
  354.  * so that we may free the memory after the window is closed...
  355.  */
  356. VOID MyCloseWindow(struct Window *w)
  357. {
  358. register UBYTE *gad;
  359.  
  360.     if (w)
  361.     {
  362.         gad=w->UserData;
  363.         CloseWindow(w);
  364.         if (gad) FreeMem(gad,sizeof(struct MyGadget));
  365.     }
  366. }
  367.  
  368. /*
  369.  * This is the example code
  370.  *
  371.  * The UserData that is passed comes from the startup message...
  372.  *
  373.  * Note that since this code could be running multiple times, it
  374.  * must not count on ANY global variables other than CONSTANTS.
  375.  * In other words, it must be re-entrant.
  376.  */
  377. VOID Do_Example(APTR UserData)
  378. {
  379. register struct Window       *w;
  380. register struct IntuiMessage *msg;
  381. register struct Gadget       *gad;
  382. register        SHORT        flag=TRUE;
  383. register        char         *t;
  384. register        char         c;
  385. register        ULONG        task;
  386.                 char         MyID[32];
  387.  
  388.     strcpy(MyID,"I am 0x00000000");
  389.     task=(ULONG)FindTask(NULL);
  390.     t=&(MyID[strlen(MyID)]);
  391.     while (task)                   /* Generate the HEX string */
  392.     {                              /* for the task's address. */
  393.         c=(task & 15);
  394.         task=task >> 4;
  395.         *--t+=c + (c > 9 ? 7 : 0);
  396.     }
  397.  
  398.     if (UserData) strcat(MyID," +");
  399.  
  400.     if (w=MyOpenWindow(MyID))
  401.     {
  402.         while (flag)
  403.         {
  404.             WaitPort(w->UserPort);
  405.             while (msg=(struct IntuiMessage *)GetMsg(w->UserPort))
  406.             {
  407.                 if (msg->Class==CLOSEWINDOW) flag=FALSE;
  408.                 else if (msg->Class==GADGETUP)
  409.                 {
  410.                     gad=(struct Gadget *)msg->IAddress;
  411.                     switch (gad->GadgetID)
  412.                     {
  413.                     case MORE_GADGET: Do_More(NULL,(APTR)1L);
  414.                                       break;
  415.                     }
  416.                 }
  417.                 ReplyMsg((struct Message *)msg);
  418.             }
  419.         }
  420.         MyCloseWindow(w);
  421.     }
  422. }
  423.  
  424. /**********************************************************************/
  425.  
  426. /*
  427.  * This is the font-end to the code that you have....
  428.  *
  429.  * When your code is called, your current directory will be
  430.  * that of where the main code was called from.
  431.  *
  432.  * Note that __saveds is much like a GetA4()
  433.  */
  434. static VOID __saveds myMain(VOID)
  435. {
  436. register struct MyMSG    *msg;
  437. register struct MsgPort  *port;
  438. register        BPTR     lock;
  439.  
  440.     /*
  441.      * First, we need to get the startup-message...
  442.      */
  443.     port=&(((struct Process *)FindTask(NULL))->pr_MsgPort);
  444.     WaitPort(port);
  445.     msg=(struct MyMSG *)GetMsg(port);
  446.  
  447.     /*
  448.      * Now, CD to the startup directory...
  449.      */
  450.     lock=CurrentDir(msg->lock);
  451.  
  452.     /*
  453.      * Do your code here...
  454.      * You are a process with a current directory but no CIS/COS
  455.      * Since you are a process, you may do disk I/O
  456.      *
  457.      * We also pass the user data field from the startup message...
  458.      */
  459.     Do_Example(msg->UserData);
  460.  
  461.     /*
  462.      * CD back to where we started...
  463.      */
  464.     CurrentDir(lock);
  465.  
  466.     /*
  467.      * Now to signal that we are done.  This lets the main task
  468.      * know that we have finished and he may exit.  The main task
  469.      * keeps track of how many tasks are running and when the last
  470.      * one exits, he may then exit...
  471.      *
  472.      * If we can't get this memory, we are in BIG trouble...
  473.      * If we can't get this memory no one will know we are done...
  474.      */
  475.     Forbid();
  476.     ReplyMsg((struct Message *)msg);
  477.  
  478.     /*
  479.      * We don't Permit() here since we wish to drop out of this
  480.      * task berfore the message gets delivered since the message
  481.      * tells the main task that we are done...
  482.      */
  483.  
  484.     /*
  485.      * Since this task then is gone, the Forbid() is thus no
  486.      * longer in effect...
  487.      */
  488. }
  489.  
  490. /*
  491.  * This is my little process start code that sends a message to
  492.  * the process if it got started...
  493.  */
  494. static struct MsgPort *MyCreateProc(struct MsgPort *MyPort,BPTR seglist,BPTR lock,APTR UserData)
  495. {
  496. register struct MsgPort *proc=NULL;
  497. register struct MyMSG   *msg;
  498.  
  499.     if (msg=AllocMem(sizeof(struct MyMSG),MEMF_PUBLIC|MEMF_CLEAR))
  500.     {
  501.         msg->msg.mn_ReplyPort=MyPort;
  502.         msg->msg.mn_Length=sizeof(struct MyMSG);
  503.         msg->type=MSG_TYPE_DIE;
  504.         msg->UserData=UserData;
  505.  
  506.         if (proc=CreateProc(MyProcessName,EXAMPLE_PRI,seglist,EXAMPLE_STACK))
  507.         {
  508.             /*
  509.              * Now that the process is running, we need to make a
  510.              * copy of the lock and send the message...
  511.              */
  512.             msg->lock=DupLock(lock);
  513.             PutMsg(proc,(struct Message *)msg);
  514.         }
  515.         else
  516.         {
  517.             /*
  518.              * Since there was no process, we need to deallocate
  519.              * the message we were goin to send...
  520.              */
  521.             FreeMem(msg,(ULONG)(msg->msg.mn_Length));
  522.         }
  523.     }
  524.     return(proc);
  525. }
  526.  
  527. /*
  528.  * The reason argc is being passed is to give us an indication of
  529.  * if we are a CLI-started program.  For this example, we will
  530.  * display different state information if started from a CLI...
  531.  *
  532.  * Note that the "if (argc) printf(..." statements are just for
  533.  * this example and normally you would not do these...
  534.  */
  535.  
  536. #define EXAMPLE 1
  537.  
  538. static VOID StartMain(int argc)
  539. {
  540. register struct MsgPort *port;
  541. register struct CodeHdr *p;
  542. register struct MyMSG   *msg;
  543. register        BPTR    b_pointer;
  544. register        BPTR    StartingLock;
  545. register        SHORT   count=1;
  546.  
  547.     /*
  548.      * Get a copy of the current directory lock...
  549.      *
  550.      * This lock will be passed to the first process that
  551.      * starts...
  552.      */
  553.     b_pointer=CurrentDir(NULL);
  554.     StartingLock=DupLock(b_pointer);
  555.     CurrentDir(b_pointer);
  556.  
  557.     /*
  558.      * Allocate the fake seglist.  Since it must be longword
  559.      * aligned, it can not be static in the code nor can it be
  560.      * allocated on the stack (without assembly code, that is)
  561.      */
  562.     if (p=AllocMem(sizeof(struct CodeHdr),MEMF_PUBLIC|MEMF_CLEAR))
  563.     {
  564.         /*
  565.          * 0x4EF9 is the 680x0 JMP instruction...
  566.          */
  567.         p->SegSize=sizeof(struct CodeHdr);
  568.         p->JumpInstr=0x4EF9;
  569.         p->Function=(APTR)myMain;   /* The code to start... */
  570.  
  571.         /*
  572.          * We need a BPTR to the NextSeg field of the SegList
  573.          * for CreateProc()...  So, this is what we make...
  574.          */
  575.         b_pointer=TO_BPTR(&p->NextSeg);
  576.  
  577.         /*
  578.          * Ok, now for some critical sections...
  579.          * We need to check if we are running somewhere else.
  580.          * When doing this check, we need to make sure that someone
  581.          * does not slip in at that time so the whole thing is done
  582.          * in a Forbid()
  583.          */
  584.         Forbid();
  585.         if (Do_More(StartingLock,NULL))
  586.         {
  587.  
  588. #ifdef EXAMPLE
  589.             Permit();
  590.             if (argc) printf("Found another MultiProc running...\n");
  591.             Forbid();
  592. #endif EXAMPLE
  593.  
  594.         }
  595.         else if (port=CreatePort(MyMessagePort,NULL))
  596.         {
  597.             if (MyCreateProc(port,b_pointer,StartingLock,NULL))
  598.             {
  599.                 while (count)
  600.                 {
  601.                     /*
  602.                      * We now are safe from intervention...
  603.                      * So, we let the system run...
  604.                      */
  605.                     Permit();
  606.  
  607. #ifdef EXAMPLE
  608.                     if (argc) printf("Now at %d processes...\n\nWaiting for message...\n",count);
  609. #endif EXAMPLE
  610.  
  611.                     WaitPort(port);
  612.  
  613.                     /*
  614.                      * In the following section of code, we need to balance
  615.                      * the Permit()/Forbid() pairs...  This Forbid() is for
  616.                      * just this purpose.  As you can see, it is imediate
  617.                      * Permit() if needed...
  618.                      */
  619.                     Forbid();
  620.  
  621.                     while (msg=(struct MyMGS *)GetMsg(port))
  622.                     {
  623.                         /*
  624.                          * Ok, we are in now, and have our public port
  625.                          * and we know we are the only ones with it so
  626.                          * we now will let the system run...
  627.                          */
  628.                         Permit();
  629.  
  630.                         switch (msg->type)
  631.                         {
  632.                         case MSG_TYPE_NEW:
  633.                              /*
  634.                               * Check if we were given a starting lock.  If not,
  635.                               * we set it to the same as the first starting lock...
  636.                               */
  637.                              if (!(msg->lock)) msg->lock=StartingLock;
  638.  
  639.                              /*
  640.                               * Try to start a process with the values
  641.                               * passed in the starting message...
  642.                               */
  643.                              if (MyCreateProc(port,b_pointer,msg->lock,msg->UserData))
  644.                              {
  645.                                  /*
  646.                                   * Count the fact that the process has
  647.                                   * started...
  648.                                   */
  649.                                  count++;
  650.                              }
  651.                              /*
  652.                               * Reply the message to the person
  653.                               * asking to create this...
  654.                               */
  655.                              ReplyMsg((struct Message *)msg);
  656.  
  657. #ifdef EXAMPLE
  658.                              if (argc) printf("\nStarted a process:\t");
  659. #endif EXAMPLE
  660.  
  661.                              break;
  662.  
  663.                         case MSG_TYPE_DIE:
  664.                              /*
  665.                               * Free the directory lock that we
  666.                               * gave the to the process that just
  667.                               * died...
  668.                               */
  669.                              if (msg->lock) UnLock(msg->lock);
  670.  
  671.                              /*
  672.                               * Count the fact that another process
  673.                               * has died...
  674.                               */
  675.                              count--;
  676.  
  677.                              /*
  678.                               * Since we sent this message, we will
  679.                               * have to deallocate it...
  680.                               */
  681.                              FreeMem(msg,(ULONG)(msg->msg.mn_Length));
  682.  
  683. #ifdef EXAMPLE
  684.                              if (argc) printf("\nA process ended:\t");
  685. #endif EXAMPLE
  686.  
  687.                              break;
  688.                         }
  689.  
  690.                         /*
  691.                          * At this point, we don't want anyone to send a message
  692.                          * to us.  This is because we may be in the middle of
  693.                          * cleaning up and going away.  So, with this Forbid()
  694.                          * we prevent any new messages until after we check for
  695.                          * them and also check to see if we are about to exit.
  696.                          * If we do exit, the message port is deleted before
  697.                          * the next Permit() and thus we are safe from any
  698.                          * stray messages...
  699.                          */
  700.                         Forbid();
  701.                     }
  702.                 }
  703.             }
  704.             DeletePort(port);
  705.  
  706. #ifdef EXAMPLE
  707.             Permit();
  708.             if (argc) printf("Exiting...\n");
  709.             Forbid();
  710. #endif EXAMPLE
  711.  
  712.         }
  713.         /*
  714.          * Now that we have either started and exited or sent the message to
  715.          * the other running MultiProc, we may let others back in...
  716.          */
  717.         Permit();
  718.  
  719.         FreeMem(p,p->SegSize);
  720.     }
  721.  
  722.     if (StartingLock) UnLock(StartingLock);
  723. }
  724.  
  725. /*
  726.  * The main code should do whatever is needed to initialize the few
  727.  * global parameters...  For this example, we don't even need GfxBase
  728.  * but it is here to just keep us company...
  729.  */
  730. VOID main(int argc,char *argv[])
  731. {
  732.     /*
  733.      * Ok, so we need to startup...  Now open the libraries that
  734.      * everyone is going to use...  (Note: Not the IEEE math
  735.      * libraries.  See above.)
  736.      *
  737.      * Also, setup the copy of the current directory lock
  738.      */
  739.     if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",33L))
  740.     {
  741.         if (GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",33L))
  742.         {
  743.             /*
  744.              * This is the main code that starts the multiple tasks
  745.              */
  746.             StartMain(argc);
  747.  
  748.             /*
  749.              * Now clean up...
  750.              */
  751.             CloseLibrary((struct Library *)GfxBase);
  752.         }
  753.         CloseLibrary((struct Library *)IntuitionBase);
  754.     }
  755. }
  756.  
  757.