* [PATCH] Add support for C++2a stop_token
@ 2019-10-23 0:59 Thomas Rodgers
2019-10-23 19:50 ` Thomas Rodgers
0 siblings, 1 reply; 10+ messages in thread
From: Thomas Rodgers @ 2019-10-23 0:59 UTC (permalink / raw)
To: gcc-patches, libstdc++
[-- Attachment #1: le patch --]
[-- Type: x-epoc/x-sisx-app, Size: 17865 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-10-23 0:59 [PATCH] Add support for C++2a stop_token Thomas Rodgers
@ 2019-10-23 19:50 ` Thomas Rodgers
2019-10-24 15:45 ` Jonathan Wakely
0 siblings, 1 reply; 10+ messages in thread
From: Thomas Rodgers @ 2019-10-23 19:50 UTC (permalink / raw)
To: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 49 bytes --]
Thomas Rodgers writes:
Let's try this again.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Le Patch --]
[-- Type: text/x-patch, Size: 18458 bytes --]
From 23e1c9402cc15666d099fd61b58a0019181a9115 Mon Sep 17 00:00:00 2001
From: Thomas Rodgers <trodgers@trodgers.remote.f30>
Date: Tue, 22 Oct 2019 17:53:00 -0700
Subject: [PATCH] Add support for C++2a stop_token
* include/Makefile.am: Add <stop_token> header.
* include/Makefile.in: Regenerate.
* include/std/stop_token: New file.
* include/std/version (__cpp_lib_jthread): New value.
* testsuite/30_threads/stop_token/1.cc: New test.
* testsuite/30_threads/stop_token/2.cc: New test.
* testsuite/30_threads/stop_token/stop_token.cc: New test.
---
libstdc++-v3/ChangeLog | 10 +
libstdc++-v3/include/Makefile.am | 1 +
libstdc++-v3/include/Makefile.in | 1 +
libstdc++-v3/include/std/stop_token | 338 ++++++++++++++++++
libstdc++-v3/include/std/version | 1 +
.../testsuite/30_threads/stop_token/1.cc | 27 ++
.../testsuite/30_threads/stop_token/2.cc | 27 ++
.../30_threads/stop_token/stop_token.cc | 93 +++++
8 files changed, 498 insertions(+)
create mode 100644 libstdc++-v3/include/std/stop_token
create mode 100644 libstdc++-v3/testsuite/30_threads/stop_token/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/stop_token/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 2ea0fe4ec40..970c5c2a018 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,13 @@
+2019-10-22 Thomas Rodgers <trodgers@redhat.com>
+
+ * include/Makefile.am: Add <stop_token> header.
+ * include/Makefile.in: Regenerate.
+ * include/std/stop_token: New file.
+ * include/std/version (__cpp_lib_jthread): New value.
+ * testsuite/30_threads/stop_token/1.cc: New test.
+ * testsuite/30_threads/stop_token/2.cc: New test.
+ * testsuite/30_threads/stop_token/stop_token.cc: New test.
+
2019-09-05 Jonathan Wakely <jwakely@redhat.com>
* doc/xml/manual/status_cxx2020.xml: Update status for P0122R7 and
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index b8b786d9260..fb6777366bd 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -72,6 +72,7 @@ std_headers = \
${std_srcdir}/sstream \
${std_srcdir}/stack \
${std_srcdir}/stdexcept \
+ ${std_srcdir}/stop_token \
${std_srcdir}/streambuf \
${std_srcdir}/string \
${std_srcdir}/string_view \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index cd1e9df5482..9b4ab670315 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -416,6 +416,7 @@ std_headers = \
${std_srcdir}/sstream \
${std_srcdir}/stack \
${std_srcdir}/stdexcept \
+ ${std_srcdir}/stop_token \
${std_srcdir}/streambuf \
${std_srcdir}/string \
${std_srcdir}/string_view \
diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token
new file mode 100644
index 00000000000..b3655b85eae
--- /dev/null
+++ b/libstdc++-v3/include/std/stop_token
@@ -0,0 +1,338 @@
+// <stop_token> -*- C++ -*-
+
+// Copyright (C) 2008-2019 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/stop_token
+ * This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_STOP_TOKEN
+#define _GLIBCXX_STOP_TOKEN
+
+#include <type_traits>
+#include <memory>
+#include <mutex>
+#include <atomic>
+
+#define __cpp_lib_jthread 201907L
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class stop_source;
+ template<class _Callback>
+ class stop_callback;
+
+ struct nostopstate_t { explicit nostopstate_t() = default; };
+ inline constexpr nostopstate_t nostopstate();
+
+ class stop_token {
+ public:
+ stop_token() noexcept = default;
+
+ stop_token(const stop_token& __other) noexcept
+ : _M_state(__other._M_state)
+ { }
+
+ stop_token(stop_token&& __other) noexcept
+ : _M_state(std::move(__other._M_state))
+ { }
+
+ ~stop_token() = default;
+
+ stop_token&
+ operator=(const stop_token& __rhs) noexcept {
+ _M_state = __rhs._M_state;
+ return *this;
+ }
+
+ stop_token&
+ operator=(stop_token&& __rhs) noexcept {
+ std::swap(_M_state, __rhs._M_state);
+ return *this;
+ }
+
+ [[nodiscard]]
+ bool
+ stop_possible() const noexcept
+ {
+ return static_cast<bool>(_M_state);
+ }
+
+ [[nodiscard]]
+ bool
+ stop_requested() const noexcept
+ {
+ return stop_possible() && _M_state->_M_stop_requested();
+ }
+
+ private:
+ friend stop_source;
+ template<typename _Callback>
+ friend class stop_callback;
+
+ struct _Stop_cb {
+ void(*_M_callback)(_Stop_cb*);
+ _Stop_cb* _M_prev = nullptr;
+ _Stop_cb* _M_next = nullptr;
+
+ template<typename _CB>
+ _Stop_cb(_CB&& __cb)
+ : _M_callback(std::move(__cb))
+ { }
+
+ static void
+ _S_execute(_Stop_cb* __cb) noexcept
+ {
+ __cb->_M_callback(__cb);
+ }
+ };
+
+ struct _Stop_state_t {
+ std::atomic<bool> _M_stopped;
+ std::mutex _M_mtx;
+ _Stop_cb* _M_head = nullptr;
+
+ _Stop_state_t()
+ : _M_stopped{false}
+ { }
+
+ bool
+ _M_stop_requested()
+ {
+ return _M_stopped;
+ }
+
+ bool
+ _M_request_stop()
+ {
+ bool __stopped = false;
+ if (_M_stopped.compare_exchange_strong(__stopped, true))
+ {
+ std::unique_lock<std::mutex> __lck{_M_mtx};
+ while (auto __p = _M_head)
+ {
+ _M_head = __p->_M_next;
+ _Stop_cb::_S_execute(__p);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool
+ _M_register_callback(_Stop_cb* __cb)
+ {
+ std::unique_lock<std::mutex> __lck{_M_mtx};
+ if (_M_stopped)
+ return false;
+
+ __cb->_M_next = _M_head;
+ _M_head->_M_prev = __cb;
+ _M_head = __cb;
+ return true;
+ }
+
+ void
+ _M_remove_callback(_Stop_cb* __cb)
+ {
+ std::unique_lock<std::mutex> __lck{_M_mtx};
+ if (__cb == _M_head)
+ {
+ _M_head = _M_head->_M_next;
+ _M_head->_M_prev = nullptr;
+ }
+ else
+ {
+ __cb->_M_prev->_M_next = __cb->_M_next;
+ if (__cb->_M_next)
+ {
+ __cb->_M_next->_M_prev = __cb->_M_prev;
+ }
+ }
+ }
+ };
+
+ using _Stop_state = std::shared_ptr<_Stop_state_t>;
+ _Stop_state _M_state;
+
+ explicit stop_token(_Stop_state __state)
+ : _M_state{std::move(__state)}
+ { }
+ };
+
+ class stop_source {
+ using _Stop_state_t = stop_token::_Stop_state_t;
+ using _Stop_state = stop_token::_Stop_state;
+
+ public:
+ stop_source()
+ : _M_state(std::make_shared<_Stop_state_t>())
+ { }
+
+ explicit stop_source(std::nostopstate_t) noexcept
+ { }
+
+ stop_source(const stop_source& __other) noexcept
+ : _M_state(__other._M_state)
+ { }
+
+ stop_source(stop_source&& __other) noexcept
+ : _M_state(std::move(__other._M_state))
+ { }
+
+ stop_source&
+ operator=(const stop_source& __rhs) noexcept
+ {
+ if (_M_state != __rhs._M_state)
+ _M_state = __rhs._M_state;
+ return *this;
+ }
+
+ stop_source&
+ operator=(stop_source&& __rhs) noexcept
+ {
+ std::swap(_M_state, __rhs._M_state);
+ return *this;
+ }
+
+ [[nodiscard]]
+ bool
+ stop_possible() const noexcept
+ {
+ return static_cast<bool>(_M_state);
+ }
+
+ [[nodiscard]]
+ bool
+ stop_requested() const noexcept
+ {
+ return stop_possible() && _M_state->_M_stop_requested();
+ }
+
+ bool
+ request_stop() const noexcept
+ {
+ if (stop_possible())
+ return _M_state->_M_request_stop();
+ return false;
+ }
+
+ [[nodiscard]]
+ stop_token
+ get_token() const noexcept
+ {
+ return stop_token{_M_state};
+ }
+
+ void
+ swap(stop_source& __other) noexcept
+ {
+ std::swap(_M_state, __other._M_state);
+ }
+
+ [[nodiscard]]
+ friend bool
+ operator==(const stop_source& __a, const stop_source& __b) noexcept
+ {
+ return __a._M_state == __b._M_state;
+ }
+
+ [[nodiscard]]
+ friend bool
+ operator!=(const stop_source& __a, const stop_source& __b) noexcept
+ {
+ return __a._M_state != __b._M_state;
+ }
+
+ private:
+ _Stop_state _M_state;
+ };
+
+ template<typename _Callback>
+ class [[nodiscard]] stop_callback
+ : private stop_token::_Stop_cb {
+ using _Stop_cb = stop_token::_Stop_cb;
+ using _Stop_state = stop_token::_Stop_state;
+ public:
+ using callback_type = _Callback;
+
+ template<typename _CB,
+ std::enable_if_t<std::is_constructible_v<_Callback, _CB>, int> = 0>
+ explicit stop_callback(const stop_token& __token, _CB&& __cb)
+ noexcept(std::is_nothrow_constructible_v<_Callback, _CB>)
+ : _Stop_cb([](_Stop_cb* __that) noexcept
+ {
+ static_cast<stop_callback*>(__that)->_M_execute();
+ }),
+ _M_cb(std::move(__cb))
+ {
+ if (__token._M_state && __token._M_state->_M_register_callback(this))
+ {
+ _M_state = __token._M_state;
+ }
+ }
+
+ template<typename _CB,
+ std::enable_if_t<std::is_constructible_v<_Callback, _CB>, int> = 0>
+ explicit stop_callback(stop_token&& __token, _CB&& __cb)
+ noexcept(std::is_nothrow_constructible_v<_Callback, _CB>)
+ : _Stop_cb([](_Stop_cb* __that) noexcept
+ {
+ static_cast<stop_callback*>(__that)->_M_execute();
+ }),
+ _M_cb(std::move(__cb))
+ {
+ if (__token._M_state && __token._M_state->_M_register_callback(this))
+ {
+ std::swap(_M_state, __token._M_state);
+ }
+ }
+
+ ~stop_callback()
+ {
+ if (_M_state)
+ _M_state->_M_remove_callback(this);
+ }
+
+ stop_callback(const stop_callback&) = delete;
+ stop_callback& operator=(const stop_callback&) = delete;
+ stop_callback(stop_callback&&) = delete;
+ stop_callback& operator=(stop_callback&&) = delete;
+
+ private:
+ _Callback _M_cb;
+ _Stop_state _M_state = nullptr;
+
+ void
+ _M_execute() noexcept
+ {
+ _M_cb();
+ }
+ };
+ _GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+
+;
+#endif // _GLIBCXX_STOP_TOKEN
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 7625d397a0d..9fb190537f8 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -168,6 +168,7 @@
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_span 201902L
#define __cpp_lib_to_array 201907L
+#define __cpp_lib_jthread 201907L
#endif // C++2a
#endif // C++17
#endif // C++14
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/1.cc b/libstdc++-v3/testsuite/30_threads/stop_token/1.cc
new file mode 100644
index 00000000000..4c0e7a33d25
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/1.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <stop_token>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <stop_token>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <stop_token>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/2.cc b/libstdc++-v3/testsuite/30_threads/stop_token/2.cc
new file mode 100644
index 00000000000..621965c8910
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <version>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
new file mode 100644
index 00000000000..96390918c84
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
@@ -0,0 +1,93 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <stop_token>
+#include <cassert>
+
+int main()
+{
+ // create stop_source
+ std::stop_source ssrc;
+ assert(ssrc.stop_possible());
+ assert(!ssrc.stop_requested());
+
+ // create stop_token from stop_source
+ std::stop_token stok{ssrc.get_token()};
+ assert(ssrc.stop_possible());
+ assert(!ssrc.stop_requested());
+ assert(stok.stop_possible());
+ assert(!stok.stop_requested());
+
+ // register callback
+ bool cb1called{false};
+ auto cb1 = [&]{
+ cb1called = true;
+ };
+ {
+ std::stop_callback<decltype(cb1)> scb1{stok, cb1}; // copies cb1
+ assert(ssrc.stop_possible());
+ assert(!ssrc.stop_requested());
+ assert(stok.stop_possible());
+ assert(!stok.stop_requested());
+ assert(!cb1called);
+ } // unregister callback
+
+ // register another callback
+ bool cb2called{false};
+ auto cb2 = [&]{
+ assert(stok.stop_requested());
+ cb2called = true;
+ };
+ std::stop_callback<decltype(cb2)> scb2a{stok, cb2}; // copies cb2
+ std::stop_callback<decltype(cb2)> scb2b{stok, std::move(cb2)};
+ assert(ssrc.stop_possible());
+ assert(!ssrc.stop_requested());
+ assert(stok.stop_possible());
+ assert(!stok.stop_requested());
+ assert(!cb1called);
+ assert(!cb2called);
+
+ // request stop
+ auto b = ssrc.request_stop();
+ assert(b);
+ assert(ssrc.stop_possible());
+ assert(ssrc.stop_requested());
+ assert(stok.stop_possible());
+ assert(stok.stop_requested());
+ assert(!cb1called);
+ assert(cb2called);
+
+ b = ssrc.request_stop();
+ assert(!b);
+
+ // register another callback
+ bool cb3called{false};
+ auto cb3 = [&]{
+ cb3called = true;
+ };
+ std::stop_callback<decltype(cb3)> scb3{stok, cb3};
+ assert(ssrc.stop_possible());
+ assert(ssrc.stop_requested());
+ assert(stok.stop_possible());
+ assert(stok.stop_requested());
+ assert(!cb1called);
+ assert(cb2called);
+ assert(cb3called);
+}
--
2.21.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-10-23 19:50 ` Thomas Rodgers
@ 2019-10-24 15:45 ` Jonathan Wakely
2019-11-14 2:00 ` Thomas Rodgers
0 siblings, 1 reply; 10+ messages in thread
From: Jonathan Wakely @ 2019-10-24 15:45 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
On 23/10/19 12:50 -0700, Thomas Rodgers wrote:
>
>Thomas Rodgers writes:
>
>Let's try this again.
That's better, thanks :-)
>+ * include/Makefile.am: Add <stop_token> header.
>+ * include/Makefile.in: Regenerate.
>+ * include/std/stop_token: New file.
>+ * include/std/version (__cpp_lib_jthread): New value.
>+ * testsuite/30_threads/stop_token/1.cc: New test.
>+ * testsuite/30_threads/stop_token/2.cc: New test.
>+ * testsuite/30_threads/stop_token/stop_token.cc: New test.
ChangeLog entries should be provided separately from the patch (e.g.
inline in the makefile, or as a separte attachment) because they never
apply cleanly.
Also it looks like you have a mixture of tabs and space there, it
should be only tabs.
>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>index cd1e9df5482..9b4ab670315 100644
>--- a/libstdc++-v3/include/Makefile.in
>+++ b/libstdc++-v3/include/Makefile.in
>@@ -416,6 +416,7 @@ std_headers = \
> ${std_srcdir}/sstream \
> ${std_srcdir}/stack \
> ${std_srcdir}/stdexcept \
>+ ${std_srcdir}/stop_token \
> ${std_srcdir}/streambuf \
> ${std_srcdir}/string \
> ${std_srcdir}/string_view \
Generated files like Makefile.in don't need to be in the patch. I use
a git alias called 'filter' to filter them out:
!f(){ git $@ | filterdiff -x '*/ChangeLog' -x '*/Makefile.in' -x '*/configure' -x '*/config.h.in' -x '*/doc/html/*' | sed -e '/^diff.*ChangeLog/{N;d}' ; }; f
And then I create a patch with this alias:
!f(){ git filter show --diff-algorithm=histogram ${*:-HEAD} > /dev/shm/patch.txt; }; f
>diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token
>new file mode 100644
>index 00000000000..b3655b85eae
>--- /dev/null
>+++ b/libstdc++-v3/include/std/stop_token
>@@ -0,0 +1,338 @@
>+// <stop_token> -*- C++ -*-
>+
>+// Copyright (C) 2008-2019 Free Software Foundation, Inc.
The copyright date should be just 2019.
>+//
>+// 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.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/stop_token
>+ * This is a Standard C++ Library header.
>+ */
>+
>+#ifndef _GLIBCXX_STOP_TOKEN
>+#define _GLIBCXX_STOP_TOKEN
>+
Please add:
#if __cplusplus > 201703L
here (and the corresponding #endif before the final #endif) so that
this file is empty when included pre-C++20.
>+#include <type_traits>
>+#include <memory>
>+#include <mutex>
>+#include <atomic>
>+
>+#define __cpp_lib_jthread 201907L
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
These BEGIN/END macros should not be indented (which has to be done
manually as editors always want to indent the content following the
opening brace of the namespace).
>+
>+ class stop_source;
>+ template<class _Callback>
s/class/typename/
>+ class stop_callback;
>+
>+ struct nostopstate_t { explicit nostopstate_t() = default; };
>+ inline constexpr nostopstate_t nostopstate();
>+
>+ class stop_token {
The opening brace should be on the next line please, in the same
column as "class".
(Same comment for all classes and structs in the patch).
>+ public:
>+ stop_token() noexcept = default;
>+
>+ stop_token(const stop_token& __other) noexcept
>+ : _M_state(__other._M_state)
>+ { }
>+
>+ stop_token(stop_token&& __other) noexcept
>+ : _M_state(std::move(__other._M_state))
>+ { }
>+
>+ ~stop_token() = default;
>+
>+ stop_token&
>+ operator=(const stop_token& __rhs) noexcept {
>+ _M_state = __rhs._M_state;
>+ return *this;
>+ }
>+
>+ stop_token&
>+ operator=(stop_token&& __rhs) noexcept {
>+ std::swap(_M_state, __rhs._M_state);
>+ return *this;
>+ }
This doesn't leave the RHS empty as required.
I think the copy/move constructors and copy/move assignment operators
can all be defined as = default and that will do the right thing.
>+
>+ [[nodiscard]]
>+ bool
>+ stop_possible() const noexcept
>+ {
>+ return static_cast<bool>(_M_state);
>+ }
>+
>+ [[nodiscard]]
>+ bool
>+ stop_requested() const noexcept
>+ {
>+ return stop_possible() && _M_state->_M_stop_requested();
>+ }
>+
>+ private:
>+ friend stop_source;
>+ template<typename _Callback>
>+ friend class stop_callback;
>+
>+ struct _Stop_cb {
>+ void(*_M_callback)(_Stop_cb*);
>+ _Stop_cb* _M_prev = nullptr;
>+ _Stop_cb* _M_next = nullptr;
>+
>+ template<typename _CB>
Please Use _Cb so it's not all uppercase (which is more likely to
conflict with libc macros).
>+ _Stop_cb(_CB&& __cb)
>+ : _M_callback(std::move(__cb))
>+ { }
>+
>+ static void
>+ _S_execute(_Stop_cb* __cb) noexcept
>+ {
>+ __cb->_M_callback(__cb);
>+ }
>+ };
>+
>+ struct _Stop_state_t {
>+ std::atomic<bool> _M_stopped;
>+ std::mutex _M_mtx;
>+ _Stop_cb* _M_head = nullptr;
>+
>+ _Stop_state_t()
>+ : _M_stopped{false}
>+ { }
>+
>+ bool
>+ _M_stop_requested()
>+ {
>+ return _M_stopped;
>+ }
>+
>+ bool
>+ _M_request_stop()
>+ {
>+ bool __stopped = false;
>+ if (_M_stopped.compare_exchange_strong(__stopped, true))
>+ {
>+ std::unique_lock<std::mutex> __lck{_M_mtx};
>+ while (auto __p = _M_head)
>+ {
>+ _M_head = __p->_M_next;
>+ _Stop_cb::_S_execute(__p);
>+ }
>+ return true;
>+ }
>+ return false;
>+ }
>+
>+ bool
>+ _M_register_callback(_Stop_cb* __cb)
>+ {
>+ std::unique_lock<std::mutex> __lck{_M_mtx};
>+ if (_M_stopped)
>+ return false;
>+
>+ __cb->_M_next = _M_head;
>+ _M_head->_M_prev = __cb;
>+ _M_head = __cb;
>+ return true;
>+ }
>+
>+ void
>+ _M_remove_callback(_Stop_cb* __cb)
>+ {
>+ std::unique_lock<std::mutex> __lck{_M_mtx};
>+ if (__cb == _M_head)
>+ {
>+ _M_head = _M_head->_M_next;
>+ _M_head->_M_prev = nullptr;
>+ }
>+ else
>+ {
>+ __cb->_M_prev->_M_next = __cb->_M_next;
>+ if (__cb->_M_next)
>+ {
>+ __cb->_M_next->_M_prev = __cb->_M_prev;
>+ }
>+ }
>+ }
>+ };
>+
>+ using _Stop_state = std::shared_ptr<_Stop_state_t>;
>+ _Stop_state _M_state;
>+
>+ explicit stop_token(_Stop_state __state)
>+ : _M_state{std::move(__state)}
>+ { }
>+ };
>+
>+ class stop_source {
>+ using _Stop_state_t = stop_token::_Stop_state_t;
>+ using _Stop_state = stop_token::_Stop_state;
>+
>+ public:
>+ stop_source()
>+ : _M_state(std::make_shared<_Stop_state_t>())
>+ { }
>+
>+ explicit stop_source(std::nostopstate_t) noexcept
>+ { }
>+
>+ stop_source(const stop_source& __other) noexcept
>+ : _M_state(__other._M_state)
>+ { }
>+
>+ stop_source(stop_source&& __other) noexcept
>+ : _M_state(std::move(__other._M_state))
>+ { }
>+
>+ stop_source&
>+ operator=(const stop_source& __rhs) noexcept
>+ {
>+ if (_M_state != __rhs._M_state)
>+ _M_state = __rhs._M_state;
>+ return *this;
>+ }
>+
>+ stop_source&
>+ operator=(stop_source&& __rhs) noexcept
>+ {
>+ std::swap(_M_state, __rhs._M_state);
>+ return *this;
>+ }
These can all be defaulted too.
>+ [[nodiscard]]
>+ bool
>+ stop_possible() const noexcept
>+ {
>+ return static_cast<bool>(_M_state);
>+ }
>+
>+ [[nodiscard]]
>+ bool
>+ stop_requested() const noexcept
>+ {
>+ return stop_possible() && _M_state->_M_stop_requested();
>+ }
>+
>+ bool
>+ request_stop() const noexcept
>+ {
>+ if (stop_possible())
>+ return _M_state->_M_request_stop();
>+ return false;
>+ }
>+
>+ [[nodiscard]]
>+ stop_token
>+ get_token() const noexcept
>+ {
>+ return stop_token{_M_state};
>+ }
>+
>+ void
>+ swap(stop_source& __other) noexcept
>+ {
>+ std::swap(_M_state, __other._M_state);
>+ }
>+
>+ [[nodiscard]]
>+ friend bool
>+ operator==(const stop_source& __a, const stop_source& __b) noexcept
>+ {
>+ return __a._M_state == __b._M_state;
>+ }
>+
>+ [[nodiscard]]
>+ friend bool
>+ operator!=(const stop_source& __a, const stop_source& __b) noexcept
>+ {
>+ return __a._M_state != __b._M_state;
>+ }
>+
>+ private:
>+ _Stop_state _M_state;
>+ };
>+
>+ template<typename _Callback>
>+ class [[nodiscard]] stop_callback
>+ : private stop_token::_Stop_cb {
Opening brace on the next line, in the same column as "class".
>+ using _Stop_cb = stop_token::_Stop_cb;
And then everything in the class body should be indented by another
two columns.
>+ using _Stop_state = stop_token::_Stop_state;
>+ public:
>+ using callback_type = _Callback;
>+
>+ template<typename _CB,
Change to _Cb here too.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-10-24 15:45 ` Jonathan Wakely
@ 2019-11-14 2:00 ` Thomas Rodgers
2019-11-14 16:12 ` Jonathan Wakely
2019-11-15 14:38 ` Jonathan Wakely
0 siblings, 2 replies; 10+ messages in thread
From: Thomas Rodgers @ 2019-11-14 2:00 UTC (permalink / raw)
To: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 185 bytes --]
The attached patch should be a complete implementation of C++20
stop_token, jthread, and changes to condition_variable_any which also
addresses the comments on the previous patch.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Le patch --]
[-- Type: text/x-patch, Size: 43660 bytes --]
From 2cdaa367ed919b24f3bbb84d6f7391a350dce77b Mon Sep 17 00:00:00 2001
From: Thomas Rodgers <trodgers@trodgers.remote.f30>
Date: Wed, 13 Nov 2019 17:51:11 -0800
Subject: [PATCH] Support for jthread and stop_token
* include/Makefile.am: Add <stop_token> header.
* include/Makefile.in: Regenerate.
* include/std/condition_variable: Add overloads for stop_token support
to condition_variable_any.
* include/std/stop_token: New file.
* include/std/thread: Add jthread type.
* include/std/version (__cpp_lib_jthread): New value.
* testsuite/30_threads/condition_variable_any/stop_token/1.cc: New test.
* testsuite/30_threads/condition_variable_any/stop_token/2.cc: New test.
* testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc: New test.
* testsuite/30_threads/jthread/1.cc: New test.
* testsuite/30_threads/jthread/2.cc: New test.
* testsuite/30_threads/jthread/jthread.cc: New test.
* testsuite/30_threads/stop_token/1.cc: New test.
* testsuite/30_threads/stop_token/2.cc: New test.
* testsuite/30_threads/stop_token/stop_token.cc: New test.
---
libstdc++-v3/include/Makefile.am | 1 +
libstdc++-v3/include/std/condition_variable | 84 ++++
libstdc++-v3/include/std/stop_token | 370 ++++++++++++++++++
libstdc++-v3/include/std/thread | 125 ++++++
libstdc++-v3/include/std/version | 1 +
.../condition_variable_any/stop_token/1.cc | 27 ++
.../condition_variable_any/stop_token/2.cc | 27 ++
.../stop_token/wait_on.cc | 136 +++++++
.../testsuite/30_threads/jthread/1.cc | 27 ++
.../testsuite/30_threads/jthread/2.cc | 27 ++
.../testsuite/30_threads/jthread/jthread.cc | 198 ++++++++++
.../testsuite/30_threads/stop_token/1.cc | 27 ++
.../testsuite/30_threads/stop_token/2.cc | 27 ++
.../30_threads/stop_token/stop_token.cc | 100 +++++
14 files changed, 1177 insertions(+)
create mode 100644 libstdc++-v3/include/std/stop_token
create mode 100644 libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/jthread/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/jthread/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/jthread/jthread.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/stop_token/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/stop_token/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 49fd41360bc..6300de9e96d 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -74,6 +74,7 @@ std_headers = \
${std_srcdir}/sstream \
${std_srcdir}/stack \
${std_srcdir}/stdexcept \
+ ${std_srcdir}/stop_token \
${std_srcdir}/streambuf \
${std_srcdir}/string \
${std_srcdir}/string_view \
diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable
index cc96661e94c..8887cee29fa 100644
--- a/libstdc++-v3/include/std/condition_variable
+++ b/libstdc++-v3/include/std/condition_variable
@@ -36,6 +36,7 @@
#else
#include <chrono>
+
#include <bits/std_mutex.h>
#include <bits/unique_lock.h>
#include <ext/concurrence.h>
@@ -45,6 +46,11 @@
#include <bits/shared_ptr.h>
#include <bits/cxxabi_forced.h>
+#if __cplusplus > 201703L
+#define __cpp_lib_jthread 201907L
+#include <stop_token>
+#endif
+
#if defined(_GLIBCXX_HAS_GTHREADS)
namespace std _GLIBCXX_VISIBILITY(default)
@@ -360,6 +366,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
wait_for(_Lock& __lock,
const chrono::duration<_Rep, _Period>& __rtime, _Predicate __p)
{ return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); }
+
+#ifdef __cpp_lib_jthread
+ template <class _Lock, class _Predicate>
+ bool wait_on(_Lock& __lock,
+ stop_token __stoken,
+ _Predicate __p)
+ {
+ if (__stoken.stop_requested())
+ {
+ return __p();
+ }
+
+ std::stop_callback __cb(__stoken, [this] { notify_all(); });
+ shared_ptr<mutex> __mutex = _M_mutex;
+ while (!__p())
+ {
+ unique_lock<mutex> __my_lock(*__mutex);
+ if (__stoken.stop_requested())
+ {
+ return false;
+ }
+ // *__mutex must be unlocked before re-locking __lock so move
+ // ownership of *__mutex lock to an object with shorter lifetime.
+ _Unlock<_Lock> __unlock(__lock);
+ unique_lock<mutex> __my_lock2(std::move(__my_lock));
+ _M_cond.wait(__my_lock2);
+ }
+ return true;
+ }
+
+ template <class _Lock, class _Clock, class _Duration, class _Predicate>
+ bool wait_on_until(_Lock& __lock,
+ stop_token __stoken,
+ const chrono::time_point<_Clock, _Duration>& __abs_time,
+ _Predicate __p)
+ {
+ if (__stoken.stop_requested())
+ {
+ return __p();
+ }
+
+ std::stop_callback __cb(__stoken, [this] { notify_all(); });
+ shared_ptr<mutex> __mutex = _M_mutex;
+ while (!__p())
+ {
+ bool __stop;
+ {
+ unique_lock<mutex> __my_lock(*__mutex);
+ if (__stoken.stop_requested())
+ {
+ return false;
+ }
+ _Unlock<_Lock> __u(__lock);
+ unique_lock<mutex> __my_lock2(std::move(__my_lock));
+ const auto __status = _M_cond.wait_until(__my_lock2, __abs_time);
+ __stop = (__status == std::cv_status::timeout) || __stoken.stop_requested();
+ }
+ if (__stop)
+ {
+ return __p();
+ }
+ }
+ return true;
+ }
+
+ template <class _Lock, class _Rep, class _Period, class _Predicate>
+ bool wait_on_for(_Lock& __lock,
+ stop_token __stoken,
+ const chrono::duration<_Rep, _Period>& __rel_time,
+ _Predicate __p)
+ {
+ auto __abst = std::chrono::steady_clock::now() + __rel_time;
+ return wait_on_until(__lock,
+ std::move(__stoken),
+ __abst,
+ std::move(__p));
+ }
+#endif
};
} // end inline namespace
diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token
new file mode 100644
index 00000000000..f5f6f64210e
--- /dev/null
+++ b/libstdc++-v3/include/std/stop_token
@@ -0,0 +1,370 @@
+// <stop_token> -*- C++ -*-
+
+// Copyright (C) 2019 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/stop_token
+ * This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_STOP_TOKEN
+#define _GLIBCXX_STOP_TOKEN
+
+#if __cplusplus >= 201703L
+
+#include <type_traits>
+#include <memory>
+#include <mutex>
+#include <atomic>
+
+#define __cpp_lib_jthread 201907L
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class stop_source;
+ template<typename _Callback>
+ class stop_callback;
+
+ struct nostopstate_t { explicit nostopstate_t() = default; };
+ inline constexpr nostopstate_t nostopstate();
+
+ class stop_token
+ {
+ public:
+ stop_token() noexcept = default;
+
+ stop_token(const stop_token& __other) noexcept = default;
+ stop_token(stop_token&& __other) noexcept = default;
+
+ ~stop_token() = default;
+
+ stop_token&
+ operator=(const stop_token& __rhs) noexcept = default;
+
+ stop_token&
+ operator=(stop_token&& __rhs) noexcept;
+
+ [[nodiscard]]
+ bool
+ stop_possible() const noexcept
+ {
+ return static_cast<bool>(_M_state);
+ }
+
+ [[nodiscard]]
+ bool
+ stop_requested() const noexcept
+ {
+ return stop_possible() && _M_state->_M_stop_requested();
+ }
+
+ [[nodiscard]]
+ friend bool
+ operator==(const stop_token& __a, const stop_token& __b)
+ {
+ return __a._M_state == __b._M_state;
+ }
+
+ [[nodiscard]]
+ friend bool
+ operator!=(const stop_token& __a, const stop_token& __b)
+ {
+ return __a._M_state == __b._M_state;
+ }
+
+ private:
+ friend stop_source;
+ template<typename _Callback>
+ friend class stop_callback;
+
+ struct _Stop_cb {
+ void(*_M_callback)(_Stop_cb*);
+ _Stop_cb* _M_prev = nullptr;
+ _Stop_cb* _M_next = nullptr;
+
+ template<typename _Cb>
+ _Stop_cb(_Cb&& __cb)
+ : _M_callback(std::move(__cb))
+ { }
+
+ bool
+ _M_linked() const
+ {
+ return (_M_prev != nullptr)
+ || (_M_next != nullptr);
+ }
+
+ static void
+ _S_execute(_Stop_cb* __cb) noexcept
+ {
+ __cb->_M_callback(__cb);
+ __cb->_M_prev = __cb->_M_next = nullptr;
+ }
+ };
+
+ struct _Stop_state_t {
+ std::atomic<bool> _M_stopped;
+ std::mutex _M_mtx;
+ _Stop_cb* _M_head = nullptr;
+
+ _Stop_state_t()
+ : _M_stopped{false}
+ { }
+
+ bool
+ _M_stop_requested()
+ {
+ return _M_stopped;
+ }
+
+ bool
+ _M_request_stop()
+ {
+ bool __stopped = false;
+ if (_M_stopped.compare_exchange_strong(__stopped, true))
+ {
+ std::unique_lock<std::mutex> __lck{_M_mtx};
+ while (_M_head)
+ {
+ auto __p = _M_head;
+ _M_head = _M_head->_M_next;
+ _Stop_cb::_S_execute(__p);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool
+ _M_register_callback(_Stop_cb* __cb)
+ {
+ std::unique_lock<std::mutex> __lck{_M_mtx};
+ if (_M_stopped)
+ return false;
+
+ __cb->_M_next = _M_head;
+ if (_M_head)
+ {
+ _M_head->_M_prev = __cb;
+ }
+ _M_head = __cb;
+ return true;
+ }
+
+ void
+ _M_remove_callback(_Stop_cb* __cb)
+ {
+ std::unique_lock<std::mutex> __lck{_M_mtx};
+ if (__cb == _M_head)
+ {
+ _M_head = _M_head->_M_next;
+ if (_M_head)
+ {
+ _M_head->_M_prev = nullptr;
+ }
+ }
+ else if (!__cb->_M_linked())
+ {
+ return;
+ }
+ else
+ {
+ __cb->_M_prev->_M_next = __cb->_M_next;
+ if (__cb->_M_next)
+ {
+ __cb->_M_next->_M_prev = __cb->_M_prev;
+ }
+ }
+ }
+ };
+
+ using _Stop_state = std::shared_ptr<_Stop_state_t>;
+ _Stop_state _M_state;
+
+ explicit stop_token(_Stop_state __state)
+ : _M_state{std::move(__state)}
+ { }
+ };
+
+ class stop_source {
+ using _Stop_state_t = stop_token::_Stop_state_t;
+ using _Stop_state = stop_token::_Stop_state;
+
+ public:
+ stop_source()
+ : _M_state(std::make_shared<_Stop_state_t>())
+ { }
+
+ explicit stop_source(std::nostopstate_t) noexcept
+ { }
+
+ stop_source(const stop_source& __other) noexcept
+ : _M_state(__other._M_state)
+ { }
+
+ stop_source(stop_source&& __other) noexcept
+ : _M_state(std::move(__other._M_state))
+ { }
+
+ stop_source&
+ operator=(const stop_source& __rhs) noexcept
+ {
+ if (_M_state != __rhs._M_state)
+ _M_state = __rhs._M_state;
+ return *this;
+ }
+
+ stop_source&
+ operator=(stop_source&& __rhs) noexcept
+ {
+ std::swap(_M_state, __rhs._M_state);
+ return *this;
+ }
+
+ [[nodiscard]]
+ bool
+ stop_possible() const noexcept
+ {
+ return static_cast<bool>(_M_state);
+ }
+
+ [[nodiscard]]
+ bool
+ stop_requested() const noexcept
+ {
+ return stop_possible() && _M_state->_M_stop_requested();
+ }
+
+ bool
+ request_stop() const noexcept
+ {
+ if (stop_possible())
+ return _M_state->_M_request_stop();
+ return false;
+ }
+
+ [[nodiscard]]
+ stop_token
+ get_token() const noexcept
+ {
+ return stop_token{_M_state};
+ }
+
+ void
+ swap(stop_source& __other) noexcept
+ {
+ std::swap(_M_state, __other._M_state);
+ }
+
+ [[nodiscard]]
+ friend bool
+ operator==(const stop_source& __a, const stop_source& __b) noexcept
+ {
+ return __a._M_state == __b._M_state;
+ }
+
+ [[nodiscard]]
+ friend bool
+ operator!=(const stop_source& __a, const stop_source& __b) noexcept
+ {
+ return __a._M_state != __b._M_state;
+ }
+
+ private:
+ _Stop_state _M_state;
+ };
+
+ template<typename _Callback>
+ class [[nodiscard]] stop_callback
+ : private stop_token::_Stop_cb
+ {
+ using _Stop_cb = stop_token::_Stop_cb;
+ using _Stop_state = stop_token::_Stop_state;
+ public:
+ using callback_type = _Callback;
+
+ template<typename _Cb,
+ std::enable_if_t<std::is_constructible_v<_Callback, _Cb>, int> = 0>
+ explicit stop_callback(const stop_token& __token, _Cb&& __cb)
+ noexcept(std::is_nothrow_constructible_v<_Callback, _Cb>)
+ : _Stop_cb([](_Stop_cb* __that) noexcept
+ {
+ static_cast<stop_callback*>(__that)->_M_execute();
+ }),
+ _M_cb(std::move(__cb))
+ {
+ auto res = __token._M_state->_M_register_callback(this);
+ if (__token._M_state && res)
+ {
+ _M_state = __token._M_state;
+ }
+ }
+
+ template<typename _Cb,
+ std::enable_if_t<std::is_constructible_v<_Callback, _Cb>, int> = 0>
+ explicit stop_callback(stop_token&& __token, _Cb&& __cb)
+ noexcept(std::is_nothrow_constructible_v<_Callback, _Cb>)
+ : _Stop_cb([](_Stop_cb* __that) noexcept
+ {
+ static_cast<stop_callback*>(__that)->_M_execute();
+ }),
+ _M_cb(std::move(__cb))
+ {
+ if (__token._M_state && __token._M_state->_M_register_callback(this))
+ {
+ std::swap(_M_state, __token._M_state);
+ }
+ }
+
+ ~stop_callback()
+ {
+ if (_M_state)
+ {
+ _M_state->_M_remove_callback(this);
+ }
+ }
+
+ stop_callback(const stop_callback&) = delete;
+ stop_callback& operator=(const stop_callback&) = delete;
+ stop_callback(stop_callback&&) = delete;
+ stop_callback& operator=(stop_callback&&) = delete;
+
+ private:
+ _Callback _M_cb;
+ _Stop_state _M_state = nullptr;
+
+ void
+ _M_execute() noexcept
+ {
+ _M_cb();
+ }
+ };
+
+ template<typename _Callback>
+ stop_callback(stop_token, _Callback) -> stop_callback<_Callback>;
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // __cplusplus >= 201703L
+#endif // _GLIBCXX_STOP_TOKEN
diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread
index 90b4be6cd16..93afa766d18 100644
--- a/libstdc++-v3/include/std/thread
+++ b/libstdc++-v3/include/std/thread
@@ -39,6 +39,13 @@
#include <memory>
#include <tuple>
#include <cerrno>
+
+#if __cplusplus > 201703L
+#define __cpp_lib_jthread 201907L
+#include <functional>
+#include <stop_token>
+#endif
+
#include <bits/functexcept.h>
#include <bits/functional_hash.h>
#include <bits/invoke.h>
@@ -409,6 +416,124 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// @} group threads
+#ifdef __cpp_lib_jthread
+
+ class jthread
+ {
+ public:
+ using id = std::thread::id;
+ using native_handle_type = std::thread::native_handle_type;
+
+ jthread() noexcept
+ : _M_stop_source{ nostopstate_t{ } }
+ { }
+
+ template<typename _Callable, typename... _Args,
+ typename = std::enable_if_t<!std::is_same_v<std::decay_t<_Callable>, jthread>>>
+ explicit
+ jthread(_Callable&& __f, _Args&&... __args)
+ : _M_thread{[](stop_token __token, auto&& __cb, auto&&... __args)
+ {
+ if constexpr(std::is_invocable_v<_Callable, stop_token, _Args...>)
+ {
+ std::invoke(std::forward<decltype(__cb)>(__cb),
+ std::move(__token),
+ std::forward<decltype(__args)>(__args)...);
+ }
+ else
+ {
+ std::invoke(std::forward<decltype(__cb)>(__cb),
+ std::forward<decltype(__args)>(__args)...);
+ }
+ },
+ _M_stop_source.get_token(),
+ std::forward<_Callable>(__f),
+ std::forward<_Args>(__args)...}
+ { }
+
+ jthread(const jthread&) = delete;
+ jthread(jthread&&) noexcept = default;
+
+ ~jthread()
+ {
+ if (joinable())
+ {
+ request_stop();
+ join();
+ }
+ }
+
+ jthread&
+ operator=(const jthread&) = delete;
+
+ jthread&
+ operator=(jthread&&) noexcept = default;
+
+ void
+ swap(jthread& __other) noexcept
+ {
+ std::swap(_M_stop_source, __other._M_stop_source);
+ std::swap(_M_thread, __other._M_thread);
+ }
+
+ bool
+ joinable() const noexcept
+ {
+ return _M_thread.joinable();
+ }
+
+ void
+ join()
+ {
+ _M_thread.join();
+ }
+
+ void
+ detach()
+ {
+ _M_thread.detach();
+ }
+
+ id
+ get_id() const noexcept
+ {
+ _M_thread.get_id();
+ }
+
+ native_handle_type
+ native_handle()
+ {
+ return _M_thread.native_handle();
+ }
+
+ static unsigned
+ hardware_concurrency() noexcept
+ {
+ return std::thread::hardware_concurrency();
+ }
+
+ [[nodiscard]] stop_source
+ get_stop_source() noexcept
+ {
+ return _M_stop_source;
+ }
+
+ [[nodiscard]] stop_token
+ get_stop_token() const noexcept
+ {
+ return _M_stop_source.get_token();
+ }
+
+ bool request_stop() noexcept
+ {
+ return get_stop_source().request_stop();
+ }
+
+ private:
+ stop_source _M_stop_source;
+ std::thread _M_thread;
+ };
+#endif // __cpp_lib_jthread
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index fa6d27467f7..27807428903 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -187,6 +187,7 @@
#define __cpp_lib_list_remove_return_type 201806L
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_span 201902L
+#define __cpp_lib_jthread 201907L
#if __cpp_impl_three_way_comparison >= 201907L
# define __cpp_lib_three_way_comparison 201711L
#endif
diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc
new file mode 100644
index 00000000000..c1f4b5849ac
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <condition_variable>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <condition_variable>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <condition_variable>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc
new file mode 100644
index 00000000000..621965c8910
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <version>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc
new file mode 100644
index 00000000000..212fc949b3f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc
@@ -0,0 +1,136 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a -pthread" }
+// { dg-do run }
+// { dg-require-effective-target c++2a }
+// { dg-require-effective-target pthread }
+
+#include <condition_variable>
+#include <thread>
+#include <mutex>
+#include <chrono>
+#include <testsuite_hooks.h>
+
+using namespace::std::literals;
+
+void test_wait_on_stop()
+{
+ bool ready = false;
+ std::mutex mtx;
+ std::condition_variable_any cv;
+
+ std::stop_source src;
+
+ auto tok = src.get_token();
+ std::thread t([&ready, &mtx, &cv, tok]
+ {
+ std::unique_lock lck(mtx);
+ auto res = cv.wait_on(lck, tok, [&ready] { return ready; });
+ if (!res)
+ {
+ VERIFY(tok.stop_requested());
+ }
+ });
+
+ std::this_thread::sleep_for(0.5s);
+ VERIFY(!src.stop_requested());
+ src.request_stop();
+ t.join();
+ VERIFY(src.stop_requested());
+}
+
+void test_wait_on_until(bool ck = true)
+{
+ bool ready = false;
+ std::mutex mtx;
+ std::condition_variable_any cv;
+
+ std::stop_source src;
+
+ auto abst = std::chrono::steady_clock::now() + 1.0s;
+ auto tok = src.get_token();
+ std::thread t([ck, &ready, &mtx, &cv, abst, tok]
+ {
+ std::unique_lock lck(mtx);
+ auto res = cv.wait_on_until(lck, tok, abst, [&ready] { return ready; });
+ if (!res && ck)
+ {
+ VERIFY(tok.stop_requested());
+ }
+ });
+
+ if (ck)
+ {
+ std::this_thread::sleep_for(0.5s);
+ VERIFY(!src.stop_requested());
+ src.request_stop();
+ t.join();
+ VERIFY(src.stop_requested());
+ }
+ else
+ {
+ std::this_thread::sleep_for(1.5s);
+ t.join();
+ VERIFY(!src.stop_requested());
+ }
+}
+
+void test_wait_on_for(bool ck = true)
+{
+ bool ready = false;
+ std::mutex mtx;
+ std::condition_variable_any cv;
+
+ std::stop_source src;
+
+ auto tok = src.get_token();
+ std::thread t([ck, &ready, &mtx, &cv, tok]
+ {
+ std::unique_lock lck(mtx);
+ auto res = cv.wait_on_for(lck, tok, 1.0s, [&ready] { return ready; });
+ if (!res && ck)
+ {
+ VERIFY(tok.stop_requested());
+ }
+ });
+
+ if (ck)
+ {
+ std::this_thread::sleep_for(0.5s);
+ VERIFY(!src.stop_requested());
+ src.request_stop();
+ t.join();
+ VERIFY(src.stop_requested());
+ }
+ else
+ {
+ std::this_thread::sleep_for(1.5s);
+ t.join();
+ VERIFY(!src.stop_requested());
+ };
+}
+
+int main()
+{
+ test_wait_on_stop();
+ test_wait_on_until(false);
+ test_wait_on_until();
+ test_wait_on_for();
+ test_wait_on_for(false);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/1.cc b/libstdc++-v3/testsuite/30_threads/jthread/1.cc
new file mode 100644
index 00000000000..1fb5650dbc6
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/jthread/1.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <thread>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <thread>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <thread>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/2.cc b/libstdc++-v3/testsuite/30_threads/jthread/2.cc
new file mode 100644
index 00000000000..621965c8910
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/jthread/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <version>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc b/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc
new file mode 100644
index 00000000000..c29db212167
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc
@@ -0,0 +1,198 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <thread>
+#include <chrono>
+#include <cassert>
+#include <atomic>
+
+using namespace::std::literals;
+
+//------------------------------------------------------
+
+void test_no_stop_token()
+{
+ // test the basic jthread API (not taking stop_token arg)
+
+ assert(std::jthread::hardware_concurrency() == std::thread::hardware_concurrency());
+ std::stop_token stoken;
+ assert(!stoken.stop_possible());
+ {
+ std::jthread::id t1ID{std::this_thread::get_id()};
+ std::atomic<bool> t1AllSet{false};
+ std::jthread t1([&t1ID, &t1AllSet] {
+ t1ID = std::this_thread::get_id();
+ t1AllSet.store(true);
+ for (int c='9'; c>='0'; --c) {
+ std::this_thread::sleep_for(222ms);
+ }
+ });
+ for (int i=0; !t1AllSet.load(); ++i) {
+ std::this_thread::sleep_for(10ms);
+ }
+ assert(t1.joinable());
+ assert(t1ID == t1.get_id());
+ stoken = t1.get_stop_token();
+ assert(!stoken.stop_requested());
+ }
+ assert(stoken.stop_requested());
+}
+
+//------------------------------------------------------
+
+void test_stop_token()
+{
+ // test the basic thread API (taking stop_token arg)
+
+ std::stop_source ssource;
+ std::stop_source origsource;
+ assert(ssource.stop_possible());
+ assert(!ssource.stop_requested());
+ {
+ std::jthread::id t1ID{std::this_thread::get_id()};
+ std::atomic<bool> t1AllSet{false};
+ std::atomic<bool> t1done{false};
+ std::jthread t1([&t1ID, &t1AllSet, &t1done] (std::stop_token st) {
+ // check some values of the started thread:
+ t1ID = std::this_thread::get_id();
+ t1AllSet.store(true);
+ for (int i=0; !st.stop_requested(); ++i) {
+ std::this_thread::sleep_for(100ms);
+ }
+ t1done.store(true);
+ },
+ ssource.get_token());
+ for (int i=0; !t1AllSet.load(); ++i) {
+ std::this_thread::sleep_for(10ms);
+ }
+ // and check all values:
+ assert(t1.joinable());
+ assert(t1ID == t1.get_id());
+
+ std::this_thread::sleep_for(470ms);
+ origsource = std::move(ssource);
+ ssource = t1.get_stop_source();
+ assert(!ssource.stop_requested());
+ auto ret = ssource.request_stop();
+ assert(ret);
+ ret = ssource.request_stop();
+ assert(!ret);
+ assert(ssource.stop_requested());
+ assert(!t1done.load());
+ assert(!origsource.stop_requested());
+
+ std::this_thread::sleep_for(470ms);
+ origsource.request_stop();
+ }
+ assert(origsource.stop_requested());
+ assert(ssource.stop_requested());
+}
+
+//------------------------------------------------------
+
+void test_join()
+{
+ std::stop_source ssource;
+ assert(ssource.stop_possible());
+ {
+ std::jthread t1([](std::stop_token stoken) {
+ for (int i=0; !stoken.stop_requested(); ++i) {
+ std::this_thread::sleep_for(100ms);
+ }
+ });
+ ssource = t1.get_stop_source();
+ std::jthread t2([ssource] () mutable {
+ for (int i=0; i < 10; ++i) {
+ std::this_thread::sleep_for(70ms);
+ }
+ ssource.request_stop();
+ });
+ // wait for all thread to finish:
+ t2.join();
+ assert(!t2.joinable());
+ assert(t1.joinable());
+ t1.join();
+ assert(!t1.joinable());
+ }
+}
+
+//------------------------------------------------------
+
+void test_detach()
+{
+ std::stop_source ssource;
+ assert(ssource.stop_possible());
+ std::atomic<bool> t1FinallyInterrupted{false};
+ {
+ std::jthread t0;
+ std::jthread::id t1ID{std::this_thread::get_id()};
+ bool t1IsInterrupted;
+ std::stop_token t1InterruptToken;
+ std::atomic<bool> t1AllSet{false};
+ std::jthread t1([&t1ID, &t1IsInterrupted, &t1InterruptToken, &t1AllSet, &t1FinallyInterrupted]
+ (std::stop_token stoken) {
+ // check some values of the started thread:
+ t1ID = std::this_thread::get_id();
+ t1InterruptToken = stoken;
+ t1IsInterrupted = stoken.stop_requested();
+ assert(stoken.stop_possible());
+ assert(!stoken.stop_requested());
+ t1AllSet.store(true);
+ for (int i=0; !stoken.stop_requested(); ++i) {
+ std::this_thread::sleep_for(100ms);
+ }
+ t1FinallyInterrupted.store(true);
+ });
+ for (int i=0; !t1AllSet.load(); ++i) {
+ std::this_thread::sleep_for(10ms);
+ }
+ assert(!t0.joinable());
+ assert(t1.joinable());
+ assert(t1ID == t1.get_id());
+ assert(t1IsInterrupted == false);
+ assert(t1InterruptToken == t1.get_stop_source().get_token());
+ ssource = t1.get_stop_source();
+ assert(t1InterruptToken.stop_possible());
+ assert(!t1InterruptToken.stop_requested());
+ t1.detach();
+ assert(!t1.joinable());
+ }
+
+ assert(!t1FinallyInterrupted.load());
+ ssource.request_stop();
+ assert(ssource.stop_requested());
+ for (int i=0; !t1FinallyInterrupted.load() && i < 100; ++i) {
+ std::this_thread::sleep_for(100ms);
+ }
+ assert(t1FinallyInterrupted.load());
+}
+
+int main()
+{
+ std::set_terminate([](){
+ assert(false);
+ });
+
+ test_no_stop_token();
+ test_stop_token();
+ test_join();
+ test_detach();
+}
+
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/1.cc b/libstdc++-v3/testsuite/30_threads/stop_token/1.cc
new file mode 100644
index 00000000000..4c0e7a33d25
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/1.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <stop_token>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <stop_token>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <stop_token>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/2.cc b/libstdc++-v3/testsuite/30_threads/stop_token/2.cc
new file mode 100644
index 00000000000..621965c8910
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_jthread
+# error "Feature-test macro for jthread missing in <version>"
+#elif __cpp_lib_jthread != 201907L
+# error "Feature-test macro for jthread has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
new file mode 100644
index 00000000000..37d79e8a64e
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
@@ -0,0 +1,100 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a -pthread" }
+// { dg-do run }
+// { dg-require-effective-target c++2a }
+// { dg-require-effective-target pthread }
+
+#include <stop_token>
+#include <iostream>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ // create stop_source
+ std::stop_source ssrc;
+ VERIFY(ssrc.stop_possible());
+ VERIFY(!ssrc.stop_requested());
+
+ // create stop_token from stop_source
+ std::stop_token stok{ssrc.get_token()};
+ VERIFY(ssrc.stop_possible());
+ VERIFY(!ssrc.stop_requested());
+ VERIFY(stok.stop_possible());
+ VERIFY(!stok.stop_requested());
+
+ // register callback
+ bool cb1called{false};
+ auto cb1 = [&]{
+ std::cout << "cb1" << std::endl;
+ cb1called = true;
+ };
+ {
+ std::stop_callback scb1{stok, cb1};
+ VERIFY(ssrc.stop_possible());
+ VERIFY(!ssrc.stop_requested());
+ VERIFY(stok.stop_possible());
+ VERIFY(!stok.stop_requested());
+ VERIFY(!cb1called);
+ } // unregister callback
+
+ // register another callback
+ bool cb2called{false};
+ auto cb2 = [&]{
+ VERIFY(stok.stop_requested());
+ cb2called = true;
+ };
+ std::stop_callback scb2a{stok, cb2}; // copies cb2
+ // std::stop_callback scb2b{stok, std::move(cb2)};
+ VERIFY(ssrc.stop_possible());
+ VERIFY(!ssrc.stop_requested());
+ VERIFY(stok.stop_possible());
+ VERIFY(!stok.stop_requested());
+ VERIFY(!cb1called);
+ VERIFY(!cb2called);
+
+ // request stop
+ auto b = ssrc.request_stop();
+ VERIFY(b);
+ VERIFY(ssrc.stop_possible());
+ VERIFY(ssrc.stop_requested());
+ VERIFY(stok.stop_possible());
+ VERIFY(stok.stop_requested());
+ VERIFY(!cb1called);
+ VERIFY(cb2called);
+
+ b = ssrc.request_stop();
+ VERIFY(!b);
+
+ // TODO verify the standard requires this
+#if 0
+ // register another callback
+ bool cb3called{false};
+ std::stop_callback scb3{stok, [&]
+ {
+ cb3called = true;
+ }};
+ VERIFY(ssrc.stop_possible());
+ VERIFY(ssrc.stop_requested());
+ VERIFY(stok.stop_possible());
+ VERIFY(stok.stop_requested());
+ VERIFY(!cb1called);
+ VERIFY(cb2called);
+ VERIFY(cb3called);
+#endif
+}
--
2.21.0
[-- Attachment #3: Type: text/plain, Size: 10719 bytes --]
Jonathan Wakely writes:
> On 23/10/19 12:50 -0700, Thomas Rodgers wrote:
>>
>>Thomas Rodgers writes:
>>
>>Let's try this again.
>
> That's better, thanks :-)
>
>>+ * include/Makefile.am: Add <stop_token> header.
>>+ * include/Makefile.in: Regenerate.
>>+ * include/std/stop_token: New file.
>>+ * include/std/version (__cpp_lib_jthread): New value.
>>+ * testsuite/30_threads/stop_token/1.cc: New test.
>>+ * testsuite/30_threads/stop_token/2.cc: New test.
>>+ * testsuite/30_threads/stop_token/stop_token.cc: New test.
>
> ChangeLog entries should be provided separately from the patch (e.g.
> inline in the makefile, or as a separte attachment) because they never
> apply cleanly.
>
> Also it looks like you have a mixture of tabs and space there, it
> should be only tabs.
>
>>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>>index cd1e9df5482..9b4ab670315 100644
>>--- a/libstdc++-v3/include/Makefile.in
>>+++ b/libstdc++-v3/include/Makefile.in
>>@@ -416,6 +416,7 @@ std_headers = \
>> ${std_srcdir}/sstream \
>> ${std_srcdir}/stack \
>> ${std_srcdir}/stdexcept \
>>+ ${std_srcdir}/stop_token \
>> ${std_srcdir}/streambuf \
>> ${std_srcdir}/string \
>> ${std_srcdir}/string_view \
>
> Generated files like Makefile.in don't need to be in the patch. I use
> a git alias called 'filter' to filter them out:
>
> !f(){ git $@ | filterdiff -x '*/ChangeLog' -x '*/Makefile.in' -x '*/configure' -x '*/config.h.in' -x '*/doc/html/*' | sed -e '/^diff.*ChangeLog/{N;d}' ; }; f
>
> And then I create a patch with this alias:
>
> !f(){ git filter show --diff-algorithm=histogram ${*:-HEAD} > /dev/shm/patch.txt; }; f
>
>
>>diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token
>>new file mode 100644
>>index 00000000000..b3655b85eae
>>--- /dev/null
>>+++ b/libstdc++-v3/include/std/stop_token
>>@@ -0,0 +1,338 @@
>>+// <stop_token> -*- C++ -*-
>>+
>>+// Copyright (C) 2008-2019 Free Software Foundation, Inc.
>
> The copyright date should be just 2019.
>
>>+//
>>+// 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.
>>+
>>+// Under Section 7 of GPL version 3, you are granted additional
>>+// permissions described in the GCC Runtime Library Exception, version
>>+// 3.1, as published by the Free Software Foundation.
>>+
>>+// You should have received a copy of the GNU General Public License and
>>+// a copy of the GCC Runtime Library Exception along with this program;
>>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+/** @file include/stop_token
>>+ * This is a Standard C++ Library header.
>>+ */
>>+
>>+#ifndef _GLIBCXX_STOP_TOKEN
>>+#define _GLIBCXX_STOP_TOKEN
>>+
>
> Please add:
>
> #if __cplusplus > 201703L
>
> here (and the corresponding #endif before the final #endif) so that
> this file is empty when included pre-C++20.
>
>>+#include <type_traits>
>>+#include <memory>
>>+#include <mutex>
>>+#include <atomic>
>>+
>>+#define __cpp_lib_jthread 201907L
>>+
>>+namespace std _GLIBCXX_VISIBILITY(default)
>>+{
>>+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> These BEGIN/END macros should not be indented (which has to be done
> manually as editors always want to indent the content following the
> opening brace of the namespace).
>
>>+
>>+ class stop_source;
>>+ template<class _Callback>
>
> s/class/typename/
>
>>+ class stop_callback;
>>+
>>+ struct nostopstate_t { explicit nostopstate_t() = default; };
>>+ inline constexpr nostopstate_t nostopstate();
>>+
>>+ class stop_token {
>
> The opening brace should be on the next line please, in the same
> column as "class".
> (Same comment for all classes and structs in the patch).
>
>
>>+ public:
>>+ stop_token() noexcept = default;
>>+
>>+ stop_token(const stop_token& __other) noexcept
>>+ : _M_state(__other._M_state)
>>+ { }
>>+
>>+ stop_token(stop_token&& __other) noexcept
>>+ : _M_state(std::move(__other._M_state))
>>+ { }
>>+
>>+ ~stop_token() = default;
>>+
>>+ stop_token&
>>+ operator=(const stop_token& __rhs) noexcept {
>>+ _M_state = __rhs._M_state;
>>+ return *this;
>>+ }
>>+
>>+ stop_token&
>>+ operator=(stop_token&& __rhs) noexcept {
>>+ std::swap(_M_state, __rhs._M_state);
>>+ return *this;
>>+ }
>
> This doesn't leave the RHS empty as required.
>
> I think the copy/move constructors and copy/move assignment operators
> can all be defined as = default and that will do the right thing.
>
>>+
>>+ [[nodiscard]]
>>+ bool
>>+ stop_possible() const noexcept
>>+ {
>>+ return static_cast<bool>(_M_state);
>>+ }
>>+
>>+ [[nodiscard]]
>>+ bool
>>+ stop_requested() const noexcept
>>+ {
>>+ return stop_possible() && _M_state->_M_stop_requested();
>>+ }
>>+
>>+ private:
>>+ friend stop_source;
>>+ template<typename _Callback>
>>+ friend class stop_callback;
>>+
>>+ struct _Stop_cb {
>>+ void(*_M_callback)(_Stop_cb*);
>>+ _Stop_cb* _M_prev = nullptr;
>>+ _Stop_cb* _M_next = nullptr;
>>+
>>+ template<typename _CB>
>
> Please Use _Cb so it's not all uppercase (which is more likely to
> conflict with libc macros).
>
>>+ _Stop_cb(_CB&& __cb)
>>+ : _M_callback(std::move(__cb))
>>+ { }
>>+
>>+ static void
>>+ _S_execute(_Stop_cb* __cb) noexcept
>>+ {
>>+ __cb->_M_callback(__cb);
>>+ }
>>+ };
>>+
>>+ struct _Stop_state_t {
>>+ std::atomic<bool> _M_stopped;
>>+ std::mutex _M_mtx;
>>+ _Stop_cb* _M_head = nullptr;
>>+
>>+ _Stop_state_t()
>>+ : _M_stopped{false}
>>+ { }
>>+
>>+ bool
>>+ _M_stop_requested()
>>+ {
>>+ return _M_stopped;
>>+ }
>>+
>>+ bool
>>+ _M_request_stop()
>>+ {
>>+ bool __stopped = false;
>>+ if (_M_stopped.compare_exchange_strong(__stopped, true))
>>+ {
>>+ std::unique_lock<std::mutex> __lck{_M_mtx};
>>+ while (auto __p = _M_head)
>>+ {
>>+ _M_head = __p->_M_next;
>>+ _Stop_cb::_S_execute(__p);
>>+ }
>>+ return true;
>>+ }
>>+ return false;
>>+ }
>>+
>>+ bool
>>+ _M_register_callback(_Stop_cb* __cb)
>>+ {
>>+ std::unique_lock<std::mutex> __lck{_M_mtx};
>>+ if (_M_stopped)
>>+ return false;
>>+
>>+ __cb->_M_next = _M_head;
>>+ _M_head->_M_prev = __cb;
>>+ _M_head = __cb;
>>+ return true;
>>+ }
>>+
>>+ void
>>+ _M_remove_callback(_Stop_cb* __cb)
>>+ {
>>+ std::unique_lock<std::mutex> __lck{_M_mtx};
>>+ if (__cb == _M_head)
>>+ {
>>+ _M_head = _M_head->_M_next;
>>+ _M_head->_M_prev = nullptr;
>>+ }
>>+ else
>>+ {
>>+ __cb->_M_prev->_M_next = __cb->_M_next;
>>+ if (__cb->_M_next)
>>+ {
>>+ __cb->_M_next->_M_prev = __cb->_M_prev;
>>+ }
>>+ }
>>+ }
>>+ };
>>+
>>+ using _Stop_state = std::shared_ptr<_Stop_state_t>;
>>+ _Stop_state _M_state;
>>+
>>+ explicit stop_token(_Stop_state __state)
>>+ : _M_state{std::move(__state)}
>>+ { }
>>+ };
>>+
>>+ class stop_source {
>>+ using _Stop_state_t = stop_token::_Stop_state_t;
>>+ using _Stop_state = stop_token::_Stop_state;
>>+
>>+ public:
>>+ stop_source()
>>+ : _M_state(std::make_shared<_Stop_state_t>())
>>+ { }
>>+
>>+ explicit stop_source(std::nostopstate_t) noexcept
>>+ { }
>>+
>>+ stop_source(const stop_source& __other) noexcept
>>+ : _M_state(__other._M_state)
>>+ { }
>>+
>>+ stop_source(stop_source&& __other) noexcept
>>+ : _M_state(std::move(__other._M_state))
>>+ { }
>>+
>>+ stop_source&
>>+ operator=(const stop_source& __rhs) noexcept
>>+ {
>>+ if (_M_state != __rhs._M_state)
>>+ _M_state = __rhs._M_state;
>>+ return *this;
>>+ }
>>+
>>+ stop_source&
>>+ operator=(stop_source&& __rhs) noexcept
>>+ {
>>+ std::swap(_M_state, __rhs._M_state);
>>+ return *this;
>>+ }
>
> These can all be defaulted too.
>
>>+ [[nodiscard]]
>>+ bool
>>+ stop_possible() const noexcept
>>+ {
>>+ return static_cast<bool>(_M_state);
>>+ }
>>+
>>+ [[nodiscard]]
>>+ bool
>>+ stop_requested() const noexcept
>>+ {
>>+ return stop_possible() && _M_state->_M_stop_requested();
>>+ }
>>+
>>+ bool
>>+ request_stop() const noexcept
>>+ {
>>+ if (stop_possible())
>>+ return _M_state->_M_request_stop();
>>+ return false;
>>+ }
>>+
>>+ [[nodiscard]]
>>+ stop_token
>>+ get_token() const noexcept
>>+ {
>>+ return stop_token{_M_state};
>>+ }
>>+
>>+ void
>>+ swap(stop_source& __other) noexcept
>>+ {
>>+ std::swap(_M_state, __other._M_state);
>>+ }
>>+
>>+ [[nodiscard]]
>>+ friend bool
>>+ operator==(const stop_source& __a, const stop_source& __b) noexcept
>>+ {
>>+ return __a._M_state == __b._M_state;
>>+ }
>>+
>>+ [[nodiscard]]
>>+ friend bool
>>+ operator!=(const stop_source& __a, const stop_source& __b) noexcept
>>+ {
>>+ return __a._M_state != __b._M_state;
>>+ }
>>+
>>+ private:
>>+ _Stop_state _M_state;
>>+ };
>>+
>>+ template<typename _Callback>
>>+ class [[nodiscard]] stop_callback
>>+ : private stop_token::_Stop_cb {
>
> Opening brace on the next line, in the same column as "class".
>
>>+ using _Stop_cb = stop_token::_Stop_cb;
>
> And then everything in the class body should be indented by another
> two columns.
>
>>+ using _Stop_state = stop_token::_Stop_state;
>>+ public:
>>+ using callback_type = _Callback;
>>+
>>+ template<typename _CB,
>
> Change to _Cb here too.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-11-14 2:00 ` Thomas Rodgers
@ 2019-11-14 16:12 ` Jonathan Wakely
2019-11-15 3:13 ` Thomas Rodgers
2019-11-15 14:38 ` Jonathan Wakely
1 sibling, 1 reply; 10+ messages in thread
From: Jonathan Wakely @ 2019-11-14 16:12 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
On 13/11/19 17:59 -0800, Thomas Rodgers wrote:
>+/** @file include/stop_token
>+ * This is a Standard C++ Library header.
>+ */
>+
>+#ifndef _GLIBCXX_STOP_TOKEN
>+#define _GLIBCXX_STOP_TOKEN
>+
>+#if __cplusplus >= 201703L
This should be > not >=
OK for trunk with that change.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-11-14 16:12 ` Jonathan Wakely
@ 2019-11-15 3:13 ` Thomas Rodgers
0 siblings, 0 replies; 10+ messages in thread
From: Thomas Rodgers @ 2019-11-15 3:13 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: gcc-patches, libstdc++
Tested x86_64-pc-linux-gnu, committed to trunk.
Jonathan Wakely writes:
> On 13/11/19 17:59 -0800, Thomas Rodgers wrote:
>>+/** @file include/stop_token
>>+ * This is a Standard C++ Library header.
>>+ */
>>+
>>+#ifndef _GLIBCXX_STOP_TOKEN
>>+#define _GLIBCXX_STOP_TOKEN
>>+
>>+#if __cplusplus >= 201703L
>
> This should be > not >=
>
> OK for trunk with that change.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-11-14 2:00 ` Thomas Rodgers
2019-11-14 16:12 ` Jonathan Wakely
@ 2019-11-15 14:38 ` Jonathan Wakely
2019-11-15 14:41 ` Jonathan Wakely
1 sibling, 1 reply; 10+ messages in thread
From: Jonathan Wakely @ 2019-11-15 14:38 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
On 13/11/19 17:59 -0800, Thomas Rodgers wrote:
>+ // TODO verify the standard requires this
>+#if 0
>+ // register another callback
>+ bool cb3called{false};
>+ std::stop_callback scb3{stok, [&]
>+ {
>+ cb3called = true;
>+ }};
>+ VERIFY(ssrc.stop_possible());
>+ VERIFY(ssrc.stop_requested());
>+ VERIFY(stok.stop_possible());
>+ VERIFY(stok.stop_requested());
>+ VERIFY(!cb1called);
>+ VERIFY(cb2called);
>+ VERIFY(cb3called);
>+#endif
The working draft definitely requires this:
Effects: Initializes callback with std::forward<C>(cb). If st.stop_requested() is true, then
std::forward<Callback>(callback)() is evaluated in the current thread before the constructor
returns.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-11-15 14:38 ` Jonathan Wakely
@ 2019-11-15 14:41 ` Jonathan Wakely
2019-11-15 23:44 ` Jonathan Wakely
0 siblings, 1 reply; 10+ messages in thread
From: Jonathan Wakely @ 2019-11-15 14:41 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 1197 bytes --]
On 15/11/19 14:38 +0000, Jonathan Wakely wrote:
>On 13/11/19 17:59 -0800, Thomas Rodgers wrote:
>>+ // TODO verify the standard requires this
>>+#if 0
>>+ // register another callback
>>+ bool cb3called{false};
>>+ std::stop_callback scb3{stok, [&]
>>+ {
>>+ cb3called = true;
>>+ }};
>>+ VERIFY(ssrc.stop_possible());
>>+ VERIFY(ssrc.stop_requested());
>>+ VERIFY(stok.stop_possible());
>>+ VERIFY(stok.stop_requested());
>>+ VERIFY(!cb1called);
>>+ VERIFY(cb2called);
>>+ VERIFY(cb3called);
>>+#endif
>
>The working draft definitely requires this:
>
>Effects: Initializes callback with std::forward<C>(cb). If st.stop_requested() is true, then
>std::forward<Callback>(callback)() is evaluated in the current thread before the constructor
>returns.
I've committed this fix to the nostopstate initializer, so it defines
a variable, instead of declaring a function. I've also added a test
that uses the stop_source(nostopstate_t) constructor, and a few other
tweaks to include the new header where it's needed.
Tested powerpc64le-linux, committed to trunk.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 8714 bytes --]
commit 293fddacf1d794d92408824c95de24d285bdc273
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Fri Nov 15 13:25:35 2019 +0000
libstdc++: Fix definition of std::nostopstate object
Also add <stop_token> header to PCH and Doxygen config.
* doc/doxygen/user.cfg.in: Add <stop_token>.
* include/precompiled/stdc++.h: Likewise.
* include/std/stop_token: Fix definition of std::nostopstate.
* testsuite/30_threads/headers/stop_token/synopsis.cc: New test.
* testsuite/30_threads/headers/thread/types_std_c++20.cc: New test.
* testsuite/30_threads/stop_token/stop_source.cc: New test.
* testsuite/30_threads/stop_token/stop_token.cc: Remove unnecessary
dg-require directives. Remove I/O and inclusion of <iostream>.
diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 18994703f0b..19f8ffd8230 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -840,6 +840,7 @@ INPUT = @srcdir@/doc/doxygen/doxygroups.cc \
include/sstream \
include/stack \
include/stdexcept \
+ include/stop_token \
include/streambuf \
include/string \
include/string_view \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 118fc8f359a..c77136ef441 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -140,6 +140,7 @@
#include <numbers>
#include <ranges>
#include <span>
+#include <stop_token>
// #include <syncstream>
#include <version>
#endif
diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token
index af64018bb0e..f96c267f22e 100644
--- a/libstdc++-v3/include/std/stop_token
+++ b/libstdc++-v3/include/std/stop_token
@@ -47,7 +47,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
class stop_callback;
struct nostopstate_t { explicit nostopstate_t() = default; };
- inline constexpr nostopstate_t nostopstate();
+ inline constexpr nostopstate_t nostopstate{};
class stop_token
{
diff --git a/libstdc++-v3/testsuite/30_threads/headers/stop_token/synopsis.cc b/libstdc++-v3/testsuite/30_threads/headers/stop_token/synopsis.cc
new file mode 100644
index 00000000000..b185bc7be39
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/headers/stop_token/synopsis.cc
@@ -0,0 +1,35 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <stop_token>
+
+namespace std
+{
+ class stop_token;
+ class stop_source;
+ struct nostopstate_t;
+ template<class>
+ class stop_callback;
+}
+
+namespace gnu
+{
+ constexpr const std::nostopstate_t* tag = &std::nostopstate;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/headers/thread/types_std_c++20.cc b/libstdc++-v3/testsuite/30_threads/headers/thread/types_std_c++20.cc
new file mode 100644
index 00000000000..86f29d8b001
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/headers/thread/types_std_c++20.cc
@@ -0,0 +1,30 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+// { dg-require-gthreads "" }
+
+// Copyright (C) 2019 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/>.
+
+#include <thread>
+
+void test01()
+{
+ using t_t = std::thread;
+ using j_t = std::jthread;
+
+ using namespace std::this_thread;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc
new file mode 100644
index 00000000000..9e477b7d7ee
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc
@@ -0,0 +1,75 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <stop_token>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ std::stop_source ssrc;
+ VERIFY( ssrc.stop_possible() );
+ VERIFY( !ssrc.stop_requested() );
+
+ std::stop_source copy(ssrc);
+ VERIFY( copy.stop_possible() );
+ VERIFY( !copy.stop_requested() );
+ VERIFY( ssrc.stop_possible() );
+ VERIFY( !ssrc.stop_requested() );
+
+ std::stop_source move(std::move(ssrc));
+ VERIFY( move.stop_possible() );
+ VERIFY( !move.stop_requested() );
+ VERIFY( copy.stop_possible() );
+ VERIFY( !copy.stop_requested() );
+ VERIFY( !ssrc.stop_possible() );
+ VERIFY( !ssrc.stop_requested() );
+}
+
+void
+test02()
+{
+ // stop_source(nostopstate_t) constructor is explicit:
+ static_assert(!std::is_convertible_v<std::nostopstate_t, std::stop_source>);
+
+ std::stop_source ssrc(std::nostopstate);
+ VERIFY( !ssrc.stop_possible() );
+ VERIFY( !ssrc.stop_requested() );
+
+ std::stop_source copy(ssrc);
+ VERIFY( !copy.stop_possible() );
+ VERIFY( !copy.stop_requested() );
+ VERIFY( !ssrc.stop_possible() );
+ VERIFY( !ssrc.stop_requested() );
+
+ std::stop_source move(std::move(ssrc));
+ VERIFY( !move.stop_possible() );
+ VERIFY( !move.stop_requested() );
+ VERIFY( !copy.stop_possible() );
+ VERIFY( !copy.stop_requested() );
+ VERIFY( !ssrc.stop_possible() );
+ VERIFY( !ssrc.stop_requested() );
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
index 37d79e8a64e..f0772c1d80f 100644
--- a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
@@ -15,13 +15,10 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
-// { dg-options "-std=gnu++2a -pthread" }
-// { dg-do run }
-// { dg-require-effective-target c++2a }
-// { dg-require-effective-target pthread }
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
#include <stop_token>
-#include <iostream>
#include <testsuite_hooks.h>
int main()
@@ -41,7 +38,6 @@ int main()
// register callback
bool cb1called{false};
auto cb1 = [&]{
- std::cout << "cb1" << std::endl;
cb1called = true;
};
{
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-11-15 14:41 ` Jonathan Wakely
@ 2019-11-15 23:44 ` Jonathan Wakely
2019-11-18 12:28 ` Jonathan Wakely
0 siblings, 1 reply; 10+ messages in thread
From: Jonathan Wakely @ 2019-11-15 23:44 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 1389 bytes --]
On 15/11/19 14:40 +0000, Jonathan Wakely wrote:
>On 15/11/19 14:38 +0000, Jonathan Wakely wrote:
>>On 13/11/19 17:59 -0800, Thomas Rodgers wrote:
>>>+ // TODO verify the standard requires this
>>>+#if 0
>>>+ // register another callback
>>>+ bool cb3called{false};
>>>+ std::stop_callback scb3{stok, [&]
>>>+ {
>>>+ cb3called = true;
>>>+ }};
>>>+ VERIFY(ssrc.stop_possible());
>>>+ VERIFY(ssrc.stop_requested());
>>>+ VERIFY(stok.stop_possible());
>>>+ VERIFY(stok.stop_requested());
>>>+ VERIFY(!cb1called);
>>>+ VERIFY(cb2called);
>>>+ VERIFY(cb3called);
>>>+#endif
>>
>>The working draft definitely requires this:
>>
>>Effects: Initializes callback with std::forward<C>(cb). If st.stop_requested() is true, then
>>std::forward<Callback>(callback)() is evaluated in the current thread before the constructor
>>returns.
>
>I've committed this fix to the nostopstate initializer, so it defines
>a variable, instead of declaring a function. I've also added a test
>that uses the stop_source(nostopstate_t) constructor, and a few other
>tweaks to include the new header where it's needed.
This fixes some other issues I noticed, and should fix the failues for
the single-threaded multilib on AIX.
Tested powerpc64le-linux, committed to trunk.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 19565 bytes --]
commit 9c9f5a4bd84b924190551b65d4b66665aaa3fbe6
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Fri Nov 15 21:27:26 2019 +0000
libstdc++: Fix <stop_token> and improve tests
* include/std/stop_token: Reduce header dependencies by including
internal headers.
(stop_token::swap(stop_token&), swap(stop_token&, stop_token&)):
Define.
(operator!=(const stop_token&, const stop_token&)): Fix return value.
(stop_token::_Stop_cb::_Stop_cb(Cb&&)): Use std::forward instead of
(stop_token::_Stop_state_t) [_GLIBCXX_HAS_GTHREADS]: Use lock_guard
instead of unique_lock.
[!_GLIBCXX_HAS_GTHREADS]: Do not use mutex.
(stop_token::stop_token(_Stop_state)): Change parameter to lvalue
reference.
(stop_source): Remove unnecessary using-declarations for names only
used once.
(swap(stop_source&, stop_source&)): Define.
(stop_callback(const stop_token&, _Cb&&))
(stop_callback(stop_token&&, _Cb&&)): Replace lambdas with a named
function. Use std::forward instead of std::move. Run callbacks if a
stop request has already been made.
(stop_source::_M_execute()): Remove.
(stop_source::_S_execute(_Stop_cb*)): Define.
* include/std/version (__cpp_lib_jthread): Define conditionally.
* testsuite/30_threads/stop_token/stop_callback.cc: New test.
* testsuite/30_threads/stop_token/stop_source.cc: New test.
* testsuite/30_threads/stop_token/stop_token.cc: Enable test for
immediate execution of callback.
diff --git a/libstdc++-v3/include/std/stop_token b/libstdc++-v3/include/std/stop_token
index f96c267f22e..bb082431d7f 100644
--- a/libstdc++-v3/include/std/stop_token
+++ b/libstdc++-v3/include/std/stop_token
@@ -31,24 +31,25 @@
#if __cplusplus > 201703L
-#include <type_traits>
-#include <memory>
-#include <mutex>
#include <atomic>
+#include <bits/std_mutex.h>
+#include <ext/concurrence.h>
+#include <bits/unique_ptr.h>
+#include <bits/shared_ptr.h>
-#define __cpp_lib_jthread 201907L
+#ifdef _GLIBCXX_HAS_GTHREADS
+# define __cpp_lib_jthread 201907L
+#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
- class stop_source;
- template<typename _Callback>
- class stop_callback;
-
+ /// Tag type indicating a stop_source should have no shared-stop-state.
struct nostopstate_t { explicit nostopstate_t() = default; };
inline constexpr nostopstate_t nostopstate{};
+ /// Allow testing whether a stop request has been made on a `stop_source`.
class stop_token
{
public:
@@ -63,7 +64,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
operator=(const stop_token& __rhs) noexcept = default;
stop_token&
- operator=(stop_token&& __rhs) noexcept;
+ operator=(stop_token&& __rhs) noexcept = default;
[[nodiscard]]
bool
@@ -79,6 +80,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return stop_possible() && _M_state->_M_stop_requested();
}
+ void
+ swap(stop_token& __rhs) noexcept
+ { _M_state.swap(__rhs._M_state); }
+
[[nodiscard]]
friend bool
operator==(const stop_token& __a, const stop_token& __b)
@@ -90,26 +95,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
friend bool
operator!=(const stop_token& __a, const stop_token& __b)
{
- return __a._M_state == __b._M_state;
+ return __a._M_state != __b._M_state;
}
- private:
- friend stop_source;
- template<typename _Callback>
- friend class stop_callback;
+ friend void
+ swap(stop_token& __lhs, stop_token& __rhs) noexcept
+ { __lhs.swap(__rhs); }
- struct _Stop_cb {
+ private:
+ friend class stop_source;
+ template<typename _Callback>
+ friend class stop_callback;
+
+ struct _Stop_cb
+ {
void(*_M_callback)(_Stop_cb*);
_Stop_cb* _M_prev = nullptr;
_Stop_cb* _M_next = nullptr;
template<typename _Cb>
- _Stop_cb(_Cb&& __cb)
- : _M_callback(std::move(__cb))
- { }
+ _Stop_cb(_Cb&& __cb)
+ : _M_callback(std::forward<_Cb>(__cb))
+ { }
bool
- _M_linked() const
+ _M_linked() const noexcept
{
return (_M_prev != nullptr)
|| (_M_next != nullptr);
@@ -123,17 +133,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
};
- struct _Stop_state_t {
- std::atomic<bool> _M_stopped;
- std::mutex _M_mtx;
+ struct _Stop_state_t
+ {
+ std::atomic<bool> _M_stopped{false};
_Stop_cb* _M_head = nullptr;
+#ifdef _GLIBCXX_HAS_GTHREADS
+ std::mutex _M_mtx;
+#endif
- _Stop_state_t()
- : _M_stopped{false}
- { }
+ _Stop_state_t() = default;
bool
- _M_stop_requested()
+ _M_stop_requested() noexcept
{
return _M_stopped;
}
@@ -144,7 +155,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool __stopped = false;
if (_M_stopped.compare_exchange_strong(__stopped, true))
{
- std::unique_lock<std::mutex> __lck{_M_mtx};
+#ifdef _GLIBCXX_HAS_GTHREADS
+ std::lock_guard<std::mutex> __lck{_M_mtx};
+#endif
while (_M_head)
{
auto __p = _M_head;
@@ -159,7 +172,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool
_M_register_callback(_Stop_cb* __cb)
{
- std::unique_lock<std::mutex> __lck{_M_mtx};
+#ifdef _GLIBCXX_HAS_GTHREADS
+ std::lock_guard<std::mutex> __lck{_M_mtx};
+#endif
if (_M_stopped)
return false;
@@ -169,13 +184,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_head->_M_prev = __cb;
}
_M_head = __cb;
- return true;
+ return true;
}
void
_M_remove_callback(_Stop_cb* __cb)
{
- std::unique_lock<std::mutex> __lck{_M_mtx};
+#ifdef _GLIBCXX_HAS_GTHREADS
+ std::lock_guard<std::mutex> __lck{_M_mtx};
+#endif
if (__cb == _M_head)
{
_M_head = _M_head->_M_next;
@@ -202,18 +219,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using _Stop_state = std::shared_ptr<_Stop_state_t>;
_Stop_state _M_state;
- explicit stop_token(_Stop_state __state)
- : _M_state{std::move(__state)}
+ explicit
+ stop_token(const _Stop_state& __state) noexcept
+ : _M_state{__state}
{ }
};
- class stop_source {
- using _Stop_state_t = stop_token::_Stop_state_t;
- using _Stop_state = stop_token::_Stop_state;
-
+ /// A type that allows a stop request to be made.
+ class stop_source
+ {
public:
stop_source()
- : _M_state(std::make_shared<_Stop_state_t>())
+ : _M_state(std::make_shared<stop_token::_Stop_state_t>())
{ }
explicit stop_source(std::nostopstate_t) noexcept
@@ -274,7 +291,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void
swap(stop_source& __other) noexcept
{
- std::swap(_M_state, __other._M_state);
+ _M_state.swap(__other._M_state);
}
[[nodiscard]]
@@ -291,59 +308,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __a._M_state != __b._M_state;
}
+ friend void
+ swap(stop_source& __lhs, stop_source& __rhs) noexcept
+ {
+ __lhs.swap(__rhs);
+ }
+
private:
- _Stop_state _M_state;
+ stop_token::_Stop_state _M_state;
};
+ /// A wrapper for callbacks to be run when a stop request is made.
template<typename _Callback>
class [[nodiscard]] stop_callback
: private stop_token::_Stop_cb
{
- using _Stop_cb = stop_token::_Stop_cb;
- using _Stop_state = stop_token::_Stop_state;
public:
using callback_type = _Callback;
template<typename _Cb,
- std::enable_if_t<std::is_constructible_v<_Callback, _Cb>, int> = 0>
- explicit stop_callback(const stop_token& __token, _Cb&& __cb)
- noexcept(std::is_nothrow_constructible_v<_Callback, _Cb>)
- : _Stop_cb([](_Stop_cb* __that) noexcept
- {
- static_cast<stop_callback*>(__that)->_M_execute();
- }),
- _M_cb(std::move(__cb))
+ enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0>
+ explicit
+ stop_callback(const stop_token& __token, _Cb&& __cb)
+ noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
+ : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb))
{
- auto res = __token._M_state->_M_register_callback(this);
- if (__token._M_state && res)
- {
- _M_state = __token._M_state;
- }
+ if (auto __state = __token._M_state)
+ {
+ if (__state->_M_stop_requested())
+ _S_execute(this); // ensures std::terminate on throw
+ else if (__state->_M_register_callback(this))
+ _M_state.swap(__state);
+ }
}
template<typename _Cb,
- std::enable_if_t<std::is_constructible_v<_Callback, _Cb>, int> = 0>
- explicit stop_callback(stop_token&& __token, _Cb&& __cb)
- noexcept(std::is_nothrow_constructible_v<_Callback, _Cb>)
- : _Stop_cb([](_Stop_cb* __that) noexcept
- {
- static_cast<stop_callback*>(__that)->_M_execute();
- }),
- _M_cb(std::move(__cb))
- {
- if (__token._M_state && __token._M_state->_M_register_callback(this))
- {
- std::swap(_M_state, __token._M_state);
- }
- }
+ enable_if_t<is_constructible_v<_Callback, _Cb>, int> = 0>
+ explicit
+ stop_callback(stop_token&& __token, _Cb&& __cb)
+ noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
+ : _Stop_cb(&_S_execute), _M_cb(std::forward<_Cb>(__cb))
+ {
+ if (auto& __state = __token._M_state)
+ {
+ if (__state->_M_stop_requested())
+ _S_execute(this); // ensures std::terminate on throw
+ else if (__state->_M_register_callback(this))
+ _M_state.swap(__state);
+ }
+ }
~stop_callback()
- {
- if (_M_state)
- {
- _M_state->_M_remove_callback(this);
- }
- }
+ {
+ if (_M_state)
+ {
+ _M_state->_M_remove_callback(this);
+ }
+ }
stop_callback(const stop_callback&) = delete;
stop_callback& operator=(const stop_callback&) = delete;
@@ -352,12 +373,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
private:
_Callback _M_cb;
- _Stop_state _M_state = nullptr;
+ stop_token::_Stop_state _M_state = nullptr;
- void
- _M_execute() noexcept
+ static void
+ _S_execute(_Stop_cb* __that) noexcept
{
- _M_cb();
+ static_cast<stop_callback*>(__that)->_M_cb();
}
};
@@ -366,5 +387,5 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
-#endif // __cplusplus >= 201703L
+#endif // __cplusplus > 201703L
#endif // _GLIBCXX_STOP_TOKEN
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 27807428903..8e08dc99049 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -184,10 +184,12 @@
#define __cpp_lib_constexpr_invoke 201907L
#define __cpp_lib_erase_if 201900L
#define __cpp_lib_interpolate 201902L
+#ifdef _GLIBCXX_HAS_GTHREADS
+# define __cpp_lib_jthread 201907L
+#endif
#define __cpp_lib_list_remove_return_type 201806L
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_span 201902L
-#define __cpp_lib_jthread 201907L
#if __cpp_impl_three_way_comparison >= 201907L
# define __cpp_lib_three_way_comparison 201711L
#endif
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback.cc
new file mode 100644
index 00000000000..beb155d494a
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback.cc
@@ -0,0 +1,128 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <stop_token>
+#include <functional>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ bool called = false;
+ std::function<void()> f = [&called]{ called = true; };
+ std::stop_source ssrc;
+ ssrc.request_stop();
+ std::stop_token tok = ssrc.get_token();
+ std::stop_callback cb1(tok, f);
+ VERIFY( tok.stop_possible() );
+ VERIFY( f != nullptr );
+ VERIFY( called == true );
+
+ called = false;
+ std::stop_callback cb2(std::move(tok), f);
+ // when callback is executed immediately, no change in ownership:
+ VERIFY( tok.stop_possible() );
+ VERIFY( f != nullptr );
+ VERIFY( called == true );
+
+ std::stop_token sink(std::move(tok)); // leave tok empty
+
+ called = false;
+ std::stop_callback cb3(tok, f);
+ VERIFY( f != nullptr );
+ VERIFY( called == false );
+
+ called = false;
+ std::stop_callback cb4(std::move(tok), f);
+ VERIFY( f != nullptr );
+ VERIFY( called == false );
+}
+
+void
+test02()
+{
+ bool called = false;
+ std::function<void()> f0 = [&called]{ called = true; };
+ std::function<void()> f = f0;
+ std::stop_source ssrc;
+ ssrc.request_stop();
+ std::stop_token tok = ssrc.get_token();
+
+ std::stop_callback cb1(tok, std::move(f));
+ VERIFY( tok.stop_possible() );
+ VERIFY( f == nullptr );
+ VERIFY( called == true );
+
+ called = false;
+ f = f0;
+ std::stop_callback cb2(std::move(tok), std::move(f));
+ // when callback is executed immediately, no change in ownership:
+ VERIFY( tok.stop_possible() );
+ VERIFY( f == nullptr );
+ VERIFY( called == true );
+
+ std::stop_token sink(std::move(tok)); // leave tok empty
+
+ called = false;
+ f = f0;
+ std::stop_callback cb3(tok, std::move(f));
+ VERIFY( f == nullptr );
+ VERIFY( called == false );
+
+ called = false;
+ f = f0;
+ std::stop_callback cb4(std::move(tok), std::move(f));
+ VERIFY( f == nullptr );
+ VERIFY( called == false );
+}
+
+void
+test03()
+{
+ bool called[4] = { };
+ std::stop_source ssrc;
+ std::stop_token tok = ssrc.get_token();
+ std::stop_callback cb1(tok, [&]{ called[0] = true; });
+ VERIFY( tok.stop_possible() );
+ VERIFY( called[0] == false );
+
+ std::stop_callback cb2(std::move(tok), [&]{ called[1] = true; });
+ VERIFY( !tok.stop_possible() );
+ VERIFY( called[1] == false );
+
+ std::stop_callback cb3(tok, [&]{ called[2] = true; });
+ VERIFY( called[2] == false );
+
+ std::stop_callback cb4(std::move(tok), [&]{ called[3] = true; });
+ VERIFY( called[3] == false );
+
+ ssrc.request_stop();
+ VERIFY( called[0] == true );
+ VERIFY( called[1] == true );
+ VERIFY( called[2] == false );
+ VERIFY( called[3] == false );
+}
+
+int main()
+{
+ test01();
+ test02();
+ test03();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc
index 9e477b7d7ee..3fe76143bd0 100644
--- a/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_source.cc
@@ -68,8 +68,27 @@ test02()
VERIFY( !ssrc.stop_requested() );
}
+void
+test03()
+{
+ std::stop_source s1;
+ s1.request_stop();
+ std::stop_source s2(std::nostopstate);
+ s1.swap(s2);
+ VERIFY( !s1.stop_possible() );
+ VERIFY( !s1.stop_requested() );
+ VERIFY( s2.stop_possible() );
+ VERIFY( s2.stop_requested() );
+ swap(s1, s2);
+ VERIFY( s1.stop_possible() );
+ VERIFY( s1.stop_requested() );
+ VERIFY( !s2.stop_possible() );
+ VERIFY( !s2.stop_requested() );
+}
+
int main()
{
test01();
test02();
+ test03();
}
diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
index f0772c1d80f..6dc850c6441 100644
--- a/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
+++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc
@@ -21,7 +21,76 @@
#include <stop_token>
#include <testsuite_hooks.h>
-int main()
+void
+test01()
+{
+ std::stop_source ssrc;
+ std::stop_token tok = ssrc.get_token();
+ VERIFY(tok.stop_possible());
+ VERIFY(!tok.stop_requested());
+
+ std::stop_token copy(tok);
+ VERIFY(copy == tok);
+ VERIFY(copy.stop_possible());
+ VERIFY(!copy.stop_requested());
+ VERIFY(tok.stop_possible());
+ VERIFY(!tok.stop_requested());
+
+ std::stop_token move(std::move(tok));
+ VERIFY(move != tok);
+ VERIFY(move == copy);
+ VERIFY(move.stop_possible());
+ VERIFY(!move.stop_requested());
+ VERIFY(!tok.stop_possible());
+ VERIFY(!tok.stop_requested());
+ VERIFY(copy.stop_possible());
+ VERIFY(!copy.stop_requested());
+
+ ssrc.request_stop();
+ VERIFY(move.stop_possible());
+ VERIFY(move.stop_requested());
+ VERIFY(!tok.stop_possible());
+ VERIFY(!tok.stop_requested());
+ VERIFY(copy.stop_possible());
+ VERIFY(copy.stop_requested());
+
+ tok.swap(move);
+ VERIFY(tok == copy);
+ VERIFY(!move.stop_possible());
+ VERIFY(!move.stop_requested());
+ VERIFY(tok.stop_possible());
+ VERIFY(tok.stop_requested());
+ VERIFY(copy.stop_possible());
+ VERIFY(copy.stop_requested());
+
+ swap(move, copy);
+ VERIFY(tok == move);
+ VERIFY(move.stop_possible());
+ VERIFY(move.stop_requested());
+ VERIFY(tok.stop_possible());
+ VERIFY(tok.stop_requested());
+ VERIFY(!copy.stop_possible());
+ VERIFY(!copy.stop_requested());
+}
+
+void
+test02()
+{
+ std::stop_source src1, src2;
+ std::stop_token tok = src1.get_token();
+ VERIFY(tok.stop_possible());
+ VERIFY(!tok.stop_requested());
+
+ std::stop_token copy = src2.get_token();
+ VERIFY(copy != tok);
+ copy = tok;
+ VERIFY(copy == tok);
+ copy = src2.get_token();
+ VERIFY(copy != tok);
+}
+
+void
+test03()
{
// create stop_source
std::stop_source ssrc;
@@ -77,8 +146,6 @@ int main()
b = ssrc.request_stop();
VERIFY(!b);
- // TODO verify the standard requires this
-#if 0
// register another callback
bool cb3called{false};
std::stop_callback scb3{stok, [&]
@@ -92,5 +159,11 @@ int main()
VERIFY(!cb1called);
VERIFY(cb2called);
VERIFY(cb3called);
-#endif
+}
+
+int main()
+{
+ test01();
+ test02();
+ test03();
}
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add support for C++2a stop_token
2019-11-15 23:44 ` Jonathan Wakely
@ 2019-11-18 12:28 ` Jonathan Wakely
0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Wakely @ 2019-11-18 12:28 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 2122 bytes --]
On 15/11/19 23:43 +0000, Jonathan Wakely wrote:
>On 15/11/19 14:40 +0000, Jonathan Wakely wrote:
>>On 15/11/19 14:38 +0000, Jonathan Wakely wrote:
>>>On 13/11/19 17:59 -0800, Thomas Rodgers wrote:
>>>>+ // TODO verify the standard requires this
>>>>+#if 0
>>>>+ // register another callback
>>>>+ bool cb3called{false};
>>>>+ std::stop_callback scb3{stok, [&]
>>>>+ {
>>>>+ cb3called = true;
>>>>+ }};
>>>>+ VERIFY(ssrc.stop_possible());
>>>>+ VERIFY(ssrc.stop_requested());
>>>>+ VERIFY(stok.stop_possible());
>>>>+ VERIFY(stok.stop_requested());
>>>>+ VERIFY(!cb1called);
>>>>+ VERIFY(cb2called);
>>>>+ VERIFY(cb3called);
>>>>+#endif
>>>
>>>The working draft definitely requires this:
>>>
>>>Effects: Initializes callback with std::forward<C>(cb). If st.stop_requested() is true, then
>>>std::forward<Callback>(callback)() is evaluated in the current thread before the constructor
>>>returns.
>>
>>I've committed this fix to the nostopstate initializer, so it defines
>>a variable, instead of declaring a function. I've also added a test
>>that uses the stop_source(nostopstate_t) constructor, and a few other
>>tweaks to include the new header where it's needed.
>
>This fixes some other issues I noticed, and should fix the failues for
>the single-threaded multilib on AIX.
And a couple more bugs fixed by this patch.
The std::jthread::get_id() function was missing a return statement.
The is_invocable check needs to be done using decayed types, as they'll
be forwarded to std::invoke as rvalues.
Also reduce header dependencies for the <thread> header. We don't need
to include <functional> for std::jthread because <bits/invoke.h> is
already included, which defines std::__invoke. We can also remove
<bits/functexcept.h> which isn't used at all. Finally, when
_GLIBCXX_HAS_GTHREADS is not defined there's no point including any
other headers, since we're not going to define anything in <thread>
anyway.
Tested powerpc64le-linux, committed to trunk.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 12582 bytes --]
commit f0c4ca8e20d9aadd8891bec246aecfc54177163c
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Mon Nov 18 11:45:44 2019 +0000
libstdc++: Fix std::jthread bugs
The std::jthread::get_id() function was missing a return statement.
The is_invocable check needs to be done using decayed types, as they'll
be forwarded to std::invoke as rvalues.
* include/std/thread: Reduce header dependencies.
(jthread::get_id()): Add missing return.
(jthread::get_stop_token()): Avoid unnecessary stop_source temporary.
(jthread::_S_create): Check is_invocable using decayed types. Add
static assertion.
* testsuite/30_threads/jthread/1.cc: Add dg-require-gthreads.
* testsuite/30_threads/jthread/2.cc: Likewise.
* testsuite/30_threads/jthread/3.cc: New test.
* testsuite/30_threads/jthread/jthread.cc: Add missing directives for
pthread and gthread support. Use VERIFY instead of assert.
diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread
index 010921b2160..825ce4096fc 100644
--- a/libstdc++-v3/include/std/thread
+++ b/libstdc++-v3/include/std/thread
@@ -35,23 +35,26 @@
# include <bits/c++0x_warning.h>
#else
-#include <chrono>
-#include <memory>
-#include <tuple>
-#include <cerrno>
-
-#if __cplusplus > 201703L
-#define __cpp_lib_jthread 201907L
-#include <functional>
-#include <stop_token>
-#endif
-
-#include <bits/functexcept.h>
-#include <bits/functional_hash.h>
-#include <bits/invoke.h>
-#include <bits/gthr.h>
+#include <bits/c++config.h>
#if defined(_GLIBCXX_HAS_GTHREADS)
+#include <bits/gthr.h>
+
+#include <chrono> // std::chrono::*
+#include <memory> // std::unique_ptr
+#include <tuple> // std::tuple
+
+#if __cplusplus > 201703L
+# include <stop_token> // std::stop_source, std::stop_token, std::nostopstate
+#endif
+
+#ifdef _GLIBCXX_USE_NANOSLEEP
+# include <cerrno> // errno, EINTR
+# include <time.h> // nanosleep
+#endif
+
+#include <bits/functional_hash.h> // std::hash
+#include <bits/invoke.h> // std::__invoke
namespace std _GLIBCXX_VISIBILITY(default)
{
@@ -483,7 +486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
[[nodiscard]] id
get_id() const noexcept
{
- _M_thread.get_id();
+ return _M_thread.get_id();
}
[[nodiscard]] native_handle_type
@@ -512,7 +515,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool request_stop() noexcept
{
- return get_stop_source().request_stop();
+ return _M_stop_source.request_stop();
}
friend void swap(jthread& __lhs, jthread& __rhs) noexcept
@@ -525,12 +528,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static thread
_S_create(stop_source& __ssrc, _Callable&& __f, _Args&&... __args)
{
- if constexpr(is_invocable_v<_Callable, stop_token, _Args...>)
+ if constexpr(is_invocable_v<decay_t<_Callable>, stop_token,
+ decay_t<_Args>...>)
return thread{std::forward<_Callable>(__f), __ssrc.get_token(),
std::forward<_Args>(__args)...};
else
- return thread{std::forward<_Callable>(__f),
- std::forward<_Args>(__args)...};
+ {
+ static_assert(is_invocable_v<decay_t<_Callable>,
+ decay_t<_Args>...>,
+ "std::thread arguments must be invocable after"
+ " conversion to rvalues");
+ return thread{std::forward<_Callable>(__f),
+ std::forward<_Args>(__args)...};
+ }
}
stop_source _M_stop_source;
@@ -539,9 +549,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif // __cpp_lib_jthread
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
-
#endif // _GLIBCXX_HAS_GTHREADS
-
#endif // C++11
-
#endif // _GLIBCXX_THREAD
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/1.cc b/libstdc++-v3/testsuite/30_threads/jthread/1.cc
index 1fb5650dbc6..574c1f25dc3 100644
--- a/libstdc++-v3/testsuite/30_threads/jthread/1.cc
+++ b/libstdc++-v3/testsuite/30_threads/jthread/1.cc
@@ -17,6 +17,7 @@
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
+// { dg-require-gthreads "" }
#include <thread>
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/2.cc b/libstdc++-v3/testsuite/30_threads/jthread/2.cc
index 621965c8910..52413c8d2a3 100644
--- a/libstdc++-v3/testsuite/30_threads/jthread/2.cc
+++ b/libstdc++-v3/testsuite/30_threads/jthread/2.cc
@@ -17,6 +17,7 @@
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
+// { dg-require-gthreads "" }
#include <version>
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/3.cc b/libstdc++-v3/testsuite/30_threads/jthread/3.cc
new file mode 100644
index 00000000000..e4a2fbb3620
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/jthread/3.cc
@@ -0,0 +1,45 @@
+// Copyright (C) 2019 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-options "-std=gnu++2a -pthread" }
+// { dg-do compile { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <thread>
+
+struct RvalCallable
+{
+ void operator()() && { }
+};
+
+void test01()
+{
+ RvalCallable r;
+ std::jthread t(r);
+}
+
+struct RvalCallableWithToken
+{
+ void operator()(std::stop_token) && { }
+};
+
+void test02()
+{
+ RvalCallableWithToken r;
+ std::jthread t(r);
+}
diff --git a/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc b/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc
index c29db212167..aeb9f1f1b7f 100644
--- a/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc
+++ b/libstdc++-v3/testsuite/30_threads/jthread/jthread.cc
@@ -15,15 +15,17 @@
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
-// { dg-options "-std=gnu++2a" }
-// { dg-do compile { target c++2a } }
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
#include <thread>
#include <chrono>
-#include <cassert>
#include <atomic>
+#include <testsuite_hooks.h>
-using namespace::std::literals;
+using namespace std::literals;
//------------------------------------------------------
@@ -31,9 +33,9 @@ void test_no_stop_token()
{
// test the basic jthread API (not taking stop_token arg)
- assert(std::jthread::hardware_concurrency() == std::thread::hardware_concurrency());
+ VERIFY(std::jthread::hardware_concurrency() == std::thread::hardware_concurrency());
std::stop_token stoken;
- assert(!stoken.stop_possible());
+ VERIFY(!stoken.stop_possible());
{
std::jthread::id t1ID{std::this_thread::get_id()};
std::atomic<bool> t1AllSet{false};
@@ -47,12 +49,12 @@ void test_no_stop_token()
for (int i=0; !t1AllSet.load(); ++i) {
std::this_thread::sleep_for(10ms);
}
- assert(t1.joinable());
- assert(t1ID == t1.get_id());
+ VERIFY(t1.joinable());
+ VERIFY(t1ID == t1.get_id());
stoken = t1.get_stop_token();
- assert(!stoken.stop_requested());
- }
- assert(stoken.stop_requested());
+ VERIFY(!stoken.stop_requested());
+ }
+ VERIFY(stoken.stop_requested());
}
//------------------------------------------------------
@@ -63,8 +65,8 @@ void test_stop_token()
std::stop_source ssource;
std::stop_source origsource;
- assert(ssource.stop_possible());
- assert(!ssource.stop_requested());
+ VERIFY(ssource.stop_possible());
+ VERIFY(!ssource.stop_requested());
{
std::jthread::id t1ID{std::this_thread::get_id()};
std::atomic<bool> t1AllSet{false};
@@ -83,26 +85,26 @@ void test_stop_token()
std::this_thread::sleep_for(10ms);
}
// and check all values:
- assert(t1.joinable());
- assert(t1ID == t1.get_id());
+ VERIFY(t1.joinable());
+ VERIFY(t1ID == t1.get_id());
std::this_thread::sleep_for(470ms);
origsource = std::move(ssource);
ssource = t1.get_stop_source();
- assert(!ssource.stop_requested());
+ VERIFY(!ssource.stop_requested());
auto ret = ssource.request_stop();
- assert(ret);
+ VERIFY(ret);
ret = ssource.request_stop();
- assert(!ret);
- assert(ssource.stop_requested());
- assert(!t1done.load());
- assert(!origsource.stop_requested());
+ VERIFY(!ret);
+ VERIFY(ssource.stop_requested());
+ VERIFY(!t1done.load());
+ VERIFY(!origsource.stop_requested());
std::this_thread::sleep_for(470ms);
origsource.request_stop();
- }
- assert(origsource.stop_requested());
- assert(ssource.stop_requested());
+ }
+ VERIFY(origsource.stop_requested());
+ VERIFY(ssource.stop_requested());
}
//------------------------------------------------------
@@ -110,7 +112,7 @@ void test_stop_token()
void test_join()
{
std::stop_source ssource;
- assert(ssource.stop_possible());
+ VERIFY(ssource.stop_possible());
{
std::jthread t1([](std::stop_token stoken) {
for (int i=0; !stoken.stop_requested(); ++i) {
@@ -126,10 +128,10 @@ void test_join()
});
// wait for all thread to finish:
t2.join();
- assert(!t2.joinable());
- assert(t1.joinable());
+ VERIFY(!t2.joinable());
+ VERIFY(t1.joinable());
t1.join();
- assert(!t1.joinable());
+ VERIFY(!t1.joinable());
}
}
@@ -138,7 +140,7 @@ void test_join()
void test_detach()
{
std::stop_source ssource;
- assert(ssource.stop_possible());
+ VERIFY(ssource.stop_possible());
std::atomic<bool> t1FinallyInterrupted{false};
{
std::jthread t0;
@@ -152,8 +154,8 @@ void test_detach()
t1ID = std::this_thread::get_id();
t1InterruptToken = stoken;
t1IsInterrupted = stoken.stop_requested();
- assert(stoken.stop_possible());
- assert(!stoken.stop_requested());
+ VERIFY(stoken.stop_possible());
+ VERIFY(!stoken.stop_requested());
t1AllSet.store(true);
for (int i=0; !stoken.stop_requested(); ++i) {
std::this_thread::sleep_for(100ms);
@@ -163,31 +165,31 @@ void test_detach()
for (int i=0; !t1AllSet.load(); ++i) {
std::this_thread::sleep_for(10ms);
}
- assert(!t0.joinable());
- assert(t1.joinable());
- assert(t1ID == t1.get_id());
- assert(t1IsInterrupted == false);
- assert(t1InterruptToken == t1.get_stop_source().get_token());
+ VERIFY(!t0.joinable());
+ VERIFY(t1.joinable());
+ VERIFY(t1ID == t1.get_id());
+ VERIFY(t1IsInterrupted == false);
+ VERIFY(t1InterruptToken == t1.get_stop_source().get_token());
ssource = t1.get_stop_source();
- assert(t1InterruptToken.stop_possible());
- assert(!t1InterruptToken.stop_requested());
+ VERIFY(t1InterruptToken.stop_possible());
+ VERIFY(!t1InterruptToken.stop_requested());
t1.detach();
- assert(!t1.joinable());
+ VERIFY(!t1.joinable());
}
- assert(!t1FinallyInterrupted.load());
+ VERIFY(!t1FinallyInterrupted.load());
ssource.request_stop();
- assert(ssource.stop_requested());
+ VERIFY(ssource.stop_requested());
for (int i=0; !t1FinallyInterrupted.load() && i < 100; ++i) {
std::this_thread::sleep_for(100ms);
}
- assert(t1FinallyInterrupted.load());
+ VERIFY(t1FinallyInterrupted.load());
}
int main()
{
std::set_terminate([](){
- assert(false);
+ VERIFY(false);
});
test_no_stop_token();
@@ -195,4 +197,3 @@ int main()
test_join();
test_detach();
}
-
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2019-11-18 12:28 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-23 0:59 [PATCH] Add support for C++2a stop_token Thomas Rodgers
2019-10-23 19:50 ` Thomas Rodgers
2019-10-24 15:45 ` Jonathan Wakely
2019-11-14 2:00 ` Thomas Rodgers
2019-11-14 16:12 ` Jonathan Wakely
2019-11-15 3:13 ` Thomas Rodgers
2019-11-15 14:38 ` Jonathan Wakely
2019-11-15 14:41 ` Jonathan Wakely
2019-11-15 23:44 ` Jonathan Wakely
2019-11-18 12:28 ` Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).