This patch ensures that this_thread::get_id() returns a value that is distinct from the "not a thread" value, and avoids undefined behaviour in pthread_equal.
Previously programs using glibc but not linking to libpthread would get the "not a thread" value for std::this_thread::get_id() in main. We were also using pthread_equal() with the "not a thread" value, which is undefined. This change assumes that pthread_t is EqualityComparable, but we already have to assume it's LessThanComparable and apparently that works everywhere that std::thread is supported, so this should too. If it fails then we can do something smarter like dispatch based on whether __gthread_t is an EqualityComparable scalar type, but I'd prefer to avoid that complexity if this simpler version works. Tested powerpc64le-linux, committed to trunk.
commit 64e418cb8e285bfa6079f176105a38ded456a976 Author: Jonathan Wakely <jwak...@redhat.com> Date: Fri Dec 4 13:58:56 2015 +0000 PR libstdc++/57060 cope with invalid thread IDs PR libstdc++/57060 * include/std/thread (operator==(thread::id, thread::id)): Do not use __gthread_equal. (operator<(thread::id, thread::id)): Add comment. (this_thread::get_id()): Do not use __gthread_self for single-threaded programs using glibc. * testsuite/30_threads/this_thread/57060.cc: New. diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index 8c01feb..efdd83e 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -89,11 +89,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend bool operator==(thread::id __x, thread::id __y) noexcept - { return __gthread_equal(__x._M_thread, __y._M_thread); } + { + // pthread_equal is undefined if either thread ID is not valid, so we + // can't safely use __gthread_equal on default-constructed values (nor + // the non-zero value returned by this_thread::get_id() for + // single-threaded programs using GNU libc). Assume EqualityComparable. + return __x._M_thread == __y._M_thread; + } friend bool operator<(thread::id __x, thread::id __y) noexcept - { return __x._M_thread < __y._M_thread; } + { + // Pthreads doesn't define any way to do this, so we just have to + // assume native_handle_type is LessThanComparable. + return __x._M_thread < __y._M_thread; + } template<class _CharT, class _Traits> friend basic_ostream<_CharT, _Traits>& @@ -269,7 +279,18 @@ _GLIBCXX_END_NAMESPACE_VERSION /// get_id inline thread::id - get_id() noexcept { return thread::id(__gthread_self()); } + get_id() noexcept + { +#ifdef __GLIBC__ + // For the GNU C library pthread_self() is usable without linking to + // libpthread.so but returns 0, so we cannot use it in single-threaded + // programs, because this_thread::get_id() != thread::id{} must be true. + // We know that pthread_t is an integral type in the GNU C library. + if (!__gthread_active_p()) + return thread::id(1); +#endif + return thread::id(__gthread_self()); + } /// yield inline void diff --git a/libstdc++-v3/testsuite/30_threads/this_thread/57060.cc b/libstdc++-v3/testsuite/30_threads/this_thread/57060.cc new file mode 100644 index 0000000..c932719 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/this_thread/57060.cc @@ -0,0 +1,37 @@ +// Copyright (C) 2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target *-*-gnu* } } +// { dg-options "-std=gnu++11" } +// { dg-require-gthreads "" } + +// N.B. this test intentionally does *not* use -pthread + +#include <thread> +#include <testsuite_hooks.h> + +void +test01() +{ + VERIFY( std::this_thread::get_id() != std::thread::id() ); +} + +int +main() +{ + test01(); +}