This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Destruction of globals
- To: egcs-patches at cygnus dot com
- Subject: Destruction of globals
- From: Martin von Loewis <martin at mira dot isdn dot cs dot tu-berlin dot de>
- Date: Mon, 31 Aug 1998 10:04:01 +0200
This patch fixes a number of problems with the destruction of global
and block-static objects:
- If initialization of a block-local fails with an exception,
initialization is not retried
- Objects are not always destroyed in reverse order of construction
- Destruction order does not correctly interleave with atexit
- In a multi-threaded environment, block-statics may be initialized
twice, or threads may access uninitialized block-statics.
To do so, a number of interfaces to libgcc2 is introduced:
struct __dtorlist{
void (*dtor)();
struct __dtorlist *next;
};
/* Register a most-recent destructor. If entry is null, do so using
atexit. */
void __cp_register_dtor (void (*dtor)(), struct __dtorlist *entry);
/* See whether the variable associated with p needs initialization.
Returns 0 if no initialization is needed. If initialization is in
progress in another thread, block until initialization is
complete. */
int __cp_init_begin (int * p);
/* Initialization failed with an exception. Unblock waiting
threads, restore *p to 'uninitialized' */
void __cp_init_failed (int* p);
/* Initialization succeeded. Record that fact in *p,
register the destructor, and release waiting threads. */
void __cp_init_complete (int *p, void (*dtor)(),
struct __dtorlist *entry);
To the user, the functionality is exposed with three command line
flags:
-fdtors-static is the current behaviour, and the default.
-fdtors-dynamic builds lists of destructors in reverse order.
atexit interleaving is not guaranteed, but it does not require
dynamic memory allocation, and it does not suffer from atexit
limitations. Potentially, it also gets the dlclose issue right.
This is implicitly enabled with -fnew-abi.
-fdtors-atexit uses atexit to register the destructor. If the C
library supports early execution or unregistration of atexit
functions, such a facility can be used.
This is implicitly enabled with -ansi.
There is a number of limitations in this implementation:
- The issues with shared images are not all resolved, yet. In
particular, libgcc2 maintains a list of dtors *per shared object*
in the dynamic case, whereas the list should be program-global.
Unregistration of specific dtors is already possible.
- If there was a global list, it is not clear whether it should
be protected against multiple threads that dlopen shared images
simultaneously.
- The interfaces proposed need review.
- Optimizations with respect to the code size are still possible.
For example, when the constructor doesn't throw exceptions,
the init_failed part is not needed, which would also remove.
The change is included below, please comment.
Martin
1998-08-31 Martin von Löwis <loewis@informatik.hu-berlin.de>
* cp-tree.h (dtor_ordering): New enumeration.
* decl.c (expand_static_init): Call expand_static_init_dynamic for
dynamic and atexit ordering.
* decl2.c (flag_dtors_ordering): New flag variable.
(lang_decode_option): Initialize it.
(do_dtors): Do nothing for dynamic and atexit ordering.
(do_ctors): Instead, call do_dynamic_dtor.
* init.c (dtorlist_type_node, dtorlist_ptr_node,
null_dtorlist_node): New static variables.
(init_init_processing): Initialize them.
(expand_static_init_dynamic, do_dynamic_dtor): New functions.
* lang_options.h (-fdtors-static, -fdtors-dynamic,
-fdtors-atexit): New flags.
* initialize.c: New file.
* Make-lang.in: Compile it into libgcc2.
Index: Make-lang.in
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/Make-lang.in,v
retrieving revision 1.17
diff -c -p -r1.17 Make-lang.in
*** Make-lang.in 1998/08/17 11:42:20 1.17
--- Make-lang.in 1998/08/31 07:24:06
*************** CXX_EXTRA_HEADERS = $(srcdir)/cp/inc/typ
*** 61,70 ****
# Extra code to include in libgcc2.
CXX_LIB2FUNCS = tinfo.o tinfo2.o new.o opnew.o opnewnt.o opvnew.o opvnewnt.o \
! opdel.o opdelnt.o opvdel.o opvdelnt.o exception.o
CXX_LIB2SRCS = $(srcdir)/cp/new.cc $(srcdir)/cp/new1.cc $(srcdir)/cp/new2.cc \
$(srcdir)/cp/exception.cc $(srcdir)/cp/tinfo.cc \
! $(srcdir)/cp/tinfo2.cc $(srcdir)/cp/tinfo.h
#
# Define the names for selecting c++ in LANGUAGES.
# Note that it would be nice to move the dependency on g++
--- 61,70 ----
# Extra code to include in libgcc2.
CXX_LIB2FUNCS = tinfo.o tinfo2.o new.o opnew.o opnewnt.o opvnew.o opvnewnt.o \
! opdel.o opdelnt.o opvdel.o opvdelnt.o exception.o initialize.o
CXX_LIB2SRCS = $(srcdir)/cp/new.cc $(srcdir)/cp/new1.cc $(srcdir)/cp/new2.cc \
$(srcdir)/cp/exception.cc $(srcdir)/cp/tinfo.cc \
! $(srcdir)/cp/tinfo2.cc $(srcdir)/cp/tinfo.h $(srcdir)/cp/initialize.c
#
# Define the names for selecting c++ in LANGUAGES.
# Note that it would be nice to move the dependency on g++
*************** tinfo2.o: cc1plus$(exeext) $(srcdir)/cp/
*** 148,153 ****
--- 148,156 ----
exception.o: cc1plus$(exeext) $(srcdir)/cp/exception.cc
$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) $(INCLUDES) \
-c -fexceptions $(srcdir)/cp/exception.cc
+ initialize.o: cc1$(exeext) $(srcdir)/cp/initialize.c
+ $(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) $(INCLUDES) \
+ -c $(srcdir)/cp/initialize.c
new.o: cc1plus$(exeext) $(srcdir)/cp/new.cc
$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) $(INCLUDES) \
-c $(srcdir)/cp/new.cc
Index: cp-tree.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.126
diff -c -p -r1.126 cp-tree.h
*** cp-tree.h 1998/08/28 16:11:31 1.126
--- cp-tree.h 1998/08/31 07:24:11
*************** extern int flag_gnu_binutils;
*** 325,330 ****
--- 326,336 ----
extern int flag_no_ident;
+ /* Three different ways of ordering destructors. */
+
+ enum dtor_ordering {dtors_static, dtors_dynamic, dtors_atexit};
+ enum dtor_ordering flag_dtors_ordering;
+
/* Nonzero means warn about implicit declarations. */
extern int warn_implicit;
*************** extern tree build_x_delete PROTO((tree
*** 2700,2705 ****
--- 2706,2713 ----
extern tree build_delete PROTO((tree, tree, tree, int, int));
extern tree build_vbase_delete PROTO((tree, tree));
extern tree build_vec_delete PROTO((tree, tree, tree, tree, int));
+ extern void expand_static_init_dynamic PROTO((tree, tree));
+ extern void do_dynamic_dtor PROTO((tree));
/* in input.c */
Index: decl.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/decl.c,v
retrieving revision 1.199
diff -c -p -r1.199 decl.c
*** decl.c 1998/08/30 11:46:42 1.199
--- decl.c 1998/08/31 07:24:25
*************** expand_static_init (decl, init)
*** 7629,7635 ****
if (TREE_PURPOSE (oldstatic) && init != NULL_TREE)
cp_error ("multiple initializations given for `%D'", decl);
}
! else if (! toplevel_bindings_p () && ! pseudo_global_level_p ())
{
/* Emit code to perform this initialization but once. */
tree temp;
--- 7631,7638 ----
if (TREE_PURPOSE (oldstatic) && init != NULL_TREE)
cp_error ("multiple initializations given for `%D'", decl);
}
! else if (! toplevel_bindings_p () && ! pseudo_global_level_p ()
! && flag_dtors_ordering == dtors_static)
{
/* Emit code to perform this initialization but once. */
tree temp;
*************** expand_static_init (decl, init)
*** 7702,7707 ****
--- 7705,7712 ----
/* Resume old (possibly temporary) allocation. */
pop_obstacks ();
}
+ else if (! toplevel_bindings_p () && ! pseudo_global_level_p ())
+ expand_static_init_dynamic (decl,init);
else
{
/* This code takes into account memory allocation
Index: decl2.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/decl2.c,v
retrieving revision 1.123
diff -c -p -r1.123 decl2.c
*** decl2.c 1998/08/28 16:11:33 1.123
--- decl2.c 1998/08/31 07:24:30
*************** int flag_init_priority = 1;
*** 181,186 ****
--- 181,190 ----
int flag_init_priority;
#endif
+ /* Define destructor ordering. */
+
+ enum dtor_ordering flag_dtors_ordering = dtors_dynamic;
+
/* Nonzero means warn about implicit declarations. */
int warn_implicit = 1;
*************** lang_decode_option (argc, argv)
*** 597,614 ****
--- 601,626 ----
error ("-fno-ansi-overloading is no longer supported");
found = 1;
}
+ else if (!strcmp (p, "dtors-static"))
+ flag_dtors_ordering = dtors_static;
+ else if (!strcmp (p, "dtors-dynamic"))
+ flag_dtors_ordering = dtors_dynamic;
+ else if (!strcmp (p, "dtors-atexit"))
+ flag_dtors_ordering = dtors_atexit;
else if (!strcmp (p, "new-abi"))
{
flag_new_abi = 1;
flag_do_squangling = 1;
flag_honor_std = 1;
flag_vtable_thunks = 1;
+ flag_dtors_ordering = dtors_dynamic;
}
else if (!strcmp (p, "no-new-abi"))
{
flag_new_abi = 0;
flag_do_squangling = 0;
flag_honor_std = 0;
+ flag_dtors_ordering = dtors_static;
}
else if (!strncmp (p, "template-depth-", 15))
{
*************** lang_decode_option (argc, argv)
*** 763,769 ****
}
else if (!strcmp (p, "-ansi"))
flag_no_nonansi_builtin = 1, flag_ansi = 1,
! flag_no_gnu_keywords = 1, flag_operator_names = 1;
#ifdef SPEW_DEBUG
/* Undocumented, only ever used when you're invoking cc1plus by hand, since
it's probably safe to assume no sane person would ever want to use this
--- 775,782 ----
}
else if (!strcmp (p, "-ansi"))
flag_no_nonansi_builtin = 1, flag_ansi = 1,
! flag_no_gnu_keywords = 1, flag_operator_names = 1,
! flag_dtors_ordering = dtors_atexit;
#ifdef SPEW_DEBUG
/* Undocumented, only ever used when you're invoking cc1plus by hand, since
it's probably safe to assume no sane person would ever want to use this
*************** do_dtors (start)
*** 3159,3164 ****
--- 3180,3189 ----
initp = TREE_INT_CST_LOW (TREE_PURPOSE (start));
vars = TREE_VALUE (start);
}
+ else if (flag_dtors_ordering != dtors_static)
+ /* For dynamic and atexit registration, the destructors will
+ be registered during construction. */
+ return;
else
{
initp = 0;
*************** do_ctors (start)
*** 3319,3329 ****
--- 3344,3360 ----
/* Cleanup any temporaries needed for the initial value. */
expand_end_target_temps ();
+ /* If we need to register the destructor, do it now. */
+ if (flag_dtors_ordering == dtors_dynamic ||
+ flag_dtors_ordering == dtors_atexit)
+ do_dynamic_dtor (decl);
+
if (protect)
expand_end_cond ();
DECL_CLASS_CONTEXT (current_function_decl) = NULL_TREE;
DECL_STATIC_FUNCTION_P (current_function_decl) = 0;
+
}
else if (decl == error_mark_node)
/* OK */;
Index: init.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/init.c,v
retrieving revision 1.62
diff -c -p -r1.62 init.c
*** init.c 1998/08/27 17:33:33 1.62
--- init.c 1998/08/31 07:24:36
*************** static tree minus_one;
*** 73,81 ****
static tree BI_header_type, BI_header_size;
void init_init_processing ()
{
! tree fields[1];
minus_one = build_int_2 (-1, -1);
--- 73,86 ----
static tree BI_header_type, BI_header_size;
+ /* Destructor lists. */
+ static tree dtorlist_type_node;
+ static tree dtorlist_ptr_node;
+ static tree null_dtorlist_node;
+
void init_init_processing ()
{
! tree t1, fields[2];
minus_one = build_int_2 (-1, -1);
*************** void init_init_processing ()
*** 87,92 ****
--- 92,111 ----
finish_builtin_type (BI_header_type, "__new_cookie", fields,
0, double_type_node);
BI_header_size = size_in_bytes (BI_header_type);
+
+ /* struct __dtorlist. This must match initialize.c. */
+ dtorlist_type_node = t1 = make_lang_type (RECORD_TYPE);
+ fields[0] = build_lang_field_decl
+ (FIELD_DECL, get_identifier ("dtor"),
+ build_pointer_type (build_function_type
+ (void_type_node, void_list_node)));
+ fields[1] = build_lang_field_decl
+ (FIELD_DECL, get_identifier ("next"), build_pointer_type (t1));
+ finish_builtin_type (t1, "__dtorlist", fields, 1, ptr_type_node);
+ dtorlist_ptr_node = build_pointer_type (t1);
+
+ null_dtorlist_node = build_int_2 (0, 0);
+ TREE_TYPE (null_dtorlist_node) = dtorlist_ptr_node;
}
/* Subroutine of emit_base_init. For BINFO, initialize all the
*************** build_vec_delete (base, maxindex, auto_d
*** 3300,3303 ****
--- 3319,3559 ----
return build_vec_delete_1 (base, maxindex, type, auto_delete_vec, auto_delete,
use_global_delete);
+ }
+
+ /* Emit code to perform this initialization but once. */
+
+ void
+ expand_static_init_dynamic (decl, init)
+ tree decl, init;
+ {
+ extern struct obstack permanent_obstack;
+ tree temp, ptemp, e, cleanup, registry;
+ tree PFV, fn;
+
+ /* Remember this information until end of file. */
+ push_obstacks (&permanent_obstack, &permanent_obstack);
+
+ /* Emit code to perform this initialization but once. */
+ temp = get_temp_name (integer_type_node, 1);
+ rest_of_decl_compilation (temp, NULL_PTR, 0, 0);
+
+ fn = get_identifier ("__cp_init_begin");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare int __cp_init_begin (int *); */
+ tree tmp;
+ tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
+ fn = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (integer_type_node, tmp));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ assemble_external (fn);
+ }
+
+ e = build1 (ADDR_EXPR, ptr_type_node, temp);
+ e = expr_tree_cons (NULL_TREE, e, NULL_TREE);
+ e = build_function_call (fn, e);
+
+ expand_start_cond (e, 0);
+ expand_start_target_temps ();
+
+ if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))
+ || (init && TREE_CODE (init) == TREE_LIST))
+ {
+ /* This implements the following pseudo code
+ try{
+ initialization;
+ }catch(...){
+ __cp_init_failed(&_tmp_flag);
+ throw;
+ }
+ */
+ begin_try_block ();
+ expand_aggr_init (decl, init, 0, 0);
+ do_pending_stack_adjust ();
+ expand_start_all_catch ();
+ /* expand_start_catch_block (NULL_TREE, NULL_TREE); */
+
+ fn = get_identifier ("__cp_init_failed");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare void __cp_init_failed (int *); */
+ tree tmp;
+ tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node);
+ fn = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (void_type_node, tmp));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ assemble_external (fn);
+ }
+
+ e = build1 (ADDR_EXPR, ptr_type_node, temp);
+ e = expr_tree_cons (NULL_TREE, e, NULL_TREE);
+ e = build_function_call (fn, e);
+ expand_expr_stmt (e);
+
+ /* expand_expr_stmt (build_throw (NULL_TREE));
+ expand_end_catch_block (); */
+ expand_end_all_catch ();
+ }
+ else if (init)
+ expand_assignment (decl, init, 0, 0);
+
+ /* Cleanup any temporaries needed for the initial value. */
+ expand_end_target_temps ();
+
+ /* void (*)(); */
+ PFV = build_pointer_type (build_function_type
+ (void_type_node, void_list_node));
+
+ fn = get_identifier ("__cp_init_complete");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare void
+ __cp_init_complete (int *, void (*)(), struct __dtorlist *); */
+ tree tmp;
+ tmp = tree_cons (NULL_TREE, ptr_type_node, tree_cons
+ (NULL_TREE, PFV, tree_cons
+ (NULL_TREE, dtorlist_ptr_node, void_list_node)));
+ fn = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (void_type_node, tmp));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ assemble_external (fn);
+ }
+
+ /* Call init_complete. */
+ if (TYPE_NEEDS_DESTRUCTOR (TREE_TYPE (decl)))
+ {
+ e = start_anon_func ();
+ expand_expr_stmt (build_cleanup (decl));
+ end_anon_func ();
+ mark_addressable (e);
+ cleanup = build_unary_op (ADDR_EXPR, e, 0);
+ if (flag_dtors_ordering == dtors_atexit)
+ /* Under strict compliance, we don't run our own
+ destructor registry, but use atexit or equivalent. */
+ registry = null_dtorlist_node;
+ else if (flag_dtors_ordering == dtors_dynamic)
+ {
+ registry = get_temp_name (dtorlist_type_node, 1);
+ rest_of_decl_compilation (registry, NULL_PTR, 0, 0);
+ registry = build1 (ADDR_EXPR, dtorlist_ptr_node, registry);
+ }
+ else
+ /* We don't do static registration here. */
+ my_friendly_abort (980830);
+ }
+ else
+ {
+ static tree null_fptr = NULL_TREE;
+ if (null_fptr == NULL_TREE)
+ {
+ tree ftype = build_pointer_type (build_function_type
+ (void_type_node, void_list_node));
+ layout_type (ftype);
+ null_fptr = build_int_2 (0, 0);
+ TREE_TYPE (null_fptr) = ftype;
+ }
+ cleanup = null_fptr;
+ registry = null_dtorlist_node;
+ }
+
+ e = expr_tree_cons (NULL_TREE, registry, NULL_TREE);
+ e = expr_tree_cons (NULL_TREE, cleanup, e);
+ ptemp = build1 (ADDR_EXPR, ptr_type_node, temp);
+ e = expr_tree_cons (NULL_TREE, ptemp, e);
+ e = build_function_call (fn, e);
+
+ expand_expr_stmt (e);
+ expand_end_cond ();
+
+ /* Resume old (possibly temporary) allocation. */
+ pop_obstacks ();
+ }
+
+ /* Register a single destructor function at runtime. */
+ void
+ do_dynamic_dtor (decl)
+ tree decl;
+ {
+ tree type = TREE_TYPE (decl);
+ tree e, fn, registry, dtor;
+
+ if (!TYPE_NEEDS_DESTRUCTOR (type) || DECL_EXTERNAL (decl))
+ return;
+
+ dtor = start_anon_func ();
+
+ if (member_p (decl))
+ {
+ DECL_CLASS_CONTEXT (current_function_decl) = DECL_CONTEXT (decl);
+ DECL_STATIC_FUNCTION_P (current_function_decl) = 1;
+ }
+
+ /* We need no protection agains multiple destruction here,
+ since the constructor is protected and registers us. */
+ expand_expr_stmt (build_cleanup (decl));
+
+ DECL_CLASS_CONTEXT (current_function_decl) = NULL_TREE;
+ DECL_STATIC_FUNCTION_P (current_function_decl) = 0;
+
+ end_anon_func ();
+ mark_addressable (dtor);
+
+ fn = get_identifier ("__cp_register_dtor");
+ if (IDENTIFIER_GLOBAL_VALUE (fn))
+ fn = IDENTIFIER_GLOBAL_VALUE (fn);
+ else
+ {
+ /* Declare void
+ __cp_register_dtor (void (*)(), struct __dtorlist *); */
+ tree tmp, PFV;
+ PFV = build_pointer_type (build_function_type
+ (void_type_node, void_list_node));
+ tmp = tree_cons(NULL_TREE, PFV, tree_cons
+ (NULL_TREE, dtorlist_ptr_node, void_list_node));
+ fn = build_lang_decl (FUNCTION_DECL, fn,
+ build_function_type (void_type_node, tmp));
+ DECL_EXTERNAL (fn) = 1;
+ TREE_PUBLIC (fn) = 1;
+ DECL_ARTIFICIAL (fn) = 1;
+ pushdecl_top_level (fn);
+ make_function_rtl (fn);
+ assemble_external (fn);
+ }
+
+ if (flag_dtors_ordering == dtors_atexit)
+ registry = null_dtorlist_node;
+ else if (flag_dtors_ordering == dtors_dynamic)
+ {
+ /* We could make this shared if the object is shared. */
+ registry = get_temp_name (dtorlist_type_node, 1);
+ rest_of_decl_compilation (registry, NULL_PTR, 0, 0);
+ registry = build1 (ADDR_EXPR, dtorlist_ptr_node, registry);
+ }
+ else
+ /* We don't do static registration here. */
+ my_friendly_abort (980830);
+
+ e = expr_tree_cons (NULL_TREE, registry, NULL_TREE);
+ e = expr_tree_cons (NULL_TREE, dtor, e);
+ e = build_function_call (fn, e);
+ expand_expr_stmt (e);
}
Index: lang-options.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/lang-options.h,v
retrieving revision 1.15
diff -c -p -r1.15 lang-options.h
*** lang-options.h 1998/08/28 16:11:34 1.15
--- lang-options.h 1998/08/31 07:24:36
*************** DEFINE_LANG_NAME ("C++")
*** 67,72 ****
--- 67,75 ----
{ "-fno-implicit-templates", "" },
{ "-finit-priority", "Handle the init_priority attribute" },
{ "-fno-init-priority", "" },
+ { "-fdtors-atexit", "Register destructors with atexit" },
+ { "-fdtors-dynamic", "Register destructors dynamically" },
+ { "-fdtors-static", "Register destructors at compile time" },
{ "-flabels-ok", "Labels can be used as first class objects" },
{ "-fno-labels-ok", "" },
{ "-fmemoize-lookups", "" },
--- /dev/null Mon Jul 18 01:46:18 1994
+++ initialize.c Sun Aug 30 20:34:13 1998
@@ -0,0 +1,144 @@
+// Functions for Initialization Support for C++, -*- C -*-
+// Copyright (C) 1998 Free Software Foundation
+
+// This file is part of GNU CC.
+
+// GNU CC is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// GNU CC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GNU CC; see the file COPYING. If not, write to
+// the Free Software Foundation, 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+// As a special exception, if you link this library with other files,
+// some of which are compiled with GCC, to produce an executable,
+// this library does not by itself cause the resulting executable
+// to be covered by the GNU General Public License.
+// This exception does not however invalidate any other reasons why
+// the executable file might be covered by the GNU General Public License.
+
+/* Initialization variables have the following states:
+ 0 - uninitialized
+ 1 - initialization in progress
+ 2 - initialization complete
+*/
+
+#include <stdlib.h>
+#include "gthr.h"
+
+/* FIXME: There should be one of these per program,
+ not per shared object. */
+
+static __gthread_mutex_t initlock = __GTHREAD_MUTEX_INIT;
+
+struct __dtorlist{
+ void (*dtor)();
+ struct __dtorlist *next;
+};
+
+/* FIXME: There should be one of these per program,
+ not per shared object. */
+
+static struct __dtorlist *__cp_dtors;
+
+/* These are meant to be per shared object. */
+static void *min_dtor, *max_dtor;
+
+/* For static initialization, this runs unprotected.
+ For dynamic (block-static) ctors, we hold the initlock. */
+
+void
+__cp_register_dtor (void (*dtor)(), struct __dtorlist *entry)
+{
+ if (!entry)
+ {
+ /* FIXME: If we have an atexit implementation that supports
+ unregistration, use it. */
+ atexit(dtor);
+ return;
+ }
+ entry->dtor = dtor;
+ entry->next = __cp_dtors;
+ __cp_dtors = entry;
+ if (!min_dtor || min_dtor > (void*)dtor)
+ min_dtor = dtor;
+ if (!max_dtor || max_dtor < (void*)dtor)
+ max_dtor = dtor;
+}
+
+static void __cp_run_dtors () __attribute__((destructor));
+
+static void
+__cp_run_dtors ()
+{
+ /* First, run everything and clear the function pointer */
+ struct __dtorlist *entry, *prev;
+ for (entry = __cp_dtors; entry; entry = entry->next)
+ if (entry->dtor && (void*)entry->dtor >= min_dtor
+ && (void*)entry->dtor <= max_dtor)
+ {
+ entry->dtor();
+ entry->dtor = 0;
+ }
+ /* When removing from the list, obtain the lock. */
+ __gthread_mutex_lock (&initlock);
+ for (entry = __cp_dtors, prev = 0; entry;
+ prev = entry, entry = entry->next)
+ if (!entry->dtor)
+ {
+ if (prev)
+ prev->next = entry->next;
+ else
+ __cp_dtors = entry->next;
+ }
+ __gthread_mutex_unlock (&initlock);
+}
+
+int
+__cp_init_begin (int * p)
+{
+ if (*p == 2)
+ /* Nothing to do. */
+ return 0;
+ __gthread_mutex_lock (&initlock);
+ switch (*p)
+ {
+ case 0:
+ *p = 1;
+ /* Could release lock here if we had condition variables. */
+ return 1;
+ case 2:
+ /* Done. */
+ __gthread_mutex_unlock (&initlock);
+ return 0;
+ }
+ /* Must have been 1, or some illegal value. With us holding the
+ lock, this should not happen. */
+ __gthread_mutex_unlock (&initlock);
+ abort ();
+ return 0;
+}
+
+void
+__cp_init_failed (int* p)
+{
+ *p = 0;
+ __gthread_mutex_unlock (&initlock);
+}
+
+void
+__cp_init_complete (int *p, void (*dtor)(), struct __dtorlist *entry)
+{
+ if (dtor)
+ __cp_register_dtor (dtor, entry);
+ *p = 2;
+ __gthread_mutex_unlock (&initlock);
+}