There is one copy of the "invalid parameter handler" in the MSVC runtime library; there is not one copy per thread. Therefore, setting this handler or deinstalling it in a gnulib function is not multithread-safe. For MT-safety the module needs two changes: 1) Install the handler once, globally. 2) Allocate the jmp_buf per thread.
Here comes part 1. 2011-09-25 Bruno Haible <br...@clisp.org> msvc-inval: Install handler globally. * lib/msvc-inval.h (STATUS_GNULIB_INVALID_PARAMETER): Define also for !_MSC_VER. (gl_msvc_invalid_parameter_handler): Remove declaration. (gl_msvc_inval_restart_valid, gl_msvc_inval_ensure_handler): New declarations. (TRY_MSVC_INVAL, CATCH_MSVC_INVAL, DONE_MSVC_INVAL) [!_MSC_VER]: Install the handler globally, don't uninstall it. * lib/msvc-inval.c (gl_msvc_inval_restart_valid): New variable. (gl_msvc_invalid_parameter_handler): Make static. If the restart is not currently valid, call RaiseException instead. (gl_msvc_inval_initialized, gl_msvc_inval_ensure_handler): Define also for !_MSC_VER. --- lib/msvc-inval.h.orig Sun Sep 25 20:58:50 2011 +++ lib/msvc-inval.h Sun Sep 25 18:48:03 2011 @@ -47,14 +47,7 @@ /* Get _invalid_parameter_handler type and _set_invalid_parameter_handler declaration. */ # include <stdlib.h> - -# if defined _MSC_VER -/* A compiler that supports __try/__except, as described in the page - "try-except statement" on microsoft.com - <http://msdn.microsoft.com/en-us/library/s58ftw19.aspx>. - With __try/__except, we can use the multithread-safe exception handling. */ - -# include <excpt.h> +# include <excpt.h> /* Gnulib can define its own status codes, as described in the page "Raising Software Exceptions" on microsoft.com @@ -64,7 +57,13 @@ - 0x474E550, a API identifier ("GNU"), - 0, 1, 2, ..., used to distinguish different status codes from the same API. */ -# define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0) +# define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0) + +# if defined _MSC_VER +/* A compiler that supports __try/__except, as described in the page + "try-except statement" on microsoft.com + <http://msdn.microsoft.com/en-us/library/s58ftw19.aspx>. + With __try/__except, we can use the multithread-safe exception handling. */ # ifdef __cplusplus extern "C" { @@ -95,8 +94,7 @@ # else /* Any compiler. - We can only use setjmp/longjmp. - Unfortunately, this is *not* multithread-safe. */ + We can only use setjmp/longjmp. */ # include <setjmp.h> @@ -109,14 +107,15 @@ TRY_MSVC_INVAL and CATCH_MSVC_INVAL. */ extern jmp_buf gl_msvc_inval_restart; -/* The invalid parameter handler that unwinds the stack up to the - gl_msvc_inval_restart. It is enabled only between TRY_MSVC_INVAL - and CATCH_MSVC_INVAL. */ -extern void cdecl gl_msvc_invalid_parameter_handler (const wchar_t *expression, - const wchar_t *function, - const wchar_t *file, - unsigned int line, - uintptr_t dummy); +/* Tells whether the contents of gl_msvc_inval_restart is valid. */ +extern int gl_msvc_inval_restart_valid; + +/* Ensure that the invalid parameter handler in installed that passes + control to the gl_msvc_inval_restart if it is valid, or raises a + software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise. + Because we assume no other part of the program installs a different + invalid parameter handler, this solution is multithread-safe. */ +extern void gl_msvc_inval_ensure_handler (void); # ifdef __cplusplus } @@ -125,23 +124,22 @@ # define TRY_MSVC_INVAL \ do \ { \ - _invalid_parameter_handler orig_handler; \ + gl_msvc_inval_ensure_handler (); \ /* First, initialize gl_msvc_inval_restart. */ \ if (setjmp (gl_msvc_inval_restart) == 0) \ { \ - /* Then, enable gl_msvc_invalid_parameter_handler. */ \ - orig_handler = \ - _set_invalid_parameter_handler (gl_msvc_invalid_parameter_handler); + /* Then, mark it as valid. */ \ + gl_msvc_inval_restart_valid = 1; # define CATCH_MSVC_INVAL \ /* Execution completed. \ - Disable gl_msvc_invalid_parameter_handler. */ \ - _set_invalid_parameter_handler (orig_handler); \ + Mark gl_msvc_inval_restart as invalid. */ \ + gl_msvc_inval_restart_valid = 0; \ } \ else \ { \ /* Execution triggered an invalid parameter notification. \ - Disable gl_msvc_invalid_parameter_handler. */ \ - _set_invalid_parameter_handler (orig_handler); + Mark gl_msvc_inval_restart as invalid. */ \ + gl_msvc_inval_restart_valid = 0; # define DONE_MSVC_INVAL \ } \ } \ --- lib/msvc-inval.c.orig Sun Sep 25 20:58:50 2011 +++ lib/msvc-inval.c Sun Sep 25 18:48:00 2011 @@ -22,11 +22,11 @@ #if HAVE_MSVC_INVALID_PARAMETER_HANDLER -# ifdef STATUS_GNULIB_INVALID_PARAMETER - /* Get declarations of the Win32 API functions. */ -# define WIN32_LEAN_AND_MEAN -# include <windows.h> +# define WIN32_LEAN_AND_MEAN +# include <windows.h> + +# if defined _MSC_VER static void cdecl gl_msvc_invalid_parameter_handler (const wchar_t *expression, @@ -38,32 +38,38 @@ RaiseException (STATUS_GNULIB_INVALID_PARAMETER, 0, 0, NULL); } -static int gl_msvc_inval_initialized /* = 0 */; - -void -gl_msvc_inval_ensure_handler (void) -{ - if (gl_msvc_inval_initialized == 0) - { - _set_invalid_parameter_handler (gl_msvc_invalid_parameter_handler); - gl_msvc_inval_initialized = 1; - } -} - # else jmp_buf gl_msvc_inval_restart; +int gl_msvc_inval_restart_valid; -void cdecl +static void cdecl gl_msvc_invalid_parameter_handler (const wchar_t *expression, const wchar_t *function, const wchar_t *file, unsigned int line, uintptr_t dummy) { - longjmp (gl_msvc_inval_restart, 1); + if (gl_msvc_inval_restart_valid) + longjmp (gl_msvc_inval_restart, 1); + else + /* An invalid parameter notification from outside the gnulib code. + Give the caller a chance to intervene. */ + RaiseException (STATUS_GNULIB_INVALID_PARAMETER, 0, 0, NULL); } # endif +static int gl_msvc_inval_initialized /* = 0 */; + +void +gl_msvc_inval_ensure_handler (void) +{ + if (gl_msvc_inval_initialized == 0) + { + _set_invalid_parameter_handler (gl_msvc_invalid_parameter_handler); + gl_msvc_inval_initialized = 1; + } +} + #endif -- In memoriam Safia Ahmed-jan <http://en.wikipedia.org/wiki/Safia_Ahmed-jan>