Switcher
Volume Number: 3
Issue Number: 5
Column Tag: Forth Forum
Background Processing Under Switcher
By Jörg Langowski, MacTutor Editorial Board, Grenoble, France
Switcher is the closest thing to a multiprocessing system on the Macintosh (not
counting Servant, a fully functional version of which has not seen the light of day as I
write this, or Finder 6.0 which Apple is rumored to be developing). It is not really
multiprocessing, since an application that is running in one partition of Switcher is
put to sleep when you switch to another one. True multiprocessing is not easy to
achieve on the Mac because it lacks memory management hardware (this, of course,
has changed with the Mac II).
Switcher does support some background processing, however. The problem is
that the application has to cooperate to allow background tasks to run. There is no way
(no easy way, at least) that Switcher could interrupt a running application and
transfer control to another one. It is the application itself that has to draw a clear line
between foreground and background tasks, setting up pieces of its code to be run in the
background that will by themselves transfer control back to Switcher.
This is very similar to the way multiprocessing is achieved in the various Forth
and Forth-based systems that you've been reading about in this column. In those
systems, tasks have to be cooperative and transfer control back to the task switcher at
convenient points in their code (through words like PAUSE in MacForth and Mach2).
A Macintosh application under Switcher contains one interface point where
control may be transferred to another task: the GetNextEvent routine. In fact, Switcher
will only switch tasks on a call to GetNextEvent (when a switch request has been
made).
It is at this point where background processing can take place. Every time an
application calls GetNextEvent, Switcher will not only check whether you clicked the
mouse on the double arrow or issued a keyboard command to initiate switching, but
also check whether any of the installed applications contain background tasks that have
to be run while they are inactive.
Such a background task routine may be a short piece of code that checks whether a
character has come in from the serial port and stores it away in a file, or vice versa
output from a file to a serial line. It may also run some ongoing calculations, collect
data from an instrument, etc. It should return not too late after it has been called, in
order not to cause too much slowing down of the active application. The backgound task
will be part of an application installed under Switcher, where the task setup is done by
the active part of the application and the task takes over when the application becomes
inactive.
I'm sure none of you will be able to write a background task from these very
general remarks. So how does one actually do it? A very useful document to read at this
point is Inside Switcher (available through APDA). This document takes the same
holistic approach as Inside Macintosh in that it requires you to understand all of it
before you can make sense of the first paragraph (or nearly so), but otherwise gives a
very good account about the things to observe when writing applications that are to run
under Switcher.
First, lets look at the way task information is stored in Switcher. There is one
global variable, SwitcherGlobals ($282), which contains a pointer to the beginning of
the Switcher global variable area in the system heap. The structure of this area is
described in Inside Switcher, and I'll shortly summarize it here.
The example listing contains the definitions for the Switcher tables. Each
application is contained in a designated piece of memory, called a world, which is
pointed to by one of the world pointers at the beginning of the Switcher globals. An
application's world starts with an 18-byte header, followed by the application stack
and heap area. The last 32K of a world are reserved for use by Switcher.
The header of a Switcher world contains in sequence:
- PSRPtr, a handle to the process state record of the application (explained later),
- a 2-byte flag field,
- BkgdProc, a pointer to a background procedure. Each installed application may
have one BkgdProc associated with it, and Switcher calls them in sequence
whenever a null event is fetched from the event queue. If this pointer is NIL,
nothing is executed.
- SavScrBits, a handle to the part of memory that contains the saved screen bitmap
of the application. If this is =0, the screen is not being saved.
- DAPtr, a window pointer to a virtual desk accessory that the Switcher uses for
Clipboard conversion. (This procedure is explained in Inside Switcher, and I
won't go into details here).
So all we'll have to do to execute a background routine under Switcher is to store
a valid pointer to the routine in the BkgdProc field of the world header. Then Switcher
will jump to that routine on each null event and probably come up with a beautiful
bomb display unless the routine to execute is either extremely simple (such as RTS)
or we have taken further precautions.
Ideally, we'd like to write the background task in Forth and execute it in our
Forth system's world. To do this, you need some interface to the Forth code that can be
jumped to directly and returns via an RTS. In Mach2, the Forth code itself has those
properties, since it is subroutine threaded. In the case of MacForth (for example),
we'll have to store the address of the routine's code field in the BkgdProc field of the
Switcher world, and make sure the code returns via an RTS (and not through the Forth
system's NEXT routine). The example program is in Mach2, but MacForth users will
easily be able to adapt the code to their system following these principles. NEON users
will have to define the background routine using :proc and ;proc, the words provided
for defining routines whose addresses may be passed as parameters to toolbox traps.
The problem with executing a background task written in Forth using the
surrounding support of the Forth system is of course that all existing Forth systems
depend heavily on a set of registers in which parameter stack, return stack, user area
pointers, loop indices, etc. are kept. The status of the registers, however, is
completely undefined when Switcher calls the background routine. Therefore, the
simple approach described above must fail.
In order to properly execute a background routine from outside, we have to do
what is known as context switching, meaning that a) the register file has to be restored
to the values used by the Forth system, and b) any system global variables that are
used by the Forth system have to be restored as well. All this is done automatically by