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