The Muq event system is a general facility for steering complex computations, with or without explicit user intervention.
It provides the ability to signal and handle hardware and software faults either entirely in software or with the active participation of the user, hence subsumes the role of unix signals and signal handlers and the debugger core, but it should not be thought of primarily as a specialized mechanism for catching errors, but rather as a powerful, general control mechanism for controlling complex programs and building sophisticated user interfaces.
The event system is based on a three-phase model of the problem resolution process:
Here is a typical sequence of events when using the event system. We suppose that part of an application is attempting to access a netserver as part of performing some task for a user:
goto
to some appropriate
tag, aborting part of the computation and
continuing along a backup path, but perhaps merely
(for example) changing some parameters in the network interface.
This problem resolution model leads in a natural way to requirements for the following sets of tools:
The Muq event system provides the following tools to meet these requirements:
Event
is provided. Each instance
of this class represents a particular kind of
problem, and inheritance links between these
instances describe the relationships between
these kinds of problems. Thus, "diskWriteError"
may inherit from "diskError" which might inherit
from "ioError" which might inherit from "error".
This inheritance lattice helps the programmer
precisely describe the set of problems which a
particular handler understands.
The functions signal
, warn
,
error
and cerror
are provided for
explicitly invoking the event system on a
particular event. (The server also implicitly
invokes these when it encounters problems.)
withRestartDo{ ... }
construct
allows execution of a block of code with a given
restart active.
withHandlersDo{ ... }
construct
allows execution of a block of code with a given
set of handlers active.
signal
and kin,
which also server to select and invoke handlers.
findRestart
function may be used to
find a single restart. The computeRestarts[
function may be used to return some or all active
restarts. The invokeRestart
operator
allows invocation of any selected restart.
One concrete example is often worth any amount of abstract hand-waving. Here's a simple example which exercises all the basic event system functions. Even without having been formally introduced to all the functions used, the general idea may come though:
makeEvent --> _myEvent withTag my-tag do{ [ :function :: 'my-tag goto ; :name 'my-restart | ]withRestartDo{ [ _myEvent :: { [] -> [] ! } 'my-restart invokeRestart ; | ]withHandlerDo{ [ :event _myEvent | ]signal } } t if nil --> _looksGood else my-tag t --> _looksGood fi }
The above example is a fairly complete exercise of the fundamental three layers (tags, restarts, handlers) of the event system:
_myEvent
which
we may send in handler signals, invoking only handlers
watching for it.
my-tag
in the current function
to which other functions may jump.
'my-restart
which
jumps to my-tag
. Other functions may find and
invoke this restart.
_myEvent
signals
which invokes 'my-restart
.
_myEvent
, triggering
all the above machinery, so that execution resumes at
my-tag
: On exit, _looksGood
will be t
.
We now discuss these tools in more detail.
Go to the first, previous, next, last section, table of contents.