Support for jthread and stop_token

* include/Makefile.am: Add <stop_token> header.
        * include/Makefile.in: Regenerate.
        * include/std/condition_variable: Add overloads for stop_token support
        to condition_variable_any.
        * include/std/stop_token: New file.
        * include/std/thread: Add jthread type.
        * include/std/version (__cpp_lib_jthread): New value.
        * testsuite/30_threads/condition_variable_any/stop_token/1.cc: New test.
        * testsuite/30_threads/condition_variable_any/stop_token/2.cc: New test.
        * testsuite/30_threads/condition_variable_any/stop_token/wait_on.cc: New test.
        * testsuite/30_threads/jthread/1.cc: New test.
        * testsuite/30_threads/jthread/2.cc: New test.
        * testsuite/30_threads/jthread/jthread.cc: New test.
        * testsuite/30_threads/stop_token/1.cc: New test.
        * testsuite/30_threads/stop_token/2.cc: New test.
        * testsuite/30_threads/stop_token/stop_token.cc: New test.

From-SVN: r278274
This commit is contained in:
Thomas Rodgers 2019-11-15 03:09:19 +00:00 committed by Thomas Rodgers
parent f8aea5e37d
commit 942c4b32b0
16 changed files with 1197 additions and 0 deletions

View 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.

View 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 \

View 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 \

View file

@ -36,6 +36,7 @@
#else
#include <chrono>
#include <bits/std_mutex.h>
#include <bits/unique_lock.h>
#include <ext/concurrence.h>
@ -45,6 +46,11 @@
#include <bits/shared_ptr.h>
#include <bits/cxxabi_forced.h>
#if __cplusplus > 201703L
#define __cpp_lib_jthread 201907L
#include <stop_token>
#endif
#if defined(_GLIBCXX_HAS_GTHREADS)
namespace std _GLIBCXX_VISIBILITY(default)
@ -360,6 +366,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
wait_for(_Lock& __lock,
const chrono::duration<_Rep, _Period>& __rtime, _Predicate __p)
{ return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); }
#ifdef __cpp_lib_jthread
template <class _Lock, class _Predicate>
bool wait_on(_Lock& __lock,
stop_token __stoken,
_Predicate __p)
{
if (__stoken.stop_requested())
{
return __p();
}
std::stop_callback __cb(__stoken, [this] { notify_all(); });
shared_ptr<mutex> __mutex = _M_mutex;
while (!__p())
{
unique_lock<mutex> __my_lock(*__mutex);
if (__stoken.stop_requested())
{
return false;
}
// *__mutex must be unlocked before re-locking __lock so move
// ownership of *__mutex lock to an object with shorter lifetime.
_Unlock<_Lock> __unlock(__lock);
unique_lock<mutex> __my_lock2(std::move(__my_lock));
_M_cond.wait(__my_lock2);
}
return true;
}
template <class _Lock, class _Clock, class _Duration, class _Predicate>
bool wait_on_until(_Lock& __lock,
stop_token __stoken,
const chrono::time_point<_Clock, _Duration>& __abs_time,
_Predicate __p)
{
if (__stoken.stop_requested())
{
return __p();
}
std::stop_callback __cb(__stoken, [this] { notify_all(); });
shared_ptr<mutex> __mutex = _M_mutex;
while (!__p())
{
bool __stop;
{
unique_lock<mutex> __my_lock(*__mutex);
if (__stoken.stop_requested())
{
return false;
}
_Unlock<_Lock> __u(__lock);
unique_lock<mutex> __my_lock2(std::move(__my_lock));
const auto __status = _M_cond.wait_until(__my_lock2, __abs_time);
__stop = (__status == std::cv_status::timeout) || __stoken.stop_requested();
}
if (__stop)
{
return __p();
}
}
return true;
}
template <class _Lock, class _Rep, class _Period, class _Predicate>
bool wait_on_for(_Lock& __lock,
stop_token __stoken,
const chrono::duration<_Rep, _Period>& __rel_time,
_Predicate __p)
{
auto __abst = std::chrono::steady_clock::now() + __rel_time;
return wait_on_until(__lock,
std::move(__stoken),
__abst,
std::move(__p));
}
#endif
};
} // end inline namespace

View file

@ -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

View file

@ -39,6 +39,13 @@
#include <memory>
#include <tuple>
#include <cerrno>
#if __cplusplus > 201703L
#define __cpp_lib_jthread 201907L
#include <functional>
#include <stop_token>
#endif
#include <bits/functexcept.h>
#include <bits/functional_hash.h>
#include <bits/invoke.h>
@ -409,6 +416,124 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// @} group threads
#ifdef __cpp_lib_jthread
class jthread
{
public:
using id = std::thread::id;
using native_handle_type = std::thread::native_handle_type;
jthread() noexcept
: _M_stop_source{ nostopstate_t{ } }
{ }
template<typename _Callable, typename... _Args,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<_Callable>, jthread>>>
explicit
jthread(_Callable&& __f, _Args&&... __args)
: _M_thread{[](stop_token __token, auto&& __cb, auto&&... __args)
{
if constexpr(std::is_invocable_v<_Callable, stop_token, _Args...>)
{
std::invoke(std::forward<decltype(__cb)>(__cb),
std::move(__token),
std::forward<decltype(__args)>(__args)...);
}
else
{
std::invoke(std::forward<decltype(__cb)>(__cb),
std::forward<decltype(__args)>(__args)...);
}
},
_M_stop_source.get_token(),
std::forward<_Callable>(__f),
std::forward<_Args>(__args)...}
{ }
jthread(const jthread&) = delete;
jthread(jthread&&) noexcept = default;
~jthread()
{
if (joinable())
{
request_stop();
join();
}
}
jthread&
operator=(const jthread&) = delete;
jthread&
operator=(jthread&&) noexcept = default;
void
swap(jthread& __other) noexcept
{
std::swap(_M_stop_source, __other._M_stop_source);
std::swap(_M_thread, __other._M_thread);
}
bool
joinable() const noexcept
{
return _M_thread.joinable();
}
void
join()
{
_M_thread.join();
}
void
detach()
{
_M_thread.detach();
}
id
get_id() const noexcept
{
_M_thread.get_id();
}
native_handle_type
native_handle()
{
return _M_thread.native_handle();
}
static unsigned
hardware_concurrency() noexcept
{
return std::thread::hardware_concurrency();
}
[[nodiscard]] stop_source
get_stop_source() noexcept
{
return _M_stop_source;
}
[[nodiscard]] stop_token
get_stop_token() const noexcept
{
return _M_stop_source.get_token();
}
bool request_stop() noexcept
{
return get_stop_source().request_stop();
}
private:
stop_source _M_stop_source;
std::thread _M_thread;
};
#endif // __cpp_lib_jthread
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace

View file

@ -187,6 +187,7 @@
#define __cpp_lib_list_remove_return_type 201806L
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_span 201902L
#define __cpp_lib_jthread 201907L
#if __cpp_impl_three_way_comparison >= 201907L
# define __cpp_lib_three_way_comparison 201711L
#endif

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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

View file

@ -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
}