msvc-inval: Make handler multithread-safe.
authorBruno Haible <bruno@clisp.org>
Sun, 25 Sep 2011 20:02:18 +0000 (22:02 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 25 Sep 2011 20:02:18 +0000 (22:02 +0200)
* lib/msvc-inval.h (struct gl_msvc_inval_per_thread): New type.
(gl_msvc_inval_restart, gl_msvc_inval_restart_valid): Remove
declarations.
(gl_msvc_inval_current): New declaration.
(TRY_MSVC_INVAL, CATCH_MSVC_INVAL, DONE_MSVC_INVAL) [!_MSC_VER]:
Operate on the structure returned by gl_msvc_inval_current().
* lib/msvc-inval.c (gl_msvc_inval_restart, gl_msvc_inval_restart_valid):
Remove varaiables.
(tls_index, tls_initialized): New variables.
(not_per_thread): New variable.
(gl_msvc_inval_current): New function.
(gl_msvc_invalid_parameter_handler) [!_MSC_VER]: Use the structure
returned by gl_msvc_inval_current().

ChangeLog
lib/msvc-inval.c
lib/msvc-inval.h

index 73ad1b0..4b51d08 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2011-09-25  Bruno Haible  <bruno@clisp.org>
 
+       msvc-inval: Make handler multithread-safe.
+       * lib/msvc-inval.h (struct gl_msvc_inval_per_thread): New type.
+       (gl_msvc_inval_restart, gl_msvc_inval_restart_valid): Remove
+       declarations.
+       (gl_msvc_inval_current): New declaration.
+       (TRY_MSVC_INVAL, CATCH_MSVC_INVAL, DONE_MSVC_INVAL) [!_MSC_VER]:
+       Operate on the structure returned by gl_msvc_inval_current().
+       * lib/msvc-inval.c (gl_msvc_inval_restart, gl_msvc_inval_restart_valid):
+       Remove varaiables.
+       (tls_index, tls_initialized): New variables.
+       (not_per_thread): New variable.
+       (gl_msvc_inval_current): New function.
+       (gl_msvc_invalid_parameter_handler) [!_MSC_VER]: Use the structure
+       returned by gl_msvc_inval_current().
+
+2011-09-25  Bruno Haible  <bruno@clisp.org>
+
        msvc-inval: Install handler globally.
        * lib/msvc-inval.h (STATUS_GNULIB_INVALID_PARAMETER): Define also for
        !_MSC_VER.
index 4618455..ba3c799 100644 (file)
@@ -40,8 +40,42 @@ gl_msvc_invalid_parameter_handler (const wchar_t *expression,
 
 # else
 
-jmp_buf gl_msvc_inval_restart;
-int gl_msvc_inval_restart_valid;
+/* An index to thread-local storage.  */
+static DWORD tls_index;
+static int tls_initialized /* = 0 */;
+
+/* Used as a fallback only.  */
+static struct gl_msvc_inval_per_thread not_per_thread;
+
+struct gl_msvc_inval_per_thread *
+gl_msvc_inval_current (void)
+{
+  if (!tls_initialized)
+    {
+      tls_index = TlsAlloc ();
+      tls_initialized = 1;
+    }
+  if (tls_index == TLS_OUT_OF_INDEXES)
+    /* TlsAlloc had failed.  */
+    return &not_per_thread;
+  else
+    {
+      struct gl_msvc_inval_per_thread *pointer =
+        (struct gl_msvc_inval_per_thread *) TlsGetValue (tls_index);
+      if (pointer == NULL)
+        {
+          /* First call.  Allocate a new 'struct gl_msvc_inval_per_thread'.  */
+          pointer =
+            (struct gl_msvc_inval_per_thread *)
+            malloc (sizeof (struct gl_msvc_inval_per_thread));
+          if (pointer == NULL)
+            /* Could not allocate memory.  Use the global storage.  */
+            pointer = &not_per_thread;
+          TlsSetValue (tls_index, pointer);
+        }
+      return pointer;
+    }
+}
 
 static void cdecl
 gl_msvc_invalid_parameter_handler (const wchar_t *expression,
@@ -50,8 +84,9 @@ gl_msvc_invalid_parameter_handler (const wchar_t *expression,
                                    unsigned int line,
                                    uintptr_t dummy)
 {
-  if (gl_msvc_inval_restart_valid)
-    longjmp (gl_msvc_inval_restart, 1);
+  struct gl_msvc_inval_per_thread *current = gl_msvc_inval_current ();
+  if (current->restart_valid)
+    longjmp (current->restart, 1);
   else
     /* An invalid parameter notification from outside the gnulib code.
        Give the caller a chance to intervene.  */
index 952e72a..5f2e446 100644 (file)
@@ -102,13 +102,16 @@ extern void gl_msvc_inval_ensure_handler (void);
 extern "C" {
 #  endif
 
-/* The restart that will resume execution at the code between
-   CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
-   TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
-extern jmp_buf gl_msvc_inval_restart;
+struct gl_msvc_inval_per_thread
+{
+  /* The restart that will resume execution at the code between
+     CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
+     TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
+  jmp_buf restart;
 
-/* Tells whether the contents of gl_msvc_inval_restart is valid.  */
-extern int gl_msvc_inval_restart_valid;
+  /* Tells whether the contents of restart is valid.  */
+  int 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
@@ -117,6 +120,9 @@ extern int gl_msvc_inval_restart_valid;
    invalid parameter handler, this solution is multithread-safe.  */
 extern void gl_msvc_inval_ensure_handler (void);
 
+/* Return a pointer to the per-thread data for the current thread.  */
+extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void);
+
 #  ifdef __cplusplus
 }
 #  endif
@@ -124,22 +130,24 @@ extern void gl_msvc_inval_ensure_handler (void);
 #  define TRY_MSVC_INVAL \
      do                                                                        \
        {                                                                       \
+         struct gl_msvc_inval_per_thread *msvc_inval_current;                  \
          gl_msvc_inval_ensure_handler ();                                      \
+         msvc_inval_current = gl_msvc_inval_current ();                        \
          /* First, initialize gl_msvc_inval_restart.  */                       \
-         if (setjmp (gl_msvc_inval_restart) == 0)                              \
+         if (setjmp (msvc_inval_current->restart) == 0)                        \
            {                                                                   \
              /* Then, mark it as valid.  */                                    \
-             gl_msvc_inval_restart_valid = 1;
+             msvc_inval_current->restart_valid = 1;
 #  define CATCH_MSVC_INVAL \
              /* Execution completed.                                           \
                 Mark gl_msvc_inval_restart as invalid.  */                     \
-             gl_msvc_inval_restart_valid = 0;                                  \
+             msvc_inval_current->restart_valid = 0;                            \
            }                                                                   \
          else                                                                  \
            {                                                                   \
              /* Execution triggered an invalid parameter notification.         \
                 Mark gl_msvc_inval_restart as invalid.  */                     \
-             gl_msvc_inval_restart_valid = 0;
+             msvc_inval_current->restart_valid = 0;
 #  define DONE_MSVC_INVAL \
            }                                                                   \
        }                                                                       \