Message ID | 56141D6F.4060306@redhat.com |
---|---|
State | New |
Headers | show |
On 10/06/2015 03:13 PM, Florian Weimer wrote: > On 10/06/2015 09:09 PM, Carlos O'Donell wrote: >> > On 10/06/2015 07:18 AM, Florian Weimer wrote: >>> >> I went with a check on an internal libstdc++ macro. The test now >>> >> remains UNSUPPORTED if the compiler supports it, but libstdc++ has >>> >> emuluation only (based on its header file). >> > >> > Did you attach the wrong patch? This v2 patch still has a contributed-by >> > line and no such libstdc++ macro check? > Sorry, must have forgotten to regenerate it before manually editing it > before submission. Perfect. Please check this in. I'm happy to have some early warning with respect to C++ integration issues in glibc, particularly given how much support you need from glibc to get it right. > 0001-Add-a-test-case-for-C-11-thread_local-support.patch > > > 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. > > 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.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" >
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. 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.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"