Practical usage of setjmp and longjmp in C

前端 未结 8 1739
忘了有多久
忘了有多久 2020-11-28 01:48

Can anyone explain me where exactly setjmp() and longjmp() functions can be used practically in embedded programming? I know that these are for err

相关标签:
8条回答
  • 2020-11-28 02:23

    The theory is that you can use them for error handling so that you can jump out of deeply nested call chain without needing to deal with handling errors in every function in the chain.

    Like every clever theory this falls apart when meeting reality. Your intermediate functions will allocate memory, grab locks, open files and do all kinds of different things that require cleanup. So in practice setjmp/longjmp are usually a bad idea except in very limited circumstances where you have total control over your environment (some embedded platforms).

    In my experience in most cases whenever you think that using setjmp/longjmp would work, your program is clear and simple enough that every intermediate function call in the call chain can do error handling, or it's so messy and impossible to fix that you should do exit when you encounter the error.

    0 讨论(0)
  • 2020-11-28 02:28

    I've written a Java-like exception handling mechanism in C using setjmp(), longjmp() and system functions. It catches custom exceptions but also signals like SIGSEGV. It features infinite nesting of exception handling blocks, which works accross function calls, and supports the two most common threading implementations. It allows you to define a tree hierarchy of exception classes that feature link-time inheritance, and the catch statement walks this tree to see if it needs to catch or pass on.

    Here's a sample of how code looks using this:

    try
    {
        *((int *)0) = 0;    /* may not be portable */
    }
    catch (SegmentationFault, e)
    {
        long f[] = { 'i', 'l', 'l', 'e', 'g', 'a', 'l' };
        ((void(*)())f)();   /* may not be portable */
    }
    finally
    {
        return(1 / strcmp("", ""));
    }
    

    And here's part of the include file that contains a lot of logic:

    #ifndef _EXCEPT_H
    #define _EXCEPT_H
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <signal.h>
    #include <setjmp.h>
    #include "Lifo.h"
    #include "List.h"
    
    #define SETJMP(env)             sigsetjmp(env, 1)
    #define LONGJMP(env, val)       siglongjmp(env, val)
    #define JMP_BUF                 sigjmp_buf
    
    typedef void (* Handler)(int);
    
    typedef struct _Class *ClassRef;        /* exception class reference */
    struct _Class
    {
        int         notRethrown;            /* always 1 (used by throw()) */
        ClassRef    parent;                 /* parent class */
        char *      name;                   /* this class name string */
        int         signalNumber;           /* optional signal number */
    };
    
    typedef struct _Class Class[1];         /* exception class */
    
    typedef enum _Scope                     /* exception handling scope */
    {
        OUTSIDE = -1,                       /* outside any 'try' */
        INTERNAL,                           /* exception handling internal */
        TRY,                                /* in 'try' (across routine calls) */
        CATCH,                              /* in 'catch' (idem.) */
        FINALLY                             /* in 'finally' (idem.) */
    } Scope;
    
    typedef enum _State                     /* exception handling state */
    {
        EMPTY,                              /* no exception occurred */
        PENDING,                            /* exception occurred but not caught */
        CAUGHT                              /* occurred exception caught */
    } State;
    
    typedef struct _Except                  /* exception handle */
    {
        int         notRethrown;            /* always 0 (used by throw()) */
        State       state;                  /* current state of this handle */
        JMP_BUF     throwBuf;               /* start-'catching' destination */
        JMP_BUF     finalBuf;               /* perform-'finally' destination */
        ClassRef    class;                  /* occurred exception class */
        void *      pData;                  /* exception associated (user) data */
        char *      file;                   /* exception file name */
        int         line;                   /* exception line number */
        int         ready;                  /* macro code control flow flag */
        Scope       scope;                  /* exception handling scope */
        int         first;                  /* flag if first try in function */
        List *      checkList;              /* list used by 'catch' checking */
        char*       tryFile;                /* source file name of 'try' */
        int         tryLine;                /* source line number of 'try' */
    
        ClassRef    (*getClass)(void);      /* method returning class reference */
        char *      (*getMessage)(void);    /* method getting description */
        void *      (*getData)(void);       /* method getting application data */
        void        (*printTryTrace)(FILE*);/* method printing nested trace */
    } Except;
    
    typedef struct _Context                 /* exception context per thread */
    {
        Except *    pEx;                    /* current exception handle */
        Lifo *      exStack;                /* exception handle stack */
        char        message[1024];          /* used by ExceptGetMessage() */
        Handler     sigAbrtHandler;         /* default SIGABRT handler */
        Handler     sigFpeHandler;          /* default SIGFPE handler */
        Handler     sigIllHandler;          /* default SIGILL handler */
        Handler     sigSegvHandler;         /* default SIGSEGV handler */
        Handler     sigBusHandler;          /* default SIGBUS handler */
    } Context;
    
    extern Context *        pC;
    extern Class            Throwable;
    
    #define except_class_declare(child, parent) extern Class child
    #define except_class_define(child, parent)  Class child = { 1, parent, #child }
    
    except_class_declare(Exception,           Throwable);
    except_class_declare(OutOfMemoryError,    Exception);
    except_class_declare(FailedAssertion,     Exception);
    except_class_declare(RuntimeException,    Exception);
    except_class_declare(AbnormalTermination, RuntimeException);  /* SIGABRT */
    except_class_declare(ArithmeticException, RuntimeException);  /* SIGFPE */
    except_class_declare(IllegalInstruction,  RuntimeException);  /* SIGILL */
    except_class_declare(SegmentationFault,   RuntimeException);  /* SIGSEGV */
    except_class_declare(BusError,            RuntimeException);  /* SIGBUS */
    
    
    #ifdef  DEBUG
    
    #define CHECKED                                                         \
            static int checked
    
    #define CHECK_BEGIN(pC, pChecked, file, line)                           \
                ExceptCheckBegin(pC, pChecked, file, line)
    
    #define CHECK(pC, pChecked, class, file, line)                          \
                     ExceptCheck(pC, pChecked, class, file, line)
    
    #define CHECK_END                                                       \
                !checked
    
    #else   /* DEBUG */
    
    #define CHECKED
    #define CHECK_BEGIN(pC, pChecked, file, line)           1
    #define CHECK(pC, pChecked, class, file, line)          1
    #define CHECK_END                                       0
    
    #endif  /* DEBUG */
    
    
    #define except_thread_cleanup(id)       ExceptThreadCleanup(id)
    
    #define try                                                             \
        ExceptTry(pC, __FILE__, __LINE__);                                  \
        while (1)                                                           \
        {                                                                   \
            Context *       pTmpC = ExceptGetContext(pC);                   \
            Context *       pC = pTmpC;                                     \
            CHECKED;                                                        \
                                                                            \
            if (CHECK_BEGIN(pC, &checked, __FILE__, __LINE__) &&            \
                pC->pEx->ready && SETJMP(pC->pEx->throwBuf) == 0)           \
            {                                                               \
                pC->pEx->scope = TRY;                                       \
                do                                                          \
                {
    
    #define catch(class, e)                                                 \
                }                                                           \
                while (0);                                                  \
            }                                                               \
            else if (CHECK(pC, &checked, class, __FILE__, __LINE__) &&      \
                     pC->pEx->ready && ExceptCatch(pC, class))              \
            {                                                               \
                Except *e = LifoPeek(pC->exStack, 1);                       \
                pC->pEx->scope = CATCH;                                     \
                do                                                          \
                {
    
    #define finally                                                         \
                }                                                           \
                while (0);                                                  \
            }                                                               \
            if (CHECK_END)                                                  \
                continue;                                                   \
            if (!pC->pEx->ready && SETJMP(pC->pEx->finalBuf) == 0)          \
                pC->pEx->ready = 1;                                         \
            else                                                            \
                break;                                                      \
        }                                                                   \
        ExceptGetContext(pC)->pEx->scope = FINALLY;                         \
        while (ExceptGetContext(pC)->pEx->ready > 0 || ExceptFinally(pC))   \
            while (ExceptGetContext(pC)->pEx->ready-- > 0)
    
    #define throw(pExceptOrClass, pData)                                    \
        ExceptThrow(pC, (ClassRef)pExceptOrClass, pData, __FILE__, __LINE__)
    
    #define return(x)                                                       \
        {                                                                   \
            if (ExceptGetScope(pC) != OUTSIDE)                              \
            {                                                               \
                void *      pData = malloc(sizeof(JMP_BUF));                \
                ExceptGetContext(pC)->pEx->pData = pData;                   \
                if (SETJMP(*(JMP_BUF *)pData) == 0)                         \
                    ExceptReturn(pC);                                       \
                else                                                        \
                    free(pData);                                            \
            }                                                               \
            return x;                                                       \
        }
    
    #define pending                                                         \
        (ExceptGetContext(pC)->pEx->state == PENDING)
    
    extern Scope    ExceptGetScope(Context *pC);
    extern Context *ExceptGetContext(Context *pC);
    extern void     ExceptThreadCleanup(int threadId);
    extern void     ExceptTry(Context *pC, char *file, int line);
    extern void     ExceptThrow(Context *pC, void * pExceptOrClass,
                                void *pData, char *file, int line);
    extern int      ExceptCatch(Context *pC, ClassRef class);
    extern int      ExceptFinally(Context *pC);
    extern void     ExceptReturn(Context *pC);
    extern int      ExceptCheckBegin(Context *pC, int *pChecked,
                                     char *file, int line);
    extern int      ExceptCheck(Context *pC, int *pChecked, ClassRef class,
                                char *file, int line);
    
    
    #endif  /* _EXCEPT_H */
    

    There's also a C module that contains the logic for signal handling and some bookkeeping.

    It was extremely tricky to implement I can tell you and I almost quit. I really pushed to make it as close to Java as possible; I found it surprising how far I got with just C.

    Give me a shout if you're interested.

    0 讨论(0)
提交回复
热议问题