bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

msvc-inval: more options


From: Bruno Haible
Subject: msvc-inval: more options
Date: Sun, 25 Sep 2011 23:10:33 +0200
User-agent: KMail/1.13.6 (Linux/2.6.37.6-0.5-desktop; KDE/4.6.0; x86_64; ; )

On MSVC, it is not only possible to install, through
_set_invalid_parameter_handler, a handler that transfers control to a point
where the program (in this case, gnulib) can provide a sensible return value
or error return to a particular function.

It is also possible to install a handler that does nothing, just returns.
When you do this, the MSVC runtime library provides an error return, with
EBADF as error code in most cases, automatically.

What kinds of options does it give us?

  1) We can set the handler globally.
     This is multithread-safe, but not good for libraries.
     a) The handler transfers control.
     b) The handler returns.
  2) We can set the handler locally.
     This is not multithread-safe, and therefore also not good for libraries.
     a) The handler transfers control.
     b) The handler returns.
  3) We can set no handler at all. Then some functions are stricter than POSIX
     specifies.

Note that a handler that transfers control may be less reliable than a handler
that returns, since it has to guess the reason why the invalid parameter
notification was raised.

So, which options are useful in which cases?

  * In all uses of gnulib for programs, option 1b) is the best one.

  * For libraries that make "hairy" calls (like close(-1), or fclose(fp)
    where fileno(fp) is closed, or simply getdtablesize()), option 1a)
    is the best one (advantage over option 1b: the code outside the library
    gets notified about invalid parameters).

  * For libraries that make no "hairy" calls, option 3) is the best.

So, while the current code implements option 1a), we need code that allows
1a), 1b), or 3), and 1b) should be the default.

It's a pity that a misdesign in Windows will require the gnulib user to
think about this topic and make the right choice. If they had designed
the invalid parameter handler to be a per-thread entity (rather than a
global variable), option 2b) would have been the perfect one for all cases.


2011-09-25  Bruno Haible  <address@hidden>

        msvc-inval: Allow three invalid parameter handling modes.
        * lib/msvc-inval.h: Don't include <stdlib.h> here.
        (DEFAULT_HANDLING, HAIRY_LIBRARY_HANDLING, SANE_LIBRARY_HANDLING): New
        macros.
        (gl_msvc_inval_ensure_handler, TRY_MSVC_INVAL, CATCH_MSVC_INVAL,
        DONE_MSVC_INVAL): Implement DEFAULT_HANDLING. Treat
        SANE_LIBRARY_HANDLING as a no-op.
        * lib/msvc-inval.c: Treat SANE_LIBRARY_HANDLING as a no-op. Include
        <stdlib.h>.
        (gl_msvc_invalid_parameter_handler): Implement DEFAULT_HANDLING.

--- lib/msvc-inval.h.orig       Sun Sep 25 23:04:59 2011
+++ lib/msvc-inval.h    Sun Sep 25 23:04:45 2011
@@ -39,15 +39,60 @@
      DONE_MSVC_INVAL;
 
    This entire block expands to a single statement.
+
+   The handling of invalid parameters can be done in three ways:
+
+     * The default way, which is reasonable for programs (not libraries):
+       AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [DEFAULT_HANDLING])
+
+     * The way for libraries that make "hairy" calls (like close(-1), or
+       fclose(fp) where fileno(fp) is closed, or simply getdtablesize()):
+       AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [HAIRY_LIBRARY_HANDLING])
+
+     * The way for libraries that make no "hairy" calls:
+       AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [SANE_LIBRARY_HANDLING])
  */
 
-#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
-/* A native Windows platform with the "invalid parameter handler" concept.  */
+#define DEFAULT_HANDLING       0
+#define HAIRY_LIBRARY_HANDLING 1
+#define SANE_LIBRARY_HANDLING  2
+
+#if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
+    && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
+/* A native Windows platform with the "invalid parameter handler" concept,
+   and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING.  */
+
+# if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
+/* Default handling.  */
+
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+
+/* Ensure that the invalid parameter handler in installed that just returns.
+   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
+}
+#  endif
+
+#  define TRY_MSVC_INVAL \
+     do                                                                        
\
+       {                                                                       
\
+         gl_msvc_inval_ensure_handler ();                                      
\
+         if (1)
+#  define CATCH_MSVC_INVAL \
+         else
+#  define DONE_MSVC_INVAL \
+       }                                                                       
\
+     while (0)
+
+# else
+/* Handling for hairy libraries.  */
 
-/* Get _invalid_parameter_handler type and _set_invalid_parameter_handler
-   declaration.  */
-# include <stdlib.h>
-# include <excpt.h>
+#  include <excpt.h>
 
 /* Gnulib can define its own status codes, as described in the page
    "Raising Software Exceptions" on microsoft.com
@@ -57,17 +102,17 @@
      - 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
+#  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
+#   ifdef __cplusplus
 extern "C" {
-#  endif
+#   endif
 
 /* Ensure that the invalid parameter handler in installed that raises a
    software exception with code STATUS_GNULIB_INVALID_PARAMETER.
@@ -75,32 +120,32 @@
    invalid parameter handler, this solution is multithread-safe.  */
 extern void gl_msvc_inval_ensure_handler (void);
 
-#  ifdef __cplusplus
+#   ifdef __cplusplus
 }
-#  endif
+#   endif
 
-#  define TRY_MSVC_INVAL \
-     do                                                                        
\
-       {                                                                       
\
-         gl_msvc_inval_ensure_handler ();                                      
\
-         __try
-#  define CATCH_MSVC_INVAL \
-         __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER      
\
-                   ? EXCEPTION_EXECUTE_HANDLER                                 
\
-                   : EXCEPTION_CONTINUE_SEARCH)
-#  define DONE_MSVC_INVAL \
-       }                                                                       
\
-     while (0)
+#   define TRY_MSVC_INVAL \
+      do                                                                       
\
+        {                                                                      
\
+          gl_msvc_inval_ensure_handler ();                                     
\
+          __try
+#   define CATCH_MSVC_INVAL \
+          __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER     
\
+                    ? EXCEPTION_EXECUTE_HANDLER                                
\
+                    : EXCEPTION_CONTINUE_SEARCH)
+#   define DONE_MSVC_INVAL \
+        }                                                                      
\
+      while (0)
 
-# else
+#  else
 /* Any compiler.
    We can only use setjmp/longjmp.  */
 
-#  include <setjmp.h>
+#   include <setjmp.h>
 
-#  ifdef __cplusplus
+#   ifdef __cplusplus
 extern "C" {
-#  endif
+#   endif
 
 struct gl_msvc_inval_per_thread
 {
@@ -123,40 +168,43 @@
 /* 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
+#   ifdef __cplusplus
 }
-#  endif
+#   endif
 
-#  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 (msvc_inval_current->restart) == 0)                        
\
-           {                                                                   
\
-             /* Then, mark it as valid.  */                                    
\
-             msvc_inval_current->restart_valid = 1;
-#  define CATCH_MSVC_INVAL \
-             /* Execution completed.                                           
\
-                Mark gl_msvc_inval_restart as invalid.  */                     
\
-             msvc_inval_current->restart_valid = 0;                            
\
-           }                                                                   
\
-         else                                                                  
\
-           {                                                                   
\
-             /* Execution triggered an invalid parameter notification.         
\
-                Mark gl_msvc_inval_restart as invalid.  */                     
\
-             msvc_inval_current->restart_valid = 0;
-#  define DONE_MSVC_INVAL \
-           }                                                                   
\
-       }                                                                       
\
-     while (0)
+#   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 (msvc_inval_current->restart) == 0)                       
\
+            {                                                                  
\
+              /* Then, mark it as valid.  */                                   
\
+              msvc_inval_current->restart_valid = 1;
+#   define CATCH_MSVC_INVAL \
+              /* Execution completed.                                          
\
+                 Mark gl_msvc_inval_restart as invalid.  */                    
\
+              msvc_inval_current->restart_valid = 0;                           
\
+            }                                                                  
\
+          else                                                                 
\
+            {                                                                  
\
+              /* Execution triggered an invalid parameter notification.        
\
+                 Mark gl_msvc_inval_restart as invalid.  */                    
\
+              msvc_inval_current->restart_valid = 0;
+#   define DONE_MSVC_INVAL \
+            }                                                                  
\
+        }                                                                      
\
+      while (0)
+
+#  endif
 
 # endif
 
 #else
-/* A platform that does not need to the invalid parameter handler.  */
+/* A platform that does not need to the invalid parameter handler,
+   or when SANE_LIBRARY_HANDLING is desired.  */
 
 /* The braces here avoid GCC warnings like
    "warning: suggest explicit braces to avoid ambiguous `else'".  */
--- lib/msvc-inval.c.orig       Sun Sep 25 23:04:59 2011
+++ lib/msvc-inval.c    Sun Sep 25 22:48:38 2011
@@ -20,13 +20,31 @@
 /* Specification.  */
 #include "msvc-inval.h"
 
-#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
+#if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
+    && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
+
+/* Get _invalid_parameter_handler type and _set_invalid_parameter_handler
+   declaration.  */
+# include <stdlib.h>
+
+# if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
+
+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)
+{
+}
+
+# else
 
 /* 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
+#  if defined _MSC_VER
 
 static void cdecl
 gl_msvc_invalid_parameter_handler (const wchar_t *expression,
@@ -38,7 +56,7 @@
   RaiseException (STATUS_GNULIB_INVALID_PARAMETER, 0, 0, NULL);
 }
 
-# else
+#  else
 
 /* An index to thread-local storage.  */
 static DWORD tls_index;
@@ -93,6 +111,8 @@
     RaiseException (STATUS_GNULIB_INVALID_PARAMETER, 0, 0, NULL);
 }
 
+#  endif
+
 # endif
 
 static int gl_msvc_inval_initialized /* = 0 */;
-- 
In memoriam Safia Ahmed-jan <http://en.wikipedia.org/wiki/Safia_Ahmed-jan>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]