Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!mcvax!cernvax!hslrswi!paul From: paul@hslrswi.UUCP (Paul Breslaw) Newsgroups: comp.lang.modula2 Subject: Re: Coroutines vs. Operating Systems Message-ID: <312@hslrswi.UUCP> Date: Thu, 27-Nov-86 05:35:34 EST Article-I.D.: hslrswi.312 Posted: Thu Nov 27 05:35:34 1986 Date-Received: Fri, 28-Nov-86 20:34:33 EST References: <8611201656.AA00169@odysseus.sun.uucp> Reply-To: paul@hslrswi.UUCP () Organization: Hasler AG, Berne, Switzerland Lines: 139 In article <8611201656.AA00169@odysseus.sun.uucp> crb@SUN.COM (Chuck Bilbe) writes: > The following approaches suggested >themselves to me, but undoubtedly there are many others: >A . ..... make NEWPROCESS, TRANSFER, and IOTRANSFER > part of a nonstandard Unix kernel. >B . ... forbid process priorities and the IOTRANSFER procedure > altogether; Implement NEWPROCESS and TRANSFER only. >C . Use IOTRANSFER as a mechanism for waking up a > coroutine on a Unix signal, treating Unix signals as > if they represented the hardware interrupts implied > by IOTRANSFER. Mask and unmask these signals as > implied by coroutine priorities >D . Assert that the coroutine abstraction isn't very > useful. Well I would choose C. In fact I already have, and it seems to work nicely. But then one asks 'nicely for what?'. Our use of Modula-2 is primarily for embedded real-time systems, which use real-time kernels designed specifically for each application. Naturally the kernels are themselves written in Modula-2. Our development environment is 4.2, on which we have both host and cross development tools. Our goal is to develop as much as possible of the cross software on the host, before committing it to target hardware. To this end we want to be able execute all the concurrency facilities of Modula-2 on the host with a semantics that is a close as possible to that which is found on the target. This excludes options D (co-routines are VERY useful) and B (we need to execute the target software as closely as possible on Unix). This is our primary aim. But since Modula-2 is also the language in which all the tools are written (compilers, linkers, debuggers, configuration management, etc), it must also be useable for implementing Unix applications. We view Modula-2 as a system executing within a given environment. This can mean 'no environment' in the case of a naked machine, or the Unix environment in the case of Unix. These we take as given, hence there is no need or wish to change them. If you like, the environment is the virtual machine. This excludes option A. Now naked machines have interrupts as their form of external (asynchronous) event. Unix (or to be more precise 4.[23]bsd) has signals as its external events. Hence IOTRANSFER is interpreted as IOTRANSFER (VAR from, to : PROCESS; externalevent : CARDINAL) where 'externalevent' is an interrupt vector on an Intel 80xxx, an interrupt vector address on a PDP/LSI-11, or a signal number on 4.x. Priorities are interpreted according to the dictates of the environment, exactly as Wirth originally specified they should be. So on an Intel machine, the priorities are what you do with your interrupt controllers - this is why Logitech provide their customers with a target run-time system in source form. On a PDP-11 it's the priority that you set on each physical interface. On an LSI-11 all devices have the same hardware priority. On 4.x all signals have the same priority - see sigvec(2), so I do not see the need to assign > the huge multitude of Unix signals its own priority > level, in some arbitrary order. The semantics I use only implicates those signals which use or have used IOTRANSFER. These are blocked/unblocked as the flow of control enters/exits high priority code, irrespective of the priority. Other signals are completely unaffected. This reduces the objection > Modula-2 programs wouldn't work well in > harmony with C functions that play with signals > outside the domain of the Modula-2 compiler. to something of a straw man. Who, for example, would write a conventional real-time program where separate parts play independent games with the same interrupt vector? Caveat programmer ! The next comment seems to reveal more a mixing of programming styles than a genuine difficulty. > Note that with this > implementation, the entire family of coroutines > will still go "to sleep" if one of them performs > a system call like "read", for example. Again take an example from 'classical' real-time programming. If one part of the program polls, say, a serial input device, then the rest of the program 'goes to sleep' during the poll. If that's what you want, that's what you get. To implement the serial input driver as a concurrent entity one must first of all arrange for the external event (serial device) to notify the virtual machine (cpu) that an event has occurred (interrupt), and to write the device driver as a device process, where it waits FIRSTLY for the event and only THEN reads the device. Now a Unix system call, like read(2), is the equivalent operation of the poll. If we genuinely want the section of code that does the reading to be a quasi-concurrent process, then we must make the same arrangements as we do in the example above. The sender must arrange to notify the device process that data is available. This wakes up the device process, which then calls read(2), but now it will be virtually non-blocking. Enter SIGIO - on tty's in 4.2, and in 4.3 (bless them !) on sockets too. Without SIGIO a fairly good second choice is for a child process to do the blocking read, write to a pipe which has the Modula-2 Unix process on the other end, and then send an agreed-upon signal to Modula-2. This wakes up the device process as desribed above. One other nice thing about this method is that hardware device drivers for target machines can be emulated very easily on the Unix host. For example, an algorithm for a process delay timer destined for a target machine, was completely implemented and debugged on Unix using SIGVTALRM and {set,get}itimer(2). Only the system dependent parts, starting and reading the clock, needed to be altered from one system to another. You may ask how efficient this all is. Not particularly, but then signal driven I/O was not provided on 4.x for efficiency, but to solve a certain class of problem. If your problem isn't one of them, then don't use it. The saving grace of the present implementation, is that a given application can use blocking system calls, signals as device processes, and conventional signal handlers together - as long as the designer is aware of their different effects. > Another difficulty is the time-dependent and non-atomic > interactions between signals and Unix processes. I'm not quite sure what this means. Can someone enlighten me ? Finally, and apart from all of the above, I chose this option because it is implemented in 60 lines ..... of assembler. Paul Breslaw ****************************************************************************** Paul Breslaw Hasler AG, Belpstrasse 23, CH-3000 Berne 14, Switzerland Tel.: +41 31 632932 X.400: paul@hslrswi.hasler Uucp: ... {seismo,decvax,ukc, ... }!mcvax!cernvax!hslrswi!paul Bitnet: paul%hslrswi.UUCP@cernvax.BITNET Arpa: paul%hslrswi.UUCP%cernvax.BITNET@wiscvm.ARPA Edu: paul%hslrswi.UUCP%cernvax.BITNET@ucbjade.Berkeley.EDU ******************************************************************************