Message ID | 56129C50.3020008@redhat.com |
---|---|
State | New |
Headers | show |
Florian Weimer <fweimer@redhat.com> writes: > diff --git a/nptl/tst-thread_local1.cc b/nptl/tst-thread_local1.cc > new file mode 100644 > index 0000000..65d374b > --- /dev/null > +++ b/nptl/tst-thread_local1.cc > @@ -0,0 +1,200 @@ > +/* Test basic thread_local support. > + Copyright (C) 2015 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. This is probably not true, and we don't add contributer lines any more anyway. Andreas.
On 10/05/2015 11:50 AM, Florian Weimer wrote: > We did not have a test case which tests the thread_local support > functionality with the C++ compiler. > > I tested this on Fedora 22 (x86_64), Red Hat Enterprise Linux 7 (ppc64), > and Debian wheezy (x86_64, after commenting out AVX512 support in the > dynamic linker). The latter correctly marks the test case as UNSUPPORTED. * Why isn't the C++ compiler testsuite sufficient? Is there any reason to have this test in glibc? In a normal bootstrap you'd build glibc, you'd test thread_local destructor support via tst-tls-atexit (the part implemented by glibc), and then you'd go on to build the full set of languages in the compiler and test C++ thread_local support more fully. Why do we want to test this here? * Testing glibc thread_local destructor support? If glibc thread_local support is broken, then libstdc++-v3/configure won't detect __cxa_thread_atexit_impl and libstdc++-v3/libsupc++/atexit_thread.cc is built with alternate compiler-specific support for destructors. Similarly if the compiler you're using was built with old-enough glibc to lack that support. Therefore a pre-requisite of this test is that it must know that the existing C++ compiler is built to use glibc's __cxa_thread_atexit_impl, otherwise you're testing libsupc++'s support for thread_local destructors and the test has nothing to do with glibc? * Had a quibble with a comment in nptl/Makefile, see below. Some top-level process nits: * Don't include ChangeLog in diff. Put it into your email at the end please. I promise we'll get to the point where it's autogenerated and you won't have to care about it (same with NEWS). * Don't include regenerated file changes in diff. They make it harder to read the patch quickly, and are not needed. The committer has to regenerate the file and review what they checkin. > 0001-Add-a-test-case-for-C-11-thread_local-support.patch > > > From b2d51b5b9152d841f3476d7d5588ce302dad1853 Mon Sep 17 00:00:00 2001 > Message-Id: <b2d51b5b9152d841f3476d7d5588ce302dad1853.1444060169.git.fweimer@redhat.com> > From: Florian Weimer <fweimer@redhat.com> > Date: Mon, 5 Oct 2015 17:48:12 +0200 > Subject: [PATCH] Add a test case for C++11 thread_local support > To: libc-alpha@sourceware.org > > This requires a C++ compiler with thread_local support, and a new > configure check is needed. OK. > --- > ChangeLog | 12 +++ > config.make.in | 1 + > configure | 47 +++++++++++ > configure.ac | 25 ++++++ > nptl/Makefile | 9 ++- > nptl/tst-thread_local1.cc | 200 ++++++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 293 insertions(+), 1 deletion(-) > create mode 100644 nptl/tst-thread_local1.cc > > diff --git a/ChangeLog b/ChangeLog > index 0bfe2bb..804a2a1 100644 > --- a/ChangeLog > +++ b/ChangeLog > @@ -1,3 +1,15 @@ > +2015-10-05 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-03 Paul Pluzhnikov <ppluzhnikov@google.com> > > * sysdeps/x86_64/fpu/libm-test-ulps: Regenerated. > 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@ OK. > static-libgcc = @libc_cv_gcc_static_libgcc@ > [cut diff of configure] > diff --git a/configure.ac b/configure.ac > index 95d700e..68e6777 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -1982,6 +1982,31 @@ 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([ > +struct S > +{ > + S (); > + ~S (); > +}; > +thread_local S s; > +S * get () { return &s; } > +])], > + [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. OK. > + > ### End of automated tests. > ### Now run sysdeps configure fragments. > > diff --git a/nptl/Makefile b/nptl/Makefile > index aaca0a4..b27bef1 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++ OK. > > 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 OK. > 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 with thread_local support. This should say: # 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..65d374b > --- /dev/null > +++ b/nptl/tst-thread_local1.cc > @@ -0,0 +1,200 @@ > +/* Test basic thread_local support. It's a fine test, but I'm not sure why this needs to be in glibc and not the libstdc++ testsuite. I count 31 thread_local related tests in gcc, with 27 being specifically about thread_local. > + Copyright (C) 2015 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. > + > + 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" > -- 2.4.3 Cheers, Carlos.
On 10/05/2015 08:19 PM, Carlos O'Donell wrote: > On 10/05/2015 11:50 AM, Florian Weimer wrote: >> We did not have a test case which tests the thread_local support >> functionality with the C++ compiler. >> >> I tested this on Fedora 22 (x86_64), Red Hat Enterprise Linux 7 (ppc64), >> and Debian wheezy (x86_64, after commenting out AVX512 support in the >> dynamic linker). The latter correctly marks the test case as UNSUPPORTED. > > * Why isn't the C++ compiler testsuite sufficient? > > Is there any reason to have this test in glibc? In a normal bootstrap you'd > build glibc, you'd test thread_local destructor support via tst-tls-atexit > (the part implemented by glibc), and then you'd go on to build the full > set of languages in the compiler and test C++ thread_local support more fully. > Why do we want to test this here? I want to prevent regressions due to glibc changes. This test case covers only the basics, we also need to check interaction with other termination callback facilities in glibc (atexit, POSIX thread-local storage), and the interaction with cancellation and dlclose. We also need white-box testing for memory allocation failures. I think the dependency on thread_local inside glibc is harmless against all the baggage these tests would pull into libstdc++. This test is just a start. Basically, this test is the minimal version I was sufficiently confident would actually pass testing. > * Testing glibc thread_local destructor support? > > If glibc thread_local support is broken, then libstdc++-v3/configure won't > detect __cxa_thread_atexit_impl and libstdc++-v3/libsupc++/atexit_thread.cc > is built with alternate compiler-specific support for destructors. Similarly > if the compiler you're using was built with old-enough glibc to lack that support. > > Therefore a pre-requisite of this test is that it must know that the existing > C++ compiler is built to use glibc's __cxa_thread_atexit_impl, otherwise you're > testing libsupc++'s support for thread_local destructors and the test has nothing > to do with glibc? Hmm, you are right. It is actually a libstdc++ matter. I guess I can work around that by interposing __cxxabiv1::__cxa_thread_atexit. So maybe we should run this test twice, once as it is now, and once with the interposed symbol. Or we can change the fallback implementation of __cxxabiv1::__cxa_thread_atexit in libstdc++ to check a weak symbol __cxxabiv1::__cxa_thread_atexit_impl first and use that if it is available. Maybe this is useful on its own? > * Had a quibble with a comment in nptl/Makefile, see below. > > Some top-level process nits: > > * Don't include ChangeLog in diff. > > Put it into your email at the end please. I promise we'll get to the point > where it's autogenerated and you won't have to care about it (same with NEWS). I've been told before that either way is fine. :-/ I can certainly script something to extract the changelog and place it outside the diff. >> 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 with thread_local support. > > This should say: > # These tests require a C++ compiler and runtime with thread_local support. Fixed. > It's a fine test, but I'm not sure why this needs to be in glibc and not > the libstdc++ testsuite. I count 31 thread_local related tests in gcc, > with 27 being specifically about thread_local. See above. Florian
On 10/05/2015 07:52 PM, Andreas Schwab wrote: > Florian Weimer <fweimer@redhat.com> writes: > >> diff --git a/nptl/tst-thread_local1.cc b/nptl/tst-thread_local1.cc >> new file mode 100644 >> index 0000000..65d374b >> --- /dev/null >> +++ b/nptl/tst-thread_local1.cc >> @@ -0,0 +1,200 @@ >> +/* Test basic thread_local support. >> + Copyright (C) 2015 Free Software Foundation, Inc. >> + This file is part of the GNU C Library. >> + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. > > This is probably not true, and we don't add contributer lines any more > anyway. Fixed, thanks. I did copy this from a Copyright (C) 2015 file. Looks like I picked the only one with this problem. Florian
From b2d51b5b9152d841f3476d7d5588ce302dad1853 Mon Sep 17 00:00:00 2001 Message-Id: <b2d51b5b9152d841f3476d7d5588ce302dad1853.1444060169.git.fweimer@redhat.com> From: Florian Weimer <fweimer@redhat.com> Date: Mon, 5 Oct 2015 17:48:12 +0200 Subject: [PATCH] Add a test case for C++11 thread_local support To: libc-alpha@sourceware.org This requires a C++ compiler with thread_local support, and a new configure check is needed. --- ChangeLog | 12 +++ config.make.in | 1 + configure | 47 +++++++++++ configure.ac | 25 ++++++ nptl/Makefile | 9 ++- nptl/tst-thread_local1.cc | 200 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 nptl/tst-thread_local1.cc diff --git a/ChangeLog b/ChangeLog index 0bfe2bb..804a2a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2015-10-05 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-03 Paul Pluzhnikov <ppluzhnikov@google.com> * sysdeps/x86_64/fpu/libm-test-ulps: Regenerated. 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..0d19d05 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,52 @@ 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_CXX="$CXX" +CXX="$CXX -std=gnu++11" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +struct S { + S(); + ~S(); +}; +thread_local S s; +S * get() { return &s; } + +_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 +CXX="$old_CXX" + +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..68e6777 100644 --- a/configure.ac +++ b/configure.ac @@ -1982,6 +1982,31 @@ 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([ +struct S +{ + S (); + ~S (); +}; +thread_local S s; +S * get () { return &s; } +])], + [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..b27bef1 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 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..65d374b --- /dev/null +++ b/nptl/tst-thread_local1.cc @@ -0,0 +1,200 @@ +/* Test basic thread_local support. + Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + 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" -- 2.4.3