Commit 5b031b9b by Jason Merrill Committed by Jason Merrill

Support C++11 thread_local destructors.

gcc/cp/
	* decl.c (get_thread_atexit_node): New.
	(register_dtor_fn): Use it for TLS.
libstdc++-v3/
	* 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.

From-SVN: r192210
parent b1db7f91
2012-10-08 Jason Merrill <jason@redhat.com> 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. Partial implementation of C++11 thread_local.
* decl.c (cp_finish_decl): Remove errors about non-trivial * decl.c (cp_finish_decl): Remove errors about non-trivial
initialization and destruction of TLS variables. initialization and destruction of TLS variables.
......
...@@ -6575,6 +6575,24 @@ get_atexit_node (void) ...@@ -6575,6 +6575,24 @@ get_atexit_node (void)
return atexit_node; 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. */ /* Returns the __dso_handle VAR_DECL. */
static tree static tree
...@@ -6666,23 +6684,27 @@ tree ...@@ -6666,23 +6684,27 @@ tree
register_dtor_fn (tree decl) register_dtor_fn (tree decl)
{ {
tree cleanup; tree cleanup;
tree addr;
tree compound_stmt; tree compound_stmt;
tree fcall; tree fcall;
tree type; tree type;
bool use_dtor; bool ob_parm, dso_parm, use_dtor;
tree arg0, arg1 = NULL_TREE, arg2 = NULL_TREE; tree arg0, arg1, arg2;
tree atex_node;
type = TREE_TYPE (decl); type = TREE_TYPE (decl);
if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)) if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
return void_zero_node; return void_zero_node;
/* If we're using "__cxa_atexit" (or "__aeabi_atexit"), and DECL is /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
a class object, we can just pass the destructor to "__aeabi_atexit"), and DECL is a class object, we can just pass the
"__cxa_atexit"; we don't have to build a temporary function to do destructor to "__cxa_atexit"; we don't have to build a temporary
the cleanup. */ function to do the cleanup. */
use_dtor = (flag_use_cxa_atexit ob_parm = (DECL_THREAD_LOCAL_P (decl)
&& !targetm.cxx.use_atexit_for_cxa_atexit () || (flag_use_cxa_atexit
&& CLASS_TYPE_P (type)); && !targetm.cxx.use_atexit_for_cxa_atexit ()));
dso_parm = ob_parm;
use_dtor = ob_parm && CLASS_TYPE_P (type);
if (use_dtor) if (use_dtor)
{ {
int idx; int idx;
...@@ -6720,44 +6742,48 @@ register_dtor_fn (tree decl) ...@@ -6720,44 +6742,48 @@ register_dtor_fn (tree decl)
end_cleanup_fn (); 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. */ /* Call atexit with the cleanup function. */
mark_used (cleanup); mark_used (cleanup);
cleanup = build_address (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) if (ob_parm)
{ {
/* We must convert CLEANUP to the type that "__cxa_atexit" if (!DECL_THREAD_LOCAL_P (decl)
expects. */ && targetm.cxx.use_aeabi_atexit ())
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 ())
{ {
arg1 = cleanup; arg1 = cleanup;
arg0 = addr; arg0 = addr;
...@@ -6769,8 +6795,11 @@ register_dtor_fn (tree decl) ...@@ -6769,8 +6795,11 @@ register_dtor_fn (tree decl)
} }
} }
else 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); arg0, arg1, arg2, NULL_TREE);
} }
......
2012-10-08 Jason Merrill <jason@redhat.com> 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/init-2.C: Tweak errors.
* g++.dg/tls/thread_local1.C: New. * g++.dg/tls/thread_local1.C: New.
* g++.dg/tls/thread_local2.C: New. * g++.dg/tls/thread_local2.C: New.
......
// { 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();
}
// 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();
}
// 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;
}
// 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> 2012-10-07 Matthias Klose <doko@ubuntu.com>
* testsuite/28_regex/algorithms/match/basic: Remove empty directory. * testsuite/28_regex/algorithms/match/basic: Remove empty directory.
......
...@@ -1531,6 +1531,10 @@ CXXABI_1.3.6 { ...@@ -1531,6 +1531,10 @@ CXXABI_1.3.6 {
} CXXABI_1.3.5; } CXXABI_1.3.5;
CXXABI_1.3.7 {
__cxa_thread_atexit;
} CXXABI_1.3.6;
# Symbols in the support library (libsupc++) supporting transactional memory. # Symbols in the support library (libsupc++) supporting transactional memory.
CXXABI_TM_1 { CXXABI_TM_1 {
......
...@@ -48,6 +48,7 @@ endif ...@@ -48,6 +48,7 @@ endif
sources = \ sources = \
array_type_info.cc \ array_type_info.cc \
atexit_arm.cc \ atexit_arm.cc \
atexit_thread.cc \
bad_alloc.cc \ bad_alloc.cc \
bad_cast.cc \ bad_cast.cc \
bad_typeid.cc \ bad_typeid.cc \
...@@ -123,6 +124,11 @@ guard.lo: guard.cc ...@@ -123,6 +124,11 @@ guard.lo: guard.cc
guard.o: guard.cc guard.o: guard.cc
$(CXXCOMPILE) -std=gnu++0x -c $< $(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 nested_exception.lo: nested_exception.cc
$(LTCXXCOMPILE) -std=gnu++0x -c $< $(LTCXXCOMPILE) -std=gnu++0x -c $<
nested_exception.o: nested_exception.cc nested_exception.o: nested_exception.cc
......
...@@ -90,7 +90,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(bitsdir)" \ ...@@ -90,7 +90,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(bitsdir)" \
"$(DESTDIR)$(stddir)" "$(DESTDIR)$(stddir)"
LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES) LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES)
libsupc___la_LIBADD = 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 \ 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 \ 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 \ eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \
...@@ -362,6 +362,7 @@ headers = $(std_HEADERS) $(bits_HEADERS) ...@@ -362,6 +362,7 @@ headers = $(std_HEADERS) $(bits_HEADERS)
sources = \ sources = \
array_type_info.cc \ array_type_info.cc \
atexit_arm.cc \ atexit_arm.cc \
atexit_thread.cc \
bad_alloc.cc \ bad_alloc.cc \
bad_cast.cc \ bad_cast.cc \
bad_typeid.cc \ bad_typeid.cc \
...@@ -800,6 +801,11 @@ guard.lo: guard.cc ...@@ -800,6 +801,11 @@ guard.lo: guard.cc
guard.o: guard.cc guard.o: guard.cc
$(CXXCOMPILE) -std=gnu++0x -c $< $(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 nested_exception.lo: nested_exception.cc
$(LTCXXCOMPILE) -std=gnu++0x -c $< $(LTCXXCOMPILE) -std=gnu++0x -c $<
nested_exception.o: nested_exception.cc nested_exception.o: nested_exception.cc
......
// 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);
}
}
...@@ -134,6 +134,10 @@ namespace __cxxabiv1 ...@@ -134,6 +134,10 @@ namespace __cxxabiv1
int int
__cxa_finalize(void*); __cxa_finalize(void*);
// TLS destruction.
int
__cxa_thread_atexit(void (*)(void*), void*, void *) _GLIBCXX_NOTHROW;
// Pure virtual functions. // Pure virtual functions.
void void
__cxa_pure_virtual(void) __attribute__ ((__noreturn__)); __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment