Thomas Rodgers writes:

Let's try this again.
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  <trodg...@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  <jwak...@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

Reply via email to