* 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
+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.
${std_srcdir}/sstream \
${std_srcdir}/stack \
${std_srcdir}/stdexcept \
+ ${std_srcdir}/stop_token \
${std_srcdir}/streambuf \
${std_srcdir}/string \
${std_srcdir}/string_view \
${std_srcdir}/sstream \
${std_srcdir}/stack \
${std_srcdir}/stdexcept \
+ ${std_srcdir}/stop_token \
${std_srcdir}/streambuf \
${std_srcdir}/string \
${std_srcdir}/string_view \
#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)
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
--- /dev/null
+// <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
#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>
// @} 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
#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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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();
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
+}