Go to the first, previous, next, last section, table of contents.


Control-struct: after{ ... }alwaysDo{ ... }
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.