Example of an OS/2 Exception Handler

The code below is an exception handler that detects access to memory that is not valid. An explanation follows the listing, and you can click on the comments for specific lines to read an explanation of that line.

You could write a C signal handler with similar functionality.

#define INCL_DOS
#define INCL_NOPMAPI
#include <os2.h>
#include <stdlib.h>
#include <setjmp.h>
#include <stdio.h>
#include <stddef.h>               /* for _threadid */

void * tss_array[100];            /* array for 100 thread-specific pointers */

APIRET APIENTRY 
MyExceptionHandler(EXCEPTIONREPORTRECORD *,
                   EXCEPTIONREGISTRATIONRECORD *,
                   CONTEXTRECORD *,
                   PVOID);
#pragma map(_Exception,"MyExceptionHandler")
#pragma handler(chkptr)

int chkptr(void * ptr, int size)
{
   volatile char c;               /* volatile to insure access occurs */
   int valid = 0;                 /* count of valid bytes */
   char * p = ptr;                /* to satisfy the type checking for p++ */
   jmp_buf jbuf;                  /* put the jump buffer in automatic storage */
                                  /* so it is unique to this thread */
   PTIB ptib;                     /* to get the TIB pointer */
   PPIB ppib;
   unsigned int tid = *_threadid; /* get the thread id */
   UCHAR FileData [100]
   ULONG Wrote;
                                  /* create a thread specific jmp_buf */
   tss_array[tid] = (void *) jbuf;
   if (!setjmp(jbuf))             /* provide a point to return to */
   {
      while (size--)              /* scan the storage */
      {
         c = *p++;
         valid++;
      }
   }
   return valid;                  /* return number of valid bytes */
}
                                  /* the exception handler itself */
APIRET APIENTRY 
MyExceptionHandler(EXCEPTIONREPORTRECORD * report_rec,
                   EXCEPTIONREGISTRATIONRECORD * register_rec,
                   CONTEXTRECORD * context_rec,
                   PVOID dummy)
{
   unsigned int tid = *_threadid; /* get the thread id */
                                  /* check the exception flags */ 
   if (EH_EXIT_UNWIND & report_rec->fHandlerFlags) 
                                  /* exiting */
      return XCPT_CONTINUE_SEARCH;
   if (EH_UNWINDING & report_rec->fHandlerFlags) 
                                  /* unwinding */
      return XCPT_CONTINUE_SEARCH;
   if (EH_NESTED_CALL & report_rec->fHandlerFlags)
                                  /* nested exceptions */
      return XCPT_CONTINUE_SEARCH;
                                 /* determine what the exception is */
   if (report_rec->ExceptionNum == XCPT_ACCESS_VIOLATION)
   { /* this is the one that is expected */
      UCHAR FileData[100];
      ULONG Wrote;
      strcpy(FileData, "Detected invalid storage address %d\n\r");
      DosWrite(2, (PVOID)FileData, strlen(FileData), &Wrote, stepnum);
      longjmp((int *)tss_array[tid],1); /* return to the point of the */
                                 /* setjmp call without */
                                 /* restarting the while loop */
   }                             /* endif */
   return XCPT_CONTINUE_SEARCH; /* if it is a different exception */
}

Explanation of the Code

This exception handler performs the following steps:

  1. Check the exception flags
    The first thing an exception handler should do is check the exception flags. If EH_EXIT_UNWIND is set, meaning the thread is ending, the handler tells the operating system to pass the exception to the next exception handler. It does the same if the EH_UNWINDING flag is set, indicating that this exception handler is being removed.
    The EH_NESTED_CALL flag indicates whether the exception occurred within an exception handler. If the handler does not check this flag, recursive exceptions could occur until there is no stack remaining.
  2. Determine what the exception is
    The handler checks the exception number. In general, you should check for only the exceptions that you expect to encounter so any addition of new exception numbers does not affect your code. Assuming the exception is XCPT_ACCESS_VIOLATION, the exception handler prints a message and calls longjmp to return to the chkptr function.
  3. Allow an unexpected exception
    If the exception is not the expected one, the handler tells the operating system to pass it to the next exception handler.

Return XCPT_CONTINUE_EXECUTION from an exception handler only if you know that the thread can continue to run, either because the exception is asynchronous and can be restarted or because you have changed the thread state so that the thread can continue. If you return XCPT_CONTINUE_EXECUTION when neither of these conditions is true, you could generate a new exception each time your exception handler ends, eventually causing your process to lock.



Signals and Exceptions


OS/2 Exceptions and Default Handling
Exception Information Provided by OS/2
Prototype of an OS/2 Exception Handler
OS/2 APIs That Interfere with Exception Handling