This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Destructor support for C++11 thread_local variables


Hi,

C++11 introduces the thread_local scope for variables that have thread
scope.  These variables are similar to the TLS variables declared with
__thread with a few added features.  One of the key features is that
they support non-trivial constructors and destructors that are called
on first-use and thread exit respectively.  Additionally, if the current
thread results in a process exit, destructors for thread_local
variables should be called for this thread as well.

Jason Merrill has already put in this support into gcc trunk.  The
compiler can handle constructors, but destructors need more runtime
context.  It could be possible that a dynamically loaded library
defines and constructs a thread_local variable, but is dlclose()'d
before thread exit.  The destructor of this variable will then have the
rug pulled from under it and crash. As a result, the dynamic linker
needs to be informed so as to not unload the DSO.

Attached patch adds a __cxa_thread_atexit_impl that handles the
destructors and their call order for libstdc++ and also ensures that
the dynamic linker does not unload the dso that defines a thread_local
variable.

I have written a small wiki page detailing this so that other compilers
may use this as well for thread_local implementation:

http://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables

I have tested this by hacking up libstdc++ from gcc HEAD to call
__cxa_thread_atexit_impl instead of its internal fallback
implementation.

A lot of the ideas for this patch came from Jakub Jelinek.  One could
find Jason's changes for thread_local support in gcc/libstdc++
in the latest trunk.  It doesn't directly check for and call
__cxa_thread_atexit_impl yet, but I understand it should be in soon.

OK to commit?

Regards,
Siddhesh

ChangeLog:

	* include/stdlib.h (__cxa_thread_atexit_impl): Declare.
	(__call_tls_dtors): Likewise.
	* stdlib/Makefile (routines): Add __cxa_thread_atexit_impl.
	* stdlib/Versions (GLIBC_2.17): Likewise.
	(GLIBC_PRIVATE): Add __call_tls_dtors.
	* stdlib/cxa_thread_atexit_impl.c: New file with helper function
	for libstdc++.
	* stdlib/exit.c (__run_exit_handlers): Call __call_tls_dtors.

nptl/ChangeLog:

	* pthread_create.c (start_thread): Call __call_tls_dtors.
commit 4226be3554db34378ccfffa86fde600127642ca5
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
Date:   Wed Oct 10 20:23:10 2012 +0530

    TLS destructors support

diff --git a/include/stdlib.h b/include/stdlib.h
index d45b2f0..4387394 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -99,6 +99,10 @@ extern int __cxa_atexit (void (*func) (void *), void *arg, void *d);
 extern int __cxa_atexit_internal (void (*func) (void *), void *arg, void *d)
      attribute_hidden;
 
+extern int __cxa_thread_atexit_impl (void (*func) (void *), void *arg,
+				     void *d);
+extern void __call_tls_dtors (void);
+
 extern void __cxa_finalize (void *d);
 
 extern int __posix_memalign (void **memptr, size_t alignment, size_t size);
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 197dfa7..34c0d30 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -311,6 +311,9 @@ start_thread (void *arg)
 #endif
     }
 
+  /* Call destructors for the thread_local TLS variables.  */
+  __call_tls_dtors ();
+
   /* Run the destructor for the thread-local data.  */
   __nptl_deallocate_tsd ();
 
diff --git a/stdlib/Makefile b/stdlib/Makefile
index a5318ee..d514bba 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -33,7 +33,7 @@ routines	:=							      \
 	bsearch qsort msort						      \
 	getenv putenv setenv secure-getenv				      \
 	exit on_exit atexit cxa_atexit cxa_finalize old_atexit		      \
-	quick_exit at_quick_exit cxa_at_quick_exit			      \
+	quick_exit at_quick_exit cxa_at_quick_exit cxa_thread_atexit_impl     \
 	abs labs llabs							      \
 	div ldiv lldiv							      \
 	mblen mbstowcs mbtowc wcstombs wctomb				      \
diff --git a/stdlib/Versions b/stdlib/Versions
index 250bd5f..50dec5d 100644
--- a/stdlib/Versions
+++ b/stdlib/Versions
@@ -105,6 +105,7 @@ libc {
   }
   GLIBC_2.17 {
     secure_getenv;
+    __cxa_thread_atexit_impl;
   }
   GLIBC_PRIVATE {
     # functions which have an additional interface since they are
@@ -114,5 +115,6 @@ libc {
     __abort_msg;
     # Used from other libraries
     __libc_secure_getenv;
+    __call_tls_dtors;
   }
 }
diff --git a/stdlib/cxa_thread_atexit_impl.c b/stdlib/cxa_thread_atexit_impl.c
new file mode 100644
index 0000000..74e068a
--- /dev/null
+++ b/stdlib/cxa_thread_atexit_impl.c
@@ -0,0 +1,94 @@
+/* Register destructors for C++ TLS variables declared with thread_local.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <ldsodefs.h>
+
+typedef void (*dtor_func) (void *);
+
+struct dtor_list
+{
+  dtor_func func;
+  void *obj;
+  struct dtor_list *next;
+};
+
+static __thread struct dtor_list *tls_dtor_list;
+
+/* Register a destructor for TLS variables declared with the 'thread_local'
+   keyword.  This function is only called from code generated by the C++
+   compiler.  */
+int
+__cxa_thread_atexit_impl (dtor_func func, void *obj, void *dso_handle)
+{
+  static void *dso_handle_cache;
+
+  /* Prepend.  */
+  struct dtor_list *new = calloc (1, sizeof (struct dtor_list));
+  new->func = func;
+  new->obj = obj;
+  new->next = tls_dtor_list;
+  tls_dtor_list = new;
+
+  if (__builtin_expect (dso_handle_cache != dso_handle, 0))
+    {
+      ElfW(Addr) caller = (ElfW(Addr)) dso_handle;
+
+      __rtld_lock_lock_recursive (GL(dl_load_lock));
+
+      /* If the address is not recognized the call comes from the main
+         program (we hope).  */
+      struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+      /* Find the highest-addressed object that DSO_HANDLE is not below.  */
+      for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
+        for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL;
+             l = l->l_next)
+          if (caller >= l->l_map_start && caller < l->l_map_end
+              && (l->l_contiguous || _dl_addr_inside_object (l, caller)))
+            {
+              match = l;
+              break;
+            }
+
+      if (match->l_type == lt_loaded)
+        match->l_flags_1 |= DF_1_NODELETE;
+      dso_handle_cache = dso_handle;
+
+      __rtld_lock_unlock_recursive (GL(dl_load_lock));
+    }
+  return 0;
+}
+
+/* Call the destructors.  This is called either when a thread returns from the
+   initial function or when the process exits via the exit(3) function.  */
+void
+__call_tls_dtors (void)
+{
+  struct dtor_list *l = tls_dtor_list;
+
+  while (l)
+    {
+      struct dtor_list *cur = l;
+
+      l = l->next;
+      cur->func (cur->obj);
+      free (cur);
+    }
+  tls_dtor_list = NULL;
+}
diff --git a/stdlib/exit.c b/stdlib/exit.c
index 1ad548f..78cb9f5 100644
--- a/stdlib/exit.c
+++ b/stdlib/exit.c
@@ -25,7 +25,6 @@
 #include "set-hooks.h"
 DEFINE_HOOK (__libc_atexit, (void))
 
-
 /* Call all functions registered with `atexit' and `on_exit',
    in the reverse of the order in which they were registered
    perform stdio cleanup, and terminate program execution with STATUS.  */
@@ -34,6 +33,9 @@ attribute_hidden
 __run_exit_handlers (int status, struct exit_function_list **listp,
 		     bool run_list_atexit)
 {
+  /* First, call the TLS destructors.  */
+  __call_tls_dtors ();
+
   /* We do it this way to handle recursive calls to exit () made by
      the functions registered with `atexit' and `on_exit'. We call
      everyone on the list and use the status value in the last

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]