Changeset 14 for trunk/src/helpers/except.c
- Timestamp:
- Dec 9, 2000, 8:19:42 PM (25 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/helpers/except.c
r13 r14 3 3 *@@sourcefile except.c: 4 4 * this file contains powerful exception handlers. 5 * except.h also defines easy-to-use macros for them. 5 6 * 6 7 * Usage: All OS/2 programs, PM or text mode. 7 8 * 8 * <B>except.h macros</B> 9 * 10 * except.h declares a few handy macros to easily install 11 * exception handling for a section of code. 12 * 13 * These macros automatically insert code for properly 14 * registering and deregistering the handlers. You should 15 * ALWAYS use these macros instead of directly registering 9 * <B>Introduction</B> 10 * 11 * OS/2 exception handlers are a mess to program and, 12 * if installed wrongly, almost impossible to debug. 13 * The problem is that for any program that does a bit 14 * more than showing a message box, using exception 15 * handlers is a must to avoid system hangs. This 16 * especially applies to multi-thread programs using 17 * mutex semaphores (more on that below). The functions 18 * and macros in here are designed to make that more simple. 19 * 20 * The macros in except.h automatically insert code for properly 21 * registering and deregistering the handlers in except.c. You 22 * should ALWAYS use these macros instead of directly registering 16 23 * the handlers to avoid accidentally forgetting to deregister 17 24 * them. If you forget to deregister an exception handler, this … … 20 27 * might get completely messed up. 21 28 * 22 * The general idea of these macros is to define 23 * TRY / CATCH blocks similar to C++. If an exception 24 * occurs in the TRY block, execution is transferred 25 * to the CATCH block. (This works in both C and C++, 26 * by the way.) 27 * 28 * In addition, with the TRY statement, you may specify 29 * an optional "OnKill" function which gets called if 30 * the thread gets killed while code in the TRY block is 31 * executed (new with V0.9.0). If you need no such 32 * function, specify NULL. Details follow. 29 * The general idea of these macros is to define TRY / CATCH 30 * blocks similar to C++. If an exception occurs in the TRY block, 31 * execution is transferred to the CATCH block. (This works in both 32 * C and C++, by the way.) 33 * 34 * The "OnKill" function that was added with V0.9.0 has been 35 * removed again with V0.9.7. Use DosEnterMustComplete instead. 36 * Details follow. 33 37 * 34 38 * The general usage is like this: … … 36 40 + int your_protected_func(int ...) 37 41 + { 38 + TRY_LOUD(excptid, OnKillFunc) 39 + // or: TRY_QUIET(excptid, OnKillFunc) 40 + // OnKillFunc can be NULL 42 + TRY_LOUD(excptid) // or: TRY_QUIET(excptid) 41 43 + { 42 44 + .... // the stuff in here is protected by … … 50 52 + } // end of your_func 51 53 * 52 * TRY_LOUD is for excHandlerLoud (see below).53 * TRY_QUIET is for excHandlerQuiet (see below).54 * TRY_LOUD is for installing excHandlerLoud. 55 * TRY_QUIET is for installing excHandlerQuiet. 54 56 * CATCH / END_CATCH are the same for the two. This 55 57 * is where the exception handler jumps to if an … … 58 60 * in there. 59 61 * 60 * "excptid" can be any identifier which is not used in62 * "excptid" can be any C identifier which is not used in 61 63 * your current variable scope, e.g. "excpt1". This 62 64 * is used for creating an EXCEPTSTRUCT variable of … … 65 67 * store the exception handler data. 66 68 * 67 * It is possible to nest these handlers though. You 68 * must use different "excptid"'s only if more than one 69 * TRY_* block exists in the same variable scope. Avoid 70 * using gotos to jump between the different CATCH blocks, 71 * because this might not properly deregister the handlers. 69 * These macros may be nested if you use different 70 * "excptid"'s for sub-macros. 71 * 72 * Inside the TRY and CATCH blocks, you must not use 73 * "goto" (to a location outside the block) or "return", 74 * because this will not deregister the handler. 72 75 * 73 76 * Keep in mind that all the code in the TRY_* block is … … 76 79 * TRY_* block, your entire application is protected. 77 80 * 78 * Your "OnKillFunc" must be declared as 79 + VOID APIENTRY YourOnKillFunc(VOID) {...}. 80 * This gets called when the thread gets terminated 81 * while being executed in the TRY block (that is, 82 * if either the thread gets killed explicitly or 83 * the entire process ends for some reason). After 84 * that function got executed, the thread will _always_ 85 * be terminated; the CATCH block will not be executed 86 * then. You can specify NULL for the "OnKillFunc", if 87 * you don't need one. 88 * 89 * To summarize: If an exception occurs in the TRY 90 * block, 91 * 92 * a) "OnKillFunc" gets called if the thread got 93 * killed, and the thread then terminates; 94 * 95 * b) the CATCH block is executed for other exceptions 96 * and execution continues. 81 * <B>Asynchronous exceptions</B> 82 * 83 * The exception handlers in this file (which are installed 84 * with the TRY/CATCH mechanism) only intercept synchronous 85 * exceptions (see excHandlerLoud for a list). They do not 86 * protect your code against asynchronous exceptions. 87 * 88 * OS/2 defines asynchronous exceptions to be those that 89 * can be delayed. With OS/2, there are only three of these: 90 * 91 * -- XCPT_PROCESS_TERMINATE 92 * -- XCPT_ASYNC_PROCESS_TERMINATE 93 * -- XCPT_SIGNAL (thread 1 only) 94 * 95 * To protect yourself against these also, put the section 96 * in question in a DosEnterMustComplete/DosExitMustComplete 97 * block as well. 97 98 * 98 99 * <B>Mutex semaphores</B> 99 100 * 100 * Here's how to deal with mutex semaphores: WHENEVER 101 * you request a mutex semaphore, enclose the block 102 * with TRY/CATCH in case an error occurs, like this: 101 * The problem with OS/2 mutex semaphores is that they are 102 * not automatically released when a thread terminates. 103 * If the thread owning the mutex died without releasing 104 * the mutex, other threads which are blocked on that mutex 105 * will wait forever and become zombie threads. Even worse, 106 * if this happens to a PM thread, this will hang the system. 107 * 108 * Here's the typical scenario with two threads: 109 * 110 * 1) Thread 2 requests a mutex and does lots of processing. 111 * 112 * 2) Thread 1 requests the mutex. Since it's still owned 113 * by thread 2, thread 1 blocks. 114 * 115 * 3) Thread 2 crashes in its processing. Without an 116 * exception handler, OS/2 will terminate the process. 117 * It will first kill thread 2 and then attempt to 118 * kill thread 1. This fails because it is still 119 * blocking on the semaphore that thread 2 never 120 * released. Boom. 121 * 122 * The same scenario happens when a process gets killed. 123 * Since OS/2 will kill secondary threads before thread 1, 124 * the same situation can arise. 125 * 126 * As a result, you must protect any section of code which 127 * requests a semaphore _both_ against crashes _and_ 128 * termination. 129 * 130 * So _whenever_ you request a mutex semaphore, enclose 131 * the block with TRY/CATCH in case the code crashes. 132 * Besides, enclose the TRY/CATCH block in a must-complete 133 * section, like this: 103 134 * 104 135 + HMTX hmtx = ... 105 +106 + VOID APIENTRY OnKillYourFunc(VOID)107 + {108 + DosReleaseMutexSem(hmtx);109 + }110 136 + 111 137 + int your_func(int) 112 138 + { 113 + BOOL fSemOwned = FALSE; 139 + BOOL fSemOwned = FALSE; 140 + ULONG ulNesting = 0; 141 + 142 + DosEnterMustComplete(&ulNesting); 114 143 + TRY_QUIET(excpt1, OnKillYourFunc) // or TRY_LOUD 115 144 + { … … 126 155 + fSemOwned = FALSE; 127 156 + } 157 + DosExitMustComplete(&ulNesting); 128 158 + } // end of your_func 129 159 * 130 * This way your mutex semaphore gets released even if 131 * exceptions occur in your code. If you don't do this, 132 * threads waiting for that semaphore will be blocked 133 * forever when exceptions occur. As a result, depending 134 * on the thread, PM will hang, or the application will 135 * never terminate. 160 * This way your mutex semaphore gets released in every 161 * possible condition. 136 162 * 137 163 * <B>Customizing</B> 138 164 * 139 * Note: As opposed to versions < 0.9.0, thisis now165 * As opposed to versions before 0.9.0, this code is now 140 166 * completely independent of XWorkplace. This file now 141 167 * contains "pure" exception handlers only. … … 146 172 * If excRegisterHooks is not called, the following safe 147 173 * defaults are used: 174 * 148 175 * -- the trap log file is TRAP.LOG in the root 149 176 * directory of your boot drive. … … 182 209 * John Currier, 183 210 * Anthony Cruise. 184 * This file is part of the XWorkplacesource package.185 * XWorkplaceis free software; you can redistribute it and/or modify211 * This file is part of the "XWorkplace helpers" source package. 212 * This is free software; you can redistribute it and/or modify 186 213 * it under the terms of the GNU General Public License as published 187 214 * by the Free Software Foundation, in version 2 as it comes in the … … 223 250 224 251 /* ****************************************************************** 225 * *226 * Global variables *227 * *252 * 253 * Global variables 254 * 228 255 ********************************************************************/ 229 256 … … 240 267 241 268 /* ****************************************************************** 242 * *243 * Exception helper routines *244 * *269 * 270 * Exception helper routines 271 * 245 272 ********************************************************************/ 246 273 … … 490 517 case XCPT_ACCESS_VIOLATION: 491 518 { 492 fprintf(file, "\n Access violation: ");519 fprintf(file, "\nXCPT_ACCESS_VIOLATION: "); 493 520 if (pReportRec->ExceptionInfo[0] & XCPT_READ_ACCESS) 494 521 fprintf(file, "Invalid read access from 0x%04lX:%08lX.\n", … … 516 543 case XCPT_INTEGER_DIVIDE_BY_ZERO: 517 544 { 518 fprintf(file, "\n Integer division by zero.\n");545 fprintf(file, "\nXCPT_INTEGER_DIVIDE_BY_ZERO.\n"); 519 546 fprintf(file, 520 547 "Explanation: An attempt was made to divide an integer value by zero,\n" … … 525 552 case XCPT_ILLEGAL_INSTRUCTION: 526 553 { 527 fprintf(file, "\n Illegal instruction found.\n");554 fprintf(file, "\nXCPT_ILLEGAL_INSTRUCTION.\n"); 528 555 fprintf(file, 529 556 "Explanation: An attempt was made to execute an instruction that\n" … … 534 561 case XCPT_PRIVILEGED_INSTRUCTION: 535 562 { 536 fprintf(file, "\n Privileged instruction found.\n");563 fprintf(file, "\nXCPT_PRIVILEGED_INSTRUCTION.\n"); 537 564 fprintf(file, 538 565 "Explanation: An attempt was made to execute an instruction that\n" … … 543 570 544 571 case XCPT_INTEGER_OVERFLOW: 545 fprintf(file, "\n Integer overflow.\n");572 fprintf(file, "\nXCPT_INTEGER_OVERFLOW.\n"); 546 573 fprintf(file, 547 574 "Explanation: An integer operation generated a carry-out of the most\n" 548 575 " significant bit. This is a sign of an attempt to store\n" 549 576 " a value which does not fit into an integer variable.\n"); 577 578 default: 579 fprintf(file, "\nUnknown OS/2 exception number %d.\n", pReportRec->ExceptionNum); 580 fprintf(file, "Look this up in the OS/2 header files.\n"); 550 581 } 551 582 … … 700 731 701 732 /* ****************************************************************** 702 * *703 * Exported routines *704 * *733 * 734 * Exported routines 735 * 705 736 ********************************************************************/ 706 737 … … 722 753 * The hooks are as follows: 723 754 * 724 * pfnExcOpenFileNew gets called to open755 * -- pfnExcOpenFileNew gets called to open 725 756 * the trap log file. This must return a FILE* 726 757 * pointer from fopen(). If this is not defined, … … 729 760 * into it before the actual exception info. 730 761 * 731 * pfnExcHookNew gets called while the trap log762 * -- pfnExcHookNew gets called while the trap log 732 763 * is being written. At this point, 733 764 * the following info has been written into … … 743 774 * Use this hook to write additional application 744 775 * info into the trap log, such as the state 745 * of your threads.746 * 747 * pfnExcHookError gets called when the TRY_* macros776 * of your own threads and mutexes. 777 * 778 * -- pfnExcHookError gets called when the TRY_* macros 748 779 * fail to install an exception handler (when 749 * DosSetExceptionHandler fails). 780 * DosSetExceptionHandler fails). I've never seen 781 * this happen. 750 782 * 751 783 *@@added V0.9.0 [umoeller] … … 772 804 * to the thread to continue execution, i.e. the 773 805 * default OS/2 exception handler will never get 774 * called. This requires a setjmp() call on 806 * called. 807 * 808 * This requires a setjmp() call on 775 809 * EXCEPTIONREGISTRATIONRECORD2.jmpThread before 776 * being installed. 777 * 778 * This is best registered thru the TRY_LOUD macro 779 * (new with V0.84, described in except.c), which 780 * does the necessary setup. 781 * 782 * Depending on the type of exception, the following 783 * happens: 784 * 785 * <B>a) "real" exceptions</B> 810 * being installed. The TRY_LOUD macro will take 811 * care of this for you (see except.c). 812 * 813 * This intercepts the following exceptions (see 814 * the OS/2 Control Program Reference for details): 786 815 * 787 816 * -- XCPT_ACCESS_VIOLATION (traps 0x0d, 0x0e) … … 795 824 * what source code corresponds to the error. 796 825 * 826 * See excRegisterHooks for the default setup of this. 827 * 797 828 * Note that to get meaningful debugging information 798 829 * in this handler's traplog, you need the following: 830 * 799 831 * a) have a MAP file created at link time (/MAP) 832 * 800 833 * b) convert the MAP to a SYM file using MAPSYM 834 * 801 835 * c) put the SYM file in the same directory of 802 * the executable. This must have the same 803 * filestem as the executable. 804 * 805 * See the "Control Programming Guide and Reference" 806 * for details. 836 * the module (EXE or DLL). This must have the 837 * same filestem as the module. 838 * 807 839 * All other exceptions are passed to the next handler 808 840 * in the exception handler chain. This might be the 809 841 * C/C++ compiler handler or the default OS/2 handler, 810 842 * which will probably terminate the process. 811 *812 * <B>b) thread kills</B>813 *814 * -- XCPT_PROCESS_TERMINATE815 * -- XCPT_ASYNC_PROCESS_TERMINATE:816 *817 * If EXCEPTIONREGISTRATIONRECORD2.pfnOnKill is != NULL,818 * that function gets called to allow for thread cleanup819 * before the thread really terminates. This should be820 * used for releasing mutex semaphores.821 843 * 822 844 *@@changed V0.9.0 [umoeller]: added support for thread termination … … 852 874 switch (pReportRec->ExceptionNum) 853 875 { 854 case XCPT_PROCESS_TERMINATE:876 /* case XCPT_PROCESS_TERMINATE: 855 877 case XCPT_ASYNC_PROCESS_TERMINATE: 856 878 // thread terminated: … … 859 881 if (pRegRec2->pfnOnKill) 860 882 // call the "OnKill" function 861 pRegRec2->pfnOnKill( );883 pRegRec2->pfnOnKill(pRegRec2); 862 884 // get outta here, which will kill the thread 863 break; 885 break; */ 864 886 865 887 case XCPT_ACCESS_VIOLATION: … … 867 889 case XCPT_ILLEGAL_INSTRUCTION: 868 890 case XCPT_PRIVILEGED_INSTRUCTION: 891 case XCPT_INVALID_LOCK_SEQUENCE: 869 892 case XCPT_INTEGER_OVERFLOW: 870 893 { … … 924 947 * this is useful for certain error-prone functions, where 925 948 * exceptions are likely to appear, for example used by 926 * cmnCheckObject to implement a fail-safe SOM object check.949 * wpshCheckObject to implement a fail-safe SOM object check. 927 950 * 928 951 * This does _not_ write an error log and makes _no_ sound. … … 953 976 switch (pReportRec->ExceptionNum) 954 977 { 955 case XCPT_PROCESS_TERMINATE:978 /* case XCPT_PROCESS_TERMINATE: 956 979 case XCPT_ASYNC_PROCESS_TERMINATE: 957 980 // thread terminated: … … 960 983 if (pRegRec2->pfnOnKill) 961 984 // call the "OnKill" function 962 pRegRec2->pfnOnKill( );985 pRegRec2->pfnOnKill(pRegRec2); 963 986 // get outta here, which will kill the thread 964 break; 987 break; */ 965 988 966 989 case XCPT_ACCESS_VIOLATION: … … 968 991 case XCPT_ILLEGAL_INSTRUCTION: 969 992 case XCPT_PRIVILEGED_INSTRUCTION: 993 case XCPT_INVALID_LOCK_SEQUENCE: 970 994 case XCPT_INTEGER_OVERFLOW: 971 995 // write excpt explanation only if the 972 // resp. debugging #define is set ( common.h)996 // resp. debugging #define is set (setup.h) 973 997 #ifdef DEBUG_WRITEQUIETEXCPT 974 998 {
Note:
See TracChangeset
for help on using the changeset viewer.