Article View: comp.lang.modula2
Article #3529Re: Coroutines vs. Operating Systems
From: Paul Breslaw
Date: Thu, 27 Nov 1986 10:35
Date: Thu, 27 Nov 1986 10:35
139 lines
7739 bytes
7739 bytes
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
******************************************************************************
Technical Details
Message-ID:
<312@hslrswi.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!mnetor!seismo!mcvax!cernvax!hslrswi!paulReferences:
<8611201656.AA00169@odysseus.sun.uucp>Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: arc1-ca.pugleaf.net!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