User-Added Signals

Introduction

The SAS/C Library enables you to add new user signals in two ways. If you want to raise a user signal within your program to indicate an unusual or error condition, you can use the raise or siggen function. This type of user signal requires no special coding, only the use of these two functions. The default action for a user signal raised in this manner is always to ignore the signal. Refer to the descriptions of the raise and siggen functions for more information.

If, however, you want to raise a signal as a result of a software interrupt (such as an end-of-subtask notification) or a hardware interrupt (such as an I/O interrupt), you can do so by coding two or more routines to intercept these interrupts and make them known to the library. The first of the required routines, called the initialization routine, defines the signal to the library. The other required routine, called the signal-generating routine, is an operating system exit that is invoked to raise the signal for the library when the interrupt occurs. Both of these routines are normally written in assembler language. Most of the other routines that define how to handle the signal can be written in either C or assembler language. Routines for Adding Signals indicates the recommended language for each routine and describes how to write the routines that add user signals to the library.

Note: This book assumes that you are adding a signal that communicates to your C program when a software or hardware interrupt occurs. You may have other uses for this facility that are not explicitly discussed in this book.

Some familiarity with MVS or CMS interrupt handling facilities is assumed. Also, thorough familiarity with SAS/C signal functions and signal-handling techniques is assumed. Refer to Chapter 5, "Signal-Handling Functions," in SAS/C Library Reference, Volume 1 for more information.

Notes on Handlers for User-Defined Signals

When you add new signals, the full array of signal-handling functions, including siginfo, sigprocmask, and sigsuspend, can be used in your program to control handling of the new signals. In some cases, you may want to define the new signal so that it does not permit the use of longjmp or return in a handler. Refer to Executive Routine for more information.

Restrictions on Synchronous and Asynchronous Signals

User signals can be synchronous or asynchronous; you can define as many as eight of each type. Synchronous signals generally must be handled immediately, while asynchronous signals either do not need to be handled immediately or cannot be handled immediately for technical reasons.

Synchronous signals must be assigned one of the signal numbers defined by the symbols SIGUSR1 through SIGUSR8. Asynchronous signals must be assigned one of the signal numbers defined by the symbols SIGASY1 through SIGASY8. When you write the routine to define the signal, you can rename it to have a more useful and mnemonic name.

There are no special requirements for defining asynchronous signals. Additional considerations may apply, however, when defining asynchronous signals for programs that use pause, sigpause, sigsuspend, or sleep; refer to the description of the ECBPP field of ZENQARGS in Routine for Asynchronous Signals: L$CZENQ .

The following list describes the requirements for defining a signal as synchronous.

Summary of Routines for Adding Signals

To add a user signal to the library, you must write at least two routines. (Depending on your needs, more routines may be necessary.) Routines for Adding Signals lists the routines you may need to write to define and control a new signal. The table also indicates the preferred language for each routine. Most routines can be written in either C or assembler language.

Assembler routines called by the SAS/C Library rather than by the operating system can use the CENTRY and CEXIT macros for function linkage. (Refer to Chapter 11, "Communication with Assembler Programs," in SAS/C Compiler and Library User's Guide, Fourth Edition , for information on the CENTRY and CEXIT macros.)

Assembler routines that call C functions must use the CENTRY and CEXIT macros to run successfully when either the optimized or minimal form of function linkage is in use. Refer to Chapter 9 of SAS/C Compiler and Library User's Guide, Fourth Edition for more information on the =optimize and =minimal options.

Note: A routine that calls operating system macros is usually written in assembler language.

Table 12.1 Routines for Adding Signals


  Routine         Required?/     Recommended    Description
                   Frequency of   Language*
                       use?


   initialization  yes/always     assembler      calls sigdef to define the
   routine                                       new signal and identify
                                                 which of the following
                                                 routines are coded.

   signal          yes/always     assembler      intercepts the hardware
                                                 or software interrupt and
                                                 informs the library that
                                                 the signal occurred. The
                                                 routine is usually an
                                                 operating system exit.

   default         no/common      C              establishes default
                                                 actions that occur when no
                                                 handler is defined for
                                                 the new signal.

   interrupt       no/ seldom     assembler      communicates to the
                                                 operating system when a
                                                 signal is ignored,
                                                 blocked, or handled in
                                                 the default manner.

   final routine   no/ very       assembler      cancels signal handling at
                   common                        the end of the program.
                                                 The routine is invoked
                                                 after all files are closed.

   executive       no/seldom      C              supervises linkage to
   routine                                       handlers. For example, the
                                                 routine can prevent use
                                                 of longjmp or normal returns.

   jump            no/rare        assembler      notifies the operating
   intercept                                     system that the interrupt
   routine                                       has been handled; that
                                                 is, it clears the interrupt.
 

*Note that all of these routines except the jump intercept routine can be coded in either C or assembler.
This table simply indicates which language is preferred.

Initialization Routine and sigdef Function

To add a signal to the library, you must code an initialization routine. This routine is usually coded in assembler language. The initialization routine calls the sigdef function to define the signal. The initialization routine also calls an operating system macro to establish the address of the operating system exit that should be invoked when the signal occurs. For example, to define a CMS I/O interrupt signal called SIGIOI, the initialization routine may issue calls similar to the ones here. The call to sigdef in assembler might look like the following:
          LA   R1,DEFPARMS
          L    R15,=V(SIGDEF)
          BALR R14,R15       sigdef(SIGASY1,0,0,0,0,"IOI");

 DEFPARMS DC   A(SIGASY1)    symbol definition obtained by
 *                           "COPY SIGNALS"
          DC   4A(0)
          DC   A(SIGNAME)

 SIGNAME  DC   C'IOI',X'00'
 
The call to sigdef renames SIGASY1 to SIGIOI but does not define any special routines for processing the signal (indicated by the 0s for the second DC in the DEFPARMS area).

A sample call to the CMS HNDINT macro to handle the interrupt might look like the following:

 HNDINT SET,(TAP1,EXIT,unit,ASAP)
 
The call to HNDINT identifies the I/O unit number that causes the interrupt (unit) and the address of the operating system exit routine (EXIT) that you code to generate the signal for the library.

You can also use the initialization routine to save the C Run-Time Anchor Block (CRAB) address so that it can be accessed by the signal generator routine. (One way to do this is to request that the operating system provide the address as a user parameter to an exit routine.) Saving the address of the CRAB is frequently necessary because register 12 is dedicated to the CRAB address only during C program execution; it is usually not preserved by the operating system when an exit routine is called.

Note: It is possible to raise, handle, or block a user signal before it has been defined by a call to sigdef. When sigdef is called, it has no effect on the signal's status; that is, the signal remains blocked or unblocked, and any user handler remains in effect. However, if the call to sigdef defines a default handler, this default replaces the previous default handler.

sigdef -- Define User Signal

SYNOPSIS

 #include <lcsignal.h>

 int sigdef(int signum, void (*dfl)(int),
            int (*intcntl)(int, int, int, int),
            void (*executive)(int, char **, _HANDLER),
            void (*final)(int, char *name));
 

DESCRIPTION

The signum argument is the signal number. Specify the signal number as one of the codes, SIGUSR1 through SIGUSR8 or SIGASY1 through SIGASY8. Only one definition of each signal number is allowed.

Note: In an OpenEdition MVS application, SIGUSR1 and SIGUSR2 may be treated as OpenEdition signals rather than as SAS/C signals, depending on the use of the oesigsetup function and the way the program is run. Attempting to define a signal controlled by OpenEdition MVS will cause sigdef to fail. Note that if oesigsetup has not been called when sigdef is invoked, a default call to oesigsetup will be generated, as described in the oesigsetup function description.

The name argument enables you to rename the signal. name is the address of a null-terminated string with a maximum of five characters. The library appends these five characters to the SIG prefix to create the new name. For example, if the name argument specifies EXT, the signal name appears in messages and traces as SIGEXT.

The dfl, intcntl, executive, and final arguments indicate the addresses of functions that provide special processing for the signal. Each of these routines is described in detail later in this appendix. Coding a 0 for any of these arguments indicates that no function is supplied for that type of processing.

Note: If you specify 0 for the dfl argument, the signal is ignored when default handling is in effect.

RETURN VALUE

sigdef returns 0 if it completes successfully or nonzero if it cannot complete. Specifying an invalid signal number or one that has already been defined is the most common reason for failure.

SEE ALSO

Chapter 5, "Signal-Handling Functions," in SAS/C Library Reference, Volume 1

Signal Generator Routine

The signal generator routine is the operating system exit that is invoked when the interrupt occurs; therefore, it is written in assembler language. (This routine can be written in C only if it is always called with C-compatible linkage, including the C use of register 12 and register 13. These conditions are unlikely to be met if this routine is an operating system exit.)

The signal generator routine raises the signal so that the library can detect it by calling the L$CZSYN routine (for synchronous signals) or the L$CZENQ routine (for asynchronous signals). These routines expect calls from assembler code and fully support them. The restrictions described in Restrictions on Synchronous and Asynchronous Signals apply when you can issue calls to L$CZSYN. There are no restrictions on when you can call L$CZENQ. These routines are described in more detail in SAS/C Library Routines for Adding New Signals .

The signal generator routine can build information that is passed to you when the user-defined handler calls siginfo. Refer to the SIGINFO field description in Routine for Synchronous Signals: L$CZSYN for more information.

Default Routine

This routine simply defines what actions should occur when the user does not specify a signal handler for the signal. The default routine is usually coded in C. The initialization routine specifies the address of this routine as the second argument in the call to sigdef. If you do not define this routine, the default action is to ignore the signal. If you prefer to have the program ABEND by default, code this routine to call the abort function.

Note: Refer to the siggen or abend function to specify a particular ABEND code.

Interrupt Control Routine

The interrupt control routine enables you to communicate to the operating system that the handling for a user-defined signal has changed. The interrupt control routine is usually coded in assembler language. The initialization routine specifies the address of this routine as the third argument in the call to sigdef. This routine is called on the following occasions: The purpose of this routine is to improve performance by eliminating unnecessary processing. For example, if the operating system's default action for a signal is to ignore the signal, and the C program calls signal with a second argument of SIG_IGN, the interrupt control function can cause the operating system to ignore the signal when it occurs instead of calling an operating system exit. This saves the processing time required to transfer control to the default handler and produces the same results.

The linkage to the interrupt control routine is defined as follows:

 int intcntl(int signum, int ignore, int default, int block,
             int context, struct sigaction *action)
 
The signum argument to the interrupt control routine specifies the signal number. The ignore and default arguments indicate how your program handles the signal. If ignore is 0, the program has either defined a signal handler or default handling is in effect. If ignore is not 0, the signal is ignored. If default is 0, the program has either defined a signal handler or the signal is to be ignored. If default is not 0, default handling is in effect. (Both ignore and default are nonzero if default handling is in effect and the default action is to ignore the signal.) The block argument is nonzero if the signal is blocked or 0 if the signal is not blocked.

The context argument is an integer indicating the reason that the interrupt control routine was called. The possible values are SC_ACTION, SC_PROCMASK, SC_DEF and SC_COPROCSWT. (These are symbolic values defined in <lcsignal.h>.) SC_ACTION indicates a call as the result of a user call to signal or sigaction, SC_PROCMASK indicates a call as the result of a user call to sigblock, sigsetmask or sigprocmask, SC_DEF indicates a call as the result of the call to sigdef, and SC_COPROCSWT indicates a call as the result of a coprocess switch.

The action argument is meaningful only for calls with context SC_ACTION or SC_DEF. In these cases, action is a pointer to information about the current handling as defined by sigaction. The sa_handler field of the action should be ignored, as it may be different from the current handler. However, the sa_mask and sa_flags fields are guaranteed to be correct. In particular, this functionality allows the interrupt control routine to take special action based on the
SA_USRFLAG n flags settings.

The interrupt control routine should return a negative number to indicate an error. If a negative number is returned, the call to signal or sigaction that caused the interrupt control routine to be called returns SIG_ERR. A return code of 1, when the context is SC_BLOCK, indicates that the interrupt control routine takes no action for changes to blocking status. This enables the performance of signal processing to be improved by avoid calls to the interrupt control routine during sigblock, sigsetmask, or sigprocmask processing. Any other positive return code (or a 1 returned when the context is not SC_PROCMASK) is treated as a success.

Note: An interrupt control routine is rarely required for correct operation of signal code; this routine simply provides improved performance for signals that can be ignored or blocked by the operating system. There may be times, however, when the interrupt control routine actually increases overhead. For example, signals are blocked while I/O is performed, so the interrupt control routine is called several times for each I/O operation.

Executive Routine

The library calls the executive routine, if you code one, instead of calling the handler defined in your program. The executive routine is then expected to call the handler itself. If no executive routine is defined, the handler is called directly. Note that the executive routine is not called for a signal generated by raise or siggen; thus, the executive routine is entered only when a signal occurs naturally.

The executive routine is usually coded in C. If you code this routine in assembler language, use the CENTRY and CEXIT macros to avoid problems calling the user-defined handler. The initialization routine specifies the address of this routine as the fourth argument in the call to sigdef. The linkage to the executive routine is defined as follows:

 void executive(int signum, char **infop, void (*handler)(int))
 
The signum argument is the number of the defined signal. The infop pointer addresses the pointer that the signal handler in the program can access by a call to siginfo. The executive routine may modify the information addressed by this pointer. The handler argument addresses the user-defined handler or contains 0 if the signal is to be ignored. This address can be used to call the user-defined handler from the executive routine.

The executive routine can be used to monitor or prevent certain handler activity. For instance, you can use blkjmp to prevent successful use of longjmp by the handler, or you can refuse to allow normal return from the handler by calling abort if the handler returns. It is assumed that the executive routine will call the handler using the normal handler linkage, but this cannot be enforced.

Final Routine

The final routine is called on program termination to allow signal-handling to be cancelled. Normally, a final routine is provided to inform the operating system that handling of the interrupt associated with the signal is no longer required.

The final routine is usually coded in assembler language. The initialization routine specifies the address of this routine as the fifth argument in the call to sigdef. The linkage to the final routine is defined as follows:

 void final(int signum)
 
The signum argument is the number of the signal whose handling will be terminated.

The final routine is called after all files have been closed by the library. Therefore, this routine is not permitted to use I/O.

Jump Intercept Routine

The jump intercept routine is invoked if a signal handler for a synchronous signal issues a longjmp. This routine must be coded in assembler language. The jump intercept routine can inform the operating system that handling of the interrupt is complete but that control should not return to the point of interrupt. This is sometimes called clearing the interrupt. The jump intercept routine may need to be called before performing the longjmp because the longjmp routine prevents return to the signal generation routine from the handler and, therefore, also prevents normal return to the operating system.

The jump intercept routine is rarely coded because it is frequently impossible to correctly clear the interrupt. If you expect a user-defined handler to call longjmp, you can disallow longjmp or define the signal as asynchronous. The jump intercept routine should not attempt to prevent a longjmp. If you want to disallow jumps, use the executive routine to block them.

The jump intercept routine is not included as an argument in the call to sigdef. To indicate that you want to provide this routine, set the JUMPINT field of the ZSYNARGS DSECT to the address of the jump intercept routine. The jump intercept routine is called using standard MVS linkage. Register 1 addresses the save area where registers have been saved (except for register 14 and register 15, which are not available).

SAS/C Library Routines for Adding New Signals

The library provides two routines (L$CZSYN and L$CZENQ) that can communicate to the library that a user-defined signal has occurred. These routines should be called from the signal generating routine, which is normally written in assembler language. The library expects calls to these routines from assembler language and fully supports them.

Routine for Synchronous Signals: L$CZSYN

When you call L$CZSYN to inform the library of a synchronous signal, register 12 must address the CRAB, and register 13 must address the current C DSA. Register 1 addresses an argument list described by the following DSECT:

 ZSYNARGS  DSECT

 SIGNUM    DS F   signal number
 SIGINFO   DS A   address of associated information
 ABCODE    DS A   pointer to associated ABEND code (null-terminated),
                  if any
 SIGNAME   DS A   pointer to five-character signal name
 SIGLOC    DS A   pointer to the interrupted instruction
 JUMPINT   DS A   address of a jump intercept routine, if needed
 
You must provide all of the information for these fields. The fields are described as follows:
SIGNUM
contains the number of the signal.
SIGINFO
contains a pointer to the value that will be made available to the signal handler with the siginfo function. Refer to Chapter 5, "Signal-Handling Functions," in SAS/C Library Reference, Volume 1 for more information on siginfo.

The signal-generating routine builds this information and stores the address in this field. Then the address is passed to the executive routine for the signal, if any. The executive routine may want to modify or make a copy of this information. For example, you might pass L$CZSYN the address of a system control block. The executive routine can make a copy of it to pass to the user-defined handler. This stops the user from attempting to modify the control block.

ABCODE
can be 0 or it can contain the address of a string that contains an ABEND code associated with the signal. The ABEND code should be null-terminated and should begin with a 'U' if it is a user ABEND rather than a system ABEND. For example, if you define a signal associated with exceeding a CPU-time quota, you probably would define ABCODE as "322" because that is the ABEND code normally produced by this condition.
SIGNAME
contains the address of the five-character string passed as the last argument in the call to sigdef. These five characters are appended to SIG to form a new name that replaces the standard name, SIGUSR1 - 8.
SIGLOC
should contain the address in the C program where processing was interrupted. This address is used in tracebacks if an ABEND occurs or the debugger where command is used. If you cannot provide this information, set this field to NULL.
JUMPINT
can provide a jump intercept routine. The normal use of a jump intercept routine is to inform the operating system that the interrupt has been handled. Set this field to 0 if you have not coded a jump intercept routine. Refer to the Jump Intercept Routine for more information on this routine.
L$CZSYN returns either a 0 or a 4 in register 15 unless handling of the signal is terminated by a longjmp, in which case no return occurs. If L$CZSYN returns a 4, the signal cannot be processed because one or more of the restrictions on the timing of synchronous signals is violated. Refer to Restrictions on Synchronous and Asynchronous Signals for more information. If L$CZSYN returns a 0, the signal is accepted, and a user-defined signal handler is called and returned. In either case, the signal generation routine should return to the operating system.

Routine for Asynchronous Signals: L$CZENQ

The L$CZENQ routine can be called at any time to inform the library that an asynchronous signal has occurred. Unlike L$CZSYN, L$CZENQ does not cause a handler to be immediately called. Instead, L$CZENQ adds the signal to an internal queue of pending signals. No handler is called until the signal can be discovered; discovery occurs when a function is called or returns and the signal is not blocked. L$CZENQ can be called in situations where normal C code cannot be executed, such as under an SRB or a subtask TCB in MVS, or from a CMS interrupt handler. L$CZENQ is reliable whether or not hardware interrupts are disabled at the time it is called. Recursive interrupts and simultaneous interrupts in a multitasking or multiprocessing environment are supported.

The address of L$CZENQ is located at offset X'1F4' (decimal 500) from the start of the CRAB. Linkage to L$CZENQ should be effected using this CRAB field, not a V-type address constant. Using a V-type constant works only if the calling routine is linked with the main load module of the application program.

The following shows the use of various registers by L$CZENQ:

 Register     Use
 

 1            addresses parameter list ZENQARGS
 2 - 6        work registers; save contents before calling L$CZENQ
 12           must address CRAB
 13           ignored; no registers saved by L$CZENQ
 14           contains return address
 15           contains address of L$CZENQ
The parameter list addressed by register 1 is described by the following DSECT:
 ZENQARGS  DSECT

 SIGNUM    DS F   signal number
 SIGINFO   DS A   address of associated information
 ABCODE    DS A   pointer to associated ABEND code (null-terminated),
                  if any
 SIGNAME   DS A   pointer to five-character signal name
 SIGELEM   DS A   address of an interrupt element
 ECBPP     DS A   address of a word in which to store an ECB address
 

The first four fields have the same meaning as the corresponding fields in L$CZSYN. The SIGELEM and ECBPP fields should be used as follows:

SIGELEM
is required. It must address a 24-byte area of storage that can be used as an interrupt element by L$CZENQ. Under MVS, this element must be allocated by GETMAIN from subpool 1. Under CMS, this element must be allocated through use of CMSSTOR (or DMSFREE under 370 mode CMS). The element must be accessible using a 24-bit address. The element is freed by the run-time library after processing of this signal is complete.
ECBPP
can address a word of memory or can contain 0s. If ECBPP is not 0, and the program is executing pause, sigpause, sigsuspend, or sleep at the time of the call to L$CZENQ, the address of the Event Control Block (ECG) used by pause, sigpause, sigsuspend, and sleep is stored in the word addressed by ECBPP. If ECBPP is 0, the ECB address is not stored; instead, the ECB is posted using SVC 2. Therefore, if you call L$CZENQ in a situation where SVC's cannot be issued (such as from an SRB routine or I/O appendage), you must provide an ECBPP value. Note that pause, sigpause, sigsuspend, and sleep do not complete until this ECB is posted. For this reason, in such cases you would normally call a branch entry to POST to awaken the C program.
L$CZENQ does not have a return code because L$CZENQ cannot fail without causing abnormal program termination.

Copyright (c) 1998 SAS Institute Inc. Cary, NC, USA. All rights reserved.