1 /* Invalid parameter handler for MSVC runtime libraries.
2 Copyright (C) 2011-2025 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
20 /* With MSVC runtime libraries with the "invalid parameter handler" concept,
21 functions like fprintf(), dup2(), or close() crash when the caller passes
22 an invalid argument. But POSIX wants error codes (such as EINVAL or EBADF)
24 This file defines macros that turn such an invalid parameter notification
25 into a non-local exit. An error code can then be produced at the target
26 of this exit. You can thus write code like
30 <Code that can trigger an invalid parameter notification
31 but does not do 'return', 'break', 'continue', nor 'goto'.>
35 <Code that handles an invalid parameter notification
36 but does not do 'return', 'break', 'continue', nor 'goto'.>
40 This entire block expands to a single statement.
42 The handling of invalid parameters can be done in three ways:
44 * The default way, which is reasonable for programs (not libraries):
45 AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [DEFAULT_HANDLING])
47 * The way for libraries that make "hairy" calls (like close(-1), or
48 fclose(fp) where fileno(fp) is closed, or simply getdtablesize()):
49 AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [HAIRY_LIBRARY_HANDLING])
51 * The way for libraries that make no "hairy" calls:
52 AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [SANE_LIBRARY_HANDLING])
55 /* This file uses HAVE_MSVC_INVALID_PARAMETER_HANDLER. */
56 #if !_GL_CONFIG_H_INCLUDED
57 #error "Please include config.h first."
60 #define DEFAULT_HANDLING 0
61 #define HAIRY_LIBRARY_HANDLING 1
62 #define SANE_LIBRARY_HANDLING 2
64 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
65 && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
66 /* A native Windows platform with the "invalid parameter handler" concept,
67 and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING. */
69 # if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
70 /* Default handling. */
76 /* Ensure that the invalid parameter handler in installed that just returns.
77 Because we assume no other part of the program installs a different
78 invalid parameter handler, this solution is multithread-safe. */
79 extern void gl_msvc_inval_ensure_handler (void);
85 # define TRY_MSVC_INVAL \
88 gl_msvc_inval_ensure_handler (); \
90 # define CATCH_MSVC_INVAL \
92 # define DONE_MSVC_INVAL \
97 /* Handling for hairy libraries. */
101 /* Gnulib can define its own status codes, as described in the page
102 "Raising Software Exceptions" on microsoft.com
103 <https://docs.microsoft.com/en-us/cpp/cpp/raising-software-exceptions>.
104 Our status codes are composed of
105 - 0xE0000000, mandatory for all user-defined status codes,
106 - 0x474E550, a API identifier ("GNU"),
107 - 0, 1, 2, ..., used to distinguish different status codes from the
109 # define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
111 # if defined _MSC_VER
112 /* A compiler that supports __try/__except, as described in the page
113 "try-except statement" on microsoft.com
114 <https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement>.
115 With __try/__except, we can use the multithread-safe exception handling. */
121 /* Ensure that the invalid parameter handler in installed that raises a
122 software exception with code STATUS_GNULIB_INVALID_PARAMETER.
123 Because we assume no other part of the program installs a different
124 invalid parameter handler, this solution is multithread-safe. */
125 extern void gl_msvc_inval_ensure_handler (void);
131 # define TRY_MSVC_INVAL \
134 gl_msvc_inval_ensure_handler (); \
136 # define CATCH_MSVC_INVAL \
137 __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER \
138 ? EXCEPTION_EXECUTE_HANDLER \
139 : EXCEPTION_CONTINUE_SEARCH)
140 # define DONE_MSVC_INVAL \
146 We can only use setjmp/longjmp. */
154 struct gl_msvc_inval_per_thread
156 /* The restart that will resume execution at the code between
157 CATCH_MSVC_INVAL and DONE_MSVC_INVAL. It is enabled only between
158 TRY_MSVC_INVAL and CATCH_MSVC_INVAL. */
161 /* Tells whether the contents of restart is valid. */
165 /* Ensure that the invalid parameter handler in installed that passes
166 control to the gl_msvc_inval_restart if it is valid, or raises a
167 software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise.
168 Because we assume no other part of the program installs a different
169 invalid parameter handler, this solution is multithread-safe. */
170 extern void gl_msvc_inval_ensure_handler (void);
172 /* Return a pointer to the per-thread data for the current thread. */
173 extern struct gl_msvc_inval_per_thread
*gl_msvc_inval_current (void);
179 # define TRY_MSVC_INVAL \
182 struct gl_msvc_inval_per_thread *msvc_inval_current; \
183 gl_msvc_inval_ensure_handler (); \
184 msvc_inval_current = gl_msvc_inval_current (); \
185 /* First, initialize gl_msvc_inval_restart. */ \
186 if (setjmp (msvc_inval_current->restart) == 0) \
188 /* Then, mark it as valid. */ \
189 msvc_inval_current->restart_valid = 1;
190 # define CATCH_MSVC_INVAL \
191 /* Execution completed. \
192 Mark gl_msvc_inval_restart as invalid. */ \
193 msvc_inval_current->restart_valid = 0; \
197 /* Execution triggered an invalid parameter notification. \
198 Mark gl_msvc_inval_restart as invalid. */ \
199 msvc_inval_current->restart_valid = 0;
200 # define DONE_MSVC_INVAL \
210 /* A platform that does not need to the invalid parameter handler,
211 or when SANE_LIBRARY_HANDLING is desired. */
213 /* The braces here avoid GCC warnings like
214 "warning: suggest explicit braces to avoid ambiguous 'else'". */
215 # define TRY_MSVC_INVAL \
219 # define CATCH_MSVC_INVAL \
221 # define DONE_MSVC_INVAL \
227 #endif /* _MSVC_INVAL_H */