"Fossies" - the Fresh Open Source Software Archive

Member "pigz-2.6/try.h" (6 Feb 2021, 20563 Bytes) of package /linux/privat/pigz-2.6.tar.gz:

As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "try.h" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.5_vs_2.6.

    1 /* try.h -- try / catch / throw exception handling for C99
    2   Copyright (C) 2013, 2015 Mark Adler
    3   Version 1.2  19 January 2015
    5   This software is provided 'as-is', without any express or implied
    6   warranty.  In no event will the author be held liable for any damages
    7   arising from the use of this software.
    9   Permission is granted to anyone to use this software for any purpose,
   10   including commercial applications, and to alter it and redistribute it
   11   freely, subject to the following restrictions:
   13   1. The origin of this software must not be misrepresented; you must not
   14      claim that you wrote the original software. If you use this software
   15      in a product, an acknowledgment in the product documentation would be
   16      appreciated but is not required.
   17   2. Altered source versions must be plainly marked as such, and must not be
   18      misrepresented as being the original software.
   19   3. This notice may not be removed or altered from any source distribution.
   21   Mark Adler    madler@alumni.caltech.edu
   22  */
   24 /*
   25     Version History
   26     1.0    7 Jan 2013   - First version
   27     1.1    2 Nov 2013   - Use variadic macros and functions instead of partial
   28                           structure assignment, allowing arbitrary arguments
   29                           to printf()
   30     1.2   19 Jan 2015   - Obey setjmp() invocation limits from C standard
   31  */
   33 /* To use, include try.h in all source files that use these operations, and
   34    compile and link try.c.  If pthread threads are used, then there must be an
   35    #include <pthread.h> in try.h to make the exception handling thread-safe.
   36    (Uncomment the include below.)  If threads other than pthread are being
   37    used, then try.h and try.c must be modified to use that environment's
   38    thread-local storage for the try_stack_ pointer.  try.h and try.c assume
   39    that the compiler and library conform to the C99 standard, at least with
   40    respect to the use of variadic macro and function arguments. */
   42 /*
   43    try.h provides a try / catch / throw exception handler, which allows
   44    catching exceptions across any number of levels of function calls.  try
   45    blocks can be nested as desired, with a throw going to the end of the
   46    innermost enclosing try, passing the thrown information to the associated
   47    catch block.  A global try stack is used, to avoid having to pass exception
   48    handler information through all of the functions down to the invocations of
   49    throw.  The try stack is thread-unique if requested by uncommenting the
   50    pthread.h include below.  In addition to the macros try, catch, and throw,
   51    the macros always, retry, punt, and drop, and the type ball_t are created.
   52    All other symbols are of the form try_*_ or TRY_*_, where the final
   53    underscore should avoid conflicts with application symbols. The eight
   54    exposed names can be changed easily in #defines below.
   56    A try block encloses code that may throw an exception with the throw()
   57    macro, either directly in the try block or in any function called directly
   58    or indirectly from the try block.  throw() must have at least one argument,
   59    which is an integer.  The try block is followed by a catch block whose code
   60    will be executed when throw() is called with a non-zero first argument.  If
   61    the first argument of throw() is zero, then execution continues after the
   62    catch block.  If the try block completes normally, with no throw() being
   63    called, then execution continues normally after the catch block.
   65    There can be only one catch block.  catch has one argument which must be a
   66    ball_t type variable declared in the current function or block containing
   67    the try and catch.  That variable is loaded with the information sent by the
   68    throw() for use in the catch block.
   70    throw() may optionally include more information that is passed to the catch
   71    block in the ball_t structure.  throw() can have one or more arguments,
   72    where the first (possibly only) argument is an integer code.  The second
   73    argument can be a pointer, which will be replaced by NULL in the ball_t
   74    structure if not provided.  The implementation of throw() in try.c assumes
   75    that if the second argument is present and is not NULL, that it is a string.
   76    If that string has any percent (%) signs in it, then throw() will run that
   77    string through vsnprintf() with any other arguments provided after the
   78    string in the throw() invocation, and save the resulting formatted string in
   79    the ball_t structure.  Information on whether or not the string was
   80    allocated is also maintained in the ball_t structure.
   82    throw() in try.c can be modified to not assume that the second argument is a
   83    string.  For example, an application may want to assume instead that the
   84    second argument is a pointer to a set of information for use in the catch
   85    block.
   87    The catch block may conditionally do a punt(), where the argument of punt()
   88    is the argument of catch.  This passes the exception on to the next
   89    enclosing try/catch handler.
   91    If a catch block does not always end with a punt(), it should contain a
   92    drop(), where the argument of drop() is the argument of catch.  This frees
   93    the allocated string made if vsnprintf() was used by throw() to generate the
   94    string.  If printf() format strings are never used, then drop() is not
   95    required.
   97    An always block may be placed between the try and catch block.  The
   98    statements in that block will be executed regardless of whether or not the
   99    try block completed normally.  As indicated by the ordering, the always
  100    block will be executed before the catch block.  This block is not named
  101    "finally", since it is different from the finally block in other languages
  102    which is executed after the catch block.
  104    A naked break or continue in a try or always block will go directly to the
  105    end of that block.
  107    A retry from the try block or from any function called from the try block at
  108    any level of nesting will restart the try block from the beginning.
  110    try is thread-safe when compiled with pthread.h.  A throw() in a thread can
  111    only be caught in the same thread.  If a throw() is attempted from a thread
  112    without an enclosing try in that thread, even if in another thread there is
  113    a try around the pthread_create() that spawned this thread, then the throw
  114    will fail on an assert.  Each thread has its own thread-unique try stack,
  115    which starts off empty.
  117    If an intermediate function does not have a need for operations in a catch
  118    block other than punt, and does not need an always block, then that function
  119    does not need a try block.  "try { block } catch (err) { punt(err); }" is
  120    the same as just "block".  More precisely, it's equivalent to "do { block }
  121    while (0);", which replicates the behavior of a naked break or continue in a
  122    block when it follows try.  throw() can be used from a function that has no
  123    try.  All that is necessary is that there is a try somewhere up the function
  124    chain that called the current function in the current thread.
  126    There must not be a return in any try block, nor a goto in any try block
  127    that leaves that block.  The always block does not catch a return from the
  128    try block.  There is no check or protection for an improper use of return or
  129    goto.  It is up to the user to assure that this doesn't happen. If it does
  130    happen, then the reference to the current try block is left on the try
  131    stack, and the next throw which is supposed to go to an enclosing try would
  132    instead go to this try, possibly after the enclosing function has returned.
  133    Mayhem will then ensue.  This may be caught by the longjmp() implementation,
  134    which would report "longjmp botch" and then abort.
  136    Any automatic storage variables that are modified in the try block and used
  137    in the catch or always block must be declared volatile.  Otherwise their
  138    value in the catch or always block is indeterminate.
  140    Any statements between try and always, between try and catch if there is no
  141    always, or between always and catch are part of those respective try or
  142    always blocks.  Use of { } to enclose those blocks is optional, but { }
  143    should be used anyway for clarity, style, and to inform smart source editors
  144    that the enclosed code is to be indented.  Enclosing the catch block with {
  145    } is not optional if there is more than one statement in the block.
  146    However, even if there is just one statement in the catch block, it should
  147    be enclosed in { } anyway for style and editing convenience.
  149    The contents of the ball_t structure after the first element (int code) can
  150    be customized for the application.  If ball_t is customized, then the code
  151    in try.c should be updated accordingly.  If there is no memory allocation in
  152    throw(), then drop() can be eliminated.
  154    Example usage:
  156     ball_t err;
  157     volatile char *temp = NULL;
  158     try {
  159         ... do something ...
  160         if (ret == -1)
  161             throw(1, "bad thing happened to %s\n", me);
  162         temp = malloc(sizeof(me) + 1);
  163         if (temp == NULL)
  164             throw(2, "out of memory");
  165         ... do more ...
  166         if (ret == -1)
  167             throw(3, "worse thing happened to %s\n", temp);
  168         ... some more code ...
  169     }
  170     always {
  171         free(temp);
  172     }
  173     catch (err) {
  174         fputs(err.why, stderr);
  175         drop(err);
  176         return err.code;
  177     }
  178     ... end up here if nothing bad happened ...
  181    More involved example:
  183     void check_part(void)
  184     {
  185         ball_t err;
  187         try {
  188             ...
  189             if (part == bad1)
  190                 throw(1);
  191             ...
  192             if (part == bad2)
  193                 throw(1);
  194             ...
  195         }
  196         catch (err) {
  197             drop(err);
  198             throw(3, "part was bad");
  199         }
  200     }
  202     void check_input(void)
  203     {
  204         ...
  205         if (input == wrong)
  206             throw(4, "input was wrong");
  207         ...
  208         if (input == stupid)
  209             throw(5, "input was stupid");
  210         ...
  211         check_part();
  212         ...
  213     }
  215     void *build_something(void)
  216     {
  217         ball_t err;
  218         volatile void *thing;
  219         try {
  220             thing = malloc(sizeof(struct thing));
  221             ... build up thing ...
  222             check_input();
  223             ... finish building it ...
  224         }
  225         catch (err) {
  226             free(thing);
  227             punt(err);
  228         }
  229         return thing;
  230     }
  232     int grand_central(void)
  233     {
  234         ball_t err;
  235         void *thing;
  236         try {
  237             thing = build_something();
  238         }
  239         catch (err) {
  240             fputs(err.why, stderr);
  241             drop(err);
  242             return err.code;
  243         }
  244         ... use thing ...
  245         free(thing);
  246         return 0;
  247     }
  249  */
  251 #ifndef _TRY_H
  252 #define _TRY_H
  254 #include <stdlib.h>
  255 #include <string.h>
  256 #include <assert.h>
  257 #include <setjmp.h>
  259 /* If pthreads are used, uncomment this include to make try thread-safe. */
  260 #ifndef NOTHREAD
  261 #  include <pthread.h>
  262 #endif
  264 /* The exposed names can be changed here. */
  265 #define ball_t try_ball_t_
  266 #define try TRY_TRY_
  267 #define always TRY_ALWAYS_
  268 #define catch TRY_CATCH_
  269 #define throw TRY_THROW_
  270 #define retry TRY_RETRY_
  271 #define punt TRY_PUNT_
  272 #define drop TRY_DROP_
  274 /* Package of an integer code and any other data to be thrown and caught. Here,
  275    why is a string with information to be displayed to indicate why an
  276    exception was thrown.  free is true if why was allocated and should be freed
  277    when no longer needed.  This structure can be customized as needed, but it
  278    must start with an int code.  If it is customized, the try_throw_() function
  279    in try.c must also be updated accordingly.  As an example, why could be a
  280    structure with information for use in the catch block. */
  281 typedef struct {
  282     int code;           /* integer code (required) */
  283     int free;           /* if true, the message string was allocated */
  284     char *why;          /* informational string or NULL */
  285 } try_ball_t_;
  287 /* Element in the global try stack (a linked list). */
  288 typedef struct try_s_ try_t_;
  289 struct try_s_ {
  290     jmp_buf env;        /* state information for longjmp() to jump back */
  291     try_ball_t_ ball;   /* data passed from the throw() */
  292     try_t_ *next;       /* link to the next enclosing try_t, or NULL */
  293 };
  295 /* Global try stack.  try.c must be compiled and linked to provide the stack
  296    pointer.  Use thread-local storage if pthread.h is included before this.
  297    Note that a throw can only be caught within the same thread.  A new and
  298    unique try stack is created for each thread, so any attempt to throw across
  299    threads will fail with an assert, by virtue of reaching the end of the
  300    stack. */
  301 #ifdef PTHREAD_ONCE_INIT
  302     extern pthread_key_t try_key_;
  303     void try_setup_(void);
  304 #   define try_stack_ ((try_t_ *)pthread_getspecific(try_key_))
  305 #   define try_stack_set_(next) \
  306         do { \
  307             assert(pthread_setspecific(try_key_, next) == 0 && \
  308                    "try: pthread_setspecific() failed"); \
  309         } while (0)
  310 #else /* !PTHREAD_ONCE_INIT */
  311     extern try_t_ *try_stack_;
  312 #   define try_setup_()
  313 #   define try_stack_set_(next) try_stack_ = (next)
  314 #endif /* PTHREAD_ONCE_INIT */
  316 /* Try a block.  The block should follow the invocation of try enclosed in { }.
  317    The block must be immediately followed by an always or a catch.  You must
  318    not goto or return out of the try block.  A naked break or continue in the
  319    try block will go to the end of the block. */
  320 #define TRY_TRY_ \
  321     do { \
  322         try_t_ try_this_; \
  323         volatile int try_pushed_ = 1; \
  324         try_this_.ball.code = 0; \
  325         try_this_.ball.free = 0; \
  326         try_this_.ball.why = NULL; \
  327         try_setup_(); \
  328         try_this_.next = try_stack_; \
  329         try_stack_set_(&try_this_); \
  330         if (setjmp(try_this_.env) < 2) \
  331             do { \
  333 /* Execute the code between always and catch, whether or not something was
  334    thrown.  An always block is optional.  If present, the always block must
  335    follow a try block and be followed by a catch block.  The always block
  336    should be enclosed in { }.  A naked break or continue in the always block
  337    will go to the end of the block.  It is permitted to use throw in the always
  338    block, which will fall up to the next enclosing try.  However this will
  339    result in a memory leak if the original throw() allocated space for the
  340    informational string.  So it's best to not throw() in an always block.  Keep
  341    the always block simple.
  343    Great care must be taken if the always block uses an automatic storage
  344    variable local to the enclosing function that can be modified in the try
  345    block.  Such variables must be declared volatile.  If such a variable is not
  346    declared volatile, and if the compiler elects to keep that variable in a
  347    register, then the throw will restore that variable to its state at the
  348    beginning of the try block, wiping out any change that occurred in the try
  349    block.  This can cause very confusing bugs until you remember that you
  350    didn't follow this rule. */
  351 #define TRY_ALWAYS_ \
  352             } while (0); \
  353         if (try_pushed_) { \
  354             try_stack_set_(try_this_.next); \
  355             try_pushed_ = 0; \
  356         } \
  357             do {
  359 /* Catch an error thrown in the preceding try block.  The catch block must
  360    follow catch and its parameter, and must be enclosed in { }.  The catch must
  361    immediately follow the try or always block.  It is permitted to use throw()
  362    in the catch block, which will fall up to the next enclosing try.  However
  363    the ball_t passed by throw() must be freed using drop() before doing another
  364    throw, to avoid a potential memory leak. The parameter of catch must be a
  365    ball_t declared in the function or block containing the catch.  It is set to
  366    the parameters of the throw() that jumped to the catch.  The catch block is
  367    not executed if the first parameter of the throw() was zero.
  369    A catch block should end with either a punt() or a drop().
  371    Great care must be taken if the catch block uses an automatic storage
  372    variable local to the enclosing function that can be modified in the try
  373    block.  Such variables must be declared volatile.  If such a variable is not
  374    declared volatile, and if the compiler elects to keep that variable in a
  375    register, then the throw will restore that variable to its state at the
  376    beginning of the try block, wiping out any change that occurred in the try
  377    block.  This can cause very confusing bugs until you remember that you
  378    didn't follow this rule. */
  379 #define TRY_CATCH_(try_ball_) \
  380             } while (0); \
  381         if (try_pushed_) { \
  382             try_stack_set_(try_this_.next); \
  383             try_pushed_ = 0; \
  384         } \
  385         try_ball_ = try_this_.ball; \
  386     } while (0); \
  387     if (try_ball_.code)
  389 /* Throw an error.  This can be in the try block or in any function called from
  390    the try block, at any level of nesting.  This will fall back to the end of
  391    the first enclosing try block in the same thread, invoking the associated
  392    catch block with a ball_t set to the arguments of throw().  throw() will
  393    abort the program with an assert() if there is no nesting try.  Make sure
  394    that there's a nesting try!
  396    try may have one or more arguments, where the first argument is an int, the
  397    optional second argument is a string, and the remaining optional arguments
  398    are referred to by printf() formatting commands in the string.  If there are
  399    formatting commands in the string, i.e. any percent (%) signs, then
  400    vsnprintf() is used to generate the formatted string from the arguments
  401    before jumping to the enclosing try block.  This allows throw() to use
  402    information on the stack in the scope of the throw() statement, which will
  403    be lost after jumping back to the enclosing try block.  That formatted
  404    string will use allocated memory, which is why it is important to use drop()
  405    in catch blocks to free that memory, or punt() to pass the string on to
  406    another catch block.  Eventually some catch block down the chain will have
  407    to drop() it.
  409    If a memory allocation fails during the execution of a throw(), then the
  410    string provided to the catch block is not the formatted string at all, but
  411    rather the string: "try: out of memory", with the integer code from the
  412    throw() unchanged.
  414    If the first argument of throw is zero, then the catch block is not
  415    executed.  A throw(0) from a function called in the try block is equivalent
  416    to a break or continue in the try block.  A throw(0) should not have any
  417    other arguments, to avoid a potential memory leak.  There is no opportunity
  418    to make use of any arguments after the 0 anyway.
  420    try.c must be compiled and linked to provide the try_throw_() function. */
  421 void try_throw_(int code, char *fmt, ...);
  422 #define TRY_THROW_(...) try_throw_(__VA_ARGS__, NULL)
  424 /* Retry the try block.  This will start over at the beginning of the try
  425    block.  This can be used in the try block or in any function called from the
  426    try block at any level of nesting, just like throw.  retry has no argument.
  427    If there is a retry in the always or catch block, then it will retry the
  428    next enclosing try, not the immediately preceding try.
  430    If you use this, make sure you have carefully thought through how it will
  431    work.  It can be tricky to correctly rerun a chunk of code that has been
  432    partially executed.  Especially if there are different degrees of progress
  433    that could have been made.  Also note that automatic variables changed in
  434    the try block and not declared volatile will have indeterminate values.
  436    We use 1 here instead of 0, since some implementations prevent returning a
  437    zero value from longjmp() to setjmp(). */
  438 #define TRY_RETRY_ \
  439     do { \
  440         try_setup_(); \
  441         assert(try_stack_ != NULL && "try: naked retry"); \
  442         longjmp(try_stack_->env, 1); \
  443     } while (0)
  445 /* Punt a caught error on to the next enclosing catcher.  This is normally used
  446    in a catch block with same argument as the catch. */
  447 #define TRY_PUNT_(try_ball_) \
  448     do { \
  449         try_setup_(); \
  450         assert(try_stack_ != NULL && "try: naked punt"); \
  451         try_stack_->ball = try_ball_; \
  452         longjmp(try_stack_->env, 2); \
  453     } while (0)
  455 /* Clean up at the end of the line in a catch (no more punts). */
  456 #define TRY_DROP_(try_ball_) \
  457     do { \
  458         if (try_ball_.free) { \
  459             free(try_ball_.why); \
  460             try_ball_.free = 0; \
  461             try_ball_.why = NULL; \
  462         } \
  463     } while (0)
  465 #endif /* _TRY_H */