7 Conditions
The classes are described in "Conditions" on page 232, and the functions are described in "Signaling Conditions" on page 344.
signal
.
The handler in Figure 7-1 may either return normally, in which case execution resumes as the call to signal
returns normally, or the handler may make a non-local exit, such as calling the exit function from a dynamically active block
statement.
The simplest way to handle an exception is to exit the signaling unit by taking a non-local exit to a target established in the outside stack. The exception clause of the block statement provides a convenient mechanism for accomplishing this.
A less common handling style is to exit the signaling unit by taking a non-local exit to a target established in the middle stack, thus leaving the handler in force.
Instead of exiting, a handler can recover by returning control to the signaling unit. This can be done either by returning values that the signaling unit will understand or by taking a non-local exit to a target established in the inside stack.
The following examples show three ways of handling a copy-protection violation while copying a series of files. Note that the signaling code does not need to know how the condition will be handled. The only changes are in the code which handles the condition.
// Assume there is a class for file-system errors. // We are interested in a special kind of file-system error // that occurs when attempting to copy a copy-protected file, // so we define a new class to indicate such errors. define class <copy-protection-violation> (<file-system-error>) slot file, init-keyword: file:; // Store the file name end class; // Define a function to copy a single file. This // function signals a <copy-protection-violation> if // the file is copy-protected. define method copy-file (source, destination) if ( copy-protected?(source) ) signal(make(<copy-protection-violation>, file: source)); else // copy normally notify-user("Copying %s to %s.", source, destination); end if; end method; // The following function copies a sequence of files. // If one of the files is copy-protected, the user is // notified, and the remaining files are copied. define method backup-all-possible (volume, archive) let handler <copy-protection-violation> = method (condition, next) // The handler just notifies the user and continues notify-user("The file %s could not be copied.", condition.file); end method; // start copying files, with the handler in effect for (each-file in volume) copy-file(each-file, archive) end for; end method; // The following function stops copying as soon as it // hits a copy-protected file define method backup-exit (volume, archive) // set up a block so we can do a non-local exit block (exit) let handler <copy-protection-violation> = method (condition, next) // Notify the user and abort the backup notify-user( "Backup interrupted: the file %s could not be copied.", condition.file); exit(#f); end method; // start copying files, with the handler in effect for (each-file in volume) copy-file(each-file, archive) end for; end block; end method; // The following function uses the convenient exception clause of // the block statement to achieve essentially the same effect as // as backup-exit. define method backup-block (volume, archive) // get ready to do backups block () // start copying files for (each-file in volume) copy-file(each-file, archive) end for; exception (condition :: <copy-protection-violation>) notify-user( "Backup interrupted: the file %s could not be copied.", condition.file); end block; end method;
A handler restarts by signaling a restart. All restarts are instances of <restart>
. Any values needed for recovery are passed in the restart (that is, in initialization arguments that the restart remembers, typically in slots). The restart is handled by a restart handler which either returns or takes a non-local exit. If the restart handler returns some values, signal
returns those values and the handler that called signal also returns them. The call to signal from the signaling unit that signaled the original condition returns the same values, and the signaling unit recovers as directed by those values.
An example recovery protocol for a hypothetical <unbound-slot>
condition could include the following:
<new-value>
is available. <new-value>
has initialization arguments value:
, the value to use, and permanent:
, which indicates whether to store the value into the slot or leave the slot unbound.At present, no formal mechanism is provided for describing recovery protocols; they are left to the documentation of a condition class. Introspective functions are provided for discovering which recovery facilities are actually available, but this is different from (and sometimes is a superset of) the recovery facilities guaranteed by a recovery protocol always to be available.
The debugger is the condition handler of last resort which receives control if no program-provided handler handles a serious condition. (This is true even if the debugger provided cannot analyze or intervene in the execution of programs but can only abort or restart them. The debugger might be merely a "core dumper," a "bomb box," or something similar.) An interactive debugger ought to offer the user the ability to signal any restart for which a restart handler is applicable and to return if the condition's recovery protocol allows it. This could, for example, be done with a menu titled "Recovery."
Generated with Harlequin WebMaker