Xref: utzoo comp.sys.amiga:15782 comp.sys.amiga.tech:15 Path: utzoo!mnetor!uunet!husc6!hao!ames!eos!aurora!labrea!agate!violet.berkeley.edu!pete From: pete@violet.berkeley.edu Newsgroups: comp.sys.amiga,comp.sys.amiga.tech Subject: IPC: Safe Ports (in any Storm?) Message-ID: <7574@agate.BERKELEY.EDU> Date: 10 Mar 88 08:10:49 GMT References: <5375@well.UUCP> <739@nuchat.UUCP> <7455@agate.BERKELEY.EDU> <2116@polya.STANFORD.EDU> <44626@sun.uucp> Sender: usenet@agate.BERKELEY.EDU Reply-To: pete@violet.berkeley.edu.UUCP ( Pete Goodeve ) Organization: University of California, Berkeley Lines: 146 Keywords: InterProgram Communication, message, port, FindPort Summary: a simple way to avoid FindPort hazards [First off, apologies (with much blushing) for duplicating ALL my messages yesterday. I thought the system was up to its old tricks and swallowing all followup messages; then just as I finished "rectifying" the situation they all magically popped up. Gnunh! Sorry again.] [This new heading derives from discussion under "Re: IPC - a proposal"] How about this for a hazard proof InterProcess Port? It's based on Chuck McManis' original suggestion <44626@sun.uucp>, with some further simplifications. I've put the necessary sequences into procedures to make a clean interface. I've also used Yet Another Structure (IPLink), which just helps encapsulate things a bit more; it isn't strictly essential. Server end of link: The server program will use CreatePort() and DeletePort() as usual, but one extra almost trivial operation is needed when it wants to close a port: ================================================================== void CutPort(port) struct Port *port; { RemPort(port); /* Removes port from public list to prevent new processes from acquiring it */ port->mp_Node.ln_Name = 0; /* Mark port as invalid for IPLink -- all further messages blocked */ } ================================================================== No Forbid/Permits are necessary because neither of these operations is hazardous. (I ASSUME RemPort() is protected internally!) After calling this procedure, the server can reply to any outstanding messages on the port at its leisure, then call DeletePort() to get rid of it completely. Chuck's safe area is not needed because the port is now isolated from new messages (provided they are sent through the IPLink protocol below). %%%%%%%%%%%% Client end of link: At the Client end we need to do a little more. For a start we want a structure defined to hold the port protection information: ================================================================== /* IPC.h */ struct IPLink { struct Port *ipl_Port; /* will get filled with port address */ APTR ipl_Key; /* a copy of the port's name pointer */ char *ipl_Name; /* the port's name (not strictly necessary, but may be useful) */ }; ================================================================== And we provide these procedures: ================================================================== /* * Connect to a named public port in another process */ struct * IPLink ConnectIPL(ipl, name) struct IPLink *ipl; char *name; { ipl->ipl_Port = ipl->ipl_Key = NULL /* for safety */ if (name) /* if we don't provide name, one already in IPL is used */ ipl->ipl_Name = name; Forbid(); ipl->ipl_Port = FindPort(ipl->ipl_Name); if (ipl->ipl_Port) ipl->ipl_Key = ipl->ipl_Port->mp_Node.ln_Name; Permit(); return (ipl->ipl_Port && ipl->ipl_Key) ? ipl : NULL; /* as SUCCESS indicator, but pointer could be convenient */ } /* * Send message to connected port */ PutIPL(ipl, msg) struct IPLink *ipl; struct Message *msg; { int res; msg->mn_Node.ln_Type = NT_MESSAGE; /* The standard protocol should be strictly obeyed (and server should change this to NT_REPLYMSG when replying) */ Forbid(); if (res = (ipl && ipl->ipl_Port && ipl->ipl_Key == ipl_Port->mp_Node.ln_Name)) PutMsg(ipl->ipl_Port, msg); Permit(); return res; } ================================================================== Usage should be fairly clear, I think. The client declares storage for as many IPLinks as it will need (which can be anywhere in its static data space), and calls ConnectIPL(&myipl,"portname") for each. (I put in the option of storing a pointer to the desired name in the structure before calling ConnectIPL(&myipl, NULL), if this happens to be more convenient.) It then can send its messages freely via the PutIPL call. If the port happens to have been closed, the call will return FALSE; the client must always look for this, naturally, or it may wait for a reply forever. The client doesn't have to take any particular action when it wants to terminate the conversation -- unless it has done something like give the server permission to originate messages to the reply port, in which case it would have to inform the server (and wait for confirmation!). If we wanted, we could use this technique on a reply port too. In this case the name would usually be a PRIVATE one (though I can't see any reason a reply port couldn't also be used as a public port). The server would store the key away in an IPLink, and reply through a ReplyIPL() procedure analogous to PutIPL(), though it wouldn't use ConnectIPL(). As a general point, all messages sent though this channel should have the mn_Node.ln_Type filled in appropriately (NT_MESSAGE or NT_REPLYMSG), because if it isn't enforced now it won't be there when we suddenly need it. Also, I think there's a need for a further type -- NT_UNKNOWNMSG -- to be returned instead of NT_REPLYMSG if the server can't handle it. (See the ongoing discussion on message format.) I had better admit that I haven't even compiled these code segments yet. [How many typos can YOU spot?] But aside from that, can anyone spot any flaws in my logic? (aside from the exceedingly remote chance of a key value being exactly duplicated in any data that happens to write over that slot.) -- Pete --