home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************************
- * *
- * IPC.c *
- * *
- * Inter-Process-Communication Procedures *
- * *
- * Release 1.2 -- 1988 July 22 *
- * *
- * Copyright 1988 Peter Goodeve *
- * *
- * This source is freely distributable, and may be used freely *
- * in any program, but its functionality should not be modified *
- * without prior consultation with the author. (This is just to * *
- * prevent proliferation of incompatible variants. Don't be *
- * inhibited from suggesting enhancements!) *
- * *
- *******************************************************************/
-
- /*******************************************************************
- * *
- * Modification History: *
- * *
- * 88:7:22 PutIPCPort accepts IPP_LOADING flag *
- * *
- * 88:7:11 Manx/Aztec compatibility added *
- * CheckIPCPort now has flag return option *
- * FindIPCPort now fails if no server *
- * CreateIPCMsg has two added args *
- * *
- * *
- *******************************************************************/
-
- /********************************************************************
- * *
- * Synopsis of usage: *
- * ======== *
- * *
- * Client: *
- * *
- * port = GetIPCPort(name); *
- * or port = FindIPCPort(name) *
- * ..... *
- * msg = CreateIPCMsg(nitems, nxbytes, replyport); *
- * ..... *
- * PutIPCMsg(port,msg); *
- * ..... *
- * DeleteIPCMsg(msg); *
- * ..... *
- * DropIPCPort(port); *
- * *
- * Server: [standard Exec procedures in brackets] *
- * *
- * port = ServeIPCPort(name); *
- * ..... *
- * [WaitPort(port); or Wait(sigbits);] *
- * ..... *
- * [msg = GetMsg(port);] *
- * ..... *
- * [ReplyMsg(msg);] *
- * ..... *
- * ShutIPCPort(port); *
- * <handle remaining messages on the port> *
- * LeaveIPCPort(port); *
- * *
- * Misc.: *
- * *
- * UseIPCPort(port); *
- * CheckIPCPort(port,flags); *
- * *
- * *
- ********************************************************************/
-
- /********************************************************************
- * *
- * These procedures provide a mechanism for independent processes *
- * to communicate with each other through "IPC ports". These are *
- * similar to standard Exec Message Ports, except that they are *
- * not "owned" by any of the processes; any one process can *
- * declare itself a "server" (provided no other is currently *
- * claiming this right), and thus becomes temporarily the handler *
- * of messages passed to this port. If there is no server, any *
- * attempt to send a message to a port will return failure, and *
- * the client may take whatever action is appropriate. A client *
- * may safely "Get" a pointer to a named port (even if there is no *
- * server yet) and can rely on it remaining valid until it "Drops" *
- * it again. (In contrast to Exec ports, which have no such *
- * protection.) *
- * *
- * IPC Ports don't appear in the Exec named port list -- they have *
- * their own. There is a single publicly available name, itself *
- * actually a port on the public list (for reasons we'll see later) *
- * to which is added some data structure including the IPC Port *
- * list. *
- * *
- * These procedures are just one part of a fully developed system. *
- * (Another is the IPCMessage structure itself -- see IPC.h and *
- * elsewhere -- which is essentially independent of the IPC Port *
- * mechanism.) They form a fairly self contained set, so they've *
- * initially been written as a single source file, but there's no *
- * reason they couldn't be split into smaller segments. Their *
- * natural home is a resident library in any case, but that can *
- * wait for the moment. *
- * *
- * Other modules will doubtless have to be added. One such is *
- * "LoadIPCPort(name)" which, after doing a GetIPCPort(), will *
- * check the returned port to see if it has a server; if not, it *
- * will check to see if there is a "broker" process serving the *
- * IPCBasePort, and if so will send a message to that asking for *
- * the port to be "served". (The broker is essentially a server *
- * like any other -- it will probably look up the port in a user *
- * supplied list and load the specified server; more advanced *
- * models may check to see if the server is on another machine, *
- * for example.) *
- * - - - - - - - - - - - *
- * *
- * This code has only been tested under Lattice 4.0. It is *
- * intended to be compiler independent, but please check it *
- * for problems. The code is not always optimized! *
- * - - - - - - - - - - - *
- * *
- * %% Modified to work with Manx 16-bit integer model %% *
- * %% 25-june-88 by brian witt %% *
- * - - - - - - - - - - - *
- * *
- * %% The fundamental concept of keeping a use-count %% *
- * %% on a port is due to Matt Dillon. Thanks Matt! %% *
- * *
- ********************************************************************/
-
-
- #include "IPCPorts.h"
- /* this should be included first to suppress defaults in IPC.h */
- /* other modules in processes using IPC will not usually need
- this header -- only IPC.h */
-
- #include "IPC.h"
- /* note -- the above includes prototypes for the procedures below */
-
- #include "exec/memory.h"
- #include "exec/tasks.h"
-
- #define SOL(s) ((LONG)sizeof(s))
-
- /* individual function declarations, so we don't need #ifdefs
- (replace these if you like with a suitable #include for your compiler
- -- e.g. proto/exec.h for Lattice 4.0, functions.h for Manx) */
-
- extern struct Task * FindTask();
- extern struct MsgPort * FindPort();
- extern struct Node * FindName();
- extern APTR AllocMem();
- extern LONG AllocSignal();
-
-
-
- /*
- * IPCBasePort is the node around which the whole IPC system is organized.
- * It is placed in AmigaExec's public port list by the first program to
- * use IPC, and remains there ever after. It is the ONLY public port
- * created by the IPC system -- all IPCPorts are hung on a list from a
- * header in the IPCBasePort structure. It may also be an actual port
- * served by a "broker" process, which will be sent messages requesting,
- * for example, that it load a server for a port.
- */
-
- struct IPCBasePort *IPCBasePort = NULL;
-
-
- /*
- * OpenIPCBase
- *
- * returns a pointer to IPCBasePort, creating, initializing and
- * adding it to the Exec public port list if it doesn't already exist.
- * This procedure will normally only be called by other IPC system
- * procedures (except possibly in a program such as a "broker").
- */
-
- struct IPCBasePort * OpenIPCBase()
- {
- char * pname = "IPC_Base_Port";
- struct MsgPort * port;
-
- if (IPCBasePort) return IPCBasePort;
- Forbid();
- if (!(IPCBasePort = (struct IPCBasePort *)FindPort(pname))) {
- IPCBasePort = (struct IPCBasePort *)
- AllocMem(SOL(struct IPCBasePort), MEMF_CLEAR | MEMF_PUBLIC);
- if (IPCBasePort) {
- port = & IPCBasePort->ipcb_Port.ipp_Port;
- port->mp_Node.ln_Name = IPCBasePort->ipcb_Port.ipp_Name;
- port->mp_Node.ln_Type = NT_MSGPORT;
- AddPort(port);
- NewList(&(IPCBasePort->ipcb_PortList));
- strcpy(IPCBasePort->ipcb_Port.ipp_Name, pname);
- }
- }
- Permit();
- return IPCBasePort;
- }
-
- /*** There is NO CloseIPCBase()!! (it stays around forever) ***/
-
-
- /*
- * CreateIPCPort is a private procedure which should not be called
- * directly by a user program. (Actually it is only called by GetIPCPort
- * but for clarity it is kept separate.)
- * The created IPCPort will be added to the list in IPCBasePort
- * (GetIPCPort won't allow duplicate names) unless the name is NULL,
- * in which case an "anonymous" port will be created that does not get
- * onto the list; such anonymous ports can only be accessed by other
- * processes if they are themselves passed as pointers in messages.
- * (Note that the call to this procedure MUST be within a
- * Forbid()/Permit() pair.)
- */
-
- static struct IPCPort * CreateIPCPort(name) char *name;
- {
- struct IPCPort * port;
- int psize;
-
- if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
- psize = sizeof(struct IPCPort) + (name ? strlen(name) : 0);
- /* psize is actually one byte too big -- do you care? */
- port = (struct IPCPort *)
- AllocMem((LONG)psize, MEMF_CLEAR | MEMF_PUBLIC);
-
- if (port) {
- port->ipp_Size = (UWORD)psize;
- NewList(&(port->ipp_Port.mp_MsgList));
- port->ipp_Port.mp_Node.ln_Type = NT_MSGPORT;
- port->ipp_Port.mp_Flags = PA_IGNORE; /* initially */
- if (name) { /* anonymous port is not put on list */
- port->ipp_Port.mp_Node.ln_Name = port->ipp_Name;
- /* point to name storage array */
- strcpy(port->ipp_Name, name); /* move name to permanent storage */
- AddHead(&IPCBasePort->ipcb_PortList, port);
- }
- }
- return port;
- }
-
-
- /*
- * FindIPCPort
- *
- * Finds the IPCPort with the name supplied as argument if
- * it has been previously created.
- * Returns pointer to port if it exists AND has a server
- * -- null otherwise;
- * registers a new connection to the port (i.e. increments UseCount)
- * via UseIPCPort.
- * (Connection must be terminated when program is done by the procedure
- * DropIPCPort.)
- * It will notify a server if requested (see UseIPCPort).
- */
-
- struct IPCPort * FindIPCPort(name) char *name;
- {
- struct IPCPort * port;
- if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
- if (!name) return NULL; /* can't ever find an anonymous port! */
- Forbid();
- port = (struct IPCPort *)FindName(&IPCBasePort->ipcb_PortList, name);
- if (port) {
- if (port->ipp_Flags & (IPP_SERVED | IPP_LOADING))
- UseIPCPort(port);
- else
- port = NULL; /* no good if not currently served */
- }
- Permit();
- return port;
- }
-
-
- /*
- * GetIPCPort
- *
- * Returns a pointer to IPCPort with the name supplied as an argument;
- * unlike FindIPCPort, it always returns pointer to port -- this is
- * created if it doesn't exist; registers a new connection to the port
- * (use DropIPCPort when done). It will notify a server if requested
- * (see UseIPCPort).
- */
-
- struct IPCPort * GetIPCPort(name) char *name;
- {
- struct IPCPort * port=NULL;
- if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
- Forbid();
- if (name) /* port could be anonymous */
- port = (struct IPCPort *)FindName(&IPCBasePort->ipcb_PortList, name);
- if (!port)
- port = CreateIPCPort(name);
- if (port)
- UseIPCPort(port);
- Permit();
- return port;
- }
-
-
- /*
- * UseIPCPort
- *
- * Registers another connection to a port (outside this module,
- * this procedure is only used when the port was passed as a
- * pointer from another process).
- * (Use DropIPCPort when done.)
- * If the current server has set the IPP_NOTIFY flag in the port, the
- * server task will be signalled using the port signal bit. NOTE that
- * WaitPort will NOT detect these signals, because no message is
- * actually sent; the program must do a Wait on this bit, and should do
- * a GetMsg as usual, but if the message pointer is null it should
- * then call, for example, CheckIPCPort.
- * NOTE -- the port pointer MUST remain valid while this procedure is
- * called: either the call (from e.g. GetIPCPort) is Forbid()den, or
- * the port was passed from another process which guarantees its
- * existence.
- */
-
- void UseIPCPort(port) struct IPCPort * port;
- {
- port->ipp_UseCount++;
- if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
- Signal(port->ipp_Port.mp_SigTask,
- 1L<<port->ipp_Port.mp_SigBit);
- }
-
-
- /*
- * DropIPCPort
- *
- * Terminate a connection to a port established by FindIPCPort,
- * GetIPCPort, or UseIPCPort. Port will be destroyed if there are
- * no other connections left.
- * If the IPP_NOTIFY flag is set in the port, the server will be
- * signalled when this procedure is called (see FindIPCPort).
- */
-
- void DropIPCPort(port) struct IPCPort * port;
- {
- if (!port) return; /* to save the client some trouble
- (in a cleanup procedure) */
- Forbid();
- if (--port->ipp_UseCount == 0) {
- /* an anonymous port is NOT on list -- ALL others MUST be! */
- if (port->ipp_Port.mp_Node.ln_Name) Remove(port);
- Permit();
- FreeMem(port, (ULONG)port->ipp_Size);
- }
- else {
- if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
- Signal(port->ipp_Port.mp_SigTask,
- 1L<<port->ipp_Port.mp_SigBit);
- Permit();
- }
- }
-
-
- /*
- * ServeIPCPort
- *
- * Registers calling task as the server on the named port (which is
- * created if it doesn't exist); null is returned if the port already
- * has a server, otherwise a pointer to it is returned. At the same
- * time the port is given the server as its SigTask and a suitable
- * signal bit is allocated.
- */
-
- struct IPCPort * ServeIPCPort(name) char *name;
- {
- struct IPCPort * port;
-
- port = GetIPCPort(name);
- if (port) {
- Forbid();
- if ((port->ipp_Flags & (IPP_SERVED | IPP_SHUT))
- || (port->ipp_Port.mp_SigBit = AllocSignal(-1L)) == -1L) {
- DropIPCPort(port);
- port = NULL;
- }
- else {
- port->ipp_Port.mp_Flags = PA_SIGNAL;
- port->ipp_Port.mp_SigTask = FindTask(NULL);
- port->ipp_Flags = IPP_SERVED; /* all other bits cleared */
- }
- Permit();
- }
- return port;
- }
-
- /*
- * ShutIPCPort
- *
- * ONLY the current server may call this procedure.
- * It prevents more messages being sent to this port, but
- * does not end server connection; remaining messages can be dealt
- * with before finally calling LeaveIPCPort.
- * Note that it does NOT inhibit the server being signalled if
- * IPP_NOTIFY is set and another client connects. (At the moment
- * there is no mechanism to reopen a shut port without Leaving
- * first; this may be possible in a future revision.)
- */
-
- void ShutIPCPort(port) struct IPCPort * port;
- {
- Forbid(); /* now required because of FindIPCPort test */
- port->ipp_Flags |= IPP_SHUT; /* Prevent other servers connecting */
- port->ipp_Flags &= ~IPP_SERVED; /* prevent messages from landing */
- port->ipp_Port.mp_Flags = PA_IGNORE; /* someone might use PutMsg! */
- Permit();
- }
-
- /*
- * LeaveIPCPort
- *
- * ONLY the current server may call this procedure.
- * Disconnects the server process from the port; another process
- * can then become a server if it desires. If there are no other
- * connections, the port is removed.
- */
-
- void LeaveIPCPort(port) struct IPCPort * port;
- {
- FreeSignal(port->ipp_Port.mp_SigBit);
- port->ipp_Port.mp_SigTask = NULL;
- port->ipp_Flags &= ~(IPP_SHUT | IPP_SERVED | IPP_SERVER_FLAGS);
- DropIPCPort(port);
- }
-
-
- /*
- * CheckIPCPort
- *
- * Normally returns the number of current connections to this port
- * (including the server); if the top bit (0x8000) of the flags
- * argument is set, it will instead return the IPCPort flag word
- * value at the time of the call. (Note that these values are
- * not guaranteed!)
- * A call by the server (only) will also set the (user
- * settable) port flags to the value in the second argument --
- * currently the only valid flag is IPP_NOTIFY.
- */
-
- CheckIPCPort(port, flags)
- struct IPCPort *port;
- UWORD flags;
- {
- UWORD origflags = port->ipp_Flags;
- if (port->ipp_Port.mp_SigTask == FindTask(NULL))
- /* only server can change flags */
- port->ipp_Flags = (port->ipp_Flags & ~IPP_SERVER_FLAGS) |
- (flags & IPP_SERVER_FLAGS);
- return (int)((flags & 0x8000)? origflags : port->ipp_UseCount);
- }
-
-
- /*
- * PutIPCMsg
- *
- * Sends an IPCMessage to an IPCPort; if the port has no server or
- * is shut, the message is not sent and the function returns FALSE;
- * otherwise it returns TRUE. (Other port flags to be added later
- * may affect these actions.)
- * Note that a ReplyPort should be supplied in the message as usual
- * (except for the rare possible usage where a message is not to
- * be replied; in this case, the IPC_TRANSFER flag must be set in
- * both the message and all the items it contains, and the ReplyPort
- * must be NULL).
- */
-
- PutIPCMsg(port,msg)
- struct IPCPort *port;
- struct IPCMessage *msg;
- {
- Forbid(); /* we do this the straightforward way -- it's very quick */
- if (port->ipp_Flags & (IPP_SERVED | IPP_LOADING)) {
- PutMsg(port, msg);
- Permit();
- return TRUE;
- }
- Permit();
- return FALSE;
- }
-
-
- /*
- * CreateIPCMsg
- *
- * Creates a standard IPCMessage block (in MEMF_PUBLIC) with the
- * number of IPCItems supplied as argument. (Special cases -- like
- * in-line data -- you will have to handle yourself, and you always
- * have to manage the data blocks yourself).
- */
-
- struct IPCMessage * CreateIPCMsg(nitems, nxbytes, replyport)
- struct MsgPort *replyport;
- {
- ULONG msgsize, mlength;
- struct IPCMessage * mp;
- msgsize = sizeof(struct IPCMessage) +
- (nitems -1)*SOL(struct IPCItem) + nxbytes;
- mlength = msgsize - sizeof(struct Message);
- if (mlength > 0x0000FFFF)
- return NULL; /* no oversize messages! */
- mp = (struct IPCMessage *)
- AllocMem(msgsize, MEMF_CLEAR | MEMF_PUBLIC);
- if (mp) {
- mp->ipc_Msg.mn_Length = mlength;
- mp->ipc_ItemCount = nitems;
- mp->ipc_Msg.mn_ReplyPort = replyport;
- }
- return mp;
- }
-
-
- /*
- * DeleteIPCMsg
- *
- * Deletes a standard IPCMessage block; you must first have disposed
- * of any attached data as appropriate.
- */
-
- void DeleteIPCMsg(msg) struct IPCMessage *msg;
- {
- FreeMem(msg, SOL(struct Message) + msg->ipc_Msg.mn_Length);
- }
-
-
- /*********************************************/
-
-