$ZINTerrupt

$ZINT[ERRUPT] specifies the code to be XECUTE'd when an interrupt (for example, through a MUPIP INTRPT) is processed. While a $ZINTERRUPT action is in process, any additional interrupt signals are discarded. When an interrupt handler is invoked, the current values of $REFERENCE is saved and restored when the interrupt handler returns. The current device ($IO) is neither saved nor restored.

GT.M permits the SET command to modify the value of $ZINTERRUPT.

If an interrupt handler changes the current IO device (via USE), it is the responsibility of the interrupt handler to restore the current IO device before returning. There are sufficient legitimate possibilities why an interrupt routine would want to change the current IO device (for example; daily log switching), that this part of the process context is not saved and restored automatically.

The initial value for $ZINTERRUPT is taken from the UNIX environment variable gtm_zinterrupt if it is specified, otherwise it defaults to the following string:

IF $ZJOBEXAM()

The IF statement executes the $ZJOBEXAM function but effectively discards the return value.

[Note] Note

If the default value for $ZINTERRUPT is modified, no $ZJOBEXAM() will occur unless the replacement value directly or indirectly invokes that function. In other words, while $ZJOBEXAM() is part of the interrupt handling by default, it is not an implicit part of the interrupt handling.

Interrupt Handling

GT.M process execution is interruptible with the following events:

  • Typing <CTRL-C> or getting SIGINT on a principal device terminal; GT.M ignores SIGINT (<CTRL-C>) if $PRINCIPAL is not a terminal.

  • READing one of the CTRAP characters

  • Exceeding $ZMAXTPTIME in a transaction

  • Getting a MUPIP INTRPT (SIGUSR1)

  • +$ZTEXit evaluates to a truth value at the outermost TCOMMIT or TROLLBACK

When GT.M detects any of these events, it transfers control to a vector that depends on the event. For CTRAP characters and ZMAXTPTIME, GT.M uses the $ETRAP or $ZTRAP vectors described in more detail in the Error Processing chapter. For INTRPT and $ZTEXit, it XECUTEs the interrupt handler code placed in $ZINTERRUPT. If $ZINTERRUPT is an empty string, the process ignores any MUPIP INTRPT directed at it. The default value of $ZINTERRUPT is "IF $ZJOBEXAM()" which redirects a dump of ZSHOW "*" to a file and reports each such occasion to the operator log. For <CTRL-C> with CENABLE, it enters Direct Mode to give the programmer control. Without CENABLE or CTRAP, GT.M ignores <CTRL-C> on a $PRINCIPAL terminal.

GT.M recognizes most of these events when they occur but transfers control to the interrupt vector at the start of each M line, at each iteration of a FOR LOOP, at certain points during the execution of commands which may take a "long" time. For example, ZWRITE, HANG, LOCK, MERGE, ZSHOW "V", OPENs of disk files and FIFOs, OPENs of SOCKETs with the CONNECT parameter (unless zero timeout,) WRITE /WAIT for SOCKETs, and READ for terminals, SOCKETs, FIFOs, and PIPEs. If +$ZTEXIT evaluates to a truth value at the outermost TCOMMIT or TROLLBACK, GT.M XECUTEs $ZINTERRUPT after completing the commit or rollback. Except for <CTRL-C> GT.M recognizes CTRAP characters when READ. CTRAP characters other than <CTRL-C> tend to be limited by terminal configuration.

If an interrupt event occurs in a long running external call (for example, waiting in a message queue), GT.M recognizes the event but makes the vector transfer after the external call returns when it reaches the next appropriate execution boundary.

When GT.M uses an interrupt handler, it saves and restores the current values of $REFERENCE. However, GT.M does not implicitly save or restore the current device ($IO). If an interrupt handler changes $IO (via USE), ensure that the interrupt handler restores the current device before returning. To restore the device which was current when the interrupt handler began, specify USE without any deviceparameters. Any attempt to do IO on a device which was actively doing IO when the interrupt was recognized may cause a ZINTERCURSEIO error.

Example:

set $zinterrupt="do ^interrupthandler($io)"
interrupthandler(currentdev)
       do ^handleinterrupt ; handle the interrupt
       use currentdev      ; restore the device which was current when the interrupt was recognized
       quit

The use of the INTRPT facility may create a temporary hang or pause while the interrupt handler code is executed. For the default case where the interrupt handler uses IF $ZJOBEXAM() to create a dump, the pause duration depends on the number of local variables in the process at the time of the dump and on the speed of the disk being written to. The dumps are slower on a network-mounted disk than on a disk directly connected to the local system. Any interrupt driven code should be designed to account for this issue.

[Important] Important

Because sending an interrupt signal requires the sender to have appropriate permissions, the use of the job interrupt facility itself does not present any inherent security exposures. Nonetheless, because the dump files created by the default action contain the values of every local variable in the context at the time they are made, inappropriate access to the dump files would constitute a security exposure. Make sure the design and implementation of any interrupt logic includes careful consideration to security issues.

During the execution of the interrupt handling code, $ZINITERRUPT evaluates to 1 (TRUE).

If an error occurs while compiling the $ZINTERRUPT code, the error handler is not invoked (the error handler is invoked if an error occurs while executing the $ZINTERRUPT code), GT.M sends the ERRWZINTR message and the compiler error message to the operator log facility. If the GT.M process is at a direct mode prompt or is executing a direct mode command (for example, a FOR loop), GT.M sends also sends the ERRWZINTR error message to the user console along with the compilation error. In both cases, the interrupted process resumes execution without performing any action specified by the defective $ZINTERRUPT vector.

If GT.M encounters an error during creation of the interrupt handler's stack frame (before transferring control to the application code specified by the vector), that error is prefixed with a ERRWZINTR error. The error handler then executes normal error processing associated with the interrupted routine. Any other errors that occur in code called by the interrupt vector invoke error processing as described in Chapter 13: “Error Processing.

[Note] Note

The interrupt handler does not operate "outside" the current M environment but rather within the environment of the process.

TP transaction is in progress (0<$TLEVEL), updates to globals are not safe since a TP restart can be signaled at any time prior to the transaction being committed - even after the interrupt handler returns. A TP restart reverses all global updates and unwinds the M stack so it is as if the interrupt never occurred. The interrupt handler is not redriven as part of a transaction restart. Referencing (reading) globals inside an interrupt handler can trigger a TP restart if a transaction is active. When programming interrupt handling, either discard interrupts when 0<$TLEVEL (forcing the interrupting party to try again), or use local variables that are not restored by a TRESTART to defer the interrupt action until after the final TCOMMIT.

The interrupt handler is executed by GT.M when on a statement boundary or on an appropriate boundary in a potentially long running COMMAND (in the same place as GT.M recognizes <CTRL-C>). If a GT.M process is in a long running external call (for example; waiting in a message queue) GT.M does not have sufficient control of the process state to immediately drive the interrupt handler. GT.M recognizes the interrupt request and drives the handler after the external call returns to GT.M and the process reaches an appropriate execution boundary.

Since sending an interrupt signal requires the sender to have appropriate permissions from UNIX, the use of the interrupt facility itself does not present any inherent security exposures. Nonetheless, because the dump files created by the default action contain the values of every local variable in the context at the time they are made, inappropriate access to the dump files could constitute a security exposure. Make sure the design and implementation of any interrupt logic includes careful consideration to security issues.

If an error occurs while compiling the $ZINTERRUPT code, the action taken depends on the process state at the time the interrupt was received and the error handler is not invoked (the error handler is invoked if an error occurs while executing the $ZINTERRUPT code). If the GT.M process is at a direct mode prompt or is executing a direct mode command (for example, a FOR loop), the error message is sent to the user console along with the ERRWZINTR error (refer to the GT.M Error and Message Recovery Manual). In addition, the ERRWZINTR error is also sent to the operator log facility. IF the process is not at a direct mode prompt or executing a direct mode command, the ERRWZINTR message and the compiler error message are sent to the operator log facility and nothing is displayed on the user's console. In both cases, the interrupted process resumes execution.

If an error occurs during execution of the interrupt handler's stack frame (before it calls anything), that error is prefixed with the ERRWZINTR error. The error handler then executes normal error processing associated with the module that was interrupted. Any other errors that occur in code called by the interrupt handler are handled by normal error handling. See Chapter 13: “Error Processing.

[Note] Note

The interrupt handler does not operate "outside" the current M environment but rather within the environment of the process.

If a TP transaction is in progress (0<$TLEVEL), updates to globals are not safe since a TP restart can be signaled at any time prior to the transaction being committed - even after the interrupt handler returns. A TP restart reverses all global updates and unwinds the M stack so it is as if the interrupt never occurred. The interrupt handler is not redriven as part of a transaction restart. Referencing (reading) globals inside an interrupt handler can trigger a TP restart if a transaction is active. When programming interrupt handling, either discard interrupts when 0<$TLEVEL (forcing the interrupting party to try again), or use local variables that are not restored by a TRESTART to defer the interrupt action until after the final TCOMMIT.

Note that it is possible for the interrupt handler to be executed while the process executing the M routine is holding the critical section for one or more regions. Use of this feature may create temporary hangs or pauses while the interrupt handler executes. For the default case where the interrupt handler uses $ZJOBEXAM() to create a dump, the pause duration depends on the number of local variables in the process at the time of the dump and on the speed of the disk being written to. The dumps are slower on a network-mounted disk than on a disk directly connected to the local system. Any interrupt driven code should be designed to account for this issue.