1 THE INTERLISP-D PROCESS MECHANISM

The Interlisp-D Process mechanism provides an environment in which multiple
Lisp processes can run in parallel.  Each executes in its own stack space,
but all share a global adress space.  The current process implementation is
cooperative; i.e., process switches happen voluntarily, either when the
process in control has nothing to do or when it is in a convenient place to
pause.  There is no preemption or guaranteed service, so you cannot run
something demanding (e.g., Chat) at the same time as something that runs
for long periods without yielding control.  Keyboard input and network
operations block with great frequency, so processes currently work best for
highly interactive tasks (editing, making remote files).

In Interlisp-D, the process mechanism is already turned on, and is expected
to stay on during normal operations, as some system facilities (in
particular, most network operations) require it.  However, under
exceptional conditions, the following function can be used to turn the
world off and on:

(PROCESSWORLD FLG)
          Starts up the process world, or if FLG = OFF, kills all processes
          and turns it off.  Normally does not return.  The environment
          starts out with two processes: a top-level EVALQT (the initial
          "tty" process) and the "background" process, which runs the
          window mouse handler and other system background tasks.  A
          HARDRESET automatically turns the process world on (or resets it
          if it was on), unless the variable AUTOPROCESSFLG is NIL.

          Note: PROCESSWORLD is intended to be called at the top level of
          Interlisp, not from within a program.  It does not toggle some
          sort of switch; rather, it constructs some new processes in a new
          part of the stack, leaving any callers of PROCESSWORLD in a now
          inaccessible part of the stack.  Calling (PROCESSWORLD 'OFF) is
          the only way the call to PROCESSWORLD ever returns.



1.1 Creating and Destroying Processes

(ADD.PROCESS FORM PROP1 VALUE1 PROPN VALUEN)
          Creates a new process evaluating FORM, and returns its process
          handle.  The process's stack environment is the top level, i.e.,
          the new process does not have access to the environment in which
          ADD.PROCESS was called; all such information must be passed as
          arguments in FORM.  The process runs until FORM returns or the
          process is explicitly deleted.  An untrapped error within the
          process also deletes the process (unless its RESTARTABLE property
          is T), in which case a message is printed to that effect.

          The remaining arguments are alternately property names and
          values.  Any property/value pairs acceptable to PROCESSPROP may
          be given, but the following two are directly relevant to
          ADD.PROCESS:


                       NAMEValue should be a litatom; if not given, the
                       process name is taken from (CAR FORM).  ADD.PROCESS
                       may pack the name with a number to make it unique.
                       This name is solely for the convenience of
                       manipulating processes at Lisp typein; e.g., the
                       name can be given as the PROC argument to most
                       process functions, and the name appears in menus of
                       processes.  However, programs should normally only
                       deal in process handles, both for efficiency and to
                       avoid the confusion that can result if two processes
                       have the same defining form.
                       SUSPENDIf the value is non-NIL, the new process is
                       created but then immediately suspended; i.e., the
                       process does not actually run until woken by a
                       WAKE.PROCESS (below).

          (PROCESSPROP PROC PROP NEWVALUE)
          Used to get or set the values of certain properties of process
          PROC, in a manner analogous to WINDOWPROP.  If NEWVALUE is
          supplied (including if it is NIL), property PROP is given that
          value.  In all cases, returns the old value of the property.  The
          following properties have special meaning for processes; all
          others are uninterpreted:


                       NAMEValue is a litatom used for identifying the
                       process to the user.
                       RESTARTABLEValue is a flag indicating the
                       disposition of the process following errors or hard
                       resets:


                                       NIL or NO(the default) If an
                                       untrapped error (or ↑E or ↑D) causes
                                       its form to be exited, the process
                                       is deleted.  The process is also
                                       deleted if a HARDRESET (or ↑D from
                                       RAID) occurs, causing the entire
                                       Process world to be reinitialized.
                                       T or YESThe process is automatically
                                       restarted on errors or HARDRESET.
                                       This is the normal setting for
                                       persistent "background" processes,
                                       such as the mouse process, that can
                                       safely restart themselves on errors.
                                       HARDRESETThe process is deleted as
                                       usual if an error causes its form to
                                       be exited, but it is restarted on a
                                       HARDRESET.  This setting is
                                       preferred for persistent processes
                                       for which an error is an unusual
                                       condition, one that might repeat
                                       itself if the process were simply
                                       blindly restarted.
                       FORMValue is the Lisp form used to start the process
                       (readonly).
                       AFTEREXITValue indicates the disposition of the
                       process following a resumption of Lisp after some
                       exit (LOGOUT, SYSOUT, MAKESYS).  Possible values
                       are:


                                       DELETEDelete the process.
                                       SUSPENDSuspend the process; i.e., do
                                       not let it run until it is
                                       explicitly woken.
                                       <an event>Cause the process to be
                                       suspended waiting for the event
                                       (page X.XX).
                       INFOHOOKValue is a function or form used to provide
                       information about the process, in conjunction with
                       the process status window (page X.XX).
                       WINDOWValue is a window associated with the process,
                       the process's "main" window.  Used in conjunction
                       with switching the tty process (page X.XX).
                       TTYENTRYFNValue is a function that is applied to the
                       process when the process is made the tty process
                       (page X.XX).
                       TTYEXITFNValue is a function that is applied to the
                       process when the process ceases to be the tty
                       process (page X.XX).

          (THIS.PROCESS)
          Returns the handle of the currently running process, or NIL if
          the Process world is turned off.

(DEL.PROCESS PROC)
          Deletes process PROC.  PROC may be a process handle (returned by
          ADD.PROCESS), or its name.  Note that if PROC is the currently
          running process, DEL.PROCESS does not return!

(PROCESS.RETURN VALUE)
          Terminates the currently running process, causing it to "return"
          VALUE.  There is an implicit PROCESS.RETURN around the FORM
          argument given to ADD.PROCESS, so that normally a process can
          finish by simply returning; PROCESS.RETURN is supplied for
          earlier termination.

(PROCESS.RESULT PROCESS WAITFORRESULT)
          If PROCESS has terminated, returns the value, if any, that it
          returned.  This is either the value of a PROCESS.RETURN or the
          value returned from the form given to ADD.PROCESS.  If the
          process was aborted, the value is NIL.  If WAITFORRESULT is true,
          PROCESS.RESULT blocks until PROCESS finishes, if necessary;
          otherwise, it returns NIL immediately if PROCESS is still
          running.  Note that PROCESS must be the actual process handle
          returned from ADD.PROCESS, not a process name, as the association
          between handle and name disappears when the process finishes (and
          the process handle itself is then garbage collected if no one
          else has a pointer to it).

(PROCESS.FINISHEDP PROCESS)
          True if PROCESS has terminated.  The value returned is an
          indication of how it finished: NORMAL or ERROR.

(PROCESSP PROC)
          True if PROC is the handle of an active process, i.e., one that
          has not yet finished.

(RELPROCESSP PROCHANDLE)
          True if PROCHANDLE is the handle of a deleted process.  This is
          analogous to RELSTKP.  It differs from PROCESS.FINISHEDP in that
          it never causes an error, while PROCESS.FINISHEDP can cause an
          error if its PROC argument is not a process at all.

          (RESTART.PROCESS PROC)
          Unwinds PROC to its top level and reevaluates its form.  This is
          effectively a DEL.PROCESS followed by the original ADD.PROCESS.

(MAP.PROCESSES MAPFN)
          Maps over all processes, calling MAPFN with three arguments: the
          process handle, its name, and its form.

(FIND.PROCESS PROC ERRORFLG)
          If PROC is a process handle or the name of a process, returns the
          process handle for it, else NIL.  If ERRORFLG is T, generates an
          error if PROC is not, and does not name, a live process.



1.2 Process Control Constructs

(BLOCK MSECSWAIT TIMER)
          Yields control to the next waiting process, assuming any is ready
          to run.  If MSECSWAIT is specified, it is a number of
          milliseconds to wait before returning (in which case BLOCK is
          very much like DISMISS), or T, meaning wait forever (until
          explicitly woken).  Alternatively, TIMER can be given as a
          millisecond timer (as returned by SETUPTIMER) of an absolute time
          at which to wake up.  In any of those cases, the process enters
          the waiting state until the time limit is up.  BLOCK with no
          arguments leaves the process in the runnable state, i.e., it
          returns as soon as every other runnable process of the same
          priority has had a chance.

(WAKE.PROCESS PROC STATUS)
          Explicitly wakes process PROC, i.e., makes it runnable, and
          causes its call to BLOCK (or other waiting function) to return
          STATUS.  This is one simple way to notify a process of some
          happening; however, note that if WAKE.PROCESS is applied to a
          process more than once before the process actually gets its turn
          to run, it sees only the latest STATUS.

(SUSPEND.PROCESS PROC)
          Blocks process PROC indefinitely, i.e., PROC will not run until
          it is woken by a WAKE.PROCESS.

The following three functions allow access to the stack context of some
other process.  They require a little bit of care, and are computationally
non-trivial, but they do provide a more powerful way of manipulating
another process than WAKE.PROCESS allows.

(PROCESS.EVALV PROC VAR)
          Performs (EVALV VAR) in the stack context of PROC.

(PROCESS.EVAL PROC FORM WAITFORRESULT)
          Evaluates FORM in the stack context of PROC.  If WAITFORRESULT is
          true, blocks until the evaluation returns a result, else allows
          the current process to run in parallel with the evaluation.  Any
          errors that occur will be in the context of PROC, so be careful.
          In particular, note that

          (PROCESS.EVAL PROC '(NLSETQ (FOO)))

          and

          (NLSETQ (PROCESS.EVAL PROC '(FOO)))

          behave quite differently if FOO causes an error.  And it is quite
          permissible to intentionally cause an error in proc by performing

          (PROCESS.EVAL PROC '(ERROR!))

          If errors are possible and WAITFORRESULT is true, the caller
          should almost certainly make sure that FORM traps the errors;
          otherwise the caller could end up waiting forever if FORM unwinds
          back into the pre-existing stack context of PROC.

(PROCESS.APPLY PROC FN ARGS WAITFORRESULT)
          Performs (APPLY FN ARGS) in the stack context of PROC.  Note same
          warnings as with PROCESS.EVAL.



1.3 Events

An "event" is a synchronizing primitive used to coordinate related
processes, typically producers and consumers.  Consumer processes can
"wait" on events, and producers "notify" events.

(CREATE.EVENT NAME)
          Returns an instance of the EVENT datatype, to be used as the
          event argument to functions listed below.  NAME is arbitrary, and
          is used for debugging or status information.

(AWAIT.EVENT EVENT TIMEOUT TIMERP)
          Suspends the current process until EVENT is notified, or until a
          timeout occurs.  If TIMEOUT is NIL, there is no timeout.
          Otherwise, timeout is either a number of milliseconds to wait,
          or, if TIMERP is T, a millisecond timer set to expire at the
          desired time using SETUPTIMER (see page X.XX).

(NOTIFY.EVENT EVENT ONCEONLY)
          If there are processes waiting for EVENT to occur, causes those
          processes to be placed in the running state, with EVENT returned
          as the value from AWAIT.EVENT.  If ONCEONLY is true, only runs
          the first process waiting for the event (this should only be done
          if the programmer knows that there can only be one process
          capable of responding to the event at once).

The meaning of an event is up to the programmer.  In general, however, the
notification of an event is merely a hint that something of interest to the
waiting process has happened; the process should still verify that the
conceptual event actually occurred.  That is, the process should be written
so that it operates correctly even if woken up before the timeout and in
the absence of the notified event.  In particular, the completion of
PROCESS.EVAL and related operations in effect wakes up the process in which
they were performed, since there is no secure way of knowing whether the
event of interest occurred while the process was busy performing the
PROCESS.EVAL.

There is currently one class of system-defined events, used with the
network code.  Each Pup and NS socket has associated with it an event that
is notified when a packet arrives on the socket; the event can be obtained
by calling (PUPSOCKETEVENT PUPSOCKET) or (NSOCKETEVENT NSOCKET),
respectively.



1.4 Monitors

It is often the case that cooperating processes perform operations on
shared structures, and some mechanism is needed to prevent more than one
process from altering the structure at the same time.  Some languages have
a construct called a monitor, a collection of functions that access a
common structure with mutual exclusion provided and enforced by the
compiler via the use of monitor locks.  Interlisp-D has taken this
implementation notion as the basis for a mutual exclusion capability
suitable for a dynamically-scoped environment.

A monitorlock is an object created by the user and associated with (e.g.,
stored in) some shared structure that is to be protected from simultaneous
access.  To access the structure, a program waits for the lock to be free,
then takes ownership of the lock, accesses the structure, then releases the
lock.  The functions and macros below are used:

(CREATE.MONITORLOCK NAME)
          Returns an instance of the MONITORLOCK datatype, to be used as
          the lock argument to functions listed below.  NAME is arbitrary,
          and is used for debugging or status information.

(WITH.MONITOR LOCK .  FORMS)
          Evaluates (PROGN .  FORMS) while owning LOCK.  Value is the last
          of FORMS.  This construct is implemented so that the lock is
          released even if the form is exited via error (currently
          implemented with RESETLST).  Ownership of a lock is dynamically
          scoped: if the current process already owns the lock (e.g., if
          the caller was itself inside a WITH.MONITOR for this lock),
          WITH.MONITOR is a noop.

(WITH.FAST.MONITOR LOCK .  FORMS)
          Like WITH.MONITOR, but implemented without the RESETLST.  User
          interrupts (e.g., ↑E) are inhibited during the evaluation of
          FORMS.

          Programming restriction: the evaluation of FORMS must not error
          (the lock would not be released).  This construct is mainly
          useful when FORMS is a small, safe computation that never errors
          and need never be interrupted.

(MONITOR.AWAIT.EVENT RELEASELOCK EVENT TIMEOUT TIMERP)
          For use in blocking inside a monitor.  Performs (AWAIT.EVENT
          EVENT TIMEOUT TIMERP), but releases RELEASELOCK first, and
          reobtains the lock (possibly waiting) on wakeup.

Typical use for MONITOR.AWAIT.EVENT: A function wants to perform some
operation on Foo, but only if it is in a certain state.  It has to obtain
the lock on the structure to make sure that the state of the structure does
not change between the time it tests the state and performs the operation.
If the state turns out to be bad, it then waits for some other process to
make the state good, meanwhile releasing the lock so that the other process
can alter the structure.

(WITH.MONITOR FooLock (until condition-of-Foo do (MONITOR.AWAIT.EVENT
FooLock EventFooChanged timeout)) operate-on-Foo)

It is sometimes convenient for a process to have WITH.MONITOR at its top
level and then do all its interesting waiting using MONITOR.AWAIT.EVENT.
Not only is this often cleaner, but in the present implementation in cases
where the lock is frequently accessed, it saves the RESETLST overhead of
WITH.MONITOR.

Programming restriction: there must not be an ERRORSET between the
enclosing WITH.MONITOR and the call to MONITOR.AWAIT.EVENT such that the
ERRORSET would catch an ERROR! and continue inside the monitor, for the
lock would not have been reobtained.  (The reason for this restriction is
that, although MONITOR.AWAIT.EVENT won't itself error, the user could have
caused an error with an interrupt, or a PROCESS.EVAL in the context of the
waiting process that produced an error.)

On rare occasions it may be useful to manipulate monitor locks directly.
The following two functions are used in the implementation of WITH.MONITOR:

(OBTAIN.MONITORLOCK LOCK DONTWAIT UNWINDSAVE)
          Takes possession of LOCK, waiting if necessary until it is free,
          unless DONTWAIT is true, in which case it returns NIL
          immediately.  If UNWINDSAVE is true, performs a RESETSAVE to be
          unwound when the enclosing RESETLST exits.  Returns LOCK if LOCK
          was successfully obtained, T if the current process already owned
          LOCK.

(RELEASE.MONITORLOCK LOCK)
          Releases LOCK if it is owned by the current process, and wakes up
          the next process, if any, waiting to obtain the lock.

When a process is deleted, any locks it owns are released.



1.5 Typein and the TTY Process

There is one global resource, the keyboard, that is particularly
problematic to share among processes.  Consider, for example, having two
processes both performing (READ T).  Since the keyboard input routines
block while there is no input, both processes would spend most of their
time blocking, and it would simply be a matter of chance which process
received each character of typein.

To resolve such dilemmas, the system designates a distinguished process,
termed the tty process, that is assumed to be the process that is involved
in terminal interaction.  Any typein from the keyboard goes to that
process.  If a process other than the tty process requests keyboard input,
it blocks until it becomes the tty process.  When the tty process is
switched (in any of the ways described further below), any typeahead that
occurred before the switch is saved and associated with the current tty
process.  Thus, it is always the case the keystrokes are sent to the
process that is the tty process at the time of the keystrokes, regardless
of when that process actually gets around to reading them.

It is less immediately obvious how to handle keyboard interrupt characters,
as their action is asynchronous and not always tied to typein.  Interrupt
handling is described on page X.XX.



1.5.1 Switching the TTY Process

Any process can make itself be the tty process by calling TTY.PROCESS.

(TTY.PROCESS PROC)
          Returns the handle of the current tty process.  In addition, if
          PROC is non-NIL, makes it be the tty process.  The special case
          of PROC = T is interpreted to mean the executive process; this is
          sometimes useful when a process wants to explicitly give up being
          the tty process.

In some cases, such as in functions invoked by the mouse or a user's
typed-in call, it is reasonable for the function to invoke TTY.PROCESS
itself so that it can take subsequent user type in.  In other cases,
however, this is too undisciplined; it is desirable to let the user
designate which process typein should be directed to.  This is most
conveniently done by mouse action.

The system supports the model that "to type to a process, you click in its
window." To cooperate with this model, any process desiring keyboard input
should put its process handle as the PROCESS property of its window(s).  To
handle the common case, the function TTYDISPLAYSTREAM does this
automatically when the ttydisplaystream is switched to a new window.  A
process can own any number of windows; clicking in any of those windows
gives the process the tty.  This mechanism suffices for most casual process
writers; for those needing tighter control over the tty, the default
mechanism can be overridden or supplemented, as described below.

There is a window property WINDOWENTRYFN that controls whether and how to
switch the tty to the process owning a window.  The mouse handler, before
invoking any normal BUTTONEVENTFN, specifically notices the case of a
button going down in a window that belongs to a process (i.e., has a
PROCESS window property) that is not the tty process.  In this case, it
invokes the window's WINDOWENTRYFN of one argument (WINDOW).  WINDOWENTRYFN
defaults to GIVE.TTY.PROCESS:

(GIVE.TTY.PROCESS WINDOW)
          If WINDOW has a PROCESS property, performs (TTY.PROCESS
          (WINDOWPROP WINDOW 'PROCESS)) and then invokes WINDOW's
          BUTTONEVENTFN (or RIGHTBUTTONFN if the right button is down).

There are some cases where clicking in a window does not always imply that
the user wants to talk to that window.  For example, clicking in a text
editor window with a shift key held down means to "shift-select" some piece
of text into the input buffer of the current tty process.  The editor
supports this by supplying a WINDOWENTRYFN that performs GIVE.TTY.PROCESS
if no shift key is down, but goes into its shift-select mode, without
changing the tty process, if a shift key is down.  The shift-select mode
performs a BKSYSBUF of the selected text when the shift key is let up, the
BKSYSBUF feeding input to the current tty process.

Sometimes a process wants to be notified when it becomes the tty process,
or stops being the tty process.  For example, Chat (page X.XX) turns off
all keyboard interrupt characters while it is the tty process, so that they
can be passed transparently to the remote host.  To support this, there are
two process properties, TTYEXITFN and TTYENTRYFN.  The actions taken by
TTY.PROCESS when it switches the tty to a new process are as follows: the
former tty process's TTYEXITFN is called with two arguments (OLDTTYPROCESS
NEWTTYPROCESS); the new process is made the tty process; finally, the new
tty process's TTYENTRYFN is called with two arguments (NEWTTYPROCESS
OLDTTYPROCESS).  Normally the TTYENTRYFN and TTYEXITFN need only their
first argument, but the other process involved in the switch is supplied
for completeness.  In the present system, most processes want to interpret
the keyboard in the same way, so it is considered the responsibility of any
process that changes the keyboard interpretation to restore it to the
normal state by its TTYEXITFN.

A window is "owned" by the last process that anyone gave as the window's
PROCESS property.  Ordinarily there is no conflict here, as processes tend
to own disjoint sets of windows (though, of course, cooperating processes
can certainly try to confuse each other).  The only likely problem arises
with that most global of windows, PROMPTWINDOW.  Programs should not be
tempted to read from PROMPTWINDOW.  This is not usually necessary anyway,
as the first attempt to read from T in a process that has not set its
TTYDISPLAYSTREAM to its own window causes a tty window to be created for
the process (see page X.XX).

The following function is supplied for some simple cases of a process
desiring just a little input from the keyboard in special places.

(PROCESS.READ WINDOW PROMPT CLEAR?)
          Temporarily resets TTYDISPLAYSTREAM to WINDOW (if non-NIL),
          clears it if CLEAR? is true, and prompts with PROMPT.  It then
          returns the value of (NLSETQ (READ T T)).



1.5.2 Handling of Interrupts

At the time that a keyboard interrupt character is struck, any process
could be running, and some decision must be made as to which process to
actually interrupt.  To the extent that keyboard interrupts are related to
typein, most interrupts are taken in the tty process; however, the
following are handled specially:


      RESET, ERROR (normally ↑D and ↑E) These interrupts are taken in the
      mouse process, if the mouse is not in its idle state; otherwise they
      are taken in the tty process.  Thus, ↑E can be used to abort some
      mouse-invoked window action, such as the Shape command.  As a
      consequence, note that if the mouse invokes some lengthy computation
      that the user thinks of as "background", ↑E still aborts it, even
      though that may not have been what the user intended.  Such lengthy
      computations, for various reasons, should generally be performed by
      spawning a separate process to perform them.

      The RESET interrupt in a process other than the executive is
      interpreted exactly as if an error unwound the process to its top
      level: if the process was designated RESTARTABLE = T, it is
      restarted; otherwise it is killed.
      HELP (Initially ↑G) A menu of processes is presented to the user, who
      is asked to select which one the interrupt should occur in.  The
      current tty process appears with a * next to its name at the top of
      the menu, for convenience in the common case of interrupting the tty
      process.
      BREAK (Initially ↑B) Performs the HELP interrupt always in the tty
      process.
      RUBOUT (Initially <del>) This interrupt clears typeahead in all
      processes.
      RAID, STACK OVERFLOW, STORAGE FULL These interrupts always occur in
      whatever process was running at the time the interrupt struck.  In
      the cases of STACK OVERFLOW and STORAGE FULL, this means that the
      interrupt is more likely to strike in the offending process
      (especially if it is a "runaway" process that is not blocking).
      Note, however, that this process is still not necessarily the guilty
      party; it could be an innocent bystander that just happened to use up
      the last of a resource prodigiously consumed by some other process.



1.6 Keeping the Mouse Alive

Since the window mouse handler runs in its own process, it is not available
while a window's BUTTONEVENTFN function (or any of the other window
functions invoked by mouse action) is running.  This leads to two sorts of
problems: (1) a long computation underneath a BUTTONEVENTFN deprives the
user of the mouse for other purposes, and (2) code that runs as a
BUTTONEVENTFN cannot rely on other BUTTONEVENTFNs running, which means that
there some pieces of code that run differently from normal when run under
the mouse process.  These problems are addressed by the following
functions:

(SPAWN.MOUSE ---)
          Spawns another mouse process, allowing the mouse to run even if
          it is currently "tied up" under the current mouse process.  This
          function is intended mainly to be typed in at the Lisp executive
          when the user notices the mouse is busy.

(ALLOW.BUTTON.EVENTS)
          Performs a (SPAWN.MOUSE) only when called underneath the mouse
          process.  This should be called (once, on entry) by any function
          that relies on BUTTONEVENTFNs for completion, if there is any
          possibility that the function will itself be invoked by a mouse
          function.

It never hurts, at least logically, to call SPAWN.MOUSE or
ALLOW.BUTTON.EVENTS needlessly, as the mouse process arranges to quietly
kill itself if it returns from the user's BUTTONEVENTFN and finds that
another mouse process has sprung up in the meantime.  (There is, of course,
some computational expense.)



1.7 Global Resources

The biggest source of problems in the multi-processing environment is the
matter of global resources.  Two processes cannot both use the same global
resource if there can be a process switch in the middle of their use
(currently this means calls to BLOCK, but ultimately with a preemptive
scheduler means anytime).  The keyboard and the mouse have been discussed
in previous sections; this section lists a few other considerations.

The following resources, which are global in Interlisp-10, are allocated
per process in Interlisp-D: primary input and output (the streams affected
by INPUT and OUTPUT), terminal input and output (the streams designated by
the name T), the primary read table and primary terminal table, and dribble
files.  Each process begins life with its primary and terminal input/output
streams set to dummies.  If the process attempts input or output using any
of those dummy streams, e.g., by calling (READ T), or (PRINT & T), a tty
window is automatically created for the process, and that window becomes
the primary input/output and terminal input/output for the process.  The
process can, of course, call TTYDISPLAYSTREAM, INPUT, and/or OUTPUT to set
up the streams it desires, in which case the automatic mechanism never
comes into play.

The default tty window is created at or near the region specified in the
variable DEFAULTTTYREGION.

Other system resources that are typically changed by RESETFORM, RESETLST,
RESETVARS are all global entities.  In the multiprocessing environment,
these constructs are suspect, as there is no provision for "undoing" them
when a process switch occurs.  For example, it is not possible to set the
print radix to 8 inside only one process, as the print radix is a global
entity.  It is expected that many more of the global resources of
Interlisp-10 will become process-specific in future releases of
Interlisp-D.

Note that RESETFORM and similar expressions are perfectly valid in the
process world, and even quite useful, when they manipulate things strictly
within one process.  The process world is arranged so that deleting a
process also unwinds any RESETxxx expressions that were performed in the
process and are still waiting to be unwound, exactly as if a ↑D had reset
the process to the top.  Additionally, there is an implicit RESETLST at the
top of each process, so that RESETSAVE can be used as a way of providing
"cleanup" functions for when a process is deleted.  For these, the value of
RESETSTATE is NIL if the process finished normally, ERROR if it was aborted
by an error, or RESET if the process was explicitly deleted.

User code should be wary of its own use of global variables, if it ever
makes sense for the code to be run in more than one process at a time.
Monitor locks can be useful here.



1.8 Debugging Processes

(PROCESS.STATUS.WINDOW WHERE)
          Puts up a window that provides several debugging commands for
          manipulating running processes.  If the window is already up,
          PROCESS.STATUS.WINDOW refreshes it.  If WHERE is a position, the
          window is placed in that position; otherwise, the user is
          prompted for a position.

          The window consists of two menus.  The first is a menu of all the
          processes at the moment.  Commands in the second menu operate on
          the process selected in the first menu.  The commands are:


                       BT, BTV, BTV*, BTV!Performs a backtrace of the
                       selected process.  The first time, it prompts for a
                       window in which to display the backtrace.
                       WHO?Changes the selection to the tty process, i.e.,
                       the one currently in control of the keyboard.
                       KBD←Associates the keyboard with the selected
                       process; i.e., makes the selected process be the tty
                       process.
                       INFOIf the selected process has an INFOHOOK, calls
                       it.  The hook may be a function, which is then
                       applied to two arguments, the process and the button
                       (LEFT or MIDDLE) used to invoke INFO, or a form,
                       which is simply EVAL'ed.  The APPLY or EVAL happens
                       in the context of the selected process, using
                       PROCESS.APPLY or PROCESS.EVAL.  The info hook can be
                       set using PROCESSPROP.
                       KILLDeletes the selected process.
                       RESTARTRestarts the selected process.
                       WAKEWakes the selected process.  Prompts for a value
                       to wake it with (see WAKE.PROCESS).
                       SUSPENDSuspends the selected process; i.e., causes
                       it to block indefinitely (until explicitly woken).
                       BREAKEnter a break under the selected process.  This
                       has the side effect of waking the process with the
                       value returned from the break.

          Currently, the process status window runs under the mouse
process, like other menus, so if the mouse is unavailable (e.g., a mouse
function is performing an extensive computation), you may be unable to use
the process status window (you can try SPAWN.MOUSE, of course).



1.9 Non-Process Compatibility

This section describes some considerations for authors of programs that ran
in the old single-process Interlisp-D environment, and now want to make
sure they run properly in the Multi-processing world.  The biggest problem
to watch out for is code that runs underneath the mouse handler.  Writers
of mouse handler functions should remember that in the process world the
mouse handler runs in its own process, and hence (a) you cannot depend on
finding information on the stack (stash it in the window instead), and (b)
while your function is running, the mouse is not available (if you have any
non-trivial computation to do, spawn a process to do it, notify one of your
existing processes to do it, or use PROCESS.EVAL to run it under some other
process).

The following functions are meaningful even if the process world is not on:
BLOCK (invokes the system background routine, which includes handling the
mouse); TTY.PROCESS, THIS.PROCESS (both return NIL), TTY.PROCESSP (returns
T, i.e., anyone is allowed to take tty input); and PROCESS.READ.  In
addition, the following two functions exist in both worlds:

(EVAL.AS.PROCESS FORM)
          Same as (ADD.PROCESS FORM 'RESTARTABLE 'NO), when processes are
          running, EVAL when not.  This is highly recommended for mouse
          functions that perform any non-trivial activity.

(EVAL.IN.TTY.PROCESS FORM WAITFORRESULT)
          Same as (PROCESS.EVAL (TTY.PROCESS) FORM WAITFORRESULT), when
          processes are running, EVAL when not.

Most of the process functions that do not take a process argument can be
called even if processes aren't running.  ADD.PROCESS creates, but does not
run, a new process (it runs when PROCESSWORLD is called).