Path: utzoo!mnetor!uunet!lll-winken!lll-tis!ames!pasteur!ucbvax!CORY.BERKELEY.EDU!dillon From: dillon@CORY.BERKELEY.EDU (Matt Dillon) Newsgroups: comp.sys.amiga Subject: Re: IPC Message-ID: <8803071801.AA15904@cory.Berkeley.EDU> Date: 7 Mar 88 18:01:30 GMT Sender: daemon@ucbvax.BERKELEY.EDU Lines: 388 Here is a more substantiated description of my ideas for IPC. Note that it is still INCOMPLETE! For instance, the actual format of the macro command string is not specified as yet. THIS IS A BRAINSTORM. Essentially, the whole thing is hidden by a run time library, with only the tip showing (a port structure) so you can use Wait(), etc... While all of you are talking about low level formats and assume nobody will ever want to upgrade the system in an upward compatible manner, I've been doing some serious thinking on what kind of capability I would want THAT DOESN'T CURRENTLY EXIST. IFF is definately the way to go. Naturally, we don't want to encumber the application programmer with figuring out all the offsets and stacking, etc... Ending comment: You asked for it! eport = SarcInit(applname, ops, cmdvect) An application logs its name with SARC. The application name need not be unique, and will automatically be qualified with a two digit number 00-99 to make it unique (you must provide enough space in the string for this). applname must also exist throughout the session. ops is a string containing the following options: (none at the moment) cmdvect is a vector to your command interpreter which is called when doing synchronous SARC commands. SarcDo()/SarcSend() is fully reentrant. For instance, if you are in the middle of a SarcRead() which has blocked, and some other application sends you a command, cmdvect will be used to execute the command without interrupting the SarcRead() (effectively, SarcRead() detects the reception, calls cmdvect, then returns to it's wait). (void) SarcUnInit(eport) Closes the SARC port and unlogs the application. Any pending commands will be returned (and removed if the SARC port was the returnee). REMOTE CONTROL AND COMMAND EXECUTION SARC remote control and command execution occurs through the extended PORT structure returned by SarcInit(). Remote commands are sent to the port via the SARC library from other processes. Macro commands may be sent to SARC which causes SARC to interpret them and relay them to one or more other processes (including the possibility of putting some of these commands back in your lap). error = SarcDoCmd(eport, command, outstream, instream, errstream) Execute a synchronous macro command. This call sends the specified command and streams to SARC, which interprets and executes it. This call does NOT return until the command is complete, but may make calls to CMDVECT specified in SarcInit() to accomplish the command. Any stream which is NULL will become a NUL stream. error = SarcSendCmd(eport, command, outstream, instream, errstream) Execute an asynchronous macro command. Exactly like SarcDoCmd(), but returns immediately. You can tell when the command is complete when SarcRemoteCmd() returns a non-zero asycmdcomplete. You can stack multiple SarcSendCmd()'s which will be run in order. You MUST use SarcRemoteCmd() when using SarcSendCmd() or completion messages may build up on eport. NOTE: SarcDoCmd() waits for any pending SarcSendCmd()'s to complete before running. This also clears the asynccmdcomplete messages automatically (asynccmdcomplete set to 0). error = SarcRemoteCmd(eport, &asyccmdcomplete, cmdvec) When you have determined something has been sent to eport, make a call to SarcRemoteCmd() to interpret the message. If you give a non-NULL &asycmdcomplete, it will be filled with the number of SarcSendCmd() requests which have completed since the last time you asked. If you give a non-null cmdvec, that cmdvec becomes the global cmdvec (replacing the one given in SarcInit()), and if any remote commands are ready, will be called in the following format: (*cmdvec)(command, outstream, instream, errstream) Otherwise, if cmdvec is NULL, pending remote commands stay pending. STREAMS SARC STREAMS are used to transfer data between applications. Streams can operate in one of two modes: IFF or RAW. Additionaly, mode mixing is allowed. (Process A writes in RAW mode while process B reads in IFF mode). Various calls are available to do operations synchronously, asynchronously, with, and without buffer copying. Streams are strictly common-ground datapaths. To get two-way communication you must open two independant streams, write to one and read from the other (while the other task reads from one and writes to the other). A SARC stream is front-ended by a PORT structure. Thus, one can 'WaitPort' or Wait() on its signal to wait for something to happen. This does not neccesarily mean that data is available for reading, but may also occur when, for example, an asynchronous write has completed. The Use of Wait() is usually coupled with the "n" A special stream "NUL" exists which always returns an EOF and is a sink for write's. stream = SarcOpen(name, modes, &error) Open a SARC stream. If the name buffer supplied holds a zero-length string, a unique name of 9 digits is generated and loaded into the name buffer. Otherwise, the supplied name is used as a common reference allowing processes to rendezvous with each other. If "c" is not specified, the stream name must already exist (either currently openned by another process or holding data). Note: "Exclusive access" means that only one SarcOpen() is allowed on a specific descriptor. You can still SarcDup() it with different modes and pass it to a remote process as stdin, stdout, or stderr. modes: "r" read "w" write "s" seek (random access), else assumes sequential access "c" create it if it doesn't exist, error if it does "i" IFF, else RAW "F" use an actual FILE to hold data (always in RAW mode). Otherwise, a memory buffer is used. "R" don't block on reads when requested data is not available. "X" Exclusive access error is set to 0 on success, or an error code on failure: -1 : not found -2 : already open for exclusive access, etc.. error = SarcSeek(stream, position, how) Seek within a file. Provides random access capability. You must have specified "s" in the SarcOpen(). IFF streams: SarcSeek() works ONLY within the lowest level data sections. newstream=SarcDup(stream, modes) Get a duplicate descriptor with different modes. For instance, you might have openned an exclusive access stream for read, and now want another descriptor that can be written to. You must specify all applicable modes except for "c", which doesn't make sense in this case. Usually used to make one way link-pairs and then using one as the stdin, stdout, or stderr streams. newstream = SarcDup(oldstream, "ws"); error = SarcClose(stream) Close a SARC stream. An error might be returned as follows: -1 Nobody ever read the data -2 Somebody didn't read all the data -3 Illegal or Incomplete IFF usage -4 IFF too complex -5 Ran out of memory -8 NOT IFF -9 IFF ERROR error = SarcDelete(name) Delete the specified SARC file. The file is removed from the namespace. However, if the file is currently open those descriptors are still valid and the memory taken up by the file is not freed until those descriptors are closed. This works even if the file is a real file (but only if all entities go through SARC to access it). (void) CheckSarcStream(stream, &error, &readytoread, &asywritecomplete) All variables are pointers to longs. Any argument may be passed as NULL if you aren't interested in the information. error: Current status 1 active 0 EOF -? some error readytoread: N # bytes available to be read (RAW) N # blocks (read calls) that can be done (IFF) asywritecomplete: N # of asynchronous write requests which have completed since the last time you requested this status (passed a non-null variable as &asywritecomplete) bytes = SarcRead(stream, buf, bytes) Read from a stream into a buffer. This call blocks until the specified number of bytes has been read or an EOF or error occurs. If the stream has been specified as IFF, decoded IFF data is read as shown in the example. Essentially, control information is qualified by a +,-, or =. The block of data after an '=' is always data and not qualified. Note that the bytes returned can be less than the bytes requested (block oriented). You MUST specify at least a 16 byte buffer when reading control information. CHUNK data can be read in multiples of any size. The number of bytes in the CHUNK data is given in the preceding "=????nnnnnnnnnn\0" buffer. 11= SarcRead(s, buf, 32); "+FORM\08SVX\0" 16= SarcRead(s, buf, 32); "=VHDR0000000020\0" (20 byte chunk) 20= SarcRead(s, buf, 1000); a Vhdr structure 16= SarcRead(s, buf, 32); "=NAME0000000008\0" (8 byte chunk) 8= SarcRead(s, buf, 53); a Name structure (just the name) 16= SarcRead(s, buf, 32); "=(c) 0000000018\0" (18 byte cpy right) 5= SarcRead(s, buf, 5); /* don't have to read it all at once */ 13= SarcRead(s, buf, 40); 16= SarcRead(s, buf, 32); "=BODY0000001022\0" 1022= SarcRead(s, buf, 1022); The body data... 6= SarcRead(s, buf, 32); "-FORM\0" 0= SarcRead(s, buf, 32); EOF This works with SarcARead() as well. bytes = SarcARead(stream, &buf, bytes) Same as SarcRead(), but uses SARC's internal buffer (maybe even the writer's buffer) to hold the data. The 'buf' pointer is modified to point to the buffer. The buffer is good until the next SarcRead() or SarcARead() call from that stream. This call may cause SARC to reorganize its internal buffers if the requested # bytes do not occur contiguously internally. IFF: Same as SarcRead(). NOTE: If -1 specified for bytes, all the available data is made contiguous and returned. If -2 specified for bytes, no reorganization occurs and you get data fragments (THIS IS THE PREFERED METHOD OF USING SARCAREAD). bytes = SarcWrite(stream, buf, bytes) Write data from a buffer to a SARC stream. The buffer is copied into SARC's internal buffers. This call never blocks. When writing IFF, the chunk names are written separately from the chunks. Stacking is accomplished by specifying the chunk-name-type as +,-, or =: + Level down - Level up = Low level chunk This allows you to organize your IFF easily. SARC automatically puts it in the correct format (adds padding and length specs, etc...) and checks it for validity. SarcWrite(s, "+FORM8SVX", 9); SarcWrite(s, "=VHDR", 5); SarcWrite(s, &Vhdr, sizeof(Vhdr)); SarcWrite(s, "=NAME", 5); SarcWrite(s, Name, strlen(name)); SarcWrite(s, "=(c) ", 5); SarcWrite(s, Cpw, strlen(Cpw)); SarcWrite(s, "=BODY", 5); SarcWrite(s, Body, 1024); SarcWrite(s, "-FORM", 5); bytes = SarcDoWrite(stream, buf, bytes) Write data from a buffer to a SARC stream. This call blocks until the recipient has read and processed the data. The caller's buffer is used (usually means no data copying). bytes = SarcSendWrite(stream, buf, bytes) Same as SarcDoWrite(), but this call does not block. The caller's buffer is used and thus should not be modified until the operation is complete. Any number of Send's may be queued on a stream. Calling SarcDoWrite() after a Send is equivalent to waiting for all Sends to complete AND the Do to complete. > >A proposal > >Yeah, let's do it! It's high time that the Amiga had a standard method of >inter-process communication. With products like ARexx and Browser coming >out, we may get stuck with a "de-facto" standard which may not be well >designed, or several "standards" that are mutually incompatable. ("It's >supports ARexx, but does it support Browser?") I agree, but let's do it right. One reason the clipboard device never caught on (to non-commercial programs) is that it is difficult to use. ARexx is interesting, but also somewhat convoluted and takes too much overhead. Whatever *we* decide on, it must be made relatively easy to use. But we don't simply want a stream oriented IPC. People don't take enough time to think of easy ways to provide maximum power and capability. This is what I envision: -Low per-task memory overhead -Symbolic Asynchronous Remote-Control capability (domain based) e.g. one application can send symbolic commands to another... A data stream, as part of a command, would be a minor item in this system. (DME.file1(unblock block block) DME.file2(bcopy)) Also, have a standard format for SARC menu item simulation. e.g. DME(File-Quit) -Macro Expansion capability (which is a superset of what ARexx would give us) e.g. program X gives the driver a symbolic macro which the driver executes, possibly causing remote-control commands be sent back to program X and/or to other applications. -Fully reentrant and stackable remote commands, with infinite loop detection, etc... e.g. program X sends a Macro to the driver, which sends resolved commands back to program X which just happen to be macros in program X which is expanded again and sent as a Macro to the driver, which sends resolved commands to program's X and Y, program X executing the command, etc.... Also allowing program X to handle such things synchronously or asynchronously (not wait for macro completion before continuing, but not getting confused either). -Exit recovery. Allow program X to exit at any time without screwing up any macro's in progress (have them gracefully fall to their deaths). i.e. calling SarcClose() clears anything pending. -Standard command format. Ability to specify logical or physical streams (i.e. supply your own functions or use DOS functions, etc...). -Low level IFF decoder for arbitrary IFF streams. Since IFF is structured, the decoder would not have to know specific formats, -Ditto Low level IFF encoder for arbitrary IFF chunks.. Structures the chunks for you. -Run time Library to handle interaction. I could work on something like this after I get DNET out. -Matt