Changeset 2878


Ignore:
Timestamp:
Aug 14, 2009 10:41:00 AM (10 years ago)
Author:
bennylp
Message:

Fixed ticket #939: Throwing exception inside exception handler will cause infinite loop (thanks Roman Puls for the report)

  • exception handler is now popped from the stack immediately in PJ_THROW
Location:
pjproject/trunk/pjlib
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/include/pj/except.h

    r2394 r2878  
    5757 * The exception handling mechanism is completely thread safe, so the exception 
    5858 * thrown by one thread will not interfere with other thread. 
    59  * 
    60  * CAVEATS: 
    61  *  - unlike C++ exception, the scheme here won't call destructors of local 
    62  *    objects if exception is thrown. Care must be taken when a function 
    63  *    hold some resorce such as pool or mutex etc. 
    64  *  - You CAN NOT make nested exception in one single function without using 
    65  *    a nested PJ_USE_EXCEPTION. 
    66  *  - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH 
    67  *    and PJ_CATCH_ANY for a single PJ_TRY. 
    68  *  - Exceptions will always be caught by the first handler (unlike C++ where 
    69  *    exception is only caught if the type matches. 
    7059 * 
    7160 * The exception handling constructs are similar to C++. The blocks will be 
     
    128117 * ID is raised by default pool policy when it fails to allocate memory. 
    129118 * 
     119 * CAVEATS: 
     120 *  - unlike C++ exception, the scheme here won't call destructors of local 
     121 *    objects if exception is thrown. Care must be taken when a function 
     122 *    hold some resorce such as pool or mutex etc. 
     123 *  - You CAN NOT make nested exception in one single function without using 
     124 *    a nested PJ_USE_EXCEPTION. Samples: 
     125  \verbatim 
     126        void wrong_sample() 
     127        { 
     128            PJ_USE_EXCEPTION; 
     129 
     130            PJ_TRY { 
     131                // Do stuffs 
     132                ... 
     133            } 
     134            PJ_CATCH_ANY { 
     135                // Do other stuffs 
     136                .... 
     137                .. 
     138 
     139                // The following block is WRONG! You MUST declare  
     140                // PJ_USE_EXCEPTION once again in this block. 
     141                PJ_TRY { 
     142                    .. 
     143                } 
     144                PJ_CATCH_ANY { 
     145                    .. 
     146                } 
     147                PJ_END; 
     148            } 
     149            PJ_END; 
     150        } 
     151 
     152  \endverbatim 
     153 
     154 *  - You MUST NOT exit the function inside the PJ_TRY block. The correct way 
     155 *    is to return from the function after PJ_END block is executed.  
     156 *    For example, the following code will yield crash not in this code, 
     157 *    but rather in the subsequent execution of PJ_TRY block: 
     158  \verbatim 
     159        void wrong_sample() 
     160        { 
     161            PJ_USE_EXCEPTION; 
     162 
     163            PJ_TRY { 
     164                // do some stuffs 
     165                ... 
     166                return;         <======= DO NOT DO THIS! 
     167            } 
     168            PJ_CATCH_ANY { 
     169            } 
     170            PJ_END; 
     171        } 
     172  \endverbatim 
     173   
     174 *  - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH 
     175 *    and PJ_CATCH_ANY for a single PJ_TRY. 
     176 *  - Exceptions will always be caught by the first handler (unlike C++ where 
     177 *    exception is only caught if the type matches. 
     178 
    130179 * \section PJ_EX_KEYWORDS Keywords 
    131180 * 
     
    311360 * Pop exception handler. 
    312361 */ 
    313 PJ_DECL(void) pj_pop_exception_handler_(void); 
     362PJ_DECL(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec); 
    314363 
    315364/** 
     
    344393 * @hideinitializer 
    345394 */ 
    346 #define PJ_END                  pj_pop_exception_handler_(); \ 
     395#define PJ_END                  pj_pop_exception_handler_(&pj_x_except__); \ 
    347396                            } else {} 
    348397 
  • pjproject/trunk/pjlib/src/pj/except.c

    r2394 r2878  
    5151        /* This will crash the system! */ 
    5252    } 
     53    pj_pop_exception_handler_(handler); 
    5354    pj_longjmp(handler->state, exception_id); 
    5455} 
     
    8788} 
    8889 
    89 PJ_DEF(void) pj_pop_exception_handler_(void) 
     90PJ_DEF(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec) 
    9091{ 
    9192    struct pj_exception_state_t *handler; 
     
    9394    handler = (struct pj_exception_state_t *) 
    9495              pj_thread_local_get(thread_local_id); 
    95     pj_assert(handler != NULL); 
    96     pj_thread_local_set(thread_local_id, handler->prev); 
     96    if (handler && handler==rec) { 
     97        pj_thread_local_set(thread_local_id, handler->prev); 
     98    } 
    9799} 
    98100#endif 
  • pjproject/trunk/pjlib/src/pjlib-test/exception.c

    r2407 r2878  
    6262    PJ_THROW( ID_2 ); 
    6363    PJ_UNREACHED(return -1;) 
     64} 
     65 
     66static int try_catch_test(void) 
     67{ 
     68    PJ_USE_EXCEPTION; 
     69    int rc = -200; 
     70 
     71    PJ_TRY { 
     72        PJ_THROW(ID_1); 
     73    } 
     74    PJ_CATCH_ANY { 
     75        rc = 0; 
     76    } 
     77    PJ_END; 
     78    return rc; 
     79} 
     80 
     81static int throw_in_handler(void) 
     82{ 
     83    PJ_USE_EXCEPTION; 
     84    int rc = 0; 
     85 
     86    PJ_TRY { 
     87        PJ_THROW(ID_1); 
     88    } 
     89    PJ_CATCH_ANY { 
     90        if (PJ_GET_EXCEPTION() != ID_1) 
     91            rc = -300; 
     92        else 
     93            PJ_THROW(ID_2); 
     94    } 
     95    PJ_END; 
     96    return rc; 
     97} 
     98 
     99static int return_in_handler(void) 
     100{ 
     101    PJ_USE_EXCEPTION; 
     102 
     103    PJ_TRY { 
     104        PJ_THROW(ID_1); 
     105    } 
     106    PJ_CATCH_ANY { 
     107        return 0; 
     108    } 
     109    PJ_END; 
     110    return -400; 
    64111} 
    65112 
     
    154201        return rc; 
    155202 
     203    /* 
     204     * Nested handlers 
     205     */ 
     206    PJ_TRY { 
     207        rc = try_catch_test(); 
     208    } 
     209    PJ_CATCH_ANY { 
     210        rc = -70; 
     211    } 
     212    PJ_END; 
     213 
     214    if (rc != 0) 
     215        return rc; 
     216 
     217    /* 
     218     * Throwing exception inside handler 
     219     */ 
     220    rc = -80; 
     221    PJ_TRY { 
     222        int rc2; 
     223        rc2 = throw_in_handler(); 
     224        if (rc2) 
     225            rc = rc2; 
     226    } 
     227    PJ_CATCH_ANY { 
     228        if (PJ_GET_EXCEPTION() == ID_2) { 
     229            rc = 0; 
     230        } else { 
     231            rc = -90; 
     232        } 
     233    } 
     234    PJ_END; 
     235 
     236    if (rc != 0) 
     237        return rc; 
     238 
     239 
     240    /* 
     241     * Return from handler. Returning from the function inside a handler 
     242     * should be okay (though returning from the function inside the 
     243     * PJ_TRY block IS NOT OKAY!!). We want to test to see if handler 
     244     * is cleaned up properly, but not sure how to do this. 
     245     */ 
     246    PJ_TRY { 
     247        int rc2; 
     248        rc2 = return_in_handler(); 
     249        if (rc2) 
     250            rc = rc2; 
     251    } 
     252    PJ_CATCH_ANY { 
     253        rc = -100; 
     254    } 
     255    PJ_END; 
     256 
     257 
    156258    return 0; 
    157259} 
Note: See TracChangeset for help on using the changeset viewer.