changeset 15785:5e020d269ce6

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().
author Bruno Haible <bruno@clisp.org>
date Sun, 25 Sep 2011 22:02:18 +0200
parents f1e8251b45f3
children 104b4f91678d
files ChangeLog lib/msvc-inval.c lib/msvc-inval.h
diffstat 3 files changed, 74 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+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.
--- a/lib/msvc-inval.c
+++ b/lib/msvc-inval.c
@@ -40,8 +40,42 @@
 
 # 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 @@
                                    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.  */
--- a/lib/msvc-inval.h
+++ b/lib/msvc-inval.h
@@ -102,13 +102,16 @@
 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 @@
    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 @@
 #  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 \
            }                                                                   \
        }                                                                       \