Many other systems, by default, generate a Posix signal whenever a software or hardware exception occurs (like dereferencing an invalid pointer, or a divide by zero error), and on those systems, a Posix signal may be equivalent and indistinguishable from an ANSI C signal and if unhandled, this will result in process termination.
OS/400 doesn't generate a signal for these hardware or software problems, but instead, generates an AS/400 exception message. The exception message is percolated up the call stack, allowing each stack frame (function on the stack or invocation entry) a chance to handle the exception. Each function invocation may choose to handle or not to handle the exception. If the exception is not handled, the message continues to the next stack frame.
When the exception message reaches certain boundaries on the call stack (like a main() entry point, usually called control boundaries) certain different events take place. These events include changing the exception to a different type, terminating the process, terminating the activation group, or terminating the thread. If an unhandled exception condition happens in a secondary thread and percolates all the way to the first invocation in the thread without being handled, the resulting action will be to terminate the thread. During this percolation, if the exception hits a control boundary and is not handled, it may terminate the process.
The C runtime was present on the system prior to the Posix signals implementation. Therefore, the C runtime uses the robust AS/400 exception model to implement ANSI C signals (raise(), signal(), abort()). The C runtime also provides the generation of an ANSI C signals when it detects a hardware exception. Thus, using the signal() API, its possible to monitor for and handle AS/400 hardware exceptions.
A signal is never automatically generated for an exception message. AS/400 hardware/software exceptions cannot be detected using asynchronous signal mechanisms. i.e. If you use sigaction() for the SIGSEGV signal, you will not detect that signal when a invalid pointer is used. If you use signal(), you will detect SIGSEGV when your code uses an invalid pointer.
If your application prefer's signals using the asynchronous signal model, its possible to use AS/400 exception handlers or ANSI C signal handlers to generate a asynchronous signal when those events occur.
The following example shows how a divide by zero error, and a dereference of an invalid pointer might be mapped to generate an asynchronous signal. This example uses ANSI C signal handlers to perform the signal mapping.
#define _MULTI_THREADED
#include <stdio.h>
#include <qp0z1170.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include "check.h"
void myAnsiSignalMapperHdlr(int sigNumber);
void *threadfunc1(void *parm);
void *threadfunc2(void *parm);
void *threadfunc1(void *parm)
{
char *p=NULL;
printf("Thread1: Unhandled exception (pointer fault) about to happen\n");
*p = `!';
printf("Thread1: After exception\n");
return NULL;
}
void *threadfunc2(void *parm)
{
int i1=0, i2=1, i3=0;
printf("Thread2: Unhandled exception (divide by zero) about to happen\n");
i1 = i2 / i3;
printf("Thread2: After exception\n");
return NULL;
}
void myAnsiSignalMapperHdlr(int sigNumber) {
/* In a multi-threaded environment this is kind of tricky. We have to */
/* re-enable the ANSI C handler immediately, because that's the way its */
/* defined. (A better alternative may be direct monitor exception */
/* handlers which are always valid in the function which they are */
/* registered, and with direct monitors, we can catch the hardware */
/* exception before its converted to an ANSI C signal */
signal(SIGALL, myAnsiSignalMapperHdlr);
/* Since ANSI C signals and hardware exceptions will only be handled in */
/* the same thread that caused them, we'll send the Posix signal to */
/* the calling thread (The signal will be delivered before returning from */
/* pthread_kill(). */
printf("Mapping ANSI signal to posix signal %d\n", sigNumber);
pthread_kill(pthread_self(), sigNumber);
return;
}
void fpViolationHldr(int sigNumber) {
printf("Thread 0x%.8x %.8x "
"Handled floating point failure SIGFPE (signal %d)\n",
pthread_getthreadid_np(), sigNumber);
/* By definition, returning from a posix signal handler handles the signal*/
}
void segFaultHdlr(int sigNumber) {
printf("Thread 0x%.8x %.8x "
"Handled segmentation violation SIGSEGV (signal %d)\n",
pthread_getthreadid_np(), sigNumber);
/* By definition, returning from a posix signal handler handles the signal*/
}
int main(int argc, char **argv)
{
int rc=0;
pthread_t threadid;
struct sigaction actions;
void *status;
printf("----------- Setup Signal Mapping/Handling -------------\n");
printf("- Register ANSI C signal handler to map ALL\n"
" ANSI C signals & hardware exceptions to Posix signals\n");
signal(SIGALL, myAnsiSignalMapperHdlr);
printf("- Register normal posix signal handling mechanisms\n"
" for floating point violations, and segmentation faults\n"
"- Other signals take the default action for asynchronous signals\n");
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
actions.sa_handler = fpViolationHldr;
rc = sigaction(SIGFPE,&actions,NULL);
checkResults("sigaction for SIGFPE\n", rc);
actions.sa_handler = segFaultHdlr;
rc = sigaction(SIGSEGV,&actions,NULL);
checkResults("sigaction for SIGSEGV\n", rc);
printf("----------- Start memory fault thread -------------\n");
printf("Create a thread\n");
rc = pthread_create(&threadid, NULL, threadfunc1, NULL);
checkResults("pthread_create()\n", rc);
rc = pthread_join(threadid, &status);
checkResults("pthread_join()\n", rc);
printf("----------- Start divide by 0 thread -------------\n");
printf("Create a thread\n");
rc = pthread_create(&threadid, NULL, threadfunc2, NULL);
checkResults("pthread_create()\n", rc);
rc = pthread_join(threadid, &status);
checkResults("pthread_join()\n", rc);
printf("Main completed\n");
return 0;
}
----------- Setup Signal Mapping/Handling ------------- - Register ANSI C signal handler to map ALL ANSI C signals & hardware exceptions to Posix signals - Register normal posix signal handling mechanisms for floating point violations, and segmentation faults - Other signals take the default action for asynchronous signals ----------- Start memory fault thread ------------- Create a thread Thread1: Unhandled exception (pointer fault) about to happen Mapping ANSI signal to posix signal 5 Thread 0x00000000 00000022 Handled segmentation violation SIGSEGV (signal 5) Thread1: After exception ----------- Start divide by 0 thread ------------- Create a thread Thread2: Unhandled exception (divide by zero) about to happen Mapping ANSI signal to posix signal 2 Thread 0x00000000 00000023 Handled floating point failure SIGFPE (signal 2) Thread2: After exception Main completedThe following example shows how a divide by zero error, and a dereference of an invalid pointer might be mapped to generate a Posix (asynchronous) signal. This example uses AS/400 exception handlers to perform the signal mapping.
#define _MULTI_THREADED
#include <stdio.h>
#include <stdlib.h>
#include <qp0z1170.h>
#include <time.h>
#include <signal.h>
#include <except.h>
#include <qusec.h> /* System API error Code structure */
#include <qmh.h> /* Message Hanlder common defs */
#include <qmhchgem.h> /* Change exception message */
#include <pthread.h>
#include "check.h"
void myHardwareExceptionMapper(_INTRPT_Hndlr_Parms_T *exception);
void *threadfunc1(void *parm);
void *threadfunc2(void *parm);
void *threadfunc1(void *parm)
{
char *p=NULL;
/* Watch for all ESCAPE type exceptions. Other types may be used for */
/* job log messages or C++ exceptions or other control flow in the process*/
/* Adjust the message type as required by your application. */
#pragma exception_handler (myHardwareExceptionMapper, 0, _C1_ALL, _C2_MH_ESCAPE)
printf("Thread1: Unhandled exception (pointer fault) about to happen\n");
*p = `!';
printf("Thread1: After exception\n");
#pragma disable_handler
return NULL;
}
void *threadfunc2(void *parm)
{
int i1=0, i2=1, i3=0;
/* Watch for all ESCAPE type exceptions. Others types may be used for */
/* job log messages or C++ exceptions or other control flow in the process*/
/* Adjust the message type as required by your application. */
#pragma exception_handler (myHardwareExceptionMapper, 0, _C1_ALL, _C2_MH_ESCAPE)
printf("Thread2: Unhandled exception (divide by zero) about to happen\n");
i1 = i2 / i3;
printf("Thread2: After exception\n");
#pragma disable_handler
return NULL;
}
void myHardwareExceptionMapper(_INTRPT_Hndlr_Parms_T *exInfo) {
int sigNumber;
Qus_EC_t errorCode = {0}; /* system API error structure */
printf("Handling system exception\n");
/* The exception information is available inside the exInfo structure */
/* for this example, we're going to handle all exceptions and then map */
/* them to an \Qappropriate' signal number. We are allowed to decide the */
/* signal mapping however is appropriate for our application. */
if (!memcmp(exInfo->Msg_Id, "MCH3601", 7)) {
sigNumber = SIGSEGV;
}
else if (!memcmp(exInfo->Msg_Id, "MCH1211", 7)) {
sigNumber = SIGFPE;
}
else {
printf("Unexpected exception! Not Handling!\n");
abort();
}
/* Even if the exception is \Qexpected', we're going to handle it and try */
/* to deliver it as a posix signal. Note that we SHOULD NOT HANDLE */
/* exceptions that are unexpected to us. Most code can't tolerate */
/* getting back into it once the exception occured, and we could get into*/
/* a nice exception loop. */
/* See the system API reference for a description of QMHCHGEM */
QMHCHGEM(&exInfo->Target, 0, &exInfo->Msg_Ref_Key, QMH_MOD_HANDLE,
(char *)NULL, 0, &errorCode);
if (errorCode.Bytes_Available != 0) {
printf("Failed to handle exception. Error Code = %7.7s\n",
errorCode.Exception_Id);
return;
}
printf("Mapping Exception %7.7s to posix signal %d\n",
exInfo->Msg_Id ,sigNumber);
/* At this point the exception is handled. If the posix signal handler */
/* returns, then the signal will be handled, and all will be complete */
pthread_kill(pthread_self(), sigNumber);
return;
}
void fpViolationHldr(int sigNumber) {
printf("Thread 0x%.8x %.8x "
"Handled floating point failure SIGFPE (signal %d)\n",
pthread_getthreadid_np(), sigNumber);
/* By definition, return from a posix signal handler handles the signal */
}
void segFaultHdlr(int sigNumber) {
printf("Thread 0x%.8x %.8x "
"Handled segmentation violation SIGSEGV (signal %d)\n",
pthread_getthreadid_np(), sigNumber);
/* By definition, returning from a posix signal handler handles the signal*/
}
int main(int argc, char **argv)
{
int rc=0;
pthread_t threadid;
struct sigaction actions;
void *status;
printf("----------- Setup Signal Mapping/Handling -------------\n");
printf("- The threads will register AS/400 Exception handler to map\n"
" hardware exceptions to Posix signals\n");
printf("- Register normal posix signal handling mechanisms\n"
" for floating point violations, and segmentation faults\n"
"- Other signals take the default action for asynchronous signals\n");
memset(&actions, 0, sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
actions.sa_handler = fpViolationHldr;
rc = sigaction(SIGFPE,&actions,NULL);
checkResults("sigaction for SIGFPE\n", rc);
actions.sa_handler = segFaultHdlr;
rc = sigaction(SIGSEGV,&actions,NULL);
checkResults("sigaction for SIGSEGV\n", rc);
printf("----------- Start memory fault thread -------------\n");
printf("Create a thread\n");
rc = pthread_create(&threadid, NULL, threadfunc1, NULL);
checkResults("pthread_create()\n", rc);
rc = pthread_join(threadid, &status);
checkResults("pthread_join()\n", rc);
printf("----------- Start divide by 0 thread -------------\n");
printf("Create a thread\n");
rc = pthread_create(&threadid, NULL, threadfunc2, NULL);
checkResults("pthread_create()\n", rc);
rc = pthread_join(threadid, &status);
checkResults("pthread_join()\n", rc);
printf("Main completed\n");
return 0;
}
----------- Setup Signal Mapping/Handling ------------- - The threads will register AS/400 Exception handler to map hardware exceptions to Posix signals - Register normal posix signal handling mechanisms for floating point violations, and segmentation faults - Other signals take the default action for asynchronous signals ----------- Start memory fault thread ------------- Create a thread Thread1: Unhandled exception (pointer fault) about to happen Handling system exception Mapping Exception MCH3601 to posix signal 5 Thread 0x00000000 00000024 Handled segmentation violation SIGSEGV (signal 5) Thread1: After exception ----------- Start divide by 0 thread ------------- Create a thread Thread2: Unhandled exception (divide by zero) about to happen Handling system exception Mapping Exception MCH1211 to posix signal 2 Thread 0x00000000 00000025 Handled floating point failure SIGFPE (signal 2) Thread2: After exception Main completed