msvc-inval: Make handler multithread-safe.
[gnulib.git] / lib / msvc-inval.h
1 /* Invalid parameter handler for MSVC runtime libraries.
2    Copyright (C) 2011 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #ifndef _MSVC_INVAL_H
19 #define _MSVC_INVAL_H
20
21 /* With MSVC runtime libraries with the "invalid parameter handler" concept,
22    functions like fprintf(), dup2(), or close() crash when the caller passes
23    an invalid argument.  But POSIX wants error codes (such as EINVAL or EBADF)
24    instead.
25    This file defines macros that turn such an invalid parameter notification
26    into a non-local exit.  An error code can then be produced at the target
27    of this exit.  You can thus write code like
28
29      TRY_MSVC_INVAL
30        {
31          <Code that can trigger an invalid parameter notification
32           but does not do 'return', 'break', 'continue', nor 'goto'.>
33        }
34      CATCH_MSVC_INVAL
35        {
36          <Code that handles an invalid parameter notification
37           but does not do 'return', 'break', 'continue', nor 'goto'.>
38        }
39      DONE_MSVC_INVAL;
40
41    This entire block expands to a single statement.
42  */
43
44 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
45 /* A native Windows platform with the "invalid parameter handler" concept.  */
46
47 /* Get _invalid_parameter_handler type and _set_invalid_parameter_handler
48    declaration.  */
49 # include <stdlib.h>
50 # include <excpt.h>
51
52 /* Gnulib can define its own status codes, as described in the page
53    "Raising Software Exceptions" on microsoft.com
54    <http://msdn.microsoft.com/en-us/library/het71c37.aspx>.
55    Our status codes are composed of
56      - 0xE0000000, mandatory for all user-defined status codes,
57      - 0x474E550, a API identifier ("GNU"),
58      - 0, 1, 2, ..., used to distinguish different status codes from the
59        same API.  */
60 # define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
61
62 # if defined _MSC_VER
63 /* A compiler that supports __try/__except, as described in the page
64    "try-except statement" on microsoft.com
65    <http://msdn.microsoft.com/en-us/library/s58ftw19.aspx>.
66    With __try/__except, we can use the multithread-safe exception handling.  */
67
68 #  ifdef __cplusplus
69 extern "C" {
70 #  endif
71
72 /* Ensure that the invalid parameter handler in installed that raises a
73    software exception with code STATUS_GNULIB_INVALID_PARAMETER.
74    Because we assume no other part of the program installs a different
75    invalid parameter handler, this solution is multithread-safe.  */
76 extern void gl_msvc_inval_ensure_handler (void);
77
78 #  ifdef __cplusplus
79 }
80 #  endif
81
82 #  define TRY_MSVC_INVAL \
83      do                                                                        \
84        {                                                                       \
85          gl_msvc_inval_ensure_handler ();                                      \
86          __try
87 #  define CATCH_MSVC_INVAL \
88          __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER      \
89                    ? EXCEPTION_EXECUTE_HANDLER                                 \
90                    : EXCEPTION_CONTINUE_SEARCH)
91 #  define DONE_MSVC_INVAL \
92        }                                                                       \
93      while (0)
94
95 # else
96 /* Any compiler.
97    We can only use setjmp/longjmp.  */
98
99 #  include <setjmp.h>
100
101 #  ifdef __cplusplus
102 extern "C" {
103 #  endif
104
105 struct gl_msvc_inval_per_thread
106 {
107   /* The restart that will resume execution at the code between
108      CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
109      TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
110   jmp_buf restart;
111
112   /* Tells whether the contents of restart is valid.  */
113   int restart_valid;
114 };
115
116 /* Ensure that the invalid parameter handler in installed that passes
117    control to the gl_msvc_inval_restart if it is valid, or raises a
118    software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise.
119    Because we assume no other part of the program installs a different
120    invalid parameter handler, this solution is multithread-safe.  */
121 extern void gl_msvc_inval_ensure_handler (void);
122
123 /* Return a pointer to the per-thread data for the current thread.  */
124 extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void);
125
126 #  ifdef __cplusplus
127 }
128 #  endif
129
130 #  define TRY_MSVC_INVAL \
131      do                                                                        \
132        {                                                                       \
133          struct gl_msvc_inval_per_thread *msvc_inval_current;                  \
134          gl_msvc_inval_ensure_handler ();                                      \
135          msvc_inval_current = gl_msvc_inval_current ();                        \
136          /* First, initialize gl_msvc_inval_restart.  */                       \
137          if (setjmp (msvc_inval_current->restart) == 0)                        \
138            {                                                                   \
139              /* Then, mark it as valid.  */                                    \
140              msvc_inval_current->restart_valid = 1;
141 #  define CATCH_MSVC_INVAL \
142              /* Execution completed.                                           \
143                 Mark gl_msvc_inval_restart as invalid.  */                     \
144              msvc_inval_current->restart_valid = 0;                            \
145            }                                                                   \
146          else                                                                  \
147            {                                                                   \
148              /* Execution triggered an invalid parameter notification.         \
149                 Mark gl_msvc_inval_restart as invalid.  */                     \
150              msvc_inval_current->restart_valid = 0;
151 #  define DONE_MSVC_INVAL \
152            }                                                                   \
153        }                                                                       \
154      while (0)
155
156 # endif
157
158 #else
159 /* A platform that does not need to the invalid parameter handler.  */
160
161 /* The braces here avoid GCC warnings like
162    "warning: suggest explicit braces to avoid ambiguous `else'".  */
163 # define TRY_MSVC_INVAL \
164     do                                                                         \
165       {                                                                        \
166         if (1)
167 # define CATCH_MSVC_INVAL \
168         else
169 # define DONE_MSVC_INVAL \
170       }                                                                        \
171     while (0)
172
173 #endif
174
175 #endif /* _MSVC_INVAL_H */