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>
* 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.
......
......@@ -6575,6 +6575,24 @@ get_atexit_node (void)
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
......@@ -6666,23 +6684,27 @@ 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;
......@@ -6720,18 +6742,14 @@ register_dtor_fn (tree decl)
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 ())
{
tree addr;
if (DECL_THREAD_LOCAL_P (decl))
atex_node = get_thread_atexit_node ();
else
atex_node = get_atexit_node ();
if (use_dtor)
{
......@@ -6749,15 +6767,23 @@ register_dtor_fn (tree decl)
before passing it in, to avoid spurious errors. */
addr = build_nop (ptr_type_node, addr);
}
else
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);
if (targetm.cxx.use_aeabi_atexit ())
else
arg2 = NULL_TREE;
if (ob_parm)
{
if (!DECL_THREAD_LOCAL_P (decl)
&& targetm.cxx.use_aeabi_atexit ())
{
arg1 = cleanup;
arg0 = addr;
......@@ -6769,8 +6795,11 @@ register_dtor_fn (tree decl)
}
}
else
{
arg0 = cleanup;
return cp_build_function_call_nary (get_atexit_node (), tf_warning_or_error,
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.
......
// { 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>
* testsuite/28_regex/algorithms/match/basic: Remove empty directory.
......
......@@ -1531,6 +1531,10 @@ CXXABI_1.3.6 {
} 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 {
......
......@@ -48,6 +48,7 @@ endif
sources = \
array_type_info.cc \
atexit_arm.cc \
atexit_thread.cc \
bad_alloc.cc \
bad_cast.cc \
bad_typeid.cc \
......@@ -123,6 +124,11 @@ guard.lo: guard.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
......
......@@ -90,7 +90,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(bitsdir)" \
"$(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 \
......@@ -362,6 +362,7 @@ headers = $(std_HEADERS) $(bits_HEADERS)
sources = \
array_type_info.cc \
atexit_arm.cc \
atexit_thread.cc \
bad_alloc.cc \
bad_cast.cc \
bad_typeid.cc \
......@@ -800,6 +801,11 @@ guard.lo: guard.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
......
// 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
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__));
......
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