home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 164.lha / IPC / Sources / IPC.c < prev    next >
Encoding:
Text File  |  1988-04-28  |  21.4 KB  |  532 lines

  1. /*******************************************************************
  2.  *                                                                 *
  3.  *                           IPC.c                                 *
  4.  *                                                                 *
  5.  *           Inter-Process-Communication Procedures                *
  6.  *                                                                 *
  7.  *              Release  1.2 -- 1988 July 22                       *
  8.  *                                                                 *
  9.  *              Copyright 1988 Peter Goodeve                       *
  10.  *                                                                 *
  11.  *  This source is freely distributable, and may be used freely    *
  12.  *  in any program,  but its functionality should not be modified  *
  13.  *  without prior consultation with the author.  (This is just to  *                    *
  14.  *  prevent proliferation of incompatible variants.  Don't be      *
  15.  *  inhibited from suggesting enhancements!)                       *
  16.  *                                                                 *
  17.  *******************************************************************/
  18.  
  19. /*******************************************************************
  20.  *                                                                 *
  21.  *  Modification History:                                          *
  22.  *                                                                 *
  23.  *      88:7:22     PutIPCPort accepts IPP_LOADING flag            *
  24.  *                                                                 *
  25.  *      88:7:11     Manx/Aztec compatibility added                 *
  26.  *                  CheckIPCPort now has flag return option        *
  27.  *                  FindIPCPort now fails if no server             *
  28.  *                  CreateIPCMsg has two added args                *
  29.  *                                                                 *
  30.  *                                                                 *
  31.  *******************************************************************/
  32.  
  33. /********************************************************************
  34.  *                                                                  *
  35.  *  Synopsis of usage:                                                       *
  36.  *  ========                                                        *
  37.  *                                                                  *
  38.  *    Client:                                                       *
  39.  *                                                                  *
  40.  *          port = GetIPCPort(name);                                *
  41.  *      or  port = FindIPCPort(name)                                *
  42.  *          .....                                                   *
  43.  *          msg = CreateIPCMsg(nitems, nxbytes, replyport);         *
  44.  *          .....                                                   *
  45.  *          PutIPCMsg(port,msg);                                    *
  46.  *          .....                                                   *
  47.  *          DeleteIPCMsg(msg);                                      *
  48.  *          .....                                                   *
  49.  *          DropIPCPort(port);                                      *
  50.  *                                                                  *
  51.  *    Server: [standard Exec procedures in brackets]                *
  52.  *                                                                  *
  53.  *          port = ServeIPCPort(name);                              *
  54.  *          .....                                                   *
  55.  *          [WaitPort(port); or Wait(sigbits);]                     *
  56.  *          .....                                                   *
  57.  *          [msg = GetMsg(port);]                                   *
  58.  *          .....                                                   *
  59.  *          [ReplyMsg(msg);]                                        *
  60.  *          .....                                                   *
  61.  *          ShutIPCPort(port);                                      *
  62.  *          <handle remaining messages on the port>                 *
  63.  *          LeaveIPCPort(port);                                     *
  64.  *                                                                  *
  65.  *    Misc.:                                                        *
  66.  *                                                                  *
  67.  *          UseIPCPort(port);                                       *
  68.  *          CheckIPCPort(port,flags);                               *
  69.  *                                                                  *
  70.  *                                                                  *
  71.  ********************************************************************/
  72.  
  73. /********************************************************************
  74.  *                                                                  *
  75.  * These procedures provide a mechanism for independent processes   *
  76.  * to communicate with each other through "IPC ports".   These are  *
  77.  * similar to standard Exec Message Ports, except that they are     *
  78.  * not "owned" by any of the processes; any one process can         *
  79.  * declare itself a "server" (provided no other is currently        *
  80.  * claiming this right), and thus becomes temporarily the handler   *
  81.  * of messages passed to this port.  If there is no server, any     *
  82.  * attempt to send a message to a port will return failure, and     *
  83.  * the client may take whatever action is appropriate.  A client    *
  84.  * may safely "Get" a pointer to a named port (even if there is no  *
  85.  * server yet) and can rely on it remaining valid until it "Drops"  *
  86.  * it again. (In contrast to Exec ports, which have no such         *
  87.  * protection.)                                                     *
  88.  *                                                                  *
  89.  * IPC Ports don't appear in the Exec named port list -- they have  *
  90.  * their own.  There is a single publicly available name, itself    *
  91.  * actually a port on the public list (for reasons we'll see later) *
  92.  * to which is added some data structure including the IPC Port     *
  93.  * list.                                                            *
  94.  *                                                                  *
  95.  * These procedures are just one part of a fully developed system.  *
  96.  * (Another is the IPCMessage structure itself -- see IPC.h and     *
  97.  * elsewhere -- which is essentially independent of the IPC Port    *
  98.  * mechanism.)  They form a fairly self contained set, so they've   *
  99.  * initially been written as a single source file, but there's no   *
  100.  * reason they couldn't be split into smaller segments.  Their      *
  101.  * natural home is a resident library in any case, but that can     *
  102.  * wait for the moment.                                             *
  103.  *                                                                  *
  104.  * Other modules will doubtless have to be added.  One such is      *
  105.  * "LoadIPCPort(name)" which, after doing a GetIPCPort(), will      *
  106.  * check the returned port to see if it has a  server; if not, it   *
  107.  * will check to see if there is a "broker"  process serving the    *
  108.  * IPCBasePort, and if so will send a message to that asking for    *
  109.  * the port to be "served".  (The broker is essentially a server    *
  110.  * like any other -- it will probably look up the port in a user    *
  111.  * supplied list and load the specified server; more advanced       *
  112.  * models may check to see if the server is on another machine,     *
  113.  * for example.)                                                    *
  114.  *                   - - - - - - - - - - -                          *
  115.  *                                                                  *
  116.  *   This code has only been tested under Lattice 4.0.  It is       *
  117.  *   intended to be compiler independent, but please check it       *
  118.  *   for problems.  The code is not always optimized!               *
  119.  *                   - - - - - - - - - - -                          *
  120.  *                                                                  *
  121.  *      %%  Modified to work with Manx 16-bit integer model %%      *
  122.  *      %%  25-june-88 by brian witt                        %%      *
  123.  *                   - - - - - - - - - - -                          *
  124.  *                                                                  *
  125.  *      %%  The fundamental concept of keeping a use-count  %%      *
  126.  *      %%  on a port is due to Matt Dillon.  Thanks Matt!  %%      *
  127.  *                                                                  *
  128.  ********************************************************************/
  129.  
  130.  
  131. #include "IPCPorts.h"
  132.     /* this should be included first to suppress defaults in IPC.h */
  133.     /* other modules in processes using IPC will not usually need
  134.        this header -- only IPC.h */
  135.  
  136. #include "IPC.h"
  137.     /* note -- the above includes prototypes for the procedures below */
  138.  
  139. #include "exec/memory.h"
  140. #include "exec/tasks.h"
  141.  
  142. #define  SOL(s)  ((LONG)sizeof(s))
  143.  
  144. /* individual function declarations, so we don't need #ifdefs
  145.  (replace these if you like with a suitable #include for your compiler
  146.   -- e.g. proto/exec.h for Lattice 4.0, functions.h for Manx) */
  147.  
  148. extern struct Task * FindTask();
  149. extern struct MsgPort * FindPort();
  150. extern struct Node * FindName();
  151. extern APTR AllocMem();
  152. extern LONG AllocSignal();
  153.  
  154.  
  155.  
  156. /*
  157.  *  IPCBasePort is the node around which the whole IPC system is organized.
  158.  *  It is placed in AmigaExec's public port list by the first program to
  159.  *  use IPC, and remains there ever after.  It is the ONLY public port
  160.  *  created by the IPC system -- all IPCPorts are hung on a list from a
  161.  *  header in the IPCBasePort structure.  It may also be an actual port
  162.  *  served by a "broker" process, which will be sent messages requesting,
  163.  *  for example, that it load a server for a port.
  164.  */
  165.  
  166. struct IPCBasePort *IPCBasePort = NULL;
  167.  
  168.  
  169. /*
  170.  *  OpenIPCBase
  171.  *
  172.  *      returns a pointer to IPCBasePort, creating, initializing and
  173.  *      adding it to the Exec public port list if it doesn't already exist.
  174.  *      This procedure will normally only be called by other IPC system
  175.  *      procedures (except possibly in a program such as a "broker").
  176.  */
  177.  
  178. struct IPCBasePort * OpenIPCBase()
  179. {
  180.     char * pname = "IPC_Base_Port";
  181.     struct MsgPort * port;
  182.  
  183.     if (IPCBasePort) return IPCBasePort;
  184.     Forbid();
  185.     if (!(IPCBasePort = (struct IPCBasePort *)FindPort(pname))) {
  186.         IPCBasePort = (struct IPCBasePort *)
  187.             AllocMem(SOL(struct IPCBasePort), MEMF_CLEAR | MEMF_PUBLIC);
  188.         if (IPCBasePort) {
  189.             port = & IPCBasePort->ipcb_Port.ipp_Port;
  190.             port->mp_Node.ln_Name = IPCBasePort->ipcb_Port.ipp_Name;
  191.             port->mp_Node.ln_Type = NT_MSGPORT;
  192.             AddPort(port);
  193.             NewList(&(IPCBasePort->ipcb_PortList));
  194.             strcpy(IPCBasePort->ipcb_Port.ipp_Name, pname);
  195.         }
  196.     }
  197.     Permit();
  198.     return IPCBasePort;
  199. }
  200.  
  201. /*** There is NO CloseIPCBase()!! (it stays around forever) ***/
  202.  
  203.  
  204. /*
  205.  *  CreateIPCPort is a private procedure which should not be called
  206.  *  directly by a user program.  (Actually it is only called by GetIPCPort
  207.  *  but for clarity it is kept separate.)
  208.  *  The created IPCPort will be added to the list in IPCBasePort
  209.  *  (GetIPCPort won't allow duplicate names) unless the name is NULL,
  210.  *  in which case an "anonymous" port will be created that does not get
  211.  *  onto the list; such anonymous ports can only be accessed by other
  212.  *  processes if they are themselves passed as pointers in messages.
  213.  *  (Note that the call to this procedure MUST be within a
  214.  *  Forbid()/Permit() pair.)
  215.  */
  216.  
  217. static struct IPCPort * CreateIPCPort(name) char *name;
  218. {
  219.     struct IPCPort * port;
  220.     int psize;
  221.  
  222.     if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
  223.     psize = sizeof(struct IPCPort) + (name ? strlen(name) : 0);
  224.         /* psize is actually one byte too big -- do you care? */
  225.     port = (struct IPCPort *)
  226.            AllocMem((LONG)psize, MEMF_CLEAR | MEMF_PUBLIC);
  227.  
  228.     if (port) {
  229.         port->ipp_Size = (UWORD)psize;
  230.         NewList(&(port->ipp_Port.mp_MsgList));
  231.         port->ipp_Port.mp_Node.ln_Type = NT_MSGPORT;
  232.         port->ipp_Port.mp_Flags = PA_IGNORE; /* initially */
  233.         if (name) { /* anonymous port is not put on list */
  234.           port->ipp_Port.mp_Node.ln_Name = port->ipp_Name;
  235.                     /* point to name storage array */
  236.           strcpy(port->ipp_Name, name); /* move name to permanent storage */
  237.           AddHead(&IPCBasePort->ipcb_PortList, port);
  238.         }
  239.     }
  240.     return port;
  241. }
  242.  
  243.  
  244. /*
  245.  *  FindIPCPort
  246.  *
  247.  *     Finds the IPCPort with the name supplied as argument if
  248.  *     it has been previously created.
  249.  *     Returns pointer to port if it exists AND has a server
  250.  *      -- null otherwise;
  251.  *     registers a new connection to the port (i.e. increments UseCount)
  252.  *     via UseIPCPort.
  253.  *     (Connection must be terminated when program is done by the procedure
  254.  *     DropIPCPort.)
  255.  *     It will notify a server if requested (see UseIPCPort).
  256.  */
  257.  
  258. struct IPCPort * FindIPCPort(name) char *name;
  259. {
  260.     struct IPCPort * port;
  261.     if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
  262.     if (!name) return NULL; /* can't ever find an anonymous port! */
  263.     Forbid();
  264.     port = (struct IPCPort *)FindName(&IPCBasePort->ipcb_PortList, name);
  265.     if (port) {
  266.         if (port->ipp_Flags & (IPP_SERVED | IPP_LOADING))
  267.             UseIPCPort(port);
  268.         else
  269.             port = NULL; /* no good if not currently served */
  270.     }
  271.     Permit();
  272.     return port;
  273. }
  274.  
  275.  
  276. /*
  277.  *  GetIPCPort
  278.  *
  279.  *     Returns a pointer to IPCPort with the name supplied as an argument;
  280.  *     unlike FindIPCPort, it always returns pointer to port -- this is
  281.  *     created if it doesn't exist; registers a new connection to the port
  282.  *     (use DropIPCPort when done).  It will notify a server if requested
  283.  *     (see UseIPCPort).
  284.  */
  285.  
  286. struct IPCPort * GetIPCPort(name) char *name;
  287. {
  288.     struct IPCPort * port=NULL;
  289.     if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
  290.     Forbid();
  291.     if (name) /* port could be anonymous */
  292.         port = (struct IPCPort *)FindName(&IPCBasePort->ipcb_PortList, name);
  293.     if (!port)
  294.         port = CreateIPCPort(name);
  295.     if (port)
  296.         UseIPCPort(port);
  297.     Permit();
  298.     return port;
  299. }
  300.  
  301.  
  302. /*
  303.  *  UseIPCPort
  304.  *
  305.  *     Registers another connection to a port (outside this module,
  306.  *     this procedure is only used  when the port was passed as a
  307.  *     pointer from another process).
  308.  *     (Use DropIPCPort when done.)
  309.  *     If the current server has set the IPP_NOTIFY flag in the port, the
  310.  *     server task will be signalled using the port signal bit.  NOTE that
  311.  *     WaitPort will NOT detect these signals, because no message is
  312.  *     actually sent; the program must do a Wait on this bit, and should do
  313.  *     a GetMsg as usual, but if the message pointer is null it should
  314.  *     then call, for example, CheckIPCPort.
  315.  *     NOTE -- the port pointer MUST remain valid while this procedure is
  316.  *     called: either the call (from e.g. GetIPCPort) is Forbid()den, or
  317.  *     the port was passed from another process which guarantees its
  318.  *     existence.
  319.  */
  320.  
  321. void UseIPCPort(port) struct IPCPort * port;
  322. {
  323.     port->ipp_UseCount++;
  324.     if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
  325.         Signal(port->ipp_Port.mp_SigTask,
  326.                1L<<port->ipp_Port.mp_SigBit);
  327. }
  328.  
  329.  
  330. /*
  331.  *  DropIPCPort
  332.  *
  333.  *     Terminate a connection to a port established by FindIPCPort,
  334.  *     GetIPCPort, or UseIPCPort.  Port will be destroyed if there are
  335.  *     no other connections left.
  336.  *     If the IPP_NOTIFY flag is set in the port, the server will be
  337.  *     signalled when this procedure is called (see FindIPCPort).
  338.  */
  339.  
  340. void DropIPCPort(port) struct IPCPort * port;
  341. {
  342.     if (!port) return; /* to save the client some trouble
  343.                           (in a cleanup procedure) */
  344.     Forbid();
  345.     if (--port->ipp_UseCount == 0) {
  346.         /* an anonymous port is NOT on list -- ALL others MUST be! */
  347.         if (port->ipp_Port.mp_Node.ln_Name) Remove(port);
  348.         Permit();
  349.         FreeMem(port, (ULONG)port->ipp_Size);
  350.     }
  351.     else {
  352.         if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
  353.             Signal(port->ipp_Port.mp_SigTask,
  354.                    1L<<port->ipp_Port.mp_SigBit);
  355.         Permit();
  356.     }
  357. }
  358.  
  359.  
  360. /*
  361.  *  ServeIPCPort
  362.  *
  363.  *     Registers calling task as the server on the named port (which is
  364.  *     created if it doesn't exist); null is returned if the port already
  365.  *     has a server, otherwise a pointer to it is returned.  At the same
  366.  *     time the port is given the server as its SigTask and a suitable
  367.  *     signal bit is allocated.
  368.  */
  369.  
  370. struct IPCPort * ServeIPCPort(name) char *name;
  371. {
  372.     struct IPCPort * port;
  373.  
  374.     port = GetIPCPort(name);
  375.     if (port) {
  376.         Forbid();
  377.         if ((port->ipp_Flags & (IPP_SERVED | IPP_SHUT))
  378.          || (port->ipp_Port.mp_SigBit = AllocSignal(-1L)) == -1L) {
  379.             DropIPCPort(port);
  380.             port = NULL;
  381.         }
  382.         else {
  383.             port->ipp_Port.mp_Flags = PA_SIGNAL;
  384.             port->ipp_Port.mp_SigTask = FindTask(NULL);
  385.             port->ipp_Flags = IPP_SERVED; /* all other bits cleared */
  386.         }
  387.         Permit();
  388.     }
  389.     return port;
  390. }
  391.  
  392. /*
  393.  *  ShutIPCPort
  394.  *
  395.  *     ONLY the current server may call this procedure.
  396.  *     It prevents more messages being sent to this port, but
  397.  *     does not end server connection; remaining messages can be dealt
  398.  *     with before finally calling LeaveIPCPort.
  399.  *     Note that it does NOT inhibit the server being signalled if
  400.  *     IPP_NOTIFY is set and another client connects.  (At the moment
  401.  *     there is no mechanism to reopen a shut port without Leaving
  402.  *     first; this may be possible in a future revision.)
  403.  */
  404.  
  405. void ShutIPCPort(port) struct IPCPort * port;
  406. {
  407.     Forbid(); /* now required because of FindIPCPort test */
  408.     port->ipp_Flags |= IPP_SHUT; /* Prevent other servers connecting */
  409.     port->ipp_Flags &= ~IPP_SERVED; /* prevent messages from landing */
  410.     port->ipp_Port.mp_Flags = PA_IGNORE; /* someone might use PutMsg! */
  411.     Permit();
  412. }
  413.  
  414. /*
  415.  *  LeaveIPCPort
  416.  *
  417.  *     ONLY the current server may call this procedure.
  418.  *     Disconnects the server process from the port; another process
  419.  *     can then become a server if it desires.  If there are no other
  420.  *     connections, the port is removed.
  421.  */
  422.  
  423. void LeaveIPCPort(port) struct IPCPort * port;
  424. {
  425.     FreeSignal(port->ipp_Port.mp_SigBit);
  426.     port->ipp_Port.mp_SigTask = NULL;
  427.     port->ipp_Flags &= ~(IPP_SHUT | IPP_SERVED | IPP_SERVER_FLAGS);
  428.     DropIPCPort(port);
  429. }
  430.  
  431.  
  432. /*
  433.  *  CheckIPCPort
  434.  *
  435.  *     Normally returns the number of current connections to this port
  436.  *     (including the server); if the top bit (0x8000) of the flags
  437.  *     argument is set, it will instead return the IPCPort flag word
  438.  *     value at the time of the call.  (Note that these values are
  439.  *     not guaranteed!)
  440.  *     A call by the server (only) will also set the (user
  441.  *     settable) port flags to the value in the second argument --
  442.  *     currently the only valid flag is IPP_NOTIFY.
  443.  */
  444.  
  445. CheckIPCPort(port, flags)
  446.     struct IPCPort *port;
  447.     UWORD flags;
  448. {
  449.     UWORD origflags = port->ipp_Flags;
  450.     if (port->ipp_Port.mp_SigTask == FindTask(NULL))
  451.         /* only server can change flags */
  452.         port->ipp_Flags = (port->ipp_Flags & ~IPP_SERVER_FLAGS) |
  453.                           (flags & IPP_SERVER_FLAGS);
  454.     return (int)((flags & 0x8000)? origflags : port->ipp_UseCount);
  455. }
  456.  
  457.  
  458. /*
  459.  *  PutIPCMsg
  460.  *
  461.  *     Sends an IPCMessage to an IPCPort; if the port has no server or
  462.  *     is shut, the message is not sent and the function returns FALSE;
  463.  *     otherwise it returns TRUE. (Other port flags to be added later
  464.  *     may affect these actions.)
  465.  *     Note that a ReplyPort should be supplied in the message as usual
  466.  *     (except for the rare possible usage where a message is not to
  467.  *     be replied; in this case, the IPC_TRANSFER flag must be set in
  468.  *     both the message and all the items it contains, and the ReplyPort
  469.  *     must be NULL).
  470.  */
  471.  
  472. PutIPCMsg(port,msg)
  473.     struct IPCPort *port;
  474.     struct IPCMessage *msg;
  475. {
  476.     Forbid(); /* we do this the straightforward way -- it's very quick */
  477.     if (port->ipp_Flags & (IPP_SERVED | IPP_LOADING)) {
  478.         PutMsg(port, msg);
  479.         Permit();
  480.         return TRUE;
  481.     }
  482.     Permit();
  483.     return FALSE;
  484. }
  485.  
  486.  
  487. /*
  488.  *  CreateIPCMsg
  489.  *
  490.  *     Creates a standard IPCMessage block (in MEMF_PUBLIC) with the
  491.  *     number of IPCItems supplied as argument. (Special cases -- like
  492.  *     in-line data -- you will have to handle yourself, and you always
  493.  *     have to manage the data blocks yourself).
  494.  */
  495.  
  496. struct IPCMessage * CreateIPCMsg(nitems, nxbytes, replyport)
  497.     struct MsgPort *replyport;
  498. {
  499.     ULONG msgsize, mlength;
  500.     struct IPCMessage * mp;
  501.     msgsize = sizeof(struct IPCMessage) +
  502.                   (nitems -1)*SOL(struct IPCItem) + nxbytes;
  503.     mlength = msgsize - sizeof(struct Message);
  504.     if (mlength > 0x0000FFFF)
  505.         return NULL; /* no oversize messages! */
  506.     mp = (struct IPCMessage *)
  507.          AllocMem(msgsize, MEMF_CLEAR | MEMF_PUBLIC);
  508.     if (mp) {
  509.         mp->ipc_Msg.mn_Length = mlength;
  510.         mp->ipc_ItemCount = nitems;
  511.         mp->ipc_Msg.mn_ReplyPort = replyport;
  512.     }
  513.     return  mp;
  514. }
  515.  
  516.  
  517. /*
  518.  *  DeleteIPCMsg
  519.  *
  520.  *     Deletes a standard IPCMessage block;  you must first have disposed
  521.  *     of any attached data as appropriate.
  522.  */
  523.  
  524. void DeleteIPCMsg(msg) struct IPCMessage *msg;
  525. {
  526.     FreeMem(msg, SOL(struct Message) + msg->ipc_Msg.mn_Length);
  527. }
  528.  
  529.  
  530.             /*********************************************/
  531.  
  532.