From patchwork Tue Oct 6 11:18:07 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 526705 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 99E46140D6C for ; Tue, 6 Oct 2015 22:18:27 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=G86wA7DI; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:subject:to:references:from:message-id:date :mime-version:in-reply-to:content-type; q=dns; s=default; b=qrFk BELJYpG7b/+ni8rHsmCqqhLuE301z9WGowbpSKumIHeOStR5x9prY8NWx6brTude UXhHfKveWi8YUign6KnZzPvrqR9nXRj8al5IGkiy/8TQHgpI4Hkwt79Fvu8lP8jF qVwJlG04QQapCB82kj7nlQpsLiTxrTZ2IFiyYkc= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:subject:to:references:from:message-id:date :mime-version:in-reply-to:content-type; s=default; bh=sFcWjQD3IU mrwk3spf6NiVnRmSk=; b=G86wA7DINxumeiZt7VcJpHAwqvZXTxWI9jk7EL7iqE qXWZh/A7kW1mzRIph8Z7wk5IWO7FDYc+iN7L8zuD/265YJVa7Fw9WKV5xxjJ559d yWcaIka3y5wMAJcbHDj+uL6Xs9SSJO1BMQF+BqCGu509R3eg1PI8/jF21D4IBMQY M= Received: (qmail 119519 invoked by alias); 6 Oct 2015 11:18:20 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 119497 invoked by uid 89); 6 Oct 2015 11:18:19 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.3 required=5.0 tests=AWL, BAYES_50, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Subject: [PATCH v2] Add test case for C++11 thread_local support To: "Carlos O'Donell" , GNU C Library References: <56129C50.3020008@redhat.com> <5612BF4E.4080202@redhat.com> <5612C369.6040203@redhat.com> From: Florian Weimer X-Enigmail-Draft-Status: N1110 Message-ID: <5613ADEF.3050102@redhat.com> Date: Tue, 6 Oct 2015 13:18:07 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0 MIME-Version: 1.0 In-Reply-To: <5612C369.6040203@redhat.com> On 10/05/2015 08:37 PM, Florian Weimer wrote: >> * 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? 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). I looked at the GCC tests. Cancellation is already tested. dlclose, atexit, and pthread_key_create interactions are not, as far as I can see. I don't think we test thread_local access from a thread_local object destructor, either. Florian From: Florian Weimer Subject: [PATCH] Add a test case for C++11 thread_local support To: libc-alpha@sourceware.org 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. 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..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 , 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 + . */ + +#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" -- 2.4.3