Xref: utzoo comp.sys.amiga:16077 comp.sys.amiga.tech:49 Path: utzoo!mnetor!uunet!husc6!mit-eddie!ll-xn!ames!pasteur!agate!violet.berkeley.edu!pete From: pete@violet.berkeley.edu (Pete Goodeve) Newsgroups: comp.sys.amiga,comp.sys.amiga.tech Subject: IPC: Messages (again) Message-ID: <7685@agate.BERKELEY.EDU> Date: 15 Mar 88 10:45:10 GMT Sender: usenet@agate.BERKELEY.EDU Reply-To: pete@violet.berkeley.edu (Pete Goodeve) Organization: University of California, Berkeley Lines: 177 Sorry to keep banging away at IPC message structure, but I just feel that a lot of people aren't taking a wide enough look at things. There are several perhaps subtle points that I think we have to keep in mind when designing a general format. ReplyMessages _____________ Many want to insist that ALL messages should be replied, but this really doesn't work. You can't always expect the client to still be around when the message has been processed. A likely case is a program that wants to send messages to the shell that invoked it, which won't resume processing until the sending program terminates. Also, you might want to "plant" a port and go away, leaving it without an owner for a while, letting it collect messages that will be handled when the owner returns to memory. More urgently (for those who think the previous scenarios too fanciful), if you are sending MIDI messages or some other time-critical type of data, the last thing you want is to burden yourself with replying and handling replies. (FreeMem probably takes as much time as Replying, but it doesn't involve the scheduler, and anyway in a situation like that you would quite likely have fixed-size messages that the server could in turn pass on somewhere else.) My philosophy is to try and hammer out a protocol that won't prove restrictive for future applications and extensions, so I think we have to allow for the possibility that a client won't want its message back. A server therefore must be prepared to deallocate that memory block if no ReplyPort is given. Data ownership and pointers ___________________________ This is linked to the previous topic, because if a message always returns to its origin we can assume that it owns all the data it references. If we pass a message one way only, though, the server becomes responsible for the memory involved. This means we must take more care with pointers in this situation. Any data that is pointed to must become the "property" of the server as well as the message itself, and will be deallocated along with the message. It MUST of course have been obtained with AllocMem by the client. Things are a lot simpler if the message block itself is the only memory involved; then we can just use the mn_Length field (adjusted suitably) for FreeMem. This is why I think we shouldn't always use pointers where they otherwise might look appropriate. I don't think we can make any general rule here: just be aware of the requirements of a particular application. A specially awkward case is the ln_Name field of a message. If we use this as a pointer to an identifying string we could get a little stuck. Unless we followed the cumbersome route of allocating space for the name string (when no doubt we would rather point directly to a string constant), the name would go away with the client. For this reason I suggested that it probably was NOT a good idea to use this as a pointer, and that would leave it free as our ID word. I was immediately flamed for this, so perhaps to keep everyone happy we should just ignore this field: anything it points to is not part of the message space (and may no longer exist). Altering message contents _________________________ It has also been suggested that message contents shouldn't be changed except for a status word, but again this is unnecessarily restricting the ways a message might be used. After all, one of its main purposes is to request and return data! A client should expect that any part of the message data area may have been rewritten (meaningfully of course!) on reply. The only item that should absolutely NOT be altered is the length field, and I can't see any reason to muck with the ReplyPort. Probably the ID word should be inviolate also; there might be some situations though where you would want to mark a change in structure by a change in ID. "Serial" message contents _________________________ One of the major advantages of messages is that they require little overhead to pass. We should therefore avoid formats that add to the overhead unless they are particularly appropriate for that task. My picture of the ideal message is a simple fixed structure that gives you direct access to any item within it, and my reaction to a couple of the recent proposals is that they rather violate this principle unnecessarily by using multiple chunks that can only be reached by scanning from the beginning. There's nothing at all inherently wrong with multiple chunks, but because most applications won't need them I don't think they ought to be part of the fundamental IPC message structure -- unnecessary overhead again. If they are used in a particular format, the first part of the data section should be a set of pointers to the individual blocks, so access is direct rather than sequential. Message status ______________ I touched on this in another article, but I include it here for completeness. It's a good idea to keep the generic "outcome" report in a replied message separate from application specific status flags. There are really very few possible outcomes, which I would flag as follows in the ln_Type byte: NT_MESSAGE -- as originally sent by the client NT_REPLYMSG -- normal reply after processing NT_UNKNOWN -- (= 0) the server couldn't handle this type NT_NOSERVER -- (new type = 0xFF) returned by a port broker that handles the message in lieu of an owner If there are any I've left out, there's lots of spare codes, and no real way any particular choice could conflict with other uses. Standard IPC Message Format ___________________________ So I come back pretty much to my original absolutely fundamental IPC message format, on which we can build applications without getting hamstrung by hidden assumptions. struct IPCMessage { struct Message ipc_Msg; ULONG ipc_ID; /* four characters or integer < 1<<24 */ /* data follows -- structure determined by ipc_ID */ }; All entries in the ipc_Msg structure must be filled in appropriately, except that ln_Name should normally be NULL. If ReplyPort is NULL, the server will deallocate the message up to the end of the data as indicated by ipc_Msg.mn_Length. Publicly defined ID codes will have a non-zero ASCII character in the first byte. Codes agreed on privately between client and server should have the high byte zero. Publicly defined IDs to be shared between applications have associated unambiguous data structure definitions that anyone can use to get at the data. Then we can consider a common extension that would be shared by a number of IDs (swiped totally without shame from Peter da Silva's BRWS structure -- only the names have been changed to protect something or other...): struct StdIPCMessage { struct Message ipc_Msg; ULONG ipc_ID; ULONG ipc_Status; /* application specific */ ULONG ipc_Func; /* specific nature of this message */ SHORT ipc_Entries; /* number of items following */ APTR ipc_Items[]; /* data items OR pointers to items depending on the ID */ /* Any data items pointed to will follow message AND be included in the mn_Length */ }; I've suggested a two level naming structure here: ipc_ID and ipc_Func. ipc_ID is the primary identification of the class and structure of the message; ipc_Func is used to break it down into application specific actions. In Peter's Browser example, ipc_ID = 'BRWS; and ipc_Func = 'ADDF', 'DELF', and so on. Note that the ipc_Items array might contain simple long values, or it could as shown here contain pointers to the actual data items (Peter's "IPCEntry"s) so we can access them without sequential search. Such data items would normally be included in the message block to keep memory management simple. There might be occasions where they would be external to the message, but the ID would have to indicate this. Actually if you look carefully at Peter's Browser structure, although there are a variable number of entries, the format for each action is strict enough that you would know where everything was without searching. You could do without the pointers in this case (so our only real disparity is in the function and positioning of the ID words. Can we get together on this Peter?) If the data items were something like IFF chunks, though, I think the pointer scheme would be well worth the slight extra space.