This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [PATCH v2] Add test case for C++11 thread_local support
- From: "Carlos O'Donell" <carlos at redhat dot com>
- To: Florian Weimer <fweimer at redhat dot com>, GNU C Library <libc-alpha at sourceware dot org>
- Date: Tue, 6 Oct 2015 15:16:24 -0400
- Subject: Re: [PATCH v2] Add test case for C++11 thread_local support
- Authentication-results: sourceware.org; auth=none
- References: <56129C50 dot 3020008 at redhat dot com> <5612BF4E dot 4080202 at redhat dot com> <5612C369 dot 6040203 at redhat dot com> <5613ADEF dot 3050102 at redhat dot com> <56141C85 dot 4040601 at redhat dot com> <56141D6F dot 4060306 at redhat dot com>
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"
>