diff --git a/ChangeLog b/ChangeLog index 9b762b3a0c..ddbd26dfa3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2015-10-06 Florian Weimer + + * 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 [BZ #19078] diff --git a/config.make.in b/config.make.in index bea371d1f0..839d86fa0c 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 fe402ea05b..f0cd17580c 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 + +// 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 95d700e835..75a3a77677 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 + +// 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 aaca0a4700..f3de49bb4d 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 0000000000..133cc27ca8 --- /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 + . */ + +#include +#include +#include +#include + +#include +#include +#include + +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 +struct counting +{ + counting () __attribute__ ((noinline, noclone)); + ~counting () __attribute__ ((noinline, noclone)); + void operation () __attribute__ ((noinline, noclone)); +}; + +template +__attribute__ ((noinline, noclone)) +counting::counting () +{ + ++Counter->constructed; +} + +template +__attribute__ ((noinline, noclone)) +counting::~counting () +{ + ++Counter->destructed; +} + +template +void __attribute__ ((noinline, noclone)) +counting::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 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 do_std_thread = + [](void *(func) (void *)) + { + std::thread thr{[func] {func (nullptr);}}; + thr.join (); + }; + + std::array>, 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"