home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / except2.zip / EXCEPT.INF (.txt) next >
OS/2 Help File  |  1994-03-25  |  55KB  |  1,552 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Introduction ΓòÉΓòÉΓòÉ
  3.  
  4. C Set/2, C Set++ and OS/2 provide a slightly different approach to handling 
  5. program exception conditions from what you may be used to with C/2 and OS/2 
  6. Version 1.x.  All programmers know from experience that programs do generate 
  7. abnormal conditions, like invalid storage accesses.  The C Set family of 
  8. compilers and OS/2 provide ways to gracefully handle these situations. 
  9.  
  10.  
  11. ΓòÉΓòÉΓòÉ 2. Handling of Abnormal Conditions ΓòÉΓòÉΓòÉ
  12.  
  13. From the application programmer's perspective, both C Set and the operating 
  14. system have the capability of detecting and reporting various abnormal 
  15. conditions.  You, the programmer, have several options once the condition is 
  16. reported.  You can: 
  17.  
  18.  1. Ignore the condition 
  19.  
  20.     In this case, C Set and/or OS/2 will take the default action.  Usually, 
  21.     this means that your process is terminated immediately.  The C Set library 
  22.     will shut down without automatically flushing your file buffers, and no 
  23.     cleanup of your process is done. 
  24.  
  25.  2. Terminate the process 
  26.  
  27.     You can provide a handler for the condition which cleans up after your 
  28.     process (e.g. deletes work files), and then terminates.  This is a little 
  29.     cleaner than the default action, but not much. 
  30.  
  31.  3. Terminate the thread 
  32.  
  33.     If an abnormal condition occurs within one thread of your process, you can 
  34.     clean up just that thread, and terminate it.  This was very difficult to do 
  35.     on OS/2 Version 1.x, but is straightforward on OS/2 Version 2.x.  From the 
  36.     your application's user's point of view, what was a hard failure on OS/2 
  37.     Version 1.x has now become a soft failure. 
  38.  
  39.  4. Terminate a thread, and restart it 
  40.  
  41.     While handling an abnormal condition, you may elect to terminate the thread 
  42.     in which it has occurred, and start a new thread.  This is an extension of 
  43.     the concept of just terminating one thread. 
  44.  
  45.  5. Continue a thread at a known point 
  46.  
  47.     You can continue the thread that had the abnormal condition from a known 
  48.     point.  This was often nearly impossible on OS/2 Version 1.x.  C Set and 
  49.     OS/2 make this relatively easy. 
  50.  
  51. Abnormal conditions can be reported to you in two ways.  You can choose which 
  52. you want to use, and even use them in combination.  One option is to use the 
  53. operating system's exception handling interface.  Exception handling is not 
  54. easy to deal with, and provides you a large amount of information which is 
  55. generally useless to a C or C++ programmer.  The C Set application libraries 
  56. (the single and multithreading libraries) provide a much simpler interface 
  57. called signal handlers. 
  58.  
  59. Terminology Note:   The terms "signal" and "exception" are not interchangeable. 
  60.                     A signal exists only within the C language.  An exception 
  61.                     is generated by OS/2, and may  be used by the C Set library 
  62.                     to generate a signal.  Additional confusion is caused by 
  63.                     the use of the word "exception" in C++.  This article does 
  64.                     not deal with C++ exceptions. 
  65.  
  66.  
  67. ΓòÉΓòÉΓòÉ 3. Signal Handlers ΓòÉΓòÉΓòÉ
  68.  
  69. The American National Standard for Information Systems - Programming Language C 
  70. (the ANSI standard) specifies that the C language must support signal handlers, 
  71. although it does not require that they be called when the operating system 
  72. detects an error.  The application libraries of C Set are ANSI compliant, and 
  73. support signal handlers.  The C Set subsystem library (selected by the /Rn 
  74. compiler option) does not.  The subsystem library requires that you do all your 
  75. exception handling using the exception handler interface described later in 
  76. this article. 
  77.  
  78. You can use the library function raise()  to generate a signal, but the 
  79. usefulness of this is marginal.  Signal handlers are much more useful if they 
  80. can respond to the operating system exceptions, such as in the C Set 
  81. application libraries.  In most cases, this provides all the functionality 
  82. required, and has the benefits of being simpler to write, and being portable to 
  83. other platforms. 
  84.  
  85. C Set provides a number of different signals, to allow you to differentiate 
  86. among error conditions.  The signals and their correspondence to OS/2 
  87. exceptions are described in the IBM C Set User's Guide.  You can select which 
  88. ones you want to handle, and which ones you want the C Set library to handle. 
  89. Specify how you want a signal is to be handled with the signal() library 
  90. function.  There are three choices: 
  91.  
  92. SIG_DFL:  Let the C library use its default handling.  For most signals, this 
  93.           means that your process is terminated, with a message giving the 
  94.           reason.  A dump of the machine state is made to file handle 2 if the 
  95.           library assumes that the cause is an error in your code.  This is a 
  96.           good choice for signals you expect never to occur, such as SIGILL. 
  97.  
  98.           Note:  You can capture the dump in a file by redirecting file handle 
  99.           2 from the command line, or by using the dup2() function to attach 
  100.           file handle 2 to a disk file.  This works even for PM programs.  In C 
  101.           Set++, you also have the option of setting the file handle using the 
  102.           _set_crt_msg_handle() function. 
  103.  
  104. SIG_IGN:  Ignore the condition.  The C library will attempt to carry on with 
  105.           your program as though nothing has happened.  Due to limitations in 
  106.           the in the underlying microprocessor, this is not possible with many 
  107.           exceptions.  In that case, the C library will treat the signal as 
  108.           though SIG_DFL was the handler. 
  109.  
  110. your own handler function: Your function is called.  The function has no 
  111.           limitations, and may call any library function.  If you choose this 
  112.           option, the handling for the signal is reset to SIG_DFL before your 
  113.           function is called, to prevent recursion.  It is up to you to set the 
  114.           signal handler back to your function with a call to signal(). 
  115.  
  116.           Your handler function may end in one of the following ways: 
  117.  
  118.     1. It may call exit() or abort().  This will terminate your process in the 
  119.        same way that it would if you called these functions outside a signal 
  120.        handler. 
  121.  
  122.     2. It may call _endthread().  This terminates the current thread in a 
  123.        multithreaded program.  The process continues to run, minus this thread. 
  124.        Of course, you must take steps to keep your process running without it. 
  125.        Thread 1 of your program is special; doing this on thread 1 of your 
  126.        program is equivalent to calling exit(). 
  127.  
  128.     3. You may call longjmp() if you have previously called the setjmp() 
  129.        function in this thread.  This allows you to continue the thread 
  130.        execution at a known point.  setjmp() saves the state of the thread in a 
  131.        buffer.  When you call longjmp(), the state of the thread is reset to 
  132.        that in the setjmp buffer, forcing execution to restart at the call to 
  133.        setjmp(). 
  134.  
  135.     4. You may return from the function.  This indicates that you wish to 
  136.        restart as though the signal had not occurred, similar to using SIG_IGN. 
  137.        The C library will terminate your process if this is not possible. In C 
  138.        Set/2 and C Set++, SIG_IGN is equivalent to the following user signal 
  139.        handler: 
  140.  
  141.                 int myhandler(int x) {
  142.                     signal(x,myhandler);    /* re-register handler */
  143.                 }
  144.  
  145.  
  146. ΓòÉΓòÉΓòÉ 3.1. A Simple Signal Handler ΓòÉΓòÉΓòÉ
  147.  
  148. Here's a simple program that has a useful function.  If you give it a pointer, 
  149. and a size, it returns the number of bytes that you can access without 
  150. generating a signal.  In other words, it tells you how much storage you can 
  151. access without problems. 
  152.  
  153.   #include <signal.h>                  /* for the signal function */
  154.   #include <setjmp.h>                  /* for the setjmp and longjmp functions */
  155.   #include <stdio.h>                   /* for the printf call */
  156.  
  157.   static void mysig(int sig);          /* the signal handler prototype */
  158.   static jmp_buf jbuf;                 /* work area to save machine state for longjmp function */
  159.  
  160.   int chkptr(void * ptr,               /* pointer to storage to check */
  161.              int size)                 /* number of bytes to check */
  162.   {
  163.       void (* oldsig)(int);            /* where to save the old signal handler */
  164.       volatile char c;                 /* volatile to insure access occurs */
  165.       int valid = 0;                   /* count of valid bytes */
  166.       char * p = ptr;                  /* to satisfy the type checking for p++ */
  167.  
  168.       oldsig = signal(SIGSEGV,mysig);  /* set the signal handler */
  169.  
  170.       if (!setjmp(jbuf)) {             /* provide a point for the signal handler */
  171.                                        /* to return to */
  172.  
  173.          while (size--)                /* scan the storage */
  174.          {
  175.             c = *p++;                  /* check the storage */
  176.             valid++;                   /* then bump the counter */
  177.          }
  178.       }
  179.  
  180.       signal(SIGSEGV,oldsig);          /* reset the signal handler */
  181.       return valid;                    /* return number of valid bytes */
  182.   }
  183.  
  184.   static void mysig(int sig) {
  185.       printf("Detected invalid storage address\n");
  186.       longjmp(jbuf,1);                 /* restart the function at the setjmp() call */
  187.                                        /* without restarting the while() loop */
  188.   }
  189.  
  190. Assuming that there is a problem accessing the buffer, let's follow the flow of 
  191. the execution: 
  192.  
  193.  1. We enter chkptr() with a buffer pointer in ptr and the expected size in 
  194.     size. 
  195.  
  196.  2. The program registers the signal handler mysig(), saving the original 
  197.     signal handler in oldsig.  Since we're going to restore the old signal 
  198.     handler before we return, oldsig can be a local variable in chkptr(). 
  199.  
  200.  3. We now put a mark on the wall, so we can get back to chkptr() if a signal 
  201.     occurs.  We do it using setjmp().  setjmp() returns 0 if it is called in 
  202.     this way.  It always returns a non-zero value when is is re-entered via a 
  203.     longjmp() call. 
  204.  
  205.  4. We now sit in a loop, examining each byte of the buffer, and incrementing 
  206.     valid for each byte that we successfully copy to the variable c. 
  207.  
  208.     There are two important concepts here: 
  209.  
  210.    o The program maintains the count separate from the pointer, since, in the 
  211.      expression c = *p++;, p may be incremented either before or after the 
  212.      store to c.  The ANSI standard requires only that the increment be 
  213.      complete when the statement is complete.  Since we don't know when the 
  214.      increment occurs relative to the buffer access, we can't use the 
  215.      difference between p and ptr to calculate the number of valid bytes. If p 
  216.      were incremented as a separate statement, either before or after the 
  217.      assignment, this problem goes away. 
  218.  
  219.    o The variable c has been defined as volatile.  This keyword tells the 
  220.      compiler that loads and stores to the variable have side effects, and that 
  221.      it is not to remove them.  We are depending on such a side effect (the 
  222.      occurrence of a signal when we move data from the buffer).  If we omit the 
  223.      volatile keyword, the C Set compilers can optimize the while loop out of 
  224.      existence, breaking the function.  The optimizer will probably generate 
  225.      similar code for the following two code fragments, which is definitely not 
  226.      desirable in this function: 
  227.  
  228.             Original Code                 Optimized Equivalent
  229.             ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ                 ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
  230.             char c, *p;                   char c, *p;
  231.             int valid,size;               int valid,size;
  232.  
  233.             while (size--)                p += size;
  234.             {                             valid += size;
  235.                c = *p++;                  c = *p;
  236.                valid++;
  237.             }
  238.  
  239.  5. Since we have assumed that we cannot access all of the buffer, at some 
  240.     point in the loop, p points to a storage location to which our process does 
  241.     not have access.  This causes an OS/2 exception to occur, which the C Set 
  242.     library translates into a SIGSEGV signal.  Since we made mysig() the 
  243.     handler for this signal, the C library calls it, after setting the signal 
  244.     handler for SIGSEGV to SIG_DFL. 
  245.  
  246.  6. mysig() is entered, dumps out a message, and branches back to the setjmp() 
  247.     call in chkptr().  From the application programmer's point of view, this 
  248.     looks very much as though setjmp() has just returned, with a return code of 
  249.     1. 
  250.  
  251.     Note:  We didn't reset the signal handler in mysig().  It isn't necessary, 
  252.     since we only expect this signal to occur once. 
  253.  
  254.  7. Since setjmp() has returned 1, we don't re-enter the while loop, but go 
  255.     straight to the call to signal(), where we reset the signal handler for 
  256.     SIGSEGV to wherever it was when we entered chkptr. 
  257.  
  258.  8. We then return the valid count. 
  259.  
  260. Yes - it's that simple!  You can, of course, dress it up, extend the function, 
  261. make it reentrant, and so on, but the basic design is there.  Please note that 
  262. this simple function is not usable as shown in a multithreaded program.  The 
  263. static variable jbuf makes it not reentrant, unless you provide some sequencing 
  264. controls (such as a semaphore).  We'll convert it to a multithreaded version in 
  265. a later section. 
  266.  
  267. This example shows one very important point.  Your program can receive a signal 
  268. (like the SIGSEGV signal), recover, and keep going! The function chkptr() 
  269. attempted an invalid memory access, and lived to tell the tale.  The only real 
  270. requirement on you is that you must provide a point for the signal handler to 
  271. go to when the signal occurs. 
  272.  
  273.  
  274. ΓòÉΓòÉΓòÉ 3.2. Extending the Signal Handling Model ΓòÉΓòÉΓòÉ
  275.  
  276. So far, I've discussed a fairly normal C signal handling structure.  Any C 
  277. compiler worthy of the name can do what I've shown as long as there is only one 
  278. thread of execution.  C Set and OS/2 show their strengths in multithreaded 
  279. programs and DLL's.  The standard ANSI signal handling model starts to show its 
  280. shortcomings under these conditions, so C Set had to provide some extensions. 
  281. I'll discuss what happens in a multithreaded program first. 
  282.  
  283.  
  284. ΓòÉΓòÉΓòÉ 3.3. Signal Handling in Multithreaded Programs ΓòÉΓòÉΓòÉ
  285.  
  286. Multithreaded programs can be viewed as tightly coupled independent programs. 
  287. They are tightly coupled, inasmuch as they share the same memory space.  They 
  288. are independent inasmuch as the machine state (registers, stack, and 
  289. instruction pointer) is unique to each thread.  It made more sense in the C Set 
  290. library design to treat threads as separate signal handling environments. To 
  291. you, the application programmer, this means that a signal handler registered on 
  292. thread x will only get signals generated by thread x.  This is different from 
  293. C/2, where a signal handler applies to all threads, no matter which thread 
  294. registers it.  This will require some code changes if you port from C/2 to C 
  295. Set/2 or C Set++.  The C Set library design group did not make this change 
  296. lightly; you don't fix what isn't broken.  The rationale was as follows: 
  297.  
  298.  1. A function like chkptr(), which we just examined, is almost as easy to 
  299.     write in the multithreaded environment as it is in the single threaded 
  300.     environment.  This is no small gain in functionality. 
  301.  
  302.  2. You can predict exactly what the signal handler is going to be when a 
  303.     signal occurs.  It doesn't matter if another thread changes its signal 
  304.     handler.  It can't affect your thread. 
  305.  
  306.  3. Independent signal handlers on each thread matched much better to the 
  307.     underlying OS/2 exception handling.  Indeed, the correspondance is almost 
  308.     one-to-one.  This significantly reduced the complexity of the C Set library 
  309.     design, thereby improving its reliability. 
  310.  
  311. So the selected design provides a different signal environment for each thread. 
  312. Nothing is completely free.  This requires you to do things a little 
  313. differently when you register signal handlers. 
  314.  
  315.  1. When you start up a new thread (using the _beginthread() function), all its 
  316.     signal handlers are set to SIG_DFL.  If you want signal handling in that 
  317.     thread, you must call signal() from that thread to register them. 
  318.  
  319.  2. There are three signals which can only occur on the first thread of your 
  320.     process.  These are SIGINT, SIGBREAK, and SIGTERM.  To handle these, you 
  321.     must register the signal handlers for them on thread 1. 
  322.  
  323.  3. The raise() function raises the signal only to the signal handler 
  324.     registered for the thread in which it is called.  You can use this function 
  325.     to signal your own conditions using the signals SIGUSER1, SIGUSER2, and 
  326.     SIGUSER3, which C Set provides for just this purpose.  You can also use 
  327.     this function to generate the other signals to test your signal handlers. 
  328.  
  329. Now, let's convert our simple signal handler example to a multithreaded 
  330. version.  Because the signal handling on each thread is independent, it's easy. 
  331. We only need to fix the reentrancy problem with jbuf.  There are many ways to 
  332. do this.  We're going to assume that there is an array of pointers, indexed by 
  333. thread number, that we can use.  Since thread numbers are reused by the 
  334. operating system, we should know how big an array we need.  The changes are 
  335. easy to pick out.  Here's our new and improved function: 
  336.  
  337.   #define  INCL_BASE
  338.   #define  INCL_NOPMAPI
  339.   #include <os2.h>                     /* for the doscall */
  340.   #include <signal.h>                  /* for the signal function */
  341.   #include <setjmp.h>                  /* for the setjmp and longjmp functions */
  342.   #include <stdio.h>                   /* for the printf call */
  343.   #include <stddef.h>                  /* for _threadid */
  344.  
  345.   static void mysig(int sig);          /* the signal handler prototype */
  346.   void * tss_array[100];               /* reserve space for 100 threads */
  347.  
  348.   int chkptr(void * ptr,               /* pointer to storage to check */
  349.              int size)                 /* number of bytes to check */
  350.   {
  351.       void (* oldsig)(int);            /* where to save the old signal handler */
  352.       volatile char c;                 /* volatile to insure access occurs */
  353.       int valid = 0;                   /* count of valid bytes */
  354.       PTIB ptib;                       /* stuff to get the TIB pointer */
  355.       PPIB ppib;
  356.       PVOID * temp;
  357.       char * p = ptr;                  /* to satisfy the type checking for p++ */
  358.       jmp_buf jbuf;                    /* the jump buffer moves to automatic storage */
  359.                                        /* so that it is unique to this thread */
  360.       unsigned int tid = *_threadid;   /* get the thread id */
  361.  
  362.       /* create a thread specific jmp_buf */
  363.       tss_array[tid] = (void *)jbuf;   /* save the pointer to the jump buffer */
  364.                                        /* in the thread specific storage array */
  365.  
  366.       oldsig = signal(SIGSEGV,mysig);  /* set the signal handler */
  367.  
  368.       if (!setjmp(jbuf)) {             /* provide a point for the signal handler */
  369.                                        /* to return to */
  370.  
  371.          while (size--)                /* scan the storage */
  372.          {
  373.             c = *p++;                  /* check the storage */
  374.             valid++;                   /* then bump the counter */
  375.          }
  376.       }
  377.  
  378.       ptib->tib_arbpointer = temp;     /* restore the user pointer */
  379.       signal(SIGSEGV,oldsig);          /* reset the signal handler */
  380.       return valid;                    /* return number of valid bytes */
  381.   }
  382.  
  383.   static void mysig(int sig) {
  384.       unsigned int tid = *_threadid;   /* get the thread id */
  385.  
  386.       /* find the thread specific jmp_buf */
  387.       printf("Detected invalid storage address\n");
  388.       longjmp((int *)tss_array[tid],1); /* restart the function at the setjmp() call */
  389.                                         /* without restarting the while() loop */
  390.   }
  391.  
  392.  
  393. ΓòÉΓòÉΓòÉ 3.4. Signal Handling and DLL's ΓòÉΓòÉΓòÉ
  394.  
  395. To lay a foundation for understanding how signal handling and DLLs interact, I 
  396. must digress and discuss how C Set knows when to call a signal handler.  The 
  397. only way that C Set knows that a problem has occurred is for it to get an 
  398. exception from the operating system.  How this is done in the general case, 
  399. we'll discuss later.  For now, it's enough to know that C Set must register its 
  400. exception handler (a function called _Exception())  with the operating system. 
  401. _Exception() then converts the exception into a signal number, and then looks 
  402. up the handling for the signal in the signal table.  The signal table is part 
  403. of the library environment, and contains an entry for each thread and signal, 
  404. giving the function to handle that signal.  The C Set library then calls the 
  405. appropriate signal handler.  This all assumes that the C Set library receives 
  406. the exception, and can find the appropriate signal table, so it can call your 
  407. signal handler. 
  408.  
  409. Using DLLs is painless if the C Set library assumptions are valid.  You can 
  410. make that so by following a few rules.  If you can meet all three of the 
  411. following conditions, you can partition your application into DLLs without 
  412. worrying about signal handling: 
  413.  
  414.  1. All your DLLs and the EXE are written using C Set 
  415.  
  416.  2. You have compiled them all with the /Gd+ switch, and are using the C Set 
  417.     library DLLs. 
  418.  
  419.  3. Your functions may call functions in third party DLLs (DLLs provided 
  420.     neither by you nor the C Set library, nor the operating system).  They may 
  421.     not call functions in your code. That's known as a callback. 
  422.  
  423. These conditions make the DLL boundaries invisible to the signal handling. The 
  424. restrictions boil down to making sure that (from the C Set library's 
  425. perspective): 
  426.  
  427.  1. There is only one copy of the C library used by your complete program. Each 
  428.     copy of the library has its own environment.  Since the signal table is 
  429.     part of that environment, the C Set library has no problem finding the 
  430.     signal tables. 
  431.  
  432.  2. The C Set library exception handler will receive the exception. 
  433.  
  434. So what do you do if you can't meet one of the restrictions?  The following 
  435. should provide some guidance: 
  436.  
  437.  1. You may not ship the C Set/2 library DLLs with your application.  This 
  438.     doesn't create much of a problem.  Just create your own version of the 
  439.     library DLLs, and export the library entry points to all your other DLLs 
  440.     and your EXE. The IBM C Set User's Guide tells you how.  The key is that 
  441.     your application should use only one copy of the C Set libraries.  Your 
  442.     application won't care if it's in a C Set/2 DLL, or one you create 
  443.     yourself. 
  444.  
  445.  2. You may not ship the C Set++ library DLLs with your application without 
  446.     renaming them.  Use the DLLRNAME utility provided with C Set++ to rename 
  447.     them. 
  448.  
  449.  3. The "no callback" restriction is impossible to meet in a PM program.  You 
  450.     can't guarantee that the C Set exception handler will get exceptions in a 
  451.     callback from code that you do not control.  It's easy to keep the C Set 
  452.     library signal handling code happy.  All we have to do is insure that the C 
  453.     Set library exception handler gets the exception.  The #pragma handler() 
  454.     statement registers the C Set exception handler, solving the problem with 
  455.     one line of code. 
  456.  
  457.     For instance, in a PM program, the window procedure is a callback function. 
  458.     We don't know if PM has changed the exception handling environment, so we 
  459.     need to register the C Set exception handler.  If your window procedure is 
  460.     MyWindowProc(), you code the following before the definition of 
  461.     MyWindowProc(). 
  462.  
  463.     #pragma handler(MyWindowProc) 
  464.  
  465.     This adds about 5 80386 instructions to your function to register and 
  466.     deregister the C Set library exception handler.  Remember that signal 
  467.     handlers need to be registered for each thread.  If the callback is not on 
  468.     a thread for which you already have registered signal handlers, you'll also 
  469.     have to register them. 
  470.  
  471.  4. Compiling without the /Gd+ option statically links the library to your EXE 
  472.     or DLL (your executable).  Each such executable has a separate library 
  473.     environment, with its own signal tables, which requires its own C Set 
  474.     library exception handler to be registered.  A DLL built this way must be 
  475.     treated as a "third party" DLL.  You can make this work, but its not 
  476.     recommended in any but the simplest case. 
  477.  
  478.     The simplest case is one where the statically linked executable calls only 
  479.     functions in the operating system; it does not call functions in any of 
  480.     your DLLs.  This allows you to provide a localized solution; all we have to 
  481.     do is treat all entry points to this executable the same way as we treated 
  482.     callbacks.  Just provide a #pragma handler() statement for each entry 
  483.     point, and everything works fine.  If you have more than one DLL like this, 
  484.     you can treat each one independently, without reference to the others. 
  485.  
  486.     Anything other than the simplest case requires you to put #pragma handler() 
  487.     statements at any DLL entry or callback point where the environment can 
  488.     change.  Keeping track of where the environment changes and which signal 
  489.     handlers are valid in which environment can quickly become very difficult. 
  490.     That's why it's not recommended. 
  491.  
  492.  
  493. ΓòÉΓòÉΓòÉ 3.5. How to Cause Problems ΓòÉΓòÉΓòÉ
  494.  
  495. The C Set signal handling is fairly robust, but as with all things in C, there 
  496. are ways to cause unexpected problems.  The following is a short list of "ways 
  497. to shoot yourself in the foot": 
  498.  
  499.  1. You can register anything as a signal handler.  The library won't care.  No 
  500.     problem will occur until the signal happens.  A variant of this occurs in 
  501.     the following situation: 
  502.  
  503.     If you load a DLL (using DosLoadModule() or _loadmod()), and register a 
  504.     function in it as a signal handler, you must not unload the DLL without 
  505.     changing the signal handler.  The C Set signal handling won't know that the 
  506.     DLL is no longer loaded when a signal occurs.  The result will usually be a 
  507.     simple abend, but if another DLL gets loaded into the same address range, 
  508.     really strange things can occur. 
  509.  
  510.  2. Don't assume that a SIGSEGV signal always means that you have a bad data 
  511.     pointer.  It can also be caused by a wild branch if the address pointer 
  512.     goes outside your code segment.. 
  513.  
  514.  3. Don't assume the SIGILL signal will always occur when you take a bad 
  515.     function call via a pointer.  The pointer may point to something that is a 
  516.     valid instruction stream. 
  517.  
  518.  4. Don't expect C Set or OS/2 to be silent about dereferencing a NULL pointer. 
  519.     This is guaranteed to cause a SIGSEGV signal, unlike C/2, which only 
  520.     generated a runtime warning as you exited the program. 
  521.  
  522.  5. When using longjmp() to leave a signal handler, you must take care to 
  523.     insure that the jump buffer that you are using is valid.  It must have been 
  524.     created by the thread in which you are currently executing, and the point 
  525.     in your program where it was created must still be on the call chain.  The 
  526.     C Set libraries check as best they can, and will terminate your process if 
  527.     they can determine that the contents of the jump buffer are not valid. 
  528.     Especially avoid calling setjmp() on one thread, and calling longjmp() with 
  529.     the same jump buffer on another thread.  The C Set library specifically 
  530.     checks for this! 
  531.  
  532.  6. The C Set signal handling has one idiosyncracy.  If you call gets(), 
  533.     scanf(), or other console I/O library functions, and a SIGINT, SIGBREAK, or 
  534.     SIGTERM signal occurs, you may think the behaviour unusual.  You don't 
  535.     receive the signal until after the library call completes.  This was an 
  536.     unavoidable side effect of protecting the C Set library internal data 
  537.     structures.  Since you are allowed to call any library function from within 
  538.     a signal handler, there is a possibility of a library function being 
  539.     unexpectedly re-entered by your signal handler.  The library protects 
  540.     itself by putting some of its code inside "must complete" sections.  This 
  541.     means that the C Set library holds off these signals until the "must 
  542.     complete" section ends.  You can do much the same thing yourself using the 
  543.     operating system APIs DosEnterMustComplete() and DosExitMustComplete(). 
  544.  
  545.  
  546. ΓòÉΓòÉΓòÉ 4. Exception Handlers ΓòÉΓòÉΓòÉ
  547.  
  548. As I noted before, it's not a good idea to use an exception handler if a signal 
  549. handler can do the job for you.  Exception handlers are more complex to write, 
  550. and awkward to debug, but they're your only option if you use the C Set 
  551. subsystems library.  After you have written your first exception handler, you 
  552. will find you have become well aquainted with the contents of the toolkit 
  553. header file BSEXCPT.H. 
  554.  
  555. Exception handlers have two advantages over signal handlers: 
  556.  
  557.  1. You get more information.  Much of it is useless to a C programmer, but 
  558.     there are some things that are useful that you don't get from a signal 
  559.     handler. 
  560.  
  561.  2. You can intercept any exception.  The C Set library doesn't convert all the 
  562.     exceptions into signals.  Some it passes on to the operating system because 
  563.     there is no appropriate C semantic for handling them.  For example, it does 
  564.     not handle any of the process termination exceptions. 
  565.  
  566. Because exception handlers do provide function which is not available from the 
  567. C Set signal handlers, it is sometimes useful to use both.  An exception 
  568. handler can be used with the C Set applications libraries if a few simple rules 
  569. are followed, as described later. 
  570.  
  571.  
  572. ΓòÉΓòÉΓòÉ 4.1. Types of Exceptions ΓòÉΓòÉΓòÉ
  573.  
  574. Exceptions break down into two major classes: 
  575.  
  576. Asynchronous Exceptions: These exceptions are caused by actions outside your 
  577.           thread of execution.  There are only two: 
  578.  
  579.     1. XCPT_SIGNAL, which includes the keyboard signals (ctrl-break, etc), and 
  580.        the kill process exception.  These only appear on thread 1 of your 
  581.        process, since all processes must have a thread 1. 
  582.  
  583.     2. XCPT_ASYNC_PROCESS_TERMINATE, which indicates that some thread in your 
  584.        process has terminated the entire process.  This exception may occur on 
  585.        any thread. 
  586.  
  587. Synchronous Exceptions: All other exceptions fall into this class. These 
  588.           exceptions are caused by code executing on the thread that got the 
  589.           exception. 
  590.  
  591. Like the signal handlers, exception handlers are only called for exceptions 
  592. happening on the thread in which they are registered.  Unlike signal handlers, 
  593. exception handlers have a hierarchy, which can be seen in the next section. 
  594.  
  595.  
  596. ΓòÉΓòÉΓòÉ 4.2. Registering an Exception Handler ΓòÉΓòÉΓòÉ
  597.  
  598. Exception handlers are registered using a structure called an 
  599. EXCEPTIONREGISTRATIONRECORD.  The operating system finds them by following a 
  600. chain rooted in the TIB.  Registering an exception handler is done by placing 
  601. the address of the exception handler and the chain pointer from the TIB in an 
  602. EXCEPTIONREGISTRATIONRECORD, The TIB is then updated to to point to the new 
  603. EXCEPTIONREGISTRATIONRECORD.  The chain is shown in the following diagram: 
  604.  
  605.              The TIB
  606.  ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  607.  Γöé              .               Γöé
  608.  Γöé              .               Γöé
  609.  Γöé              .               Γöé
  610.  Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  611.  Γöé chain pointer (tib_pexchain) Γö£ΓöÇΓöÇΓöÇΓöÉ
  612.  ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ   Γöé
  613.                                     Γöé
  614.                ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  615.                Γöé
  616.                Γöé                         The stack
  617.                Γöé         ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  618.                Γöé         Γöé                   .                   Γöé
  619.                Γöé         Γöé                   .                   Γöé
  620.                Γöé         Γöé                   .                   Γöé
  621.  Decreasing    Γöé         Γöé                                       Γöé
  622.  Memory        Γöé         Γöé     EXCEPTIONREGISTRATIONRECORD 1     Γöé
  623.  Addresses     Γöé         Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  624.     Γöé          Γöé         Γöé pointer to handler (ExceptionHandler) Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ> Handler Function Handler Function
  625.     Γöé          Γöé         Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  626.     Γöé          Γöé  NULL <ΓöÇΓöñ    chain pointer (prev_structure)     Γöé<ΓöÇΓöÇΓöÉ
  627.     Γöé          Γöé         Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ   Γöé
  628.     Γöé          Γöé         Γöé                   .                   Γöé   Γöé
  629.     Γöé          Γöé         Γöé                   .                   Γöé   Γöé
  630.     Γöé          Γöé         Γöé                   .                   Γöé   Γöé
  631.     Γöé          Γöé         Γöé                   .                   Γöé   Γöé
  632.     Γöé          Γöé         Γöé                   .                   Γöé   Γöé
  633.     Γöé          Γöé         Γöé     EXCEPTIONREGISTRATIONRECORD 2     Γöé   Γöé
  634.     Γöé          Γöé         Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ   Γöé
  635.     Γöé          Γöé         Γöé pointer to handler (ExceptionHandler) Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ> Handler Function
  636.     Γöé          Γöé         Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ   Γöé
  637.     Γöé          ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ>Γöé    chain pointer (prev_structure)     Γö£ΓöÇΓöÇΓöÇΓöÿ
  638.     Γöé                    Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  639.     Γöé                    Γöé                   .                   Γöé
  640.     Γöé                    Γöé                   .                   Γöé
  641.     V                    Γöé                   .                   Γöé
  642.  
  643.  
  644.  
  645.  
  646.   NOTE: Names in parentheses are the names of the fields in the structures
  647.  
  648. Each EXCEPTIONREGISTRATIONRECORD is chained to the next.  When an exception 
  649. occurs, the operating system starts at the TIB, and goes to each 
  650. EXCEPTIONREGISTRATIONRECORD in turn, starting, in our example, with 
  651. EXCEPTIONREGISTRATIONRECORD 2. It calls the exception handler, whose address is 
  652. in the record, giving it the exception information.  The exception handler can 
  653. then either handle the exception, or tell the operating system to pass the 
  654. exception to the next handler in the chain.  If the last exception handler in 
  655. the chain does not handle the exception, the operating system takes its default 
  656. action.  The last exception handler is recognized by the NULL in its chain 
  657. pointer. The operating system is very picky about where the 
  658. EXCEPTIONREGISTRATIONRECORDs are in memory.  They must be on the stack, and 
  659. each record, starting from the TIB must be at a higher address than the 
  660. previous one. 
  661.  
  662. Terminology Note: The term "unwind" when applied to an exception handler refers 
  663.           to forced deregistration.  Suppose that I wish to longjmp() to a 
  664.           point in the code before the point at which I registered exception 
  665.           handler 1 in the above diagram.  This means the stack pointer is 
  666.           going to be moved, and EXCEPTIONREGISTRATIONRECORD 1 is now at a 
  667.           lower address than the ESP register (the stack grows downward).  This 
  668.           is definitely an unstable state of affairs, so the operating system 
  669.           handles it by providing the API DosUnwindException() to "unwind" the 
  670.           exception handlers.  When it is called, the operating system calls 
  671.           each exception handler whose EXCEPTIONREGISTRATIONRECORD is about to 
  672.           be removed, telling it that it is being unwound.  When the exception 
  673.           handler returns, its EXCEPTIONREGISTRATIONRECORD is then removed from 
  674.           the chain.  Since DosUnwindException() requires that you provide a 
  675.           machine state (which is not available in a high level language like 
  676.           C), the C Set library function longjmp() calls it for you. 
  677.  
  678. There are several ways to attach an EXCEPTIONREGISTRATIONRECORD to the chain. 
  679. The most obvious way is to do all the code to link it in yourself, since you 
  680. can get the TIB address yourself.  That will work, but the operating system 
  681. provides APIs to do the same thing.  The APIs are shown in the following 
  682. example: 
  683.  
  684.   #define INCL_BASE
  685.   #include <os2.h>
  686.  
  687.   /* the prototype for the exception handler */
  688.   APIRET APIENTRY MyExceptionHandler(EXCEPTIONREPORTRECORD *,
  689.                                      EXCEPTIONREGISTRATIONRECORD *,
  690.                                      CONTEXTRECORD *,
  691.                                      PVOID);
  692.   int myfunction(.....
  693.      EXCEPTIONREGISTRATIONRECORD err = { NULL,MyExceptionHandler };
  694.  
  695.      DosSetExceptionHandler(&err);  /* register */
  696.      .
  697.      .
  698.      .
  699.      DosUnsetExceptionHandler(&err);  /* deregister */
  700.   }
  701.  
  702. For the time being, ignore the details of the exception handler prototype. 
  703. We'll look at its interface later.  The actual registration is simple.  Define 
  704. your EXCEPTIONREGISTRATIONRECORD, load it up, and then call the APIs to 
  705. register and deregister it. 
  706.  
  707. You can choose to register the exception handler over only a part of your 
  708. function.  However, you must remember to deregister the exception handler 
  709. before you leave.  If you don't, the next exception you get on this thread may 
  710. cause strange and unexpected results.  If the function has many return 
  711. statements, it's easy to miss one.  A "single entry/single exit" programming 
  712. style is recommended. 
  713.  
  714. This type of registration can be used to register more than one exception 
  715. handler in a function.  Just insure that the EXCEPTIONREGISTRATIONRECORDs are 
  716. in the correct order on the stack. 
  717.  
  718. The C Set compiler provides another way to register an exception handler.  It 
  719. will execute much faster, since it puts the registration and deregistration 
  720. code inline to the function.  It also makes use of the fact that the TIB is 
  721. always at location FS:0000, so the total extra generated code is 5 
  722. instructions. It is slightly less flexible, since it registers the exception 
  723. handler on function entry, and deregisters it on exit; you can't register over 
  724. part of a function. It is an extension of how we registered the C Set library 
  725. exception handler. 
  726.  
  727.   #pragma map(_Exception,"MyExceptionHandler")
  728.   #pragma handler(myfunction)
  729.   int myfunction(.....
  730.  
  731.  1. Your exception handler, MyExceptionHandler() must have external linkage; 
  732.     you can't make it a static function.  For you ex-assembler programmers, 
  733.     that means it must be a PUBLIC symbol.  The compiler must assume that 
  734.     MyExceptionHandler() is the name of your exception handler.  It can't even 
  735.     verify that this is the name of a function, much less one of the correct 
  736.     type.  Be careful! 
  737.  
  738.  2. The #pragma map() statement tells the compiler to map the name _Exception 
  739.     to MyExceptionHandler.  Any external reference in this module to _Exception 
  740.     will now be converted to a reference to MyExceptionHandler. 
  741.  
  742.  3. #pragma handler(myfunction) would normally tell the compiler to generate 
  743.     code to register the exception handler _Exception for function 
  744.     myfunction().  Since we have mapped the name, the compiler will actually 
  745.     use MyExceptionHandler as the exception handler name. Remember, the 
  746.     EXCEPTIONREGISTRATIONRECORD is on the stack.  Bad things happen if we leave 
  747.     this function without deregistering it, so the compiler does the right 
  748.     thing, and generates code to deregister it as the function returns. 
  749.  
  750. Note:  This method can handle only one exception handler in a module, since it 
  751. changes the name of the exception handler registered by #pragma handler().  It 
  752. may require you to place functions in separate modules to get the exception 
  753. handling you want. 
  754.  
  755.  
  756. ΓòÉΓòÉΓòÉ 4.3. The Exception Handler Interface ΓòÉΓòÉΓòÉ
  757.  
  758. Now we know how to register an exception handler, we'll look at how OS/2 calls 
  759. the exception handler, and what it tells us about the exception.  You'll find 
  760. declarations of the exception handling interface structures in the toolkit 
  761. header file BSEXCPT.h.  All exception handlers must have the following 
  762. prototype: 
  763.  
  764.   #define INCL_BASE
  765.   #include <os2.h>
  766.  
  767.   APIRET APIENTRY MyExceptionHandler(EXCEPTIONREPORTRECORD *,
  768.                                      EXCEPTIONREGISTRATIONRECORD *,
  769.                                      CONTEXTRECORD *,
  770.                                      PVOID);
  771.  
  772. Only the first three arguments are of any concern.  The last one is a pointer 
  773. used by the operating system. 
  774.  
  775. APIRET:   A standard definition used in the toolkit header files. If you return 
  776.           from the exception handler, you must return one of two values: 
  777.  
  778.    XCPT_CONTINUE_SEARCH: This means that you have not handled the exception, 
  779.              and want the operating system to pass the exception to the next 
  780.              handler on the chain. 
  781.  
  782.    XCPT_CONTINUE_EXECUTION: This means that you have fixed up the exception 
  783.              condition, and want the operating system to resume execution of 
  784.              your application at machine state given in the CONTEXTRECORD. 
  785.  
  786. APIENTRY: This defines the function linkage.  The toolkit headers have defined 
  787.           this as _System linkage.  Use the defined value to protect yourself 
  788.           against any operating system changes. 
  789.  
  790. EXCEPTIONREPORTRECORD *: This is a pointer to a structure which contains high 
  791.           level information about the exception. 
  792.  
  793. EXCEPTIONREGISTRATIONRECORD *: This is a pointer to the record that registered 
  794.           this exception handler.  The address is always on the stack.  If you 
  795.           your exception handler was registered with DosSetExceptionHandler(), 
  796.           you can make the EXCEPTIONREGISTRATIONRECORD part of a larger 
  797.           structure, and access the information in that structure from inside 
  798.           the exception handler. 
  799.  
  800. CONTEXTRECORD *: This is a pointer to a structure which contains the thread 
  801.           state at the time of the exception.  i.e. all the registers, the 
  802.           state of the 80387, and the flags.  In general, C can't make much use 
  803.           of this, since its use requires knowledge of the 80386 instruction 
  804.           stream for your application.  If any handler returns 
  805.           XCPT_CONTINUE_EXECUTION, the machine state is reloaded from this 
  806.           structure.  Don't modify it unless you intend to return 
  807.           XCPT_CONTINUE_EXECUTION. 
  808.  
  809. PVOID:    This is a pointer which the OS/2 exception handler requires you to 
  810.           pass back unchanged.  The operating system documentation is silent as 
  811.           to its meaning. 
  812.  
  813. The operating system gives us quite a bit of information about the exception. 
  814. Most of it concerns the machine state at the time of exception.  Since we're in 
  815. a high level language, it's meaningless to us, since we can't relate it to our 
  816. high level language constructs.  However, there are some useful nuggets of 
  817. information in the EXCEPTIONREPORTRECORD that we can use.  This structure looks 
  818. like this: 
  819.  
  820.   struct _EXCEPTIONREPORTRECORD
  821.      {
  822.      ULONG   ExceptionNum;                                /* exception number */
  823.      ULONG   fHandlerFlags;                               /* exception handler flags */
  824.      struct  _EXCEPTIONREPORTRECORD *NestedERR;           /* used only if a nested exception */
  825.      PVOID   ExceptionAddress;                            /* EIP at which exception occurred */
  826.      ULONG   cParameters;                                 /* Size of Exception Specific Info */
  827.      ULONG   ExceptionInfo[EXCEPTION_MAXIMUM_PARAMETERS]; /* Exception Specfic Info */
  828.      };
  829.  
  830.  1. We get an exception number in the ExceptionNum field.  Unlike the C Set 
  831.     signals, we get to see all the exceptions, including the ones that C Set 
  832.     ignores and lets the operating system handle.  There are some useful 
  833.     operating system exceptions which we can get no other way: 
  834.  
  835.    XCPT_PROCESS_TERMINATE: This exception tells us that our process is about to 
  836.              end.  It's useful, because it is indicates that this thread has 
  837.              called DosExit().  Until you leave your exception handler, your 
  838.              thread continues as though DosExit() had not been called. 
  839.  
  840.    XCPT_ASYNC_PROCESS_TERMINATE: This exception tells you that some other 
  841.              thread in your process has called DosExit(), and that the 
  842.              XCPT_PROCESS_TERMINATE exception that results has been passed 
  843.              through its the exception handlers.  In other words, your thread 
  844.              is being told that it is terminating.  You can decide not to 
  845.              terminate the thread, since this exception can be returned as 
  846.              "handled". 
  847.  
  848.    XCPT_ACCESS_VIOLATION: This exception is analagous to the SIGSEGV signal. 
  849.              You do get extra information:  the ExceptionInfo field gives you 
  850.              the address that generated the exception, and the type of access 
  851.              (read/write). 
  852.  
  853.    XCPT_GUARD_PAGE_VIOLATION: This exception tells you that your thread has 
  854.              attempted to access a memory page marked as a "guard page".  See 
  855.              the OS/2 documentation on the DosAllocMem API.  Unless you've used 
  856.              this API yourself to allocate memory, it means that your 
  857.              application has accessed a guard page on the stack.  Normally, 
  858.              this is of no consequence.  You pass the exception on to the 
  859.              operating system, and let it grab another 4K of committed memory 
  860.              for your thread, and move the guard page.  But if you know how big 
  861.              your stack is, you can stop things gracefully if you are running 
  862.              out of stack. Remember, the operating system needs about 1.5 
  863.              Kbytes to dispatch an exception when you calculate how much stack 
  864.              you have left. 
  865.  
  866.    XCPT_UNABLE_TO_GROW_STACK: This exception means you're in deep trouble. The 
  867.              operating system attempted to move your stack's guard page, and 
  868.              found that there was no uncommitted memory to move it into.  If 
  869.              you compiled your code with the /Gs option, you should have about 
  870.              500 bytes of stack for your exception handler.  If you didn't use 
  871.              the /Gs option, you may not get this exception; your process may 
  872.              terminate with an operating system trap. 
  873.  
  874.     We can alos create our own exception numbers and use them for signalling. 
  875.     The OS provides the DosRaiseException() API to raise exceptions.  You are 
  876.     not limited to the ones the operating system defines.  You can create your 
  877.     own, and use your own exception handler to catch them. 
  878.  
  879.  2. The fHandlerFlags can tell us some things about how the exception occurred, 
  880.     and what we are permitted to do about it.  The following bits are found 
  881.     here: 
  882.  
  883.    EH_NONCONTINUABLE: This means that you cannot continue execution of this 
  884.              thread once you leave the exception handler.  Returning 
  885.              XCPT_CONTINUE_EXECUTION if this bit is set is an error.  You can 
  886.              also set this bit, even if you do not handle the exception, 
  887.              thereby making any exception noncontinuable.  You cannot reset the 
  888.              bit; it's "sticky". 
  889.  
  890.    EH_UNWINDING: This means that someone has longjmp()ed over this exception 
  891.              handler, and it is about to be deregistered.  If your function 
  892.              uses a mutex semaphore, this is a good point to hook in to release 
  893.              them. 
  894.  
  895.    EH_EXIT_UNWIND: This flag means that a DosExit call has been made, the 
  896.              exception has been passed back to the operating system, and this 
  897.              is your last chance to do anything before your exception handler 
  898.              is deregistered. 
  899.  
  900.    EH_NESTED_CALL:This tells you that you were handling an exception, when 
  901.              another one occurred.  Be careful if you see this one!  Each 
  902.              exception uses up about 1.5 Kbytes of stack.  If you nest 
  903.              exceptions too deep, you'll run out of stack. 
  904.  
  905.  3. The OS tells us where the instruction address at which the exception 
  906.     occurred in the ExceptionAddress field.  You usually can't determine which 
  907.     function caused the problem at run time without special preparations. 
  908.  
  909.  4. For some exceptions, such as XCPT_ACCESS_VIOLATION, the ExceptionInfo field 
  910.     may contain additional information, such as the address at which the memory 
  911.     access failed.  The number of bytes of information is found in the 
  912.     cParameters field. 
  913.  
  914. The CONTEXTRECORD structure is of limited use to a high level programmer.  It 
  915. contains the state of the thread.  When you return XCPT_CONTINUE_EXECUTION, the 
  916. thread is restarted at the machine state stored in this structure. 
  917.  
  918.  1. If you are smart enough, you can fix some of the exceptions and continue 
  919.     execution from where the exception happened.  The C Set library only does 
  920.     this for the asynchronous exceptions.  For synchronous exceptions, 
  921.     resumption of execution requires knowledge of the application and 
  922.     modification of the CONTEXTRECORD.  Don't attempt this unless it's the only 
  923.     solution to your problems, since it is extremely difficult to get the 
  924.     exception handler code to be correct for all possible conditions. 
  925.  
  926.  2. You have all the information to do a stack trace in the CONTEXTRECORD.  You 
  927.     may think that this allows us to change the machine state, since we can 
  928.     find the return addresses from functions this way.  Don't do it!  Because 
  929.     the calling conventions used by C Set and OS/2 specify that some of the 
  930.     machine registers are preserved across calls, it is almost impossible to 
  931.     reconstruct these registers by traversing the stack.  So this isn't likely 
  932.     to help us recover from the problem, but could dump out some useful debug 
  933.     information. You are guaranteed that a 32 bit stack always looks like this: 
  934.  
  935.                Γöé          .             Γöé
  936.           A    Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  937.           Γöé    Γöé     Return Address     Γöé            Γöé
  938.           Γöé    Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ            Γöé
  939.           ΓööΓöÇΓöÇΓöÇΓöÇΓöñ          EBP           Γöé<ΓöÇΓöÇΓöÇΓöÉ       Γöé
  940.                Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ    Γöé       Γöé
  941.                Γöé          .             Γöé    Γöé       Γöé
  942.                Γöé          .             Γöé    Γöé       Γöé  Stack grows
  943.                           .                  Γöé       Γöé  this way
  944.                           .                  Γöé       Γöé
  945.                           .                  Γöé       Γöé
  946.                Γöé          .             Γöé    Γöé       Γöé
  947.                Γöé          .             Γöé    Γöé       Γöé
  948.                Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ    Γöé       Γöé
  949.                Γöé     Return Address     Γöé    Γöé       Γöé
  950.                Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ    Γöé       Γöé
  951.           ΓöîΓöÇΓöÇΓöÇ>Γöé          EBP           Γö£ΓöÇΓöÇΓöÇΓöÇΓöÿ       Γöé
  952.           Γöé    Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ            Γöé
  953.           Γöé    Γöé          .             Γöé            Γöé
  954.           Γöé    Γöé          .             Γöé            Γöé
  955.           Γöé               .                          V
  956.           Γöé               .
  957.           Γöé               .
  958.           Γöé    Γöé          .             Γöé
  959.           Γöé    Γöé          .             Γöé
  960.           Γöé    Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  961.           Γöé    Γöé     Return Address     Γöé
  962.           Γöé    Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  963.           ΓööΓöÇΓöÇΓöÇΓöÇΓöñ          EBP           Γöé<ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ EBP from exception context record
  964.                Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ              points here
  965.                Γöé          .             Γöé
  966.                Γöé          .             Γöé
  967.  
  968.     Be careful when traversing the stack using the EBP chain.  The stack may 
  969.     have been damaged (i.e. the chain points to outer space!).  Also, if there 
  970.     are 16 bit calls on the stack, you will not be able to trace over them. 
  971.  
  972. Now we have some idea what information we are given in an exception handler, 
  973. what does one look like?  Let's take our little chkptr() example, and do it 
  974. with an exception handler.  It going to get a lot bigger!  This is the main 
  975. program: 
  976.  
  977.   #define  INCL_DOS
  978.   #define  INCL_NOPMAPI
  979.   #include <os2.h>                     /* for the doscall */
  980.   #include <stdlib.h>                  /* for the signal function */
  981.   #include <setjmp.h>                  /* for the setjmp and longjmp functions */
  982.   #include <stdio.h>                   /* for the printf call */
  983.   #include <stddef.h>                  /* for _threadid */
  984.  
  985.   void * tss_array[100];               /* array for 100 thread specific pointers */
  986.  
  987.   APIRET APIENTRY MyExceptionHandler(EXCEPTIONREPORTRECORD *,
  988.                                      EXCEPTIONREGISTRATIONRECORD *,
  989.                                      CONTEXTRECORD *,
  990.                                      PVOID);
  991.   #pragma map(_Exception,"MyExceptionHandler")
  992.   #pragma handler(chkptr)
  993.  
  994.   int chkptr(void * ptr,               /* pointer to storage to check */
  995.              int size)                 /* number of bytes to check */
  996.   {
  997.       volatile char c;                 /* volatile to insure access occurs */
  998.       int valid = 0;                   /* count of valid bytes */
  999.       char * p = ptr;                  /* to satisfy the type checking for p++ */
  1000.       jmp_buf jbuf;                    /* the jump buffer moves to automatic storage */
  1001.                                        /* so that it is unique to this thread */
  1002.       PTIB ptib;                       /* stuff to get the TIB pointer */
  1003.       PPIB ppib;
  1004.       PVOID * temp;
  1005.       unsigned int tid = *_threadid;   /* get the thread id */
  1006.  
  1007.       /* create a thread specific jmp_buf */
  1008.       tss_array[tid] = (void *) jbuf;
  1009.  
  1010.       if (!setjmp(jbuf)) {             /* provide a point for the signal handler */
  1011.                                        /* to return to */
  1012.  
  1013.          while (size--)                /* scan the storage */
  1014.          {
  1015.             c = *p++;                  /* check the storage */
  1016.             valid++;                   /* then bump the counter */
  1017.          }
  1018.       }
  1019.  
  1020.       ptib->tib_arbpointer = temp;     /* restore the user pointer */
  1021.       return valid;                    /* return number of valid bytes */
  1022.   }
  1023.  
  1024. The main function really hasn't changed much; you should recognize everything. 
  1025. All we've done is add in the linkage to our exception handler.  The signal 
  1026. handler is now history.  But this is what the exception handler (doing exactly 
  1027. the same function) looks like: 
  1028.  
  1029.   APIRET APIENTRY MyExceptionHandler(EXCEPTIONREPORTRECORD * report_rec,
  1030.                                      EXCEPTIONREGISTRATIONRECORD * register_rec,
  1031.                                      CONTEXTRECORD * context_rec,
  1032.                                      PVOID dummy)
  1033.   {
  1034.      unsigned int tid = *_threadid;   /* get the thread id */
  1035.  
  1036.      /* check the exception flags */
  1037.      if (EH_EXIT_UNWIND & report_rec->fHandlerFlags) /* exiting */
  1038.         return XCPT_CONTINUE_SEARCH;
  1039.  
  1040.      if (EH_UNWINDING & report_rec->fHandlerFlags)   /* unwinding */
  1041.         return XCPT_CONTINUE_SEARCH;
  1042.  
  1043.      if (EH_NESTED_CALL & report_rec->fHandlerFlags) /* nested exceptions */
  1044.         return XCPT_CONTINUE_SEARCH;
  1045.  
  1046.      /* determine what the exception is */
  1047.      if (report_rec->ExceptionNum == XCPT_ACCESS_VIOLATION) {
  1048.         /* this is the only one we expect */
  1049.  
  1050.         printf("Detected invalid storage address\n");
  1051.         longjmp((int *)tss_array[tid],1);      /* restart the function at the setjmp() call */
  1052.                                                /* without restarting the while() loop */
  1053.  
  1054.      } /* endif */
  1055.  
  1056.      return XCPT_CONTINUE_SEARCH;              /* we don't handle this exception */
  1057.   }
  1058.  
  1059. That's a lot bigger than that simple three line signal handler we had.  Let's 
  1060. look at what it does: 
  1061.  
  1062.  1. On entry we check the exception flags first.  The EH_EXIT_UNWIND flag 
  1063.     indicates that this thread is terminating, and that this is our last chance 
  1064.     to do anything.  Since we don't care, we return XCPT_CONTINUE_SEARCH, which 
  1065.     tells the operating system to pass the exception to the next exception 
  1066.     handler. 
  1067.  
  1068.  2. Next we look at the EH_UNWINDING flag.  This tells us that we are being 
  1069.     removed as an exception handler.  Again, we don't care, since this also 
  1070.     means that chkptr() is being removed from the stack. 
  1071.  
  1072.  3. Then we check to see if this exception occurred inside an exception 
  1073.     handler. The EH_NESTED_CALL flag indicates this.  Since we check this flag, 
  1074.     it means that chkptr() cannot be used by an exception handler.  If we don't 
  1075.     check the flag, there is the possibility of recursive exceptions until we 
  1076.     run out of stack.  This example shows the check, but you don't have to put 
  1077.     it in. 
  1078.  
  1079.  4. Now we've finished the preamble, which should be in all exception handlers. 
  1080.     We can look at the exception number!  A good rule of thumb is, "only check 
  1081.     for exceptions that you expect".  This protects you against possible new 
  1082.     exception numbers.  If we got the only expected exception, which is 
  1083.     XCPT_ACCESS_VIOLATION, we do exactly what we did in the signal handler.  We 
  1084.     print a message, and longjmp().  This terminates the exception handler for 
  1085.     us. 
  1086.  
  1087.     There is (theoretically), another way.  If we knew the format of the jump 
  1088.     buffer (which we don't), we could have changed the machine state to match 
  1089.     it, and then returned XCPT_CONTINUE_EXECUTION.  This would have had the 
  1090.     same effect. However, using longjmp() is a lot easier. 
  1091.  
  1092.  5. Finally, if we get to the end of the exception handler, we must then tell 
  1093.     the operating system to pass this exception on to the next exception 
  1094.     handler. We haven't handled it, so we return XCPT_CONTINUE_SEARCH. 
  1095.  
  1096. A word of warning: don't return XCPT_CONTINUE_EXECUTION from an exception 
  1097. handler unless you know that the thread can continue execution.  This means 
  1098. that you either know that the exception can be restarted (one of the 
  1099. asynchronous exceptions), or you have changed the exception context record so 
  1100. that it can.  Otherwise, you will probably hang your process by generating a 
  1101. new exception each time you exit your exception handler. 
  1102.  
  1103.  
  1104. ΓòÉΓòÉΓòÉ 4.4. Exception Handling for DLLs and Multiple Threads ΓòÉΓòÉΓòÉ
  1105.  
  1106. Since the OS/2 exceptions are thread based, multithreaded programs are handled 
  1107. just like we did with signals.  The only thing we have to watch for is 
  1108. non-reentrancy problems, like our jump buffer. 
  1109.  
  1110. Exception handling in DLLs is actually cleaner than with signals.  Since 
  1111. exception handlers don't use the C Set library, we are not dependant on any 
  1112. library environment, and can always ignore DLL boundaries.  However, if you are 
  1113. writing a "system" DLL, one that is used by a number of different applications, 
  1114. it is a good idea to protect yourself by registering an exception handler at 
  1115. each entry point to it.  That way, you control what happens during an 
  1116. exception.  If you don't register the exception handlers, your using 
  1117. application will get the exception.  What happens then is not under your 
  1118. control. 
  1119.  
  1120.  
  1121. ΓòÉΓòÉΓòÉ 4.5. Applications of Exception Handlers ΓòÉΓòÉΓòÉ
  1122.  
  1123. Since exception handlers can do a number of things that the C Set signal 
  1124. handlers cannot, let's look at a couple of useful ideas: 
  1125.  
  1126.  1. Since an exception handler is called during an unwind operation, we have a 
  1127.     way to determine that a function is longjmp'ed over.  This can be extremely 
  1128.     useful in a multithreaded program that uses semaphores.  Suppose we have 
  1129.     the following code: 
  1130.  
  1131.          jmp_buf(jbuf);
  1132.          HMTX sem;
  1133.  
  1134.          int x(.... ){
  1135.              .
  1136.              .
  1137.              .
  1138.              setjmp(jbuf);
  1139.              .
  1140.              .
  1141.              .
  1142.              y();
  1143.              .
  1144.              .
  1145.              .
  1146.          }
  1147.  
  1148.          void y(...  ) {
  1149.              .
  1150.              .
  1151.              .
  1152.              DosRequestMutexSem(sem,-1);
  1153.              .
  1154.              .
  1155.              z();
  1156.              .
  1157.              .
  1158.              DosReleaseMutexSem(sem);
  1159.              .
  1160.              .
  1161.              .
  1162.           }
  1163.  
  1164.           void z(... ) {
  1165.              .
  1166.              .
  1167.              .
  1168.              longjmp(jbuf);
  1169.              .
  1170.              .
  1171.              .
  1172.           }
  1173.  
  1174.     This looks rather innocuous, but demonstrates a problem.  When we take the 
  1175.     longjmp() in function z(), we leave the semaphore sem in on "owned" state. 
  1176.     This can cause problems in other threads in our program, since they may no 
  1177.     longer be able to obtain ownership of this semaphore.  If we register an 
  1178.     exception handler for function y(), it can recognize the unwind that occurs 
  1179.     as a result of the longjmp(), and release the semaphore. 
  1180.  
  1181.     Note:  z doesn't have to call longjmp() itself. longjmp() could have been 
  1182.     called from inside a signal or exception handler called as a result of an 
  1183.     exception inside z.  The problem is still the same. 
  1184.  
  1185.  2. Since we can intercept a termination exception, we can hold off the 
  1186.     processing of it until we have cleaned up.  For example, in a "system" DLL, 
  1187.     we can update tables, and generally bring the operations to a clean halt. 
  1188.     We probably can't do this using DLL initialization and termination, since 
  1189.     this type of DLL often has global initialization and termination, which is 
  1190.     not invoked until the last caller has terminated. 
  1191.  
  1192.  
  1193. ΓòÉΓòÉΓòÉ 4.6. Ways to Cause Problems ΓòÉΓòÉΓòÉ
  1194.  
  1195. The exception handling structures are more fragile than those used for signal 
  1196. handling.  Exception handlers are subject to all the problems of signal 
  1197. handlers, and a few new ones of their own: 
  1198.  
  1199.  1. Forgetting to deregister an exception handler will usually crash your 
  1200.     process.  Debugging this situation can be difficult.  If you suspect it, 
  1201.     look at the TIB and exception handler chain using the debugger.  The TIB is 
  1202.     at offset 0 from the FS register. 
  1203.  
  1204.  2. Forgetting to check the flags in an exception handler may look like a good 
  1205.     way to save excution time.  Remember that even a simple exception handler 
  1206.     like the one for chkptr() can get unwound by an subsequent exception 
  1207.     handler.  Always check the flags. 
  1208.  
  1209.  3. Trying to do too much in one exception handler.  Exception handlers are 
  1210.     simpler to write, and easier to maintain, if you limit what they can do.  A 
  1211.     "does eveything" exception handler is going to be huge. 
  1212.  
  1213.  4. Having a "default exception handler".  Always check for and handle only 
  1214.     those exceptions you expect.  The operating system may generate new 
  1215.     exceptions on the next release, or you may forget and create your own 
  1216.     exceptions. 
  1217.  
  1218.  
  1219. ΓòÉΓòÉΓòÉ 4.7. Impossibilities ΓòÉΓòÉΓòÉ
  1220.  
  1221. As powerful as the OS/2 exception handling is, there are two situations that 
  1222. you cannot do anything about. 
  1223.  
  1224.  1. If you have less than 1.5 Kbytes (approximately) of stack left, the 
  1225.     operating system can't call your exception handler.  If you generate an 
  1226.     exception in this case, the operating system takes over, and terminates 
  1227.     your process.  It isn't even guaranteed that it will give you a message 
  1228.     saying why. 
  1229.  
  1230.  2. If you run out of space on your swapper file, nothing, and I mean nothing 
  1231.     is guaranteed to work.  The behaviour of the operating system becomes 
  1232.     undefined. 
  1233.  
  1234.  
  1235. ΓòÉΓòÉΓòÉ 4.8. Exceptions in library functions ΓòÉΓòÉΓòÉ
  1236.  
  1237. The C Set subsystem library has no exception handling built in.  You may treat 
  1238. its library as an extension of your own code for the purposes of exception 
  1239. handling. 
  1240.  
  1241. The story is different in the C Set applications libaries.  The exception 
  1242. handling behaviour of the functions in these libraries splits into three 
  1243. categories: 
  1244.  
  1245. Critical functions: Many of the functions in the library are not reentrant, 
  1246.           including most I/O functions and all memory allocation functions.  A 
  1247.           full list is in the IBM C Set User's Guide. Exceptions occurring in 
  1248.           these functions indicate that the library environment is probably 
  1249.           damaged.  That means that you have inadvertently modified the 
  1250.           library's environment, either by storing into it yourself, or by 
  1251.           calling a library function with an invalid argument.  In either case, 
  1252.           the C Set library cannot guarantee continued correct operation; the 
  1253.           critical function will treat this as a terminal error, and its 
  1254.           exception handler will call DosExit().  The handler is registered by 
  1255.           the library function itself; your exception handlers will not receive 
  1256.           the original exception.  They will only receive the termination 
  1257.           exception. 
  1258.  
  1259. Math functions: All the functions in the header file math.h are defined as math 
  1260.           functions.  These functions are completely reentrant.  However, 
  1261.           exceptions in them should be handled by the C Set library exception 
  1262.           handler.  Math functions generate floating point exceptions when 
  1263.           domain and range errors are encountered. The C Set library exception 
  1264.           handler can recognize these specific exceptions, and fix them up. 
  1265.  
  1266. Other functions: The remainder of the library has no special exception handling 
  1267.           considerations.  You may treat exceptions occurring in it as though 
  1268.           they occurred in your own code. 
  1269.  
  1270.  
  1271. ΓòÉΓòÉΓòÉ 4.9. Coexistence with C Set exception handling ΓòÉΓòÉΓòÉ
  1272.  
  1273. Since the C Set signal handling is built on top of the operating system's 
  1274. exception handling, it's relatively easy to get your signal handlers to coexist 
  1275. with the C Set application libraries.  There are two simple rules: 
  1276.  
  1277.  1. The math functions (defined in math.h) require special exception handling 
  1278.     to completely maintain the semantics of C. Therefore, it is necessary for 
  1279.     the C Set library exception handler to handle any floating point exceptions 
  1280.     originating in these functions.  This is only a problem if you have 
  1281.     registered your own exception handler, and wish to call one of these 
  1282.     functions.  In this case, ensure that one of the following two C Set 
  1283.     library exception handlers is registered when calling math functions: 
  1284.  
  1285.    _Lib_except(): This is a very limited exception handler.  It handles 
  1286.              floating point exceptions which have occurred in math functions, 
  1287.              and nothing else.  It recognizes which math function caused the 
  1288.              exception, performs fixup, and continues your program's execution. 
  1289.  
  1290.    _Exception(): This is the full C Set library exception handler, intercepting 
  1291.              most exceptions, providing signal handling and default fixups.  It 
  1292.              will call _Lib_except() for the math functions, but it will also 
  1293.              handle exceptions occurring outside the math functions. 
  1294.  
  1295.  2. If you have registered your own exception handler, any exception you handle 
  1296.     will not be seen by a signal handler.  The ones you don't handle will fall 
  1297.     through to the next exception handler.  If that one is the C Set exception 
  1298.     handler, it will call a signal handler.  If you want to completely 
  1299.     re-establish signal handling, you must register the C Set library exception 
  1300.     handler _Exception. 
  1301.  
  1302.  
  1303. ΓòÉΓòÉΓòÉ 5. Floating Point Exceptions ΓòÉΓòÉΓòÉ
  1304.  
  1305. Floating point exceptions are a major subject on their own, and require 
  1306. in-depth knowledge of the 80387 to really understand. I intend only to give 
  1307. some simple pointers on the subject, and will have to assume that you are 
  1308. familiar with the terminology of IEEE floating point numbers. 
  1309.  
  1310. In general, floating point exceptions cannot be retried without significant 
  1311. knowledge of the 80387 chip and your application.  The C Set library design 
  1312. team decided that this was beyond the scope of what they were willing to 
  1313. attempt. So, in general, the library treats floating point exceptions as a 
  1314. terminating condition.  You do have some control, however. The C Set library 
  1315. function, _control87(), affects the floating point exceptions.  It allows you 
  1316. to mask the individual exceptions that the 80387 can generate, and let the 
  1317. 80387 take a default fixup action.  You call it as follows: 
  1318.  
  1319.   oldstate = _control87(value,mask)
  1320.  
  1321. You can look up the complete function description in the IBM C Set User's 
  1322. Guide.  The bit masks that affect exception handling are defined in float.h. 
  1323. Unless otherwise noted, exceptions are masked on (enabled). The fixups 
  1324. described are the ones performed by the 80387 if the exception is masked off. 
  1325. The bit masks have the following meanings: 
  1326.  
  1327. EM_INVALID:       Mask off exceptions resulting from invalid operations to the 
  1328.                   80387.  It's not usually a good idea to mask this exception, 
  1329.                   since it indicates that something is seriously wrong.  It can 
  1330.                   be caused by attempting to operate on certain invalid 
  1331.                   floating point numbers, such as NaNS (a signalling NaN - not 
  1332.                   a number).  It may also be caused by problems with the 
  1333.                   80387's stack, which you can cause that by omitting a 
  1334.                   prototype for a function that returns a floating point 
  1335.                   number.  The 80387 fixup is a result of NaNQ (a 
  1336.                   non-signalling NaN). 
  1337.  
  1338. EM_DENORMAL:      Mask off exceptions relating to the use of denormalized 
  1339.                   floating point numbers.  The 80387 fixup is to use them 
  1340.                   "as-is", and allow gradual underflow. This exception is not 
  1341.                   meaningful when using C Set, and is masked off by default..* 
  1342.  
  1343. EM_ZERODIVIDE:    Mask off the divide by zero exception.  The 80387 fixup is a 
  1344.                   result of infinity. 
  1345.  
  1346. EM_OVERFLOW:      Mask off the overflow exception.  The 80387 fixup is a result 
  1347.                   of infinity. 
  1348.  
  1349. EM_UNDERFLOW:     Masks off the underflow exception.  The 80387 fixup is either 
  1350.                   a denormal number or zero. 
  1351.  
  1352. EM_INEXACT:       Mask off the exception which indicates that precision has 
  1353.                   been lost.  This exception will occur on most 80387 floating 
  1354.                   point operations, and is really only useful when doing 
  1355.                   integer arithmetic on the 80387.  The C Set compiler uses the 
  1356.                   80387 for floating point arithmetic only, so this exception 
  1357.                   is not meaningful, and should be left masked off (its default 
  1358.                   state).  The 80387 fixup is to ignore it. 
  1359.  
  1360. Each of these flags corresponds to a unique floating point exception.  You can 
  1361. mask them off individually.  To prevent the floating point underflow exception, 
  1362. you would code: 
  1363.  
  1364.   oldstate = _control87(EM_UNDERFLOW,EM_UNDERFLOW)
  1365.  
  1366. To turn it back on, you code: 
  1367.  
  1368.   oldstate = _control87(0,EM_UNDERFLOW)
  1369.  
  1370. Note:  Since the functions in math.h use the 80387, you should ensure that the 
  1371. 80387 control word is in its default state when they are called. Otherwise, the 
  1372. C Set library may not get the exceptions it needs to completely fulfill the 
  1373. requirements of the standards.  You can reset the 80387 control word to its 
  1374. default state with the _ fpreset() library function.  Note that the 80387 state 
  1375. is unique for each thread.  Changing the 80387 controls in one thread does not 
  1376. affect any other thread. 
  1377.  
  1378.  
  1379. ΓòÉΓòÉΓòÉ 6. Restricted OS/2 APIs ΓòÉΓòÉΓòÉ
  1380.  
  1381. The OS/2 APIs described in this section affect one of the following aspects of 
  1382. signal and exception handling: 
  1383.  
  1384.  1. thread switching 
  1385.  
  1386.  2. OS/2 exception processing 
  1387.  
  1388.  3. C Set library behaviour 
  1389.  
  1390. You should be aware of how these APIs can affect your program. 
  1391.  
  1392.  
  1393. ΓòÉΓòÉΓòÉ 6.1. DosKillThread() ΓòÉΓòÉΓòÉ
  1394.  
  1395. This API is not recommended for use with the C set compiler. It causes an 
  1396. immediate XCPT_PROCESS_TERMINATE exception on the thread being terminated (the 
  1397. equivalent of a call to DosExit() on that thread). Under most circumstances, 
  1398. this causes the thread being killed to terminate immediately. This may result 
  1399. in the C Set library hanging because a semaphore was not properly released. 
  1400.  
  1401. Handling an XCPT_PROCESS_TERMINATE exception is generally very difficult, as it 
  1402. cannot be continued.  You should therefore assume, unless specifically told 
  1403. otherwise, that any subsystem that you are using (including the operating 
  1404. system itself) cannot survive having a thread killed via this API. 
  1405.  
  1406. Alternatives to this API all involve having the thread check, at appropriate 
  1407. points in its execution, whether or not it should kill itself.  This is more 
  1408. work, but will result in a more reliable application. 
  1409.  
  1410.  
  1411. ΓòÉΓòÉΓòÉ 6.2. DosExit() ΓòÉΓòÉΓòÉ
  1412.  
  1413. This API comes in two flavours: 
  1414.  
  1415.  1. DosExit(0,x). 
  1416.  
  1417.     This exits a thread.  The library does not get a chance to clean up its 
  1418.     thread specific storage, which may result in a subsequently started thread 
  1419.     inheriting the (no longer initialized) storage.  Thread specific storage 
  1420.     contains the following items 
  1421.  
  1422.    o The signal handler functions. 
  1423.  
  1424.    o The random number seed. 
  1425.  
  1426.    o The static pointer used by the strtok() function. 
  1427.  
  1428.    o The errno value. 
  1429.  
  1430.    o The value pointed to by the "hp2._threadstore() function (C Set++ only). 
  1431.  
  1432.     You should use _endthread(), or fall out the bottom of the origninal thread 
  1433.     function. 
  1434.  
  1435.  2. DosExit(1,x). 
  1436.  
  1437.     This terminates the process.  The library does not get the chance to 
  1438.     properly clean up its environment, and flush file buffers.  Functions 
  1439.     registered with atexit() and onexit() will not be run. 
  1440.  
  1441.     Yo should use exit() or abort(), or fall out the bottom of main().  These 
  1442.     do much the same thing, and they're portable too! 
  1443.  
  1444.  
  1445. ΓòÉΓòÉΓòÉ 6.3. DosUnwindException() ΓòÉΓòÉΓòÉ
  1446.  
  1447. This API will unwind exception handlers from the stack and set the machine 
  1448. state.  In C, you usually do not have the information required to use the API 
  1449. properly.  Its use is not recommended under C Set.  The longjmp() library 
  1450. function does essentially the same thing in a portable way. 
  1451.  
  1452.  
  1453. ΓòÉΓòÉΓòÉ 6.4. DosSetSignalExceptionFocus() ΓòÉΓòÉΓòÉ
  1454.  
  1455. C Set applications assume that they have the focus for signal exceptions. 
  1456. Removing the focus may prevent you from getting SIGINT and SIGBREAK exceptions 
  1457. from the keyboard. 
  1458.  
  1459.  
  1460. ΓòÉΓòÉΓòÉ 6.5. DosAcknowledgeSignalException() ΓòÉΓòÉΓòÉ
  1461.  
  1462. The signal functions of C Set assume that they are the ones responsible 
  1463. acknowledging signal exceptions.  You should only use this function in an OS/2 
  1464. exception handler that you write yourself. 
  1465.  
  1466.  
  1467. ΓòÉΓòÉΓòÉ 6.6. DosCreateThread() ΓòÉΓòÉΓòÉ
  1468.  
  1469. This API doesn't register an C Set library exception handler for the new 
  1470. thread, or handle the thread setup properly.  Use _beginthread() if possible. 
  1471. If not, the function to be run in the new thread should look like the 
  1472. following: 
  1473.  
  1474.      /* register the C/C++ exception handler */
  1475.      #pragma handler(threadfn)
  1476.      void threadfn(ULONG arg) {
  1477.         _fpreset();   /* reset the 80386 exception handler */
  1478.         .
  1479.         .
  1480.         .
  1481.         _endthread();  /* exit with _endthread() so the library */
  1482.                        /* data areas are cleaned up */
  1483.      }
  1484.  
  1485. Threads started this way must always be terminated with _endthread(). 
  1486.  
  1487.  
  1488. ΓòÉΓòÉΓòÉ 6.7. DosEnterMustComplete() ΓòÉΓòÉΓòÉ
  1489.  
  1490. This API is used to hold off asynchronous exceptions, including 
  1491. XCPT_ASYNC_PROCESS_TERMINATE.  You must call DosExitMustComplete() to reenable 
  1492. asynchronous exceptions.  This API also blocks the XCPT_ASYNC_PROCESS_TERMINATE 
  1493. exception, which occurs on process termination.  i.e.  If a thread in your 
  1494. process is in a complete section, the other threads cannot terminate the 
  1495. process until that thread exits its must complete section. 
  1496.  
  1497.  
  1498. ΓòÉΓòÉΓòÉ 6.8. DosEnterCritSec() ΓòÉΓòÉΓòÉ
  1499.  
  1500. This API prevents thread switching within your process.  For each call to 
  1501. DosEnterCritSec(), you must call DosExitCritSec(). Keep critical sections 
  1502. short, and avoid putting calls that may block inside them.  Use these APIs only 
  1503. when a mutex semaphore cannot be used. 
  1504.  
  1505. It is not advisable to call any operating system API or any C/C++ library 
  1506. function between the calls to DosEnterCritSec() and DosExitCritSec(), as your 
  1507. process may hang. 
  1508.  
  1509.  
  1510. ΓòÉΓòÉΓòÉ 6.9. DosSuspendThread() ΓòÉΓòÉΓòÉ
  1511.  
  1512. This API causes the thread to stop at an arbitrary point.  If the thread owns a 
  1513. semaphore, you may be exposed to a deadlock.  Use DosResumeThread() to restart 
  1514. the thread. 
  1515.  
  1516. It is not advisable to call any operating system API or any C/C++ library 
  1517. function between the calls to DosSuspendThread() and DosResumeThread(), as your 
  1518. process may hang. 
  1519.  
  1520.  
  1521. ΓòÉΓòÉΓòÉ 7. Conclusion ΓòÉΓòÉΓòÉ
  1522.  
  1523. Signal and exception handling under C Set and OS/2 is powerful.  Of the two, 
  1524. signal handling is easier to use, and easier to port.  Exception handling is 
  1525. more difficult to use, and more powerful, but is non-portable. 
  1526.  
  1527.  
  1528. ΓòÉΓòÉΓòÉ 8. Bibliography ΓòÉΓòÉΓòÉ
  1529.  
  1530. American National Standard for Information Systems - Programming Language C, X3 
  1531. Secretariat: Computer and Business Equipment Manufacturers Association, 
  1532. Washington, D.C.  Document Number: X3J11/90-13, dated Feb. 14, 1990. 
  1533.  
  1534. IBM C Set User's Guide, Armonk, N.Y.  Document Number S10G-4444, dated April 
  1535. 1992. 
  1536.  
  1537. Systems Application Architecture, Common Programming Interface, C Reference - 
  1538. Level 2, International Business Machines Corp., Armonk, N.Y. Document Number 
  1539. SC09-1308-01, dated Dec. 1989. 
  1540.  
  1541. OS/2 Programmer's Reference, International Business Machines Corp., Armonk, 
  1542. N.Y. 
  1543.  
  1544. 80386 Programmer's Reference Manual, Intel Corporation, Santa Clara, Calif., 
  1545. dated 1986. 
  1546.  
  1547. 80387 Programmer's Reference Manual, Intel Corporation, Santa Clara, Calif., 
  1548. dated 1987. 
  1549.  
  1550. i486 Microprocessor Programmer's Reference Manual, Intel Corporation, Santa 
  1551. Clara, Calif., dated 1990. 
  1552.