Add a test case for C++11 thread_local support
This requires a C++ compiler with thread_local support, and a new configure check is needed.
This commit is contained in:
parent
6c9678ebd4
commit
99e1dc0a68
12
ChangeLog
12
ChangeLog
|
@ -1,3 +1,15 @@
|
||||||
|
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.
|
||||||
|
|
||||||
2015-10-06 Joseph Myers <joseph@codesourcery.com>
|
2015-10-06 Joseph Myers <joseph@codesourcery.com>
|
||||||
|
|
||||||
[BZ #19078]
|
[BZ #19078]
|
||||||
|
|
|
@ -68,6 +68,7 @@ bind-now = @bindnow@
|
||||||
have-hash-style = @libc_cv_hashstyle@
|
have-hash-style = @libc_cv_hashstyle@
|
||||||
use-default-link = @use_default_link@
|
use-default-link = @use_default_link@
|
||||||
output-format = @libc_cv_output_format@
|
output-format = @libc_cv_output_format@
|
||||||
|
have-cxx-thread_local = @libc_cv_cxx_thread_local@
|
||||||
|
|
||||||
static-libgcc = @libc_cv_gcc_static_libgcc@
|
static-libgcc = @libc_cv_gcc_static_libgcc@
|
||||||
|
|
||||||
|
|
56
configure
vendored
56
configure
vendored
|
@ -614,6 +614,7 @@ use_nscd
|
||||||
libc_cv_gcc_unwind_find_fde
|
libc_cv_gcc_unwind_find_fde
|
||||||
libc_extra_cppflags
|
libc_extra_cppflags
|
||||||
libc_extra_cflags
|
libc_extra_cflags
|
||||||
|
libc_cv_cxx_thread_local
|
||||||
CPPUNDEFS
|
CPPUNDEFS
|
||||||
sizeof_long_double
|
sizeof_long_double
|
||||||
have_selinux
|
have_selinux
|
||||||
|
@ -7215,6 +7216,61 @@ if test $libc_cv_builtin_trap = yes; then
|
||||||
|
|
||||||
fi
|
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 <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
|
||||||
|
|
||||||
|
_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.
|
### End of automated tests.
|
||||||
### Now run sysdeps configure fragments.
|
### Now run sysdeps configure fragments.
|
||||||
|
|
||||||
|
|
33
configure.ac
33
configure.ac
|
@ -1982,6 +1982,39 @@ if test $libc_cv_builtin_trap = yes; then
|
||||||
AC_DEFINE([HAVE_BUILTIN_TRAP])
|
AC_DEFINE([HAVE_BUILTIN_TRAP])
|
||||||
fi
|
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.
|
### End of automated tests.
|
||||||
### Now run sysdeps configure fragments.
|
### Now run sysdeps configure fragments.
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,8 @@ CFLAGS-recvfrom.c = -fexceptions -fasynchronous-unwind-tables
|
||||||
CFLAGS-pt-system.c = -fexceptions
|
CFLAGS-pt-system.c = -fexceptions
|
||||||
|
|
||||||
LDLIBS-tst-once5 = -lstdc++
|
LDLIBS-tst-once5 = -lstdc++
|
||||||
|
CFLAGS-tst-thread_local1.o = -std=gnu++11
|
||||||
|
LDLIBS-tst-thread_local1 = -lstdc++
|
||||||
|
|
||||||
tests = tst-typesizes \
|
tests = tst-typesizes \
|
||||||
tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
|
tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
|
||||||
|
@ -283,7 +285,8 @@ tests = tst-typesizes \
|
||||||
tst-getpid3 \
|
tst-getpid3 \
|
||||||
tst-setuid3 \
|
tst-setuid3 \
|
||||||
tst-initializers1 $(addprefix tst-initializers1-,c89 gnu89 c99 gnu99) \
|
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 \
|
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
|
||||||
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
|
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
|
||||||
test-srcs = tst-oddstacklimit
|
test-srcs = tst-oddstacklimit
|
||||||
|
@ -403,6 +406,10 @@ ifeq (,$(CXX))
|
||||||
# These tests require a C++ compiler and runtime.
|
# These tests require a C++ compiler and runtime.
|
||||||
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5
|
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5
|
||||||
endif
|
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
|
include ../Rules
|
||||||
|
|
||||||
|
|
199
nptl/tst-thread_local1.cc
Normal file
199
nptl/tst-thread_local1.cc
Normal file
|
@ -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"
|
Loading…
Reference in a new issue