Home | History | Annotate | Line # | Download | only in import
msvc-inval.h revision 1.1
      1  1.1  christos /* Invalid parameter handler for MSVC runtime libraries.
      2  1.1  christos    Copyright (C) 2011-2020 Free Software Foundation, Inc.
      3  1.1  christos 
      4  1.1  christos    This program is free software; you can redistribute it and/or modify
      5  1.1  christos    it under the terms of the GNU General Public License as published by
      6  1.1  christos    the Free Software Foundation; either version 3, or (at your option)
      7  1.1  christos    any later version.
      8  1.1  christos 
      9  1.1  christos    This program is distributed in the hope that it will be useful,
     10  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  1.1  christos    GNU General Public License for more details.
     13  1.1  christos 
     14  1.1  christos    You should have received a copy of the GNU General Public License along
     15  1.1  christos    with this program; if not, see <https://www.gnu.org/licenses/>.  */
     16  1.1  christos 
     17  1.1  christos #ifndef _MSVC_INVAL_H
     18  1.1  christos #define _MSVC_INVAL_H
     19  1.1  christos 
     20  1.1  christos /* With MSVC runtime libraries with the "invalid parameter handler" concept,
     21  1.1  christos    functions like fprintf(), dup2(), or close() crash when the caller passes
     22  1.1  christos    an invalid argument.  But POSIX wants error codes (such as EINVAL or EBADF)
     23  1.1  christos    instead.
     24  1.1  christos    This file defines macros that turn such an invalid parameter notification
     25  1.1  christos    into a non-local exit.  An error code can then be produced at the target
     26  1.1  christos    of this exit.  You can thus write code like
     27  1.1  christos 
     28  1.1  christos      TRY_MSVC_INVAL
     29  1.1  christos        {
     30  1.1  christos          <Code that can trigger an invalid parameter notification
     31  1.1  christos           but does not do 'return', 'break', 'continue', nor 'goto'.>
     32  1.1  christos        }
     33  1.1  christos      CATCH_MSVC_INVAL
     34  1.1  christos        {
     35  1.1  christos          <Code that handles an invalid parameter notification
     36  1.1  christos           but does not do 'return', 'break', 'continue', nor 'goto'.>
     37  1.1  christos        }
     38  1.1  christos      DONE_MSVC_INVAL;
     39  1.1  christos 
     40  1.1  christos    This entire block expands to a single statement.
     41  1.1  christos 
     42  1.1  christos    The handling of invalid parameters can be done in three ways:
     43  1.1  christos 
     44  1.1  christos      * The default way, which is reasonable for programs (not libraries):
     45  1.1  christos        AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [DEFAULT_HANDLING])
     46  1.1  christos 
     47  1.1  christos      * The way for libraries that make "hairy" calls (like close(-1), or
     48  1.1  christos        fclose(fp) where fileno(fp) is closed, or simply getdtablesize()):
     49  1.1  christos        AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [HAIRY_LIBRARY_HANDLING])
     50  1.1  christos 
     51  1.1  christos      * The way for libraries that make no "hairy" calls:
     52  1.1  christos        AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [SANE_LIBRARY_HANDLING])
     53  1.1  christos  */
     54  1.1  christos 
     55  1.1  christos #define DEFAULT_HANDLING       0
     56  1.1  christos #define HAIRY_LIBRARY_HANDLING 1
     57  1.1  christos #define SANE_LIBRARY_HANDLING  2
     58  1.1  christos 
     59  1.1  christos #if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
     60  1.1  christos     && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
     61  1.1  christos /* A native Windows platform with the "invalid parameter handler" concept,
     62  1.1  christos    and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING.  */
     63  1.1  christos 
     64  1.1  christos # if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
     65  1.1  christos /* Default handling.  */
     66  1.1  christos 
     67  1.1  christos #  ifdef __cplusplus
     68  1.1  christos extern "C" {
     69  1.1  christos #  endif
     70  1.1  christos 
     71  1.1  christos /* Ensure that the invalid parameter handler in installed that just returns.
     72  1.1  christos    Because we assume no other part of the program installs a different
     73  1.1  christos    invalid parameter handler, this solution is multithread-safe.  */
     74  1.1  christos extern void gl_msvc_inval_ensure_handler (void);
     75  1.1  christos 
     76  1.1  christos #  ifdef __cplusplus
     77  1.1  christos }
     78  1.1  christos #  endif
     79  1.1  christos 
     80  1.1  christos #  define TRY_MSVC_INVAL \
     81  1.1  christos      do                                                                        \
     82  1.1  christos        {                                                                       \
     83  1.1  christos          gl_msvc_inval_ensure_handler ();                                      \
     84  1.1  christos          if (1)
     85  1.1  christos #  define CATCH_MSVC_INVAL \
     86  1.1  christos          else
     87  1.1  christos #  define DONE_MSVC_INVAL \
     88  1.1  christos        }                                                                       \
     89  1.1  christos      while (0)
     90  1.1  christos 
     91  1.1  christos # else
     92  1.1  christos /* Handling for hairy libraries.  */
     93  1.1  christos 
     94  1.1  christos #  include <excpt.h>
     95  1.1  christos 
     96  1.1  christos /* Gnulib can define its own status codes, as described in the page
     97  1.1  christos    "Raising Software Exceptions" on microsoft.com
     98  1.1  christos    <https://docs.microsoft.com/en-us/cpp/cpp/raising-software-exceptions>.
     99  1.1  christos    Our status codes are composed of
    100  1.1  christos      - 0xE0000000, mandatory for all user-defined status codes,
    101  1.1  christos      - 0x474E550, a API identifier ("GNU"),
    102  1.1  christos      - 0, 1, 2, ..., used to distinguish different status codes from the
    103  1.1  christos        same API.  */
    104  1.1  christos #  define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
    105  1.1  christos 
    106  1.1  christos #  if defined _MSC_VER
    107  1.1  christos /* A compiler that supports __try/__except, as described in the page
    108  1.1  christos    "try-except statement" on microsoft.com
    109  1.1  christos    <https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement>.
    110  1.1  christos    With __try/__except, we can use the multithread-safe exception handling.  */
    111  1.1  christos 
    112  1.1  christos #   ifdef __cplusplus
    113  1.1  christos extern "C" {
    114  1.1  christos #   endif
    115  1.1  christos 
    116  1.1  christos /* Ensure that the invalid parameter handler in installed that raises a
    117  1.1  christos    software exception with code STATUS_GNULIB_INVALID_PARAMETER.
    118  1.1  christos    Because we assume no other part of the program installs a different
    119  1.1  christos    invalid parameter handler, this solution is multithread-safe.  */
    120  1.1  christos extern void gl_msvc_inval_ensure_handler (void);
    121  1.1  christos 
    122  1.1  christos #   ifdef __cplusplus
    123  1.1  christos }
    124  1.1  christos #   endif
    125  1.1  christos 
    126  1.1  christos #   define TRY_MSVC_INVAL \
    127  1.1  christos       do                                                                       \
    128  1.1  christos         {                                                                      \
    129  1.1  christos           gl_msvc_inval_ensure_handler ();                                     \
    130  1.1  christos           __try
    131  1.1  christos #   define CATCH_MSVC_INVAL \
    132  1.1  christos           __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER     \
    133  1.1  christos                     ? EXCEPTION_EXECUTE_HANDLER                                \
    134  1.1  christos                     : EXCEPTION_CONTINUE_SEARCH)
    135  1.1  christos #   define DONE_MSVC_INVAL \
    136  1.1  christos         }                                                                      \
    137  1.1  christos       while (0)
    138  1.1  christos 
    139  1.1  christos #  else
    140  1.1  christos /* Any compiler.
    141  1.1  christos    We can only use setjmp/longjmp.  */
    142  1.1  christos 
    143  1.1  christos #   include <setjmp.h>
    144  1.1  christos 
    145  1.1  christos #   ifdef __cplusplus
    146  1.1  christos extern "C" {
    147  1.1  christos #   endif
    148  1.1  christos 
    149  1.1  christos struct gl_msvc_inval_per_thread
    150  1.1  christos {
    151  1.1  christos   /* The restart that will resume execution at the code between
    152  1.1  christos      CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
    153  1.1  christos      TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
    154  1.1  christos   jmp_buf restart;
    155  1.1  christos 
    156  1.1  christos   /* Tells whether the contents of restart is valid.  */
    157  1.1  christos   int restart_valid;
    158  1.1  christos };
    159  1.1  christos 
    160  1.1  christos /* Ensure that the invalid parameter handler in installed that passes
    161  1.1  christos    control to the gl_msvc_inval_restart if it is valid, or raises a
    162  1.1  christos    software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise.
    163  1.1  christos    Because we assume no other part of the program installs a different
    164  1.1  christos    invalid parameter handler, this solution is multithread-safe.  */
    165  1.1  christos extern void gl_msvc_inval_ensure_handler (void);
    166  1.1  christos 
    167  1.1  christos /* Return a pointer to the per-thread data for the current thread.  */
    168  1.1  christos extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void);
    169  1.1  christos 
    170  1.1  christos #   ifdef __cplusplus
    171  1.1  christos }
    172  1.1  christos #   endif
    173  1.1  christos 
    174  1.1  christos #   define TRY_MSVC_INVAL \
    175  1.1  christos       do                                                                       \
    176  1.1  christos         {                                                                      \
    177  1.1  christos           struct gl_msvc_inval_per_thread *msvc_inval_current;                 \
    178  1.1  christos           gl_msvc_inval_ensure_handler ();                                     \
    179  1.1  christos           msvc_inval_current = gl_msvc_inval_current ();                       \
    180  1.1  christos           /* First, initialize gl_msvc_inval_restart.  */                      \
    181  1.1  christos           if (setjmp (msvc_inval_current->restart) == 0)                       \
    182  1.1  christos             {                                                                  \
    183  1.1  christos               /* Then, mark it as valid.  */                                   \
    184  1.1  christos               msvc_inval_current->restart_valid = 1;
    185  1.1  christos #   define CATCH_MSVC_INVAL \
    186  1.1  christos               /* Execution completed.                                          \
    187  1.1  christos                  Mark gl_msvc_inval_restart as invalid.  */                    \
    188  1.1  christos               msvc_inval_current->restart_valid = 0;                           \
    189  1.1  christos             }                                                                  \
    190  1.1  christos           else                                                                 \
    191  1.1  christos             {                                                                  \
    192  1.1  christos               /* Execution triggered an invalid parameter notification.        \
    193  1.1  christos                  Mark gl_msvc_inval_restart as invalid.  */                    \
    194  1.1  christos               msvc_inval_current->restart_valid = 0;
    195  1.1  christos #   define DONE_MSVC_INVAL \
    196  1.1  christos             }                                                                  \
    197  1.1  christos         }                                                                      \
    198  1.1  christos       while (0)
    199  1.1  christos 
    200  1.1  christos #  endif
    201  1.1  christos 
    202  1.1  christos # endif
    203  1.1  christos 
    204  1.1  christos #else
    205  1.1  christos /* A platform that does not need to the invalid parameter handler,
    206  1.1  christos    or when SANE_LIBRARY_HANDLING is desired.  */
    207  1.1  christos 
    208  1.1  christos /* The braces here avoid GCC warnings like
    209  1.1  christos    "warning: suggest explicit braces to avoid ambiguous 'else'".  */
    210  1.1  christos # define TRY_MSVC_INVAL \
    211  1.1  christos     do                                                                         \
    212  1.1  christos       {                                                                        \
    213  1.1  christos         if (1)
    214  1.1  christos # define CATCH_MSVC_INVAL \
    215  1.1  christos         else
    216  1.1  christos # define DONE_MSVC_INVAL \
    217  1.1  christos       }                                                                        \
    218  1.1  christos     while (0)
    219  1.1  christos 
    220  1.1  christos #endif
    221  1.1  christos 
    222  1.1  christos #endif /* _MSVC_INVAL_H */
    223