Updated: 11 December 1998 |
OpenVMS Programming Concepts Manual
Previous | Contents | Index |
You can exit from a condition handler in one of three ways:
INTEGER FUNCTION HANDLER (SIGARGS, 2 MECHARGS) ! Declare dummy arguments INTEGER*4 SIGARGS(*), 2 MECHARGS(*) INCLUDE '($FORDEF)' INCLUDE '($SSDEF)' INTEGER*4 INDEX, 2 LIB$MATCH_COND INDEX = LIB$MATCH_COND (SIGARGS(2), 2 FOR$_FILNOTFOU, 2 FOR$_OPEFAI, 2 FOR$_NO_SUCDEV, 2 FOR$_FILNAMSPE) IF (INDEX .GT. 0) THEN . . . ! Display the message . . . HANDLER = SS$_CONTINUE END IF |
INDEX = LIB$MATCH_COND (SIGARGS(2), 2 FOR$_FILNOTFOU, 2 FOR$_OPEFAI, 2 FOR$_NO_SUCDEV, 2 FOR$_FILNAMSPE) IF (INDEX .EQ. 0) THEN HANDLER = SS$_RESIGNAL END IF |
Your handlers should return control either to the program unit that established the handler or to the program unit that invoked the program unit that established the handler.
To return control to the program unit that established the handler, invoke SYS$UNWIND and pass the call depth (third element of the mechanism array) as the first argument with no second argument.
! Declare dummy arguments INTEGER*4 SIGARGS(*), 2 MECHARGS(*) . . . CALL SYS$UNWIND (MECHARGS(3),) |
To return control to the caller of the program unit that established the handler, invoke SYS$UNWIND without passing any arguments.
! Declare dummy arguments INTEGER*4 SIGARGS(*), 2 MECHARGS(*) . . . CALL SYS$UNWIND (,) |
The first argument SYS$UNWIND specifies the number of program units to unwind (remove from the stack). If you specify this argument at all, you should do so as shown in the previous example. (MECHARGS(3) contains the number of program units that must be unwound to reach the program unit that established the handler that invoked SYS$UNWIND.) The second argument SYS$UNWIND contains the location of the next statement to be executed. Typically, you omit the second argument to indicate that the program should resume execution at the statement following the last statement executed in the program unit that is regaining control.
Each time SYS$UNWIND removes a program unit from the stack, it invokes any condition handler established by that program unit and passes the condition handler the SS$_UNWIND condition code. To prevent the condition handler from resignaling the SS$_UNWIND condition code (and so complicating the unwind operation), include SS$_UNWIND as an expected condition code when you invoke LIB$MATCH_COND. When the condition code is SS$_UNWIND, your condition handler might perform necessary cleanup operations or do nothing.
In the following example, if the condition code is SS$_UNWIND, no action is performed. If the condition code is another of the expected codes, the handler displays the message and then returns control to the program unit that called the program unit that established the condition handler.
INTEGER FUNCTION HANDLER (SIGARGS, 2 MECHARGS) ! Declare dummy arguments INTEGER*4 SIGARGS(*), 2 MECHARGS(*) INCLUDE '($FORDEF)' INCLUDE '($SSDEF)' INTEGER*4 INDEX, 2 LIB$MATCH_COND INDEX = LIB$MATCH_COND (SIGARGS(2), 2 SS$_UNWIND, 2 FOR$_FILNOTFOU, 2 FOR$_OPEFAI, 2 FOR$_NO_SUCDEV, 2 FOR$_FILNAMSPE) IF (INDEX .EQ. 0) THEN ! Unexpected condition, resignal HANDLER = SS$_RESIGNAL ELSE IF (INDEX .EQ. 1) THEN ! Unwinding, do nothing ELSE IF (INDEX .GT. 1) THEN . . . ! Display the message . . . CALL SYS$UNWIND (,) END IF |
The following example shows two procedures, A and B, that have declared condition handlers. The notes describe the sequence of events that would occur if a call to a system service failed during the execution of procedure B.
/* PGMA */ #include <stdio.h> #include <ssdef.h> unsigned int sigargs[],mechargs[]; main() { unsigned int status, vector=0, old_handler; old_handler = LIB$ESTABLISH( handlera ); (1) status = pgmb (arglst); (2) . . . } /* PGMB */ #include <stdio.h> #include <ssdef.h> main() { old_handler = LIB$ESTABLISH( handlerb ); (3) . . . } (4) /* Handler A */ (5) int handlera( sigargs, mechargs ) { /* Compare condition value signalled with expected value */ (6) if (sigargs[2] != SS$_SSFAIL) goto no_fail; . . . /* Signal to continue */ return SS$_CONTINUE; /* Signal to resignal */ no_fail: return SS$_RESIGNAL; } /* Handler B */ int handlerb( sigargs, mechargs ) { /* Compare condition value signalled with expected value */ if (sigargs[2] != SS$_BREAK) (7) goto no_fail; . . . return SS$_CONTINUE; no_fail: return SS$_RESIGNAL; } |
You can debug a condition handler as you would any subprogram, except that you cannot use the DEBUG command STEP/INTO to enter a condition handler. You must set a breakpoint in the handler and wait for the debugger to invoke the handler.
Typically, to trace execution of a condition handler, you set breakpoints at the statement in your program that should signal the condition code, at the statement following the one that should signal, and at the first executable statement in your condition handler.
The SET BREAK debugger command with the /HANDLER qualifier causes the
debugger to scan the call stack and attempt to set a breakpoint on
every established frame-based handler whenever the program being
debugged has an exception. The debugger does not discriminate between
standard RTL handlers and user-defined handlers.
15.14 Run-Time Library Condition-Handling Routines
The following sections present information about Alpha systems RTL
jacket handlers, and RTL routines that can be established as condition
handlers or called from a condition handler to handle signaled
exception conditions.
15.14.1 RTL Jacket Handlers (Alpha Only)
On Alpha systems, many RTLs establish a jacket RTL handler on a frame where the user program has defined its own handler. This RTL jacket does some setup and argument manipulation before actually calling the handler defined by the user. When processing the exception, the debugger sets the breakpoint on the jacket RTL jacket handler, because that is the address on the call stack. If the debugger suspends program execution at a jacket RTL handler, it is usually possible to reach the user-defined handler by entering a STEP/CALL command followed by a STEP/INTO command. Some cases might require that additional sequences of STEP/CALL and STEP/INTO commands be entered. For more information on frame-based handlers, see OpenVMS Calling Standard.
If the jacket RTL handler is part of an installed shared image such as ALPHA LIBOTS, the debugger cannot set a breakpoint on it. In this case, activate the RTL as a private image by defining the RTL as a logical name, as in the following example:
$DEFINE LIBOTS SYS$SHARE:LIBOTS.EXE; |
In the previous example, the trailing semicolon (;) is required. |
On VAX systems, a trap is an exception condition that is signaled after the instruction that caused it has finished executing. A fault is an exception condition that is signaled during the execution of the instruction. When a trap is signaled, the program counter (PC) in the signal argument vector points to the next instruction after the one that caused the exception condition. When a fault is signaled, the PC in the signal argument vector points to the instruction that caused the exception condition. See the VAX Architecture Reference Manual for more information about faults and traps.
LIB$SIM_TRAP can be established as a condition handler or be called
from a condition handler to convert a floating-point fault to a
floating-point trap. After LIB$SIM_TRAP is called, the PC points to the
instruction after the one that caused the exception condition. Thus,
your program can continue execution without fixing up the original
condition. LIB$SIM_TRAP intercepts only floating-point overflow,
floating-point underflow, and divide-by-zero faults.
15.14.3 Changing a Signal to a Return Status
When it is preferable to detect errors by signaling but the calling routine expects a returned status, LIB$SIG_TO_RET can be used by the routine that signals. For instance, if you expect a particular condition code to be signaled, you can prevent the operating system from invoking the default condition handler by establishing a different condition handler. LIB$SIG_TO_RET is a condition handler that converts any signaled condition to a return status. The status is returned to the caller of the routine that established LIB$SIG_TO_RET. You may establish LIB$SIG_TO_RET as a condition handler by specifying it in a call to LIB$ESTABLISH.
On Alpha systems, LIB$ESTABLISH is not supported, though high-level languages may support it for compatibility.
LIB$SIG_TO_RET can also be called from another condition handler. If LIB$SIG_TO_RET is called from a condition handler, the signaled condition is returned as a function value to the caller of the establisher of that handler when the handler returns to the OpenVMS Condition Handling facility. When a signaled exception condition occurs, LIB$SIG_TO_RET routine does the following:
Your calling routine can now test R0, as if the called routine had returned a status, and specify an error recovery action.
The following paragraphs describe how to establish and use the system-defined condition handler LIB$SIG_TO_RET, which changes a signal to a return status that your program can examine.
To change a signal to a return status, you must put any code that might signal a condition code into a function where the function value is a return status. The function containing the code must perform the following operations:
If the program unit GET_1_STAT in the following function signals a condition code, LIB$SIG_TO_RET changes the signal to the return status of the INTERCEPT_SIGNAL function and returns control to the program unit that invoked INTERCEPT_SIGNAL. (If GET_1_STAT has a condition handler established, the operating system invokes that handler before invoking LIB$SIG_TO_RET.)
FUNCTION INTERCEPT_SIGNAL (STAT, 2 ROW, 2 COLUMN) ! Dummy arguments for GET_1_STAT INTEGER STAT, 2 ROW, 2 COLUMN ! Declare SS$_NORMAL INCLUDE '($SSDEF)' ! Declare condition handler EXTERNAL LIB$SIG_TO_RET ! Declare user routine INTEGER GET_1_STAT ! Establish LIB$SIG_TO_RET CALL LIB$ESTABLISH (LIB$SIG_TO_RET) ! Set return status to success INTERCEPT_SIGNAL = SS$_NORMAL ! Statements and/or subprograms that ! signal expected error condition codes STAT = GET_1_STAT (ROW, 2 COLUMN) END |
When the program unit that invoked INTERCEPT_SIGNAL regains control, it
should check the return status (as shown in Section 15.5.1) to determine
which condition code, if any, was signaled during execution of
INTERCEPT_SIGNAL.
15.14.4 Changing a Signal to a Stop
LIB$SIG_TO_STOP causes a signal to appear as though it had been signaled by a call to LIB$STOP.
LIB$SIG_TO_STOP can be enabled as a condition handler for a routine or
be called from a condition handler. When a signal is generated by
LIB$STOP, the severity code is forced to severe, and control cannot
return to the routine that signaled the condition. See Section 15.12.1
for a description of continuing normal execution after a signal.
15.14.5 Matching Condition Values
LIB$MATCH_COND checks for a match between two condition values to allow
a program to branch according to the condition found. If no match is
found, the routine returns zero. The routine matches only the condition
identification field (STS$V_COND_ID) of the condition value; it ignores
the control bits and the severity field. If the facility-specific bit
(STS$V_FAC_SP = bit <15>) is clear in cond-val
(meaning that the condition value is systemwide), LIB$MATCH_COND
ignores the facility code field (STS$V_FAC_NO = bits <27:17>) and
compares only the STS$V_MSG_ID fields (bits <15:3>).
15.14.6 Correcting a Reserved Operand Condition (VAX Only)
On VAX systems, after a signal of SS$_ROPRAND during a floating-point
instruction, LIB$FIXUP_FLT finds the operand and changes it from --0.0
to a new value or to +0.0.
15.14.7 Decoding the Instruction That Generated a Fault (VAX Only)
On VAX systems, LIB$DECODE_FAULT locates the operands for an
instruction that caused a fault and passes the information to a user
action routine. When called from a condition handler, LIB$DECODE_FAULT
locates all the operands and calls an action routine that you supply.
Your action routine performs the steps necessary to handle the
exception condition and returns control to LIB$DECODE_FAULT.
LIB$DECODE_FAULT then restores the operands and the environment, as
modified by the action routine, and continues execution of the
instruction.
15.15 Exit Handlers
When an image exits, the operating system performs the following operations:
If any exit handler exits using the EXIT (SYS$EXIT) system service, none of the remaining handlers is executed. In addition, if an image is aborted by the DCL command STOP (the user presses Ctrl/Y and then enters STOP), the system performs image rundown and does not invoke any exit handlers. Like the DCL STOP/ID, SYS$DELPRC bypasses all exit handlers, except the rundown specified in the privileged library vector (PLV) privileged shareable images, and deletes the process. (The DCL command EXIT invokes the exit handlers before running down the image.)
A hang-up to a terminal line causes DCL to delete the master process's subprocesses. However, if the subprocesses's exit handler is in a main image installed with privilege, then that exit handler is run even with the DCL command STOP. Also, if the subprocess was spawned NOWAIT, then the spawning process's exit handler is run as well.
Use exit handlers to perform any cleanup that your program requires in
addition to the normal rundown operations performed by the operating
system. In particular, if your program must perform some final action
regardless of whether it exits normally or is aborted, you should write
and establish an exit handler to perform that action.
15.15.1 Establishing an Exit Handler
To establish an exit handler, use the SYS$DCLEXH system service. The SYS$DCLEXH system service requires one argument---a variable-length data structure that describes the exit handler. Figure 15-16 illustrates the structure of an exit handler.
Figure 15-16 Structure of an Exit Handler
The first longword of the structure contains the address of the next handler. The operating system uses this argument to keep track of the established exit handlers; do not modify this value. The second longword of the structure contains the address of the exit handler being established. The low-order byte of the third longword contains the number of arguments to be passed to the exit handler. Each of the remaining longwords contains the address of an argument.
The first argument passed to an exit handler is an integer value containing the final status of the exiting program. The status argument is mandatory. However, do not supply the final status value; when the operating system invokes an exit handler, it passes the handler the final status value of the exiting program.
To pass an argument with a numeric data type, use programming language statements to assign the address of a numeric variable to one of the longwords in the exit-handler data structure. To pass an argument with a character data type, create a descriptor of the following form:
Use the language statements to assign the address of the descriptor to one of the longwords in the exit-handler data structure.
The following program segment establishes an exit handler with two arguments, the mandatory status argument and a character argument:
. . . ! Arguments for exit handler INTEGER EXIT_STATUS ! Status CHARACTER*12 STRING ! String STRUCTURE /DESCRIPTOR/ INTEGER SIZE, 2 ADDRESS END STRUCTURE RECORD /DESCRIPTOR/ EXIT_STRING ! Setup for exit handler STRUCTURE /EXIT_DESCRIPTOR/ INTEGER LINK, 2 ADDR, 2 ARGS /2/, 2 STATUS_ADDR, 2 STRING_ADDR END STRUCTURE RECORD /EXIT_DESCRIPTOR/ HANDLER ! Exit handler EXTERNAL EXIT_HANDLER . . . ! Set up descriptor EXIT_STRING.SIZE = 12 ! Pass entire string EXIT_STRING.ADDRESS = %LOC (STRING) ! Enter the handler and argument addresses ! into the exit handler description HANDLER.ADDR = %LOC(EXIT_HANDLER) HANDLER.STATUS_ADDR = %LOC(EXIT_STATUS) HANDLER.STRING_ADDR = %LOC(EXIT_STRING) ! Establish the exit handler CALL SYS$DCLEXH (HANDLER) . . . |
An exit handler can be established at any time during your program and remains in effect until it is canceled (with SYS$CANEXH) or executed. If you establish more than one handler, the handlers are executed in reverse order: the handler established last is executed first; the handler established first is executed last.
Previous | Next | Contents | Index |
Copyright © Compaq Computer Corporation 1998. All rights reserved. Legal |
5841PRO_045.HTML
|