From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 8796 invoked by alias); 15 Oct 2012 07:31:55 -0000 Received: (qmail 8787 invoked by uid 22791); 15 Oct 2012 07:31:54 -0000 X-SWARE-Spam-Status: No, hits=-6.4 required=5.0 tests=AWL,BAYES_00,KHOP_RCVD_UNTRUST,KHOP_SPAMHAUS_DROP,RCVD_IN_DNSWL_HI,RCVD_IN_HOSTKARMA_W,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_CX X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 15 Oct 2012 07:31:48 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q9F7Vjqc005584 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 15 Oct 2012 03:31:45 -0400 Received: from [10.3.113.77] (ovpn-113-77.phx2.redhat.com [10.3.113.77]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q9F7VhxK015816; Mon, 15 Oct 2012 03:31:43 -0400 Message-ID: <507BBBDE.1090909@redhat.com> Date: Mon, 15 Oct 2012 07:38:00 -0000 From: Jason Merrill User-Agent: Mozilla/5.0 (X11; Linux i686; rv:16.0) Gecko/20121009 Thunderbird/16.0 MIME-Version: 1.0 To: gcc-patches List CC: Jack Howarth , Dominique Dhumieres Subject: C++ PATCH for target/54908 (thread_local vs emutls) Content-Type: multipart/mixed; boundary="------------070908000804050205090608" Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2012-10/txt/msg01375.txt.bz2 This is a multi-part message in MIME format. --------------070908000804050205090608 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 379 This patch completely rewrites atexit_thread.cc to use __gthread_getspecific/setspecific rather than a thread_local variable to store the cleanup list, so that the list won't vanish if emutls_destroy runs first. With this patch all the TLS tests pass on i686-pc-linux-gnu configured with --disable-tls to force use of emutls. Tested x86_64-pc-linux-gnu, applying to trunk. --------------070908000804050205090608 Content-Type: text/x-patch; name="atexit-setspecific.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="atexit-setspecific.patch" Content-length: 5351 commit 8735583f399914e92f126d98c120aba317f6626a Author: Jason Merrill Date: Mon Oct 8 10:50:20 2012 -0400 PR target/54908 * libsupc++/atexit_thread.cc: Rewrite to keep the cleanup list with get/setspecific. Destroy the key on dlclose. diff --git a/gcc/testsuite/g++.dg/tls/thread_local7g.C b/gcc/testsuite/g++.dg/tls/thread_local7g.C index 6960598..3479aeb 100644 --- a/gcc/testsuite/g++.dg/tls/thread_local7g.C +++ b/gcc/testsuite/g++.dg/tls/thread_local7g.C @@ -3,7 +3,7 @@ // { dg-require-alias } // The reference temp should be TLS, not normal data. -// { dg-final { scan-assembler-not "\\.data" } } +// { dg-final { scan-assembler-not "\\.data" { target tls_native } } } thread_local int&& ir = 42; diff --git a/libstdc++-v3/libsupc++/atexit_thread.cc b/libstdc++-v3/libsupc++/atexit_thread.cc index 5e47708..95bdcf0 100644 --- a/libstdc++-v3/libsupc++/atexit_thread.cc +++ b/libstdc++-v3/libsupc++/atexit_thread.cc @@ -27,109 +27,92 @@ #include "bits/gthr.h" namespace { - // Data structure for the list of destructors: Singly-linked list - // of arrays. - class list + // One element in a singly-linked stack of cleanups. + struct elt { - 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 *); + void (*destructor)(void *); + void *object; + elt *next; }; - // 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(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. + // Keep a per-thread list of cleanups in gthread_key storage. __gthread_key_t key; - __gthread_once_t once = __GTHREAD_ONCE_INIT; - void run_current () { first.run(); } + // But also support non-threaded mode. + elt *single_thread; + + // Run the specified stack of cleanups. + void run (void *p) + { + elt *e = static_cast(p); + for (; e; e = e->next) + e->destructor (e->object); + } + + // Run the stack of cleanups for the current thread. + void run () + { + void *e; + if (__gthread_active_p ()) + e = __gthread_getspecific (key); + else + e = single_thread; + run (e); + } + + // Initialize the key for the cleanup stack. We use a static local for + // key init/delete rather than atexit so that delete is run on dlclose. void key_init() { - __gthread_key_create (&key, list::run); + struct key_s { + key_s() { __gthread_key_create (&key, run); } + ~key_s() { __gthread_key_delete (key); } + }; + static key_s ks; // 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); + std::atexit (run); } - struct sentinel - { - sentinel() +} + +extern "C" int +__cxxabiv1::__cxa_thread_atexit (void (*dtor)(void *), void *obj, void */*dso_handle*/) + _GLIBCXX_NOTHROW +{ + // Do this initialization once. + if (__gthread_active_p ()) { - if (__gthread_active_p ()) + // When threads are active use __gthread_once. + static __gthread_once_t once = __GTHREAD_ONCE_INIT; + __gthread_once (&once, key_init); + } + else + { + // And when threads aren't active use a static local guard. + static bool queued; + if (!queued) { - __gthread_once (&once, key_init); - __gthread_setspecific (key, &first); + queued = true; + std::atexit (run); } - 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; - } -} + elt *first; + if (__gthread_active_p ()) + first = static_cast(__gthread_getspecific (key)); + else + first = single_thread; + + elt *new_elt = new (std::nothrow) elt; + if (!new_elt) + return -1; + new_elt->destructor = dtor; + new_elt->object = obj; + new_elt->next = first; + + if (__gthread_active_p ()) + __gthread_setspecific (key, new_elt); + else + single_thread = new_elt; -namespace __cxxabiv1 -{ - extern "C" int - __cxa_thread_atexit (void (*dtor)(void *), void *obj, void */*dso_handle*/) - _GLIBCXX_NOTHROW - { - return first.add_elt (dtor, obj); - } + return 0; } --------------070908000804050205090608--