aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2015-10-06 21:27:55 +0200
committerFlorian Weimer <fweimer@redhat.com>2015-10-06 21:27:55 +0200
commit99e1dc0a688d6c25d3f422bc9f3fa29adb483339 (patch)
tree425b9d0a7670515c8ca8cbbbde0717a1fa180b96
parent6c9678ebd42358f931100130a368fafe375a0ba2 (diff)
downloadglibc-99e1dc0a688d6c25d3f422bc9f3fa29adb483339.zip
glibc-99e1dc0a688d6c25d3f422bc9f3fa29adb483339.tar.gz
glibc-99e1dc0a688d6c25d3f422bc9f3fa29adb483339.tar.bz2
Add a test case for C++11 thread_local support
This requires a C++ compiler with thread_local support, and a new configure check is needed.
-rw-r--r--ChangeLog12
-rw-r--r--config.make.in1
-rwxr-xr-xconfigure56
-rw-r--r--configure.ac33
-rw-r--r--nptl/Makefile9
-rw-r--r--nptl/tst-thread_local1.cc199
6 files changed, 309 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 9b762b3..ddbd26d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2015-10-06 Florian Weimer <fweimer@redhat.com>
+
+ * configure.ac (libc_cv_cxx_thread_local): Define.
+ * configure: Regenerate.
+ * config.make.in (have-cxx-thread_local): Define.
+ * nptl/Makefile (CFLAGS-tst-thread_local1.o):
+ (LDLIBS-tst-thread_local1): Define.
+ (tests): Add tst-thread_local1.
+ [have-cxx-thread_local != yes] (tests-unsupported): Add
+ tst-thread_local1.
+ * nptl/tst-thread_local1.cc: New file.
+
2015-10-06 Joseph Myers <joseph@codesourcery.com>
[BZ #19078]
diff --git a/config.make.in b/config.make.in
index bea371d..839d86f 100644
--- a/config.make.in
+++ b/config.make.in
@@ -68,6 +68,7 @@ bind-now = @bindnow@
have-hash-style = @libc_cv_hashstyle@
use-default-link = @use_default_link@
output-format = @libc_cv_output_format@
+have-cxx-thread_local = @libc_cv_cxx_thread_local@
static-libgcc = @libc_cv_gcc_static_libgcc@
diff --git a/configure b/configure
index fe402ea..f0cd175 100755
--- a/configure
+++ b/configure
@@ -614,6 +614,7 @@ use_nscd
libc_cv_gcc_unwind_find_fde
libc_extra_cppflags
libc_extra_cflags
+libc_cv_cxx_thread_local
CPPUNDEFS
sizeof_long_double
have_selinux
@@ -7215,6 +7216,61 @@ if test $libc_cv_builtin_trap = yes; then
fi
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler supports thread_local" >&5
+$as_echo_n "checking whether the C++ compiler supports thread_local... " >&6; }
+if ${libc_cv_cxx_thread_local+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+old_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS="$CXXFLAGS -std=gnu++11"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <thread>
+
+// Compiler support.
+struct S
+{
+ S ();
+ ~S ();
+};
+thread_local S s;
+S * get () { return &s; }
+
+// libstdc++ support.
+#ifndef _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL
+#error __cxa_thread_atexit_impl not supported
+#endif
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ libc_cv_cxx_thread_local=yes
+else
+ libc_cv_cxx_thread_local=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+CXXFLAGS="$old_CXXFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_cxx_thread_local" >&5
+$as_echo "$libc_cv_cxx_thread_local" >&6; }
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
### End of automated tests.
### Now run sysdeps configure fragments.
diff --git a/configure.ac b/configure.ac
index 95d700e..75a3a77 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1982,6 +1982,39 @@ if test $libc_cv_builtin_trap = yes; then
AC_DEFINE([HAVE_BUILTIN_TRAP])
fi
+dnl C++ feature tests.
+AC_LANG_PUSH([C++])
+
+AC_CACHE_CHECK([whether the C++ compiler supports thread_local],
+ libc_cv_cxx_thread_local, [
+old_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS="$CXXFLAGS -std=gnu++11"
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#include <thread>
+
+// Compiler support.
+struct S
+{
+ S ();
+ ~S ();
+};
+thread_local S s;
+S * get () { return &s; }
+
+// libstdc++ support.
+#ifndef _GLIBCXX_HAVE___CXA_THREAD_ATEXIT_IMPL
+#error __cxa_thread_atexit_impl not supported
+#endif
+])],
+ [libc_cv_cxx_thread_local=yes],
+ [libc_cv_cxx_thread_local=no])
+CXXFLAGS="$old_CXXFLAGS"
+])
+AC_SUBST(libc_cv_cxx_thread_local)
+
+AC_LANG_POP([C++])
+dnl End of C++ feature tests.
+
### End of automated tests.
### Now run sysdeps configure fragments.
diff --git a/nptl/Makefile b/nptl/Makefile
index aaca0a4..f3de49b 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -212,6 +212,8 @@ CFLAGS-recvfrom.c = -fexceptions -fasynchronous-unwind-tables
CFLAGS-pt-system.c = -fexceptions
LDLIBS-tst-once5 = -lstdc++
+CFLAGS-tst-thread_local1.o = -std=gnu++11
+LDLIBS-tst-thread_local1 = -lstdc++
tests = tst-typesizes \
tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
@@ -283,7 +285,8 @@ tests = tst-typesizes \
tst-getpid3 \
tst-setuid3 \
tst-initializers1 $(addprefix tst-initializers1-,c89 gnu89 c99 gnu99) \
- tst-bad-schedattr
+ tst-bad-schedattr \
+ tst-thread_local1
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
test-srcs = tst-oddstacklimit
@@ -403,6 +406,10 @@ ifeq (,$(CXX))
# These tests require a C++ compiler and runtime.
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5
endif
+# These tests require a C++ compiler and runtime with thread_local support.
+ifneq ($(have-cxx-thread_local),yes)
+tests-unsupported += tst-thread_local1
+endif
include ../Rules
diff --git a/nptl/tst-thread_local1.cc b/nptl/tst-thread_local1.cc
new file mode 100644
index 0000000..133cc27
--- /dev/null
+++ b/nptl/tst-thread_local1.cc
@@ -0,0 +1,199 @@
+/* Test basic thread_local support.
+ Copyright (C) 2015 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 <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <functional>
+#include <string>
+#include <thread>
+
+struct counter
+{
+ int constructed {};
+ int destructed {};
+
+ void reset ();
+};
+
+void
+counter::reset ()
+{
+ constructed = 0;
+ destructed = 0;
+}
+
+static std::string
+to_string (const counter &c)
+{
+ char buf[128];
+ snprintf (buf, sizeof (buf), "%d/%d",
+ c.constructed, c.destructed);
+ return buf;
+}
+
+template <counter *Counter>
+struct counting
+{
+ counting () __attribute__ ((noinline, noclone));
+ ~counting () __attribute__ ((noinline, noclone));
+ void operation () __attribute__ ((noinline, noclone));
+};
+
+template<counter *Counter>
+__attribute__ ((noinline, noclone))
+counting<Counter>::counting ()
+{
+ ++Counter->constructed;
+}
+
+template<counter *Counter>
+__attribute__ ((noinline, noclone))
+counting<Counter>::~counting ()
+{
+ ++Counter->destructed;
+}
+
+template<counter *Counter>
+void __attribute__ ((noinline, noclone))
+counting<Counter>::operation ()
+{
+ // Optimization barrier.
+ asm ("");
+}
+
+static counter counter_static;
+static counter counter_anonymous_namespace;
+static counter counter_extern;
+static counter counter_function_local;
+static bool errors (false);
+
+static std::string
+all_counters ()
+{
+ return to_string (counter_static)
+ + ' ' + to_string (counter_anonymous_namespace)
+ + ' ' + to_string (counter_extern)
+ + ' ' + to_string (counter_function_local);
+}
+
+static void
+check_counters (const char *name, const char *expected)
+{
+ std::string actual{all_counters ()};
+ if (actual != expected)
+ {
+ printf ("error: %s: (%s) != (%s)\n",
+ name, actual.c_str (), expected);
+ errors = true;
+ }
+}
+
+static void
+reset_all ()
+{
+ counter_static.reset ();
+ counter_anonymous_namespace.reset ();
+ counter_extern.reset ();
+ counter_function_local.reset ();
+}
+
+static thread_local counting<&counter_static> counting_static;
+namespace {
+ thread_local counting<&counter_anonymous_namespace>
+ counting_anonymous_namespace;
+}
+extern thread_local counting<&counter_extern> counting_extern;
+thread_local counting<&counter_extern> counting_extern;
+
+static void *
+thread_without_access (void *)
+{
+ return nullptr;
+}
+
+static void *
+thread_with_access (void *)
+{
+ thread_local counting<&counter_function_local> counting_function_local;
+ counting_function_local.operation ();
+ check_counters ("early in thread_with_access", "0/0 0/0 0/0 1/0");
+ counting_static.operation ();
+ counting_anonymous_namespace.operation ();
+ counting_extern.operation ();
+ check_counters ("in thread_with_access", "1/0 1/0 1/0 1/0");
+ return nullptr;
+}
+
+static int
+do_test (void)
+{
+ std::function<void (void *(void *))> do_pthread =
+ [](void *(func) (void *))
+ {
+ pthread_t thr;
+ int ret = pthread_create (&thr, nullptr, func, nullptr);
+ if (ret != 0)
+ {
+ errno = ret;
+ printf ("error: pthread_create: %m\n");
+ errors = true;
+ return;
+ }
+ ret = pthread_join (thr, nullptr);
+ if (ret != 0)
+ {
+ errno = ret;
+ printf ("error: pthread_join: %m\n");
+ errors = true;
+ return;
+ }
+ };
+ std::function<void (void *(void *))> do_std_thread =
+ [](void *(func) (void *))
+ {
+ std::thread thr{[func] {func (nullptr);}};
+ thr.join ();
+ };
+
+ std::array<std::pair<const char *, std::function<void (void *(void *))>>, 2>
+ do_thread_X
+ {{
+ {"pthread_create", do_pthread},
+ {"std::thread", do_std_thread},
+ }};
+
+ for (auto do_thread : do_thread_X)
+ {
+ printf ("info: testing %s\n", do_thread.first);
+ check_counters ("initial", "0/0 0/0 0/0 0/0");
+ do_thread.second (thread_without_access);
+ check_counters ("after thread_without_access", "0/0 0/0 0/0 0/0");
+ reset_all ();
+ do_thread.second (thread_with_access);
+ check_counters ("after thread_with_access", "1/1 1/1 1/1 1/1");
+ reset_all ();
+ }
+
+ return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"