pete@violet.berkeley.edu (03/10/88)
[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 --
cmcmanis%pepper@Sun.COM (Chuck McManis) (03/11/88)
[Well, I like it :-) ] In article <7574@agate.BERKELEY.EDU> ( Pete Goodeve ) writes: > 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. Well, on FALSE return it could fake the reply as well as return FALSE but that may be a bit exotic. How about change the mn_Type to LONELY_MSG or something to indicate the client attempted to send it to a closed port? > 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(). Things stay much simpler when you have distinct client/server relationships. If you want bi-directional asynchrounous communication you should be required to act as both a server and a client. If you are going to make these things really asyncronous bidirectional links you might as well implement "streams" right now no? Which brings up an interesting question, AT&T streams let you 'push' a 'module' onto a stream, essentially injecting itself into the message flow. Would such a capability be useful for our IPC links? It means messages would flow through 'n' message ports but it would be nice to put say a filter in the message stream that dumped messages that you didn't want to deal with. --Chuck McManis uucp: {anywhere}!sun!cmcmanis BIX: cmcmanis ARPAnet: cmcmanis@sun.com These opinions are my own and no one elses, but you knew that didn't you.