file: job.t package: muf status: alpha
It is often important that certain cleanup operations be done at the end of a given section of code, even if that section should fail in some way.
For example, if one is doing a network file transfer, the socket should be closed at the end, even if the tranfer fails to complete normally for some reason.
Or if one has a session open to an X server, one should be very careful to release resources allocated in the server at the end of the session, to avoid having it gradually fill up with unused garbage.
Almost every nontrivial application has invariants like these which it is intended to maintain, but which must be temporarily broken from time to time during processing: One of the hallmarks of high-quality muf code is the careful attention given to preserving all invariants in the face of unexpected error conditions.
The Muq control construct designed for these cases is
after{ clause-1 }alwaysDo{ clause-2 }
Muq goes to great lengths internally to guarantee that
clause-2 will always execute, even if the code in clause one
should do a divide-by-zero or attempt to do a throw
or return
past us: It will silently interrupt the
error-recovery, throw, or return processing long enough for
clause-2 to execute, and then transparently resume the
interrupted processing.
Almost the sole exception to this rule is that if
killJobMessily
is used to terminate the job
during execution of clause-1 or clause-2, no attempt is
made to execute (or complete execution of) clause-2:
the job is killed dead, instantly. This ensures that
users and root always have some way to kill a
runaway process: if Muq insisted on always running
clause-2 to completion, an accidental or malicious
recursion or infinite loop in clause-2 could result in
an immortal, cancerous process endlessly chewing up
system resources until the server crashed.
(This is, of course, a good reason to use
killJobMessily
only as a last resort: one
should always first attempt to kill an unwanted job
with a friendly killJob
signal, giving it a
fair chance to clean up after itself before exiting.)
What should happen if clause-1 does a fork? This leaves two jobs capable of executing clause-2, but in general one wishes clause-2 to be executed exactly once.
There are two obvious policies: Have only the child job execute clause-2, or have only the parent job execute clause-2. Since convincing examples exist supporting each possibility, Muq provides both, via the syntax
afterChildDoes{ clause-1 }alwaysDo{ clause-2 } afterParentDoes{ clause-1 }alwaysDo{ clause-2 }
The vanilla syntax
after{ clause-1 }alwaysDo{ clause-2 }
is currently a synonym for the
afterParentDoes{
syntax.
Go to the first, previous, next, last section, table of contents.