Support for jthread and stop_token
authorThomas Rodgers <trodgers@redhat.com>
Fri, 15 Nov 2019 03:09:19 +0000 (03:09 +0000)
committerThomas Rodgers <rodgertq@gcc.gnu.org>
Fri, 15 Nov 2019 03:09:19 +0000 (03:09 +0000)
        * 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.

From-SVN: r278274

16 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/include/Makefile.am
libstdc++-v3/include/Makefile.in
libstdc++-v3/include/std/condition_variable
libstdc++-v3/include/std/stop_token [new file with mode: 0644]
libstdc++-v3/include/std/thread
libstdc++-v3/include/std/version
libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/1.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/2.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/jthread/1.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/jthread/2.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/jthread/jthread.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/stop_token/1.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/stop_token/2.cc [new file with mode: 0644]
libstdc++-v3/testsuite/30_threads/stop_token/stop_token.cc [new file with mode: 0644]

index 13cca24e368fa631dbdc27d149ed832e32609d8b..973dec2daba95c94164e3bc3ace7bd50f3442d1a 100644 (file)
@@ -1,3 +1,22 @@
+2019-11-14  Thomas Rodgers  <trodgers@redhat.com>
+
+       * 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.
+
 2019-11-14  Edward Smith-Rowland  <3dw4rd@verizon.net>
 
        Implement the <array> part of C++20 p1032 Misc constexpr bits.
index 49fd41360bca942275ff927beba34ccee30107f4..6300de9e96d2f7ce69a974f7b91353a20d182b5c 100644 (file)
@@ -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 \
index acc4fe5f4c969ac2e50d534636ab92e420fc0ed7..ae4a493ea65829277453431cc087bad2a5d4409e 100644 (file)
@@ -418,6 +418,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 \
index cc96661e94cf8d41f850bb5d4e1713c31413a478..8887cee29fa2b10d72795c3a026c9af9c7169aa0 100644 (file)
@@ -36,6 +36,7 @@
 #else
 
 #include <chrono>
+
 #include <bits/std_mutex.h>
 #include <bits/unique_lock.h>
 #include <ext/concurrence.h>
 #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 (file)
index 0000000..af64018
--- /dev/null
@@ -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
index 90b4be6cd168640ccf48988cdbd1067fa03cedf1..93afa766d181f71397a1c4a41aecba23fe6e747c 100644 (file)
 #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
 
index fa6d27467f798a443285f81192984179726cd61d..278074289036ecdf45db8025e0f794bd522407f3 100644 (file)
 #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 (file)
index 0000000..c1f4b58
--- /dev/null
@@ -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 (file)
index 0000000..621965c
--- /dev/null
@@ -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 (file)
index 0000000..212fc94
--- /dev/null
@@ -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 (file)
index 0000000..1fb5650
--- /dev/null
@@ -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 (file)
index 0000000..621965c
--- /dev/null
@@ -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 (file)
index 0000000..c29db21
--- /dev/null
@@ -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 (file)
index 0000000..4c0e7a3
--- /dev/null
@@ -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 (file)
index 0000000..621965c
--- /dev/null
@@ -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 (file)
index 0000000..37d79e8
--- /dev/null
@@ -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
+}