re PR c++/13684 (local static object variable constructed once but ctors and dtors...
authorJason Merrill <jason@redhat.com>
Sat, 28 Aug 2004 02:33:54 +0000 (22:33 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Sat, 28 Aug 2004 02:33:54 +0000 (22:33 -0400)
        PR c++/13684
        * cp/decl.c (expand_static_init): Use thread-safety API.
        (register_dtor_fn): Return the call, don't expand it.
        * cp/tree.c (add_stmt_to_compound): New fn.
        (stabilize_call): Use it.
        * gimplify.c (gimplify_cleanup_point_expr): Handle CLEANUP_EH_ONLY.
        (gimple_push_cleanup): Add eh_only parm.
        (gimplify_target_expr): Pass it.
        * c.opt (-fno-threadsafe-statics): New option.
        * c-opts.c (c_common_handle_option): Handle it.
        * c-common.h (flag_threadsafe_statics): Declare it.
        * c-common.c (flag_threadsafe_statics): Record it.
        * doc/invoke.texi: Document it.
        * tsystem.h (_GNU_SOURCE): Define.
        * gthr-posix.h (__gthread_recursive_mutex_t): New typedef.
        (__GTHREAD_RECURSIVE_MUTEX_INIT): New macro.
        (__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION): New macro.
        (__gthread_recursive_mutex_init_function): New fn.
        (__gthread_recursive_mutex_lock): New fn.
        (__gthread_recursive_mutex_trylock): New fn.
        (__gthread_recursive_mutex_unlock): New fn.
        * gthr-solaris.h, gthr-single.h, gthr-dce.h: Likewise.
        * gthr-win32.h, gthr-vxworks.h: Likewise.
        * gthr.h: Document.

        * libsupc++/guard.cc (static_mutex): Internal class implementing a
        recursive mutex which controls initialization of local statics.
        (__gnu_cxx::recursive_init): New exception class.
        (__cxa_guard_acquire): Deal with locking and recursion detection.
        (acquire_1, __cxa_guard_abort, __cxa_guard_release): Likewise.

From-SVN: r86687

23 files changed:
gcc/ChangeLog
gcc/c-common.c
gcc/c-common.h
gcc/c-opts.c
gcc/c.opt
gcc/config/i386/gthr-win32.c
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/tree.c
gcc/doc/invoke.texi
gcc/gimplify.c
gcc/gthr-dce.h
gcc/gthr-posix.h
gcc/gthr-single.h
gcc/gthr-solaris.h
gcc/gthr-vxworks.h
gcc/gthr-win32.h
gcc/gthr.h
gcc/tsystem.h
libstdc++-v3/ChangeLog
libstdc++-v3/libsupc++/guard.cc

index 54d4c5f1a25f8ede00e7377e23debae67b951d9d..74a6b13be1663eb0a22793c7956adf64feabb589 100644 (file)
@@ -1,3 +1,26 @@
+2004-08-27  Jason Merrill  <jason@redhat.com>
+
+       PR c++/13684
+       * gimplify.c (gimplify_cleanup_point_expr): Handle CLEANUP_EH_ONLY.
+       (gimple_push_cleanup): Add eh_only parm.
+       (gimplify_target_expr): Pass it.
+       * c.opt (-fno-threadsafe-statics): New option.
+       * c-opts.c (c_common_handle_option): Handle it.
+       * c-common.h (flag_threadsafe_statics): Declare it.
+       * c-common.c (flag_threadsafe_statics): Record it.
+       * doc/invoke.texi: Document it.
+       * tsystem.h (_GNU_SOURCE): Define.
+       * gthr-posix.h (__gthread_recursive_mutex_t): New typedef.
+       (__GTHREAD_RECURSIVE_MUTEX_INIT): New macro.
+       (__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION): New macro.
+       (__gthread_recursive_mutex_init_function): New fn.
+       (__gthread_recursive_mutex_lock): New fn.
+       (__gthread_recursive_mutex_trylock): New fn.
+       (__gthread_recursive_mutex_unlock): New fn.
+       * gthr-solaris.h, gthr-single.h, gthr-dce.h: Likewise.
+       * gthr-win32.h, gthr-vxworks.h: Likewise.
+       * gthr.h: Document.
+
 2004-08-27  David Edelsohn  <edelsohn@gnu.org>
 
        * config/rs6000/rs6000.c (rs6000_override_options): Increase
index 03531d3ca035787bbfc96f8b8dc2a861eb00e640..bd8ea6c32c383fd0b67255944186d6f24c57c6da 100644 (file)
@@ -459,6 +459,11 @@ int flag_permissive;
 
 int flag_enforce_eh_specs = 1;
 
+/* Nonzero means to generate thread-safe code for initializing local
+   statics.  */
+
+int flag_threadsafe_statics = 1;
+
 /* Nonzero means warn about implicit declarations.  */
 
 int warn_implicit = 1;
index e9a91d50fdd7ec57134a2c7e8e6f358f550ea885..b2da85d8188540a3eebbe4e97acab6f42baa0c8d 100644 (file)
@@ -571,6 +571,11 @@ extern int flag_permissive;
 
 extern int flag_enforce_eh_specs;
 
+/* Nonzero (the default) means to generate thread-safe code for
+   initializing local statics.  */
+
+extern int flag_threadsafe_statics;
+
 /* Nonzero means warn about implicit declarations.  */
 
 extern int warn_implicit;
index 92a6c7273a36cc51a22d3eb4390d3cbd098d365e..31bbb48d9b05feda9d870ec2de0d97fbc254c899 100644 (file)
@@ -773,6 +773,10 @@ c_common_handle_option (size_t scode, const char *arg, int value)
       flag_weak = value;
       break;
 
+    case OPT_fthreadsafe_statics:
+      flag_threadsafe_statics = value;
+      break;
+
     case OPT_fzero_link:
       flag_zero_link = value;
       break;
index b84c5ba84081be4eaf77c8841bd84a517d40451b..c93fe7e895a538716a6e82baa17d322d17ce71d7 100644 (file)
--- a/gcc/c.opt
+++ b/gcc/c.opt
@@ -675,6 +675,10 @@ C++ ObjC++ Joined RejectNegative UInteger
 fthis-is-variable
 C++ ObjC++
 
+fthreadsafe-statics
+C++ ObjC++
+-fno-threadsafe-statics        Do not generate thread-safe code for initializing local statics.
+
 funsigned-bitfields
 C ObjC C++ ObjC++
 When \"signed\" or \"unsigned\" is not given make the bitfield unsigned
index c53369bca50c4c81cf1a3dcf957d06e8a21389cb..6fb3cf8853d53c9ede00aa073be0a617a0aa1e36 100644 (file)
@@ -182,3 +182,73 @@ __gthr_win32_mutex_unlock (__gthread_mutex_t *mutex)
   else
     return 0;
 }
+
+void
+__gthr_win32_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
+{
+  mutex->counter = -1;
+  mutex->depth = 0;
+  mutex->owner = 0;
+  mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL);
+}
+
+int
+__gthr_win32_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  DWORD me = GetCurrentThreadId();
+  if (InterlockedIncrement (&mutex->counter) == 0)
+    {
+      mutex->depth = 1;
+      mutex->owner = me;
+    }
+  else if (mutex->owner == me)
+    {
+      InterlockedDecrement (&mx->lock_idx);
+      ++(mutex->depth);
+    }
+  else if (WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0)
+    {
+      mutex->depth = 1;
+      mutex->owner = me;
+    }
+  else
+    {
+      /* WaitForSingleObject returns WAIT_FAILED, and we can only do
+         some best-effort cleanup here.  */
+      InterlockedDecrement (&mutex->counter);
+      return 1;
+    }
+  return 0;
+}
+
+int
+__gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  DWORD me = GetCurrentThreadId();
+  if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0)
+    {
+      mutex->depth = 1;
+      mutex->owner = me;
+    }
+  else if (mutex->owner == me)
+    ++(mutex->depth);
+  else
+    return 1;
+
+  return 0;
+}
+
+int
+__gthr_win32_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  --(mutex->depth);
+  if (mutex->depth == 0)
+    {
+      mutex->owner = 0;
+
+      if (InterlockedDecrement (&mutex->counter) >= 0)
+       return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1;
+    }
+
+  return 0;
+}
index afd820fb222444e670cf93a18f474dcd40db5452..f1cac07a80369d4ae3d028e79194597aa3d13e46 100644 (file)
@@ -1,3 +1,11 @@
+2004-08-27  Jason Merrill  <jason@redhat.com>
+
+       PR c++/13684
+       * decl.c (expand_static_init): Use thread-safety API.
+       (register_dtor_fn): Return the call, don't expand it.
+       * tree.c (add_stmt_to_compound): New fn.
+       (stabilize_call): Use it.
+
 2004-08-27  Richard Henderson  <rth@redhat.com>
 
        * cp-tree.def (OFFSETOF_EXPR): New.
index 4597b45ff81c3aa169466d13fe5765e6639d11b9..2eba7a316e422ffc7e9ed805bd41f78c90b1483e 100644 (file)
@@ -3801,7 +3801,7 @@ extern tree build_target_expr_with_type         (tree, tree);
 extern int local_variable_p                     (tree);
 extern int nonstatic_local_decl_p               (tree);
 extern tree declare_global_var                  (tree, tree);
-extern void register_dtor_fn                    (tree);
+extern tree register_dtor_fn                    (tree);
 extern tmpl_spec_kind current_tmpl_spec_kind    (int);
 extern tree cp_fname_init                      (const char *, tree *);
 extern tree builtin_function (const char *name, tree type,
@@ -4198,6 +4198,7 @@ extern void lang_check_failed                     (const char *, int,
 extern tree stabilize_expr                     (tree, tree *);
 extern void stabilize_call                     (tree, tree *);
 extern bool stabilize_init                     (tree, tree *);
+extern tree add_stmt_to_compound               (tree, tree);
 extern tree cxx_maybe_build_cleanup            (tree);
 extern void init_tree                          (void);
 extern int pod_type_p                          (tree);
index 95cf0f5e71785ad6d33121aeb9d92466ed51b956..c746e336fbac8cfa59efeaf6733018038d2b5442 100644 (file)
@@ -5081,7 +5081,7 @@ end_cleanup_fn (void)
 /* Generate code to handle the destruction of DECL, an object with
    static storage duration.  */
 
-void
+tree
 register_dtor_fn (tree decl)
 {
   tree cleanup;
@@ -5090,7 +5090,7 @@ register_dtor_fn (tree decl)
   tree fcall;
 
   if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)))
-    return;
+    return void_zero_node;
 
   /* Call build_cleanup before we enter the anonymous function so that
      any access checks will be done relative to the current scope,
@@ -5129,7 +5129,7 @@ register_dtor_fn (tree decl)
     }
   else
     args = tree_cons (NULL_TREE, cleanup, NULL_TREE);
-  finish_expr_stmt (build_function_call (get_atexit_node (), args));
+  return build_function_call (get_atexit_node (), args);
 }
 
 /* DECL is a VAR_DECL with static storage duration.  INIT, if present,
@@ -5151,36 +5151,42 @@ expand_static_init (tree decl, tree init)
   if (DECL_FUNCTION_SCOPE_P (decl))
     {
       /* Emit code to perform this initialization but once.  */
-      tree if_stmt;
-      tree then_clause;
-      tree assignment;
-      tree guard;
-      tree guard_init;
+      tree if_stmt, inner_if_stmt;
+      tree then_clause, inner_then_clause;
+      tree guard, guard_addr, guard_addr_list;
+      tree acquire_fn, release_fn, abort_fn;
+      tree flag, begin;
 
       /* Emit code to perform this initialization but once.  This code
         looks like:
 
-           static int guard = 0;
-           if (!guard) {
-             // Do initialization.
-            guard = 1;
-            // Register variable for destruction at end of program.
+           static <type> guard;
+           if (!guard.first_byte) {
+            if (__cxa_guard_acquire (&guard)) {
+              bool flag = false;
+              try {
+                // Do initialization.
+                flag = true; __cxa_guard_release (&guard);
+                // Register variable for destruction at end of program.
+              } catch {
+                if (!flag) __cxa_guard_abort (&guard);
+              }
           }
 
-        Note that the `temp' variable is only set to 1 *after* the
+        Note that the `flag' variable is only set to 1 *after* the
         initialization is complete.  This ensures that an exception,
         thrown during the construction, will cause the variable to
         reinitialized when we pass through this code again, as per:
 
           [stmt.dcl]
 
-          If the initialization exits by throwing an exception, the
+          If the initialization exits by throwing an exception, the  
           initialization is not complete, so it will be tried again
           the next time control enters the declaration.
 
-         In theory, this process should be thread-safe, too; multiple
-        threads should not be able to initialize the variable more
-        than once.  We don't yet attempt to ensure thread-safety.  */
+         This process should be thread-safe, too; multiple threads
+        should not be able to initialize the variable more than
+        once.  */
 
       /* Create the guard variable.  */
       guard = get_guard (decl);
@@ -5188,29 +5194,68 @@ expand_static_init (tree decl, tree init)
       /* Begin the conditional initialization.  */
       if_stmt = begin_if_stmt ();
       finish_if_stmt_cond (get_guard_cond (guard), if_stmt);
-      then_clause = begin_compound_stmt (0);
-
-      /* Do the initialization itself.  */
-      assignment = init ? init : NULL_TREE;
-
-      /* Once the assignment is complete, set TEMP to 1.  Since the
-        construction of the static object is complete at this point,
-        we want to make sure TEMP is set to 1 even if a temporary
-        constructed during the initialization throws an exception
-        when it is destroyed.  So, we combine the initialization and
-        the assignment to TEMP into a single expression, ensuring
-        that when we call finish_expr_stmt the cleanups will not be
-        run until after TEMP is set to 1.  */
-      guard_init = set_guard (guard);
-      if (assignment)
-       assignment = build_compound_expr (assignment, guard_init);
+      then_clause = begin_compound_stmt (BCS_NO_SCOPE);
+
+      if (flag_threadsafe_statics)
+       {
+         guard_addr = build_address (guard);
+         guard_addr_list = build_tree_list (NULL_TREE, guard_addr);
+
+         acquire_fn = get_identifier ("__cxa_guard_acquire");
+         release_fn = get_identifier ("__cxa_guard_release");
+         abort_fn = get_identifier ("__cxa_guard_abort");
+         if (!get_global_value_if_present (acquire_fn, &acquire_fn))
+           {
+             tree argtypes = tree_cons (NULL_TREE, TREE_TYPE (guard_addr),
+                                        void_list_node);
+             tree vfntype = build_function_type (void_type_node, argtypes);
+             acquire_fn = push_library_fn
+               (acquire_fn, build_function_type (integer_type_node, argtypes));
+             release_fn = push_library_fn (release_fn, vfntype);
+             abort_fn = push_library_fn (abort_fn, vfntype);
+           }
+         else
+           {
+             release_fn = identifier_global_value (release_fn);
+             abort_fn = identifier_global_value (abort_fn);
+           }
+
+         inner_if_stmt = begin_if_stmt ();
+         finish_if_stmt_cond (build_call (acquire_fn, guard_addr_list),
+                              inner_if_stmt);
+
+         inner_then_clause = begin_compound_stmt (BCS_NO_SCOPE);
+         begin = get_target_expr (boolean_false_node);
+         flag = TARGET_EXPR_SLOT (begin);
+
+         TARGET_EXPR_CLEANUP (begin)
+           = build (COND_EXPR, void_type_node, flag,
+                    void_zero_node,
+                    build_call (abort_fn, guard_addr_list));
+         CLEANUP_EH_ONLY (begin) = 1;
+
+         /* Do the initialization itself.  */
+         init = add_stmt_to_compound (begin, init);
+         init = add_stmt_to_compound
+           (init, build (MODIFY_EXPR, void_type_node, flag, boolean_true_node));
+         init = add_stmt_to_compound
+           (init, build_call (release_fn, guard_addr_list));
+       }
       else
-       assignment = guard_init;
-      finish_expr_stmt (assignment);
+       init = add_stmt_to_compound (init, set_guard (guard));
 
       /* Use atexit to register a function for destroying this static
         variable.  */
-      register_dtor_fn (decl);
+      init = add_stmt_to_compound (init, register_dtor_fn (decl));
+
+      finish_expr_stmt (init);
+
+      if (flag_threadsafe_statics)
+       {
+         finish_compound_stmt (inner_then_clause);
+         finish_then_clause (inner_if_stmt);
+         finish_if_stmt (inner_if_stmt);
+       }
 
       finish_compound_stmt (then_clause);
       finish_then_clause (if_stmt);
index 5da240f6075cf7b8f4a0f179addee479e20e47e2..2d852f71481002da1a30707200ac919b12bc3dfe 100644 (file)
@@ -2446,7 +2446,7 @@ do_static_initialization (tree decl, tree init)
   /* If we're using __cxa_atexit, register a a function that calls the
      destructor for the object.  */
   if (flag_use_cxa_atexit)
-    register_dtor_fn (decl);
+    finish_expr_stmt (register_dtor_fn (decl));
 
   /* Finish up.  */
   finish_static_initialization_or_destruction (guard_if_stmt);
index 4bdadad690d9b3595dbc1d55f08dca72d8c1cebd..99334678a7c252ad44aa207742d485480f751189 100644 (file)
@@ -2254,6 +2254,19 @@ stabilize_expr (tree exp, tree* initp)
   return exp;
 }
 
+/* Add NEW, an expression whose value we don't care about, after the
+   similar expression ORIG.  */
+
+tree
+add_stmt_to_compound (tree orig, tree new)
+{
+  if (!new || !TREE_SIDE_EFFECTS (new))
+    return orig;
+  if (!orig || !TREE_SIDE_EFFECTS (orig))
+    return new;
+  return build2 (COMPOUND_EXPR, void_type_node, orig, new);
+}
+
 /* Like stabilize_expr, but for a call whose args we want to
    pre-evaluate.  */
 
@@ -2275,12 +2288,7 @@ stabilize_call (tree call, tree *initp)
       {
        tree init;
        TREE_VALUE (t) = stabilize_expr (TREE_VALUE (t), &init);
-       if (!init)
-         /* Nothing.  */;
-       else if (inits)
-         inits = build2 (COMPOUND_EXPR, void_type_node, inits, init);
-       else
-         inits = init;
+       inits = add_stmt_to_compound (inits, init);
       }
 
   *initp = inits;
index 25acc613c8e8f3c6bd2a31229b6e4d85dc524e2c..e5e176f4dfda9be42df5af37cd7ef949b54d625f 100644 (file)
@@ -182,7 +182,7 @@ in the following sections.
 -fno-nonansi-builtins  -fno-operator-names @gol
 -fno-optional-diags  -fpermissive @gol
 -frepo  -fno-rtti  -fstats  -ftemplate-depth-@var{n} @gol
--fuse-cxa-atexit  -fno-weak  -nostdinc++ @gol
+-fno-threadsafe-statics -fuse-cxa-atexit  -fno-weak  -nostdinc++ @gol
 -fno-default-inline  -fvisibility-inlines-hidden @gol
 -Wabi  -Wctor-dtor-privacy @gol
 -Wnon-virtual-dtor  -Wreorder @gol
@@ -1470,6 +1470,13 @@ A limit on the template instantiation depth is needed to detect
 endless recursions during template class instantiation.  ANSI/ISO C++
 conforming programs must not rely on a maximum depth greater than 17.
 
+@item -fno-threadsafe-statics
+@opindex fno-threadsafe-statics
+Do not emit the extra code to use the routines specified in the C++
+ABI for thread-safe initialization of local statics.  You can use this
+option to reduce code size slightly in code that doesn't need to be
+thread-safe.
+
 @item -fuse-cxa-atexit
 @opindex fuse-cxa-atexit
 Register destructors for objects with static storage duration with the
index 323df36177339b7e94665dce0e1a16847ccb546a..2de6cf6e091ee9defa7e23918f28e80f7b2ad36c 100644 (file)
@@ -3271,9 +3271,15 @@ gimplify_cleanup_point_expr (tree *expr_p, tree *pre_p)
          else
            {
              tree sl, tfe;
+             enum tree_code code;
+
+             if (CLEANUP_EH_ONLY (wce))
+               code = TRY_CATCH_EXPR;
+             else
+               code = TRY_FINALLY_EXPR;
 
              sl = tsi_split_statement_list_after (&iter);
-             tfe = build (TRY_FINALLY_EXPR, void_type_node, sl, NULL_TREE);
+             tfe = build (code, void_type_node, sl, NULL_TREE);
              append_to_statement_list (TREE_OPERAND (wce, 0),
                                        &TREE_OPERAND (tfe, 1));
              *wce_p = tfe;
@@ -3301,7 +3307,7 @@ gimplify_cleanup_point_expr (tree *expr_p, tree *pre_p)
    is the cleanup action required.  */
 
 static void
-gimple_push_cleanup (tree var, tree cleanup, tree *pre_p)
+gimple_push_cleanup (tree var, tree cleanup, bool eh_only, tree *pre_p)
 {
   tree wce;
 
@@ -3352,6 +3358,7 @@ gimple_push_cleanup (tree var, tree cleanup, tree *pre_p)
   else
     {
       wce = build (WITH_CLEANUP_EXPR, void_type_node, cleanup);
+      CLEANUP_EH_ONLY (wce) = eh_only;
       append_to_statement_list (wce, pre_p);
     }
 
@@ -3399,7 +3406,8 @@ gimplify_target_expr (tree *expr_p, tree *pre_p, tree *post_p)
       if (TARGET_EXPR_CLEANUP (targ))
        {
          gimplify_stmt (&TARGET_EXPR_CLEANUP (targ));
-         gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ), pre_p);
+         gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ),
+                              CLEANUP_EH_ONLY (targ), pre_p);
        }
 
       /* Only expand this once.  */
index 563754ea4401660c3aec40a8315c45c8cb56b99a..ccdf82dead8f3ef4cb9f8b73e28c3ed07a9c044b 100644 (file)
@@ -1,4 +1,4 @@
-
+/* Threads compatibility routines for libgcc2 and libobjc.  */
 /* Compile this one with gcc.  */
 /* Copyright (C) 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
 
@@ -52,10 +52,12 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 typedef pthread_key_t __gthread_key_t;
 typedef pthread_once_t __gthread_once_t;
 typedef pthread_mutex_t __gthread_mutex_t;
+typedef pthread_mutex_t __gthread_recursive_mutex_t;
 
 #define __GTHREAD_ONCE_INIT pthread_once_init
 
 #define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
+#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
 
 #define __GTHREAD_MUTEX_INIT_DEFAULT pthread_once_init
 
@@ -481,6 +483,43 @@ __gthread_mutex_unlock (__gthread_mutex_t *mutex)
     return 0;
 }
 
+static inline int
+__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    {
+      pthread_mutexattr_t attr;
+      int r;
+
+      r = pthread_mutexattr_create (&attr);
+      if (!r)
+       r = pthread_mutexattr_setkind_np (&attr, MUTEX_RECURSIVE_NP);
+      if (!r)
+       r = pthread_mutex_init (mutex, attr);
+      if (!r)
+       r = pthread_mutexattr_delete (&attr);
+      return r;
+    }
+}
+
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_lock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_trylock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_unlock (mutex);
+}
+
 #endif /* _LIBOBJC */
 
 #undef UNUSED
index e2b5a5739990ccc6cb2fc1569088b87702170230..91204104658c3c5ab764cf3cee7a9094387f90ce 100644 (file)
@@ -46,9 +46,17 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 typedef pthread_key_t __gthread_key_t;
 typedef pthread_once_t __gthread_once_t;
 typedef pthread_mutex_t __gthread_mutex_t;
+typedef pthread_mutex_t __gthread_recursive_mutex_t;
 
 #define __GTHREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
 #define __GTHREAD_ONCE_INIT PTHREAD_ONCE_INIT
+#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
+#define __GTHREAD_RECURSIVE_MUTEX_INIT PTHREAD_RECURSIVE_MUTEX_INITIALIZER
+#elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
+#define __GTHREAD_RECURSIVE_MUTEX_INIT PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+#else
+#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
+#endif
 
 #if SUPPORTS_WEAK && GTHREAD_USE_WEAK
 
@@ -516,6 +524,45 @@ __gthread_mutex_unlock (__gthread_mutex_t *mutex)
     return 0;
 }
 
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+static inline int
+__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    {
+      pthread_mutexattr_t attr;
+      int r;
+
+      r = pthread_mutexattr_init (&attr);
+      if (!r)
+       r = pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
+      if (!r)
+       r = pthread_mutex_init (mutex, &attr);
+      if (!r)
+       r = pthread_mutexattr_destroy (&attr);
+      return r;
+    }
+}
+#endif
+
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_lock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_trylock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_unlock (mutex);
+}
+
 #endif /* _LIBOBJC */
 
 #endif /* ! GCC_GTHR_POSIX_H */
index 0bb4682615dbe8b6ca835411b0d959e263ae70ca..d42ee15becc2bb4752f9a4c425af8cf3fe95e926 100644 (file)
@@ -32,6 +32,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 /* Just provide compatibility for mutex handling.  */
 
 typedef int __gthread_mutex_t;
+typedef int __gthread_recursive_mutex_t;
 
 #define __GTHREAD_MUTEX_INIT 0
 
@@ -232,6 +233,24 @@ __gthread_mutex_unlock (__gthread_mutex_t * UNUSED(mutex))
   return 0;
 }
 
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_lock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_trylock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_unlock (mutex);
+}
+
 #endif /* _LIBOBJC */
 
 #undef UNUSED
index 33ddf7a363caf51436d91a844b41cf748d53df69..b44a4de26bf52dc011fb7f78a379227e49a7f60b 100644 (file)
@@ -44,9 +44,11 @@ typedef struct {
   int once;
 } __gthread_once_t;
 typedef mutex_t __gthread_mutex_t;
+typedef mutex_t __gthread_recursive_mutex_t;
 
 #define __GTHREAD_ONCE_INIT { DEFAULTMUTEX, 0 }
 #define __GTHREAD_MUTEX_INIT DEFAULTMUTEX
+#define __GTHREAD_RECURSIVE_MUTEX_INIT RECURSIVE_ERRORCHECKMUTEX
 
 #if SUPPORTS_WEAK && GTHREAD_USE_WEAK
 
@@ -466,6 +468,24 @@ __gthread_mutex_unlock (__gthread_mutex_t *mutex)
     return 0;
 }
 
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_lock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_trylock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_unlock (mutex);
+}
+
 #endif /* _LIBOBJC */
 
 #endif /* ! GCC_GTHR_SOLARIS_H */
index 4fb3b09404aa1894715eeda29b9db103533a4946..0f3ad23412ad6c81e8cad6fc3859c55883c38e9d 100644 (file)
@@ -45,7 +45,10 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include <semLib.h>
 
 typedef SEM_ID __gthread_mutex_t;
+/* All VxWorks mutexes are recursive.  */
+typedef SEM_ID __gthread_recursive_mutex_t;
 #define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
+#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
 
 static inline void
 __gthread_mutex_init_function (__gthread_mutex_t *mutex)
@@ -71,6 +74,30 @@ __gthread_mutex_unlock (__gthread_mutex_t *mutex)
   return semGive (*mutex);
 }
 
+static inline void
+__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
+{
+  __gthread_mutex_init_function (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_lock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_trylock (mutex);
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  return __gthread_mutex_unlock (mutex);
+}
+
 /* pthread_once is complicated enough that it's implemented
    out-of-line.  See config/vxlib.c.  */
 
index b7618048430107b85106efdc428797f9d8f84f0f..3e1aac7fce093930e3ad64dc36e8c47caeb25b12 100644 (file)
@@ -343,9 +343,18 @@ typedef struct {
   void *sema;
 } __gthread_mutex_t;
 
+typedef struct {
+  long counter;
+  long depth;
+  DWORD owner;
+  void *sema;
+} __gthread_recursive_mutex_t;
+
 #define __GTHREAD_ONCE_INIT {0, -1}
 #define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
 #define __GTHREAD_MUTEX_INIT_DEFAULT {-1, 0}
+#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
+#define __GTHREAD_RECURSIVE_MUTEX_INIT_DEFAULT {-1, 0}
 
 #if __MINGW32_MAJOR_VERSION >= 1 || \
   (__MINGW32_MAJOR_VERSION == 0 && __MINGW32_MINOR_VERSION > 2)
@@ -472,6 +481,33 @@ __gthread_mutex_unlock (__gthread_mutex_t *mutex)
     return 0;
 }
 
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    return __gthr_win32_recursive_mutex_lock (mutex);
+  else
+    return 0;
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    return __gthr_win32_recursive_mutex_trylock (mutex);
+  else
+    return 0;
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    return __gthr_win32_recursive_mutex_unlock (mutex);
+  else
+    return 0;
+}
+
 #else /* ! __GTHREAD_HIDE_WIN32API */
 
 #include <windows.h>
@@ -610,6 +646,83 @@ __gthread_mutex_unlock (__gthread_mutex_t *mutex)
   return 0;
 }
 
+static inline void
+__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
+{
+  mutex->counter = -1;
+  mutex->depth = 0;
+  mutex->owner = 0;
+  mutex->sema = CreateSemaphore (NULL, 0, 65535, NULL);
+}
+
+static inline int
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    {
+      DWORD me = GetCurrentThreadId();
+      if (InterlockedIncrement (&mutex->counter) == 0)
+       {
+         mutex->depth = 1;
+         mutex->owner = me;
+       }
+      else if (mutex->owner == me)
+       {
+         InterlockedDecrement (&mx->lock_idx);
+         ++(mutex->depth);
+       }
+      else if (WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0)
+       {
+         mutex->depth = 1;
+         mutex->owner = me;
+       }
+      else
+       {
+         /* WaitForSingleObject returns WAIT_FAILED, and we can only do
+            some best-effort cleanup here.  */
+         InterlockedDecrement (&mutex->counter);
+         return 1;
+       }
+    }
+  return 0;
+}
+
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    {
+      DWORD me = GetCurrentThreadId();
+      if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0)
+       {
+         mutex->depth = 1;
+         mutex->owner = me;
+       }
+      else if (mutex->owner == me)
+       ++(mutex->depth);
+      else
+       return 1;
+    }
+  return 0;
+}
+
+static inline int
+__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
+{
+  if (__gthread_active_p ())
+    {
+      --(mutex->depth);
+      if (mutex->depth == 0)
+       {
+         mutex->owner = 0;
+
+         if (InterlockedDecrement (&mutex->counter) >= 0)
+           return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1;
+       }
+    }
+  return 0;
+}
+
 #endif /*  __GTHREAD_HIDE_WIN32API */
 
 #ifdef __cplusplus
index 22b2ecd0ca4413c3e5fc6c9c873c5cda52831ad8..42f4d1a4d3ad10d35d425d752a1aae5ef89c15c1 100644 (file)
@@ -42,6 +42,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
      __gthread_key_t
      __gthread_once_t
      __gthread_mutex_t
+     __gthread_recursive_mutex_t
 
    The threads interface must define the following macros:
 
@@ -56,6 +57,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
                function which looks like this:
                  void __GTHREAD_MUTEX_INIT_FUNCTION (__gthread_mutex_t *)
                Don't define __GTHREAD_MUTEX_INIT in this case
+     __GTHREAD_RECURSIVE_MUTEX_INIT
+     __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION
+               as above, but for a recursive mutex.
 
    The threads interface must define the following static functions:
 
@@ -71,6 +75,10 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
      int __gthread_mutex_trylock (__gthread_mutex_t *mutex);
      int __gthread_mutex_unlock (__gthread_mutex_t *mutex);
 
+     int __gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex);
+     int __gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex);
+     int __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex);
+
    All functions returning int should return zero on success or the error
    number.  If the operation is not supported, -1 is returned.
 
index f24bab78cc5e4258fc8784ddafe5d5f227dee500..d7f1ebc9a5ccc063d8f7305808da4b1d1f350147 100644 (file)
@@ -40,6 +40,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #define HAVE_DECL_GETOPT 1
 #endif
 
+/* We want everything from the glibc headers.  */
+#define _GNU_SOURCE 1
+
 /* GCC supplies these headers.  */
 #include <stddef.h>
 #include <float.h>
index d96a583e89ad93ddb8da9c90468fe8997114b31f..c77647b7e6a9b6b0e1f856c152aaf6a4b10a4a61 100644 (file)
@@ -1,3 +1,12 @@
+2004-08-27  Jason Merrill  <jason@redhat.com>
+
+       PR c++/13684
+       * libsupc++/guard.cc (static_mutex): Internal class implementing a
+       recursive mutex which controls initialization of local statics.
+       (__gnu_cxx::recursive_init): New exception class.
+       (__cxa_guard_acquire): Deal with locking and recursion detection.
+       (acquire_1, __cxa_guard_abort, __cxa_guard_release): Likewise.
+
 2004-08-27  Matthias Klose  <doko@debian.org>
 
        * configure.host: For mips*-*-linux* update cpu_include_dir
index 255108f5f7e8163203250c593d53405166b4886b..a9280bccdc54c548ef915b2aefe7e8f11346b6c7 100644 (file)
 // Written by Mark Mitchell, CodeSourcery LLC, <mark@codesourcery.com>
 
 #include <cxxabi.h>
+#include <exception>
+#include <bits/c++config.h>
+#include <bits/gthr.h>
 
 // The IA64/generic ABI uses the first byte of the guard variable.
 // The ARM EABI uses the least significant bit.
 
+// Thread-safe static local initialization support.
+#ifdef __GTHREADS
+namespace
+{
+  // static_mutex is a single mutex controlling all static initializations.
+  // This is a static class--the need for a static initialization function
+  // to pass to __gthread_once precludes creating multiple instances, though
+  // I suppose you could achieve the same effect with a template.
+  class static_mutex
+  {
+    static __gthread_recursive_mutex_t mutex;
+
+#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION
+    static void init();
+#endif
+
+  public:
+    static void lock();
+    static void unlock();
+  };
+
+  __gthread_recursive_mutex_t static_mutex::mutex
+#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT
+  = __GTHREAD_RECURSIVE_MUTEX_INIT
+#endif
+  ;
+
+#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION
+  void static_mutex::init()
+  {
+    __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION (&mutex);
+  }
+#endif
+
+  void static_mutex::lock()
+  {
+#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION
+    static __gthread_once_t once = __GTHREAD_ONCE_INIT;
+    __gthread_once (&once, init);
+#endif
+    __gthread_recursive_mutex_lock (&mutex);
+  }
+
+  void static_mutex::unlock ()
+  {
+    __gthread_recursive_mutex_unlock (&mutex);
+  }
+}
+#endif
+
+namespace __gnu_cxx
+{
+  // 6.7[stmt.dcl]/4: If control re-enters the declaration (recursively)
+  // while the object is being initialized, the behavior is undefined.
+
+  // Since we already have a library function to handle locking, we might
+  // as well check for this situation and throw an exception.
+  // We use the second byte of the guard variable to remember that we're
+  // in the middle of an initialization.
+  class recursive_init: public std::exception
+  {
+  public:
+    recursive_init() throw() { }
+    virtual ~recursive_init() throw ();
+  };
+
+  recursive_init::~recursive_init() throw() { }
+}
+
 namespace __cxxabiv1 
 {
+  static int
+  acquire_1 (__guard *g)
+  {
+    if (_GLIBCXX_GUARD_ACQUIRE (g))
+      {
+       if (((char *)g)[1]++)
+         {
+#ifdef __EXCEPTIONS
+           throw __gnu_cxx::recursive_init();
+#else
+           abort ();
+#endif
+         }
+       return 1;
+      }
+    return 0;
+  }
+  
   extern "C"
   int __cxa_guard_acquire (__guard *g) 
   {
-    return _GLIBCXX_GUARD_ACQUIRE (g);
+#ifdef __GTHREADS
+    if (__gthread_active_p ())
+      {
+       // Simple wrapper for exception safety.
+       struct mutex_wrapper
+       {
+         bool unlock;
+         mutex_wrapper (): unlock(true)
+         {
+           static_mutex::lock ();
+         }
+         ~mutex_wrapper ()
+         {
+           if (unlock)
+             static_mutex::unlock ();
+         }
+       } mw;
+
+       if (acquire_1 (g))
+         {
+           mw.unlock = false;
+           return 1;
+         }
+
+       return 0;
+      }
+#endif
+
+    return acquire_1 (g);
   }
 
   extern "C"
-  void __cxa_guard_release (__guard *g)
+  void __cxa_guard_abort (__guard *g)
   {
-    _GLIBCXX_GUARD_RELEASE (g);
+    ((char *)g)[1]--;
+#ifdef __GTHREADS
+    if (__gthread_active_p ())
+      static_mutex::unlock ();
+#endif
   }
 
   extern "C"
-  void __cxa_guard_abort (__guard *)
+  void __cxa_guard_release (__guard *g)
   {
+    ((char *)g)[1]--;
+    _GLIBCXX_GUARD_RELEASE (g);
+#ifdef __GTHREADS
+    if (__gthread_active_p ())
+      static_mutex::unlock ();
+#endif
   }
 }