2012-10-08 Jason Merrill <jason@redhat.com>
+ * decl.c (get_thread_atexit_node): New.
+ (register_dtor_fn): Use it for TLS.
+
Partial implementation of C++11 thread_local.
* decl.c (cp_finish_decl): Remove errors about non-trivial
initialization and destruction of TLS variables.
return atexit_node;
}
+/* Like get_atexit_node, but for thread-local cleanups. */
+
+static tree
+get_thread_atexit_node (void)
+{
+ /* The declaration for `__cxa_thread_atexit' is:
+
+ int __cxa_thread_atexit (void (*)(void *), void *, void *) */
+ tree fn_type = build_function_type_list (integer_type_node,
+ get_atexit_fn_ptr_type (),
+ ptr_type_node, ptr_type_node,
+ NULL_TREE);
+
+ /* Now, build the function declaration. */
+ tree atexit_fndecl = build_library_fn_ptr ("__cxa_thread_atexit", fn_type);
+ return decay_conversion (atexit_fndecl, tf_warning_or_error);
+}
+
/* Returns the __dso_handle VAR_DECL. */
static tree
register_dtor_fn (tree decl)
{
tree cleanup;
+ tree addr;
tree compound_stmt;
tree fcall;
tree type;
- bool use_dtor;
- tree arg0, arg1 = NULL_TREE, arg2 = NULL_TREE;
+ bool ob_parm, dso_parm, use_dtor;
+ tree arg0, arg1, arg2;
+ tree atex_node;
type = TREE_TYPE (decl);
if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
return void_zero_node;
- /* If we're using "__cxa_atexit" (or "__aeabi_atexit"), and DECL is
- a class object, we can just pass the destructor to
- "__cxa_atexit"; we don't have to build a temporary function to do
- the cleanup. */
- use_dtor = (flag_use_cxa_atexit
- && !targetm.cxx.use_atexit_for_cxa_atexit ()
- && CLASS_TYPE_P (type));
+ /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
+ "__aeabi_atexit"), and DECL is a class object, we can just pass the
+ destructor to "__cxa_atexit"; we don't have to build a temporary
+ function to do the cleanup. */
+ ob_parm = (DECL_THREAD_LOCAL_P (decl)
+ || (flag_use_cxa_atexit
+ && !targetm.cxx.use_atexit_for_cxa_atexit ()));
+ dso_parm = ob_parm;
+ use_dtor = ob_parm && CLASS_TYPE_P (type);
if (use_dtor)
{
int idx;
end_cleanup_fn ();
}
- if (DECL_THREAD_LOCAL_P (decl))
- /* We don't have a thread-local atexit yet. FIXME write one using
- pthread_key_create and friends. */
- sorry ("thread-local variable %q#D with non-trivial "
- "destructor", decl);
-
/* Call atexit with the cleanup function. */
mark_used (cleanup);
cleanup = build_address (cleanup);
- if (flag_use_cxa_atexit && !targetm.cxx.use_atexit_for_cxa_atexit ())
+
+ if (DECL_THREAD_LOCAL_P (decl))
+ atex_node = get_thread_atexit_node ();
+ else
+ atex_node = get_atexit_node ();
+
+ if (use_dtor)
{
- tree addr;
+ /* We must convert CLEANUP to the type that "__cxa_atexit"
+ expects. */
+ cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup);
+ /* "__cxa_atexit" will pass the address of DECL to the
+ cleanup function. */
+ mark_used (decl);
+ addr = build_address (decl);
+ /* The declared type of the parameter to "__cxa_atexit" is
+ "void *". For plain "T*", we could just let the
+ machinery in cp_build_function_call convert it -- but if the
+ type is "cv-qualified T *", then we need to convert it
+ before passing it in, to avoid spurious errors. */
+ addr = build_nop (ptr_type_node, addr);
+ }
+ else if (ob_parm)
+ /* Since the cleanup functions we build ignore the address
+ they're given, there's no reason to pass the actual address
+ in, and, in general, it's cheaper to pass NULL than any
+ other value. */
+ addr = null_pointer_node;
+
+ if (dso_parm)
+ arg2 = cp_build_addr_expr (get_dso_handle_node (),
+ tf_warning_or_error);
+ else
+ arg2 = NULL_TREE;
- if (use_dtor)
- {
- /* We must convert CLEANUP to the type that "__cxa_atexit"
- expects. */
- cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup);
- /* "__cxa_atexit" will pass the address of DECL to the
- cleanup function. */
- mark_used (decl);
- addr = build_address (decl);
- /* The declared type of the parameter to "__cxa_atexit" is
- "void *". For plain "T*", we could just let the
- machinery in cp_build_function_call convert it -- but if the
- type is "cv-qualified T *", then we need to convert it
- before passing it in, to avoid spurious errors. */
- addr = build_nop (ptr_type_node, addr);
- }
- else
- /* Since the cleanup functions we build ignore the address
- they're given, there's no reason to pass the actual address
- in, and, in general, it's cheaper to pass NULL than any
- other value. */
- addr = null_pointer_node;
- arg2 = cp_build_addr_expr (get_dso_handle_node (),
- tf_warning_or_error);
- if (targetm.cxx.use_aeabi_atexit ())
+ if (ob_parm)
+ {
+ if (!DECL_THREAD_LOCAL_P (decl)
+ && targetm.cxx.use_aeabi_atexit ())
{
arg1 = cleanup;
arg0 = addr;
}
}
else
- arg0 = cleanup;
- return cp_build_function_call_nary (get_atexit_node (), tf_warning_or_error,
+ {
+ arg0 = cleanup;
+ arg1 = NULL_TREE;
+ }
+ return cp_build_function_call_nary (atex_node, tf_warning_or_error,
arg0, arg1, arg2, NULL_TREE);
}
2012-10-08 Jason Merrill <jason@redhat.com>
+ * g++.dg/tls/thread_local3.C: New.
+ * g++.dg/tls/thread_local4.C: New.
+ * g++.dg/tls/thread_local5.C: New.
+ * g++.dg/tls/thread_local6.C: New.
+
* g++.dg/tls/init-2.C: Tweak errors.
* g++.dg/tls/thread_local1.C: New.
* g++.dg/tls/thread_local2.C: New.
--- /dev/null
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+#include <pthread.h>
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
--- /dev/null
+// Test for cleanups with pthread_cancel.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+ while (true)
+ {
+ pthread_testcancel();
+ sleep (1);
+ }
+}
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
--- /dev/null
+// Test for cleanups in the main thread, too.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() {
+ if (++d == 3)
+ _exit (0);
+ }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+int main()
+{
+ pthread_t thread;
+ thread_main(0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ if (c != 3 || d != 2)
+ __builtin_abort();
+ return 1;
+}
--- /dev/null
+// Test for cleanups in the main thread without -pthread.
+
+// { dg-do run }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+
+extern "C" void _exit (int);
+
+int c;
+struct A
+{
+ A() { ++c; }
+ ~A() { if (c == 1) _exit(0); }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+int main()
+{
+ thread_main(0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ return 1;
+}
+2012-10-08 Jason Merrill <jason@redhat.com>
+
+ * libsupc++/cxxabi.h: Declare __cxa_thread_atexit.
+ * libsupc++/atexit_thread.cc: New.
+ * libsupc++/Makefile.am (nested_exception.lo): Add it.
+ * config/abi/pre/gnu.ver: Add __cxa_thread_atexit.
+
2012-10-07 Matthias Klose <doko@ubuntu.com>
* testsuite/28_regex/algorithms/match/basic: Remove empty directory.
} CXXABI_1.3.5;
+CXXABI_1.3.7 {
+ __cxa_thread_atexit;
+} CXXABI_1.3.6;
+
# Symbols in the support library (libsupc++) supporting transactional memory.
CXXABI_TM_1 {
sources = \
array_type_info.cc \
atexit_arm.cc \
+ atexit_thread.cc \
bad_alloc.cc \
bad_cast.cc \
bad_typeid.cc \
guard.o: guard.cc
$(CXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.lo: atexit_thread.cc
+ $(LTCXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.o: atexit_thread.cc
+ $(CXXCOMPILE) -std=gnu++0x -c $<
+
nested_exception.lo: nested_exception.cc
$(LTCXXCOMPILE) -std=gnu++0x -c $<
nested_exception.o: nested_exception.cc
"$(DESTDIR)$(stddir)"
LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES)
libsupc___la_LIBADD =
-am__objects_1 = array_type_info.lo atexit_arm.lo bad_alloc.lo \
+am__objects_1 = array_type_info.lo atexit_arm.lo atexit_thread.lo bad_alloc.lo \
bad_cast.lo bad_typeid.lo class_type_info.lo del_op.lo \
del_opnt.lo del_opv.lo del_opvnt.lo dyncast.lo eh_alloc.lo \
eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \
sources = \
array_type_info.cc \
atexit_arm.cc \
+ atexit_thread.cc \
bad_alloc.cc \
bad_cast.cc \
bad_typeid.cc \
guard.o: guard.cc
$(CXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.lo: atexit_thread.cc
+ $(LTCXXCOMPILE) -std=gnu++0x -c $<
+atexit_thread.o: atexit_thread.cc
+ $(CXXCOMPILE) -std=gnu++0x -c $<
+
nested_exception.lo: nested_exception.cc
$(LTCXXCOMPILE) -std=gnu++0x -c $<
nested_exception.o: nested_exception.cc
--- /dev/null
+// Copyright (C) 2012 Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+//
+// GCC 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 3, or (at your option)
+// any later version.
+
+// GCC 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <cxxabi.h>
+#include <cstdlib>
+#include <new>
+#include "bits/gthr.h"
+
+namespace {
+ // Data structure for the list of destructors: Singly-linked list
+ // of arrays.
+ class list
+ {
+ struct elt
+ {
+ void *object;
+ void (*destructor)(void *);
+ };
+
+ static const int max_nelts = 32;
+
+ list *next;
+ int nelts;
+ elt array[max_nelts];
+
+ elt *allocate_elt();
+ public:
+ void run();
+ static void run(void *p);
+ int add_elt(void (*)(void *), void *);
+ };
+
+ // Return the address of an open slot.
+ list::elt *
+ list::allocate_elt()
+ {
+ if (nelts < max_nelts)
+ return &array[nelts++];
+ if (!next)
+ next = new (std::nothrow) list();
+ if (!next)
+ return 0;
+ return next->allocate_elt();
+ }
+
+ // Run all the cleanups in the list.
+ void
+ list::run()
+ {
+ for (int i = nelts - 1; i >= 0; --i)
+ array[i].destructor (array[i].object);
+ if (next)
+ next->run();
+ }
+
+ // Static version to use as a callback to __gthread_key_create.
+ void
+ list::run(void *p)
+ {
+ static_cast<list *>(p)->run();
+ }
+
+ // The list of cleanups is per-thread.
+ thread_local list first;
+
+ // The pthread data structures for actually running the destructors at
+ // thread exit are shared. The constructor of the thread-local sentinel
+ // object in add_elt performs the initialization.
+ __gthread_key_t key;
+ __gthread_once_t once = __GTHREAD_ONCE_INIT;
+ void run_current () { first.run(); }
+ void key_init() {
+ __gthread_key_create (&key, list::run);
+ // Also make sure the destructors are run by std::exit.
+ // FIXME TLS cleanups should run before static cleanups and atexit
+ // cleanups.
+ std::atexit (run_current);
+ }
+ struct sentinel
+ {
+ sentinel()
+ {
+ if (__gthread_active_p ())
+ {
+ __gthread_once (&once, key_init);
+ __gthread_setspecific (key, &first);
+ }
+ else
+ std::atexit (run_current);
+ }
+ };
+
+ // Actually insert an element.
+ int
+ list::add_elt(void (*dtor)(void *), void *obj)
+ {
+ thread_local sentinel s;
+ elt *e = allocate_elt ();
+ if (!e)
+ return -1;
+ e->object = obj;
+ e->destructor = dtor;
+ return 0;
+ }
+}
+
+namespace __cxxabiv1
+{
+ extern "C" int
+ __cxa_thread_atexit (void (*dtor)(void *), void *obj, void */*dso_handle*/)
+ _GLIBCXX_NOTHROW
+ {
+ return first.add_elt (dtor, obj);
+ }
+}
int
__cxa_finalize(void*);
+ // TLS destruction.
+ int
+ __cxa_thread_atexit(void (*)(void*), void*, void *) _GLIBCXX_NOTHROW;
+
// Pure virtual functions.
void
__cxa_pure_virtual(void) __attribute__ ((__noreturn__));