diff --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst --- a/libcxx/docs/Status/Cxx20.rst +++ b/libcxx/docs/Status/Cxx20.rst @@ -50,6 +50,7 @@ .. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0. .. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet. .. [#note-P0408] P0408: Only `view()` members implemented. + .. [#note-P0660] P0660: Section 32.3 Stop Tokens is complete. ``jthread`` hasn't been implemented yet. .. _issues-status-cxx20: diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -210,7 +210,7 @@ "`3250 `__","``std::format``\ : ``#``\ (alternate form) for NaN and inf","Prague","|Complete|","14.0","|format|" "`3251 `__","Are ``std::format``\ alignment specifiers applied to string arguments?","Prague","|Complete|","14.0","|format|" "`3252 `__","Parse locale's aware modifiers for commands are not consistent with POSIX spec","Prague","","","|chrono|" -"`3254 `__","Strike ``stop_token``\ 's ``operator!=``\ ","Prague","","" +"`3254 `__","Strike ``stop_token``\ 's ``operator!=``\ ","Prague","|Complete|","17.0" "`3255 `__","``span``\ 's ``array``\ constructor is too strict","Prague","|Complete|","" "`3260 `__","``year_month*``\ arithmetic rejects durations convertible to years","Prague","","","|chrono|" "`3262 `__","Formatting of negative durations is not specified","Prague","|Complete|","16.0","|chrono| |format|" diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -104,7 +104,7 @@ "`P0553R4 `__","LWG","Bit operations","Cologne","|Complete|","9.0" "`P0631R8 `__","LWG","Math Constants","Cologne","|Complete|","11.0" "`P0645R10 `__","LWG","Text Formatting","Cologne","|Complete| [#note-P0645]_","14.0" -"`P0660R10 `__","LWG","Stop Token and Joining Thread, Rev 10","Cologne","","" +"`P0660R10 `__","LWG","Stop Token and Joining Thread, Rev 10.","Cologne","|In Progress| [#note-P0660]_","" "`P0784R7 `__","CWG","More constexpr containers","Cologne","|Complete|","12.0" "`P0980R1 `__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0" "`P1004R2 `__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -635,6 +635,10 @@ __stop_token/atomic_unique_lock.h __stop_token/intrusive_list_view.h __stop_token/intrusive_shared_ptr.h + __stop_token/stop_callback.h + __stop_token/stop_source.h + __stop_token/stop_state.h + __stop_token/stop_token.h __string/char_traits.h __string/constexpr_c_functions.h __string/extern_template_lists.h @@ -951,6 +955,7 @@ stdint.h stdio.h stdlib.h + stop_token streambuf string string.h diff --git a/libcxx/include/__stop_token/stop_callback.h b/libcxx/include/__stop_token/stop_callback.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stop_token/stop_callback.h @@ -0,0 +1,99 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___STOP_TOKEN_STOP_CALLBACK_H +#define _LIBCPP___STOP_TOKEN_STOP_CALLBACK_H + +#include <__availability> +#include <__concepts/constructible.h> +#include <__concepts/destructible.h> +#include <__concepts/invocable.h> +#include <__config> +#include <__stop_token/intrusive_shared_ptr.h> +#include <__stop_token/stop_state.h> +#include <__stop_token/stop_token.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__utility/forward.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +class _LIBCPP_AVAILABILITY_SYNC stop_callback : private __stop_callback_base { + static_assert(invocable<_Callback>, + "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that " + "satisfies invocable."); + static_assert(destructible<_Callback>, + "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that " + "satisfies destructible."); + +public: + using callback_type = _Callback; + + template + requires constructible_from<_Callback, _Cb> + _LIBCPP_HIDE_FROM_ABI explicit stop_callback(const stop_token& __st, + _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>) + : stop_callback(__private_tag{}, __st.__state_, std::forward<_Cb>(__cb)) {} + + template + requires constructible_from<_Callback, _Cb> + _LIBCPP_HIDE_FROM_ABI explicit stop_callback(stop_token&& __st, + _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>) + : stop_callback(__private_tag{}, std::move(__st.__state_), std::forward<_Cb>(__cb)) {} + + _LIBCPP_HIDE_FROM_ABI ~stop_callback() { + if (__state_) { + __state_->__remove_callback(this); + } + } + + stop_callback(const stop_callback&) = delete; + stop_callback(stop_callback&&) = delete; + stop_callback& operator=(const stop_callback&) = delete; + stop_callback& operator=(stop_callback&&) = delete; + +private: + _LIBCPP_NO_UNIQUE_ADDRESS _Callback __callback_; + __intrusive_shared_ptr<__stop_state> __state_; + + friend __stop_callback_base; + + struct __private_tag {}; + + template + _LIBCPP_HIDE_FROM_ABI explicit stop_callback(__private_tag, _StatePtr&& __state, _Cb&& __cb) noexcept( + is_nothrow_constructible_v<_Callback, _Cb>) + : __stop_callback_base([](__stop_callback_base* __cb_base) noexcept { + // stop callback is supposed to only be called once + std::forward<_Callback>(static_cast(__cb_base)->__callback_)(); + }), + __callback_(std::forward<_Cb>(__cb)), + __state_() { + if (__state && __state->__add_callback(this)) { + // st.stop_requested() was false and this is successfully added to the linked list + __state_ = std::forward<_StatePtr>(__state); + } + } +}; + +template +stop_callback(stop_token, Callback) -> stop_callback; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_STOP_TOKEN_H diff --git a/libcxx/include/__stop_token/stop_source.h b/libcxx/include/__stop_token/stop_source.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stop_token/stop_source.h @@ -0,0 +1,92 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___STOP_TOKEN_STOP_SOURCE_H +#define _LIBCPP___STOP_TOKEN_STOP_SOURCE_H + +#include <__availability> +#include <__config> +#include <__stop_token/intrusive_shared_ptr.h> +#include <__stop_token/stop_state.h> +#include <__stop_token/stop_token.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +struct nostopstate_t { + explicit nostopstate_t() = default; +}; + +inline constexpr nostopstate_t nostopstate{}; + +class _LIBCPP_AVAILABILITY_SYNC stop_source { +public: + _LIBCPP_HIDE_FROM_ABI stop_source() : __state_(new __stop_state()) { __state_->__increment_stop_source_counter(); } + + _LIBCPP_HIDE_FROM_ABI explicit stop_source(nostopstate_t) noexcept : __state_(nullptr) {} + + _LIBCPP_HIDE_FROM_ABI stop_source(const stop_source& __other) noexcept : __state_(__other.__state_) { + if (__state_) { + __state_->__increment_stop_source_counter(); + } + } + + _LIBCPP_HIDE_FROM_ABI stop_source(stop_source&& __other) noexcept = default; + + _LIBCPP_HIDE_FROM_ABI stop_source& operator=(const stop_source& __other) noexcept { + // increment `__other` first so that we don't hit 0 in case of self-assignment + if (__other.__state_) { + __other.__state_->__increment_stop_source_counter(); + } + if (__state_) { + __state_->__decrement_stop_source_counter(); + } + __state_ = __other.__state_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI stop_source& operator=(stop_source&&) noexcept = default; + + _LIBCPP_HIDE_FROM_ABI ~stop_source() { + if (__state_) { + __state_->__decrement_stop_source_counter(); + } + } + + _LIBCPP_HIDE_FROM_ABI void swap(stop_source& __other) noexcept { __state_.swap(__other.__state_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI stop_token get_token() const noexcept { return stop_token(__state_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_possible() const noexcept { return __state_ != nullptr; } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_requested() const noexcept { + return __state_ != nullptr && __state_->__stop_requested(); + } + + _LIBCPP_HIDE_FROM_ABI bool request_stop() noexcept { return __state_ && __state_->__request_stop(); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const stop_source&, const stop_source&) noexcept = default; + + _LIBCPP_HIDE_FROM_ABI friend void swap(stop_source& __lhs, stop_source& __rhs) noexcept { __lhs.swap(__rhs); } + +private: + __intrusive_shared_ptr<__stop_state> __state_; +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_STOP_SOURCE_H diff --git a/libcxx/include/__stop_token/stop_state.h b/libcxx/include/__stop_token/stop_state.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stop_token/stop_state.h @@ -0,0 +1,234 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___STOP_TOKEN_STOP_STATE_H +#define _LIBCPP___STOP_TOKEN_STOP_STATE_H + +#include <__availability> +#include <__config> +#include <__stop_token/atomic_unique_lock.h> +#include <__stop_token/intrusive_list_view.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +struct __stop_callback_base : __intrusive_node_base<__stop_callback_base> { + using __callback_fn_t = void(__stop_callback_base*) noexcept; + _LIBCPP_HIDE_FROM_ABI explicit __stop_callback_base(__callback_fn_t* __callback_fn) : __callback_fn_(__callback_fn) {} + + _LIBCPP_HIDE_FROM_ABI void __invoke() noexcept { __callback_fn_(this); } + + __callback_fn_t* __callback_fn_; + atomic __completed_ = false; + bool* __destroyed_ = nullptr; +}; + +class _LIBCPP_AVAILABILITY_SYNC __stop_state { + static constexpr uint32_t __stop_requested_bit = 1; + static constexpr uint32_t __callback_list_locked_bit = 1 << 1; + static constexpr uint32_t __stop_source_counter_shift = 2; + + // The "stop_source counter" is not used for lifetime reference counting. + // When the number of stop_source reaches 0, the remaining stop_tokens's + // stop_possible will return false. We need this counter to track this. + // + // The "callback list locked" bit implements the atomic_unique_lock to + // guard the operations on the callback list + // + // 31 - 2 | 1 | 0 | + // stop_source counter | callback list locked | stop_requested | + atomic __state_ = 0; + + // Reference count for stop_token + stop_callback + stop_source + // When the counter reaches zero, the state is destroyed + // It is used by __intrusive_shared_ptr, but it is stored here for better layout + atomic __ref_count_ = 0; + + using __state_t = uint32_t; + using __callback_list_lock = __atomic_unique_lock<__state_t, __callback_list_locked_bit>; + using __callback_list = __intrusive_list_view<__stop_callback_base>; + + __callback_list __callback_list_; + thread::id __requesting_thread_; + +public: + _LIBCPP_HIDE_FROM_ABI __stop_state() noexcept = default; + + _LIBCPP_HIDE_FROM_ABI void __increment_stop_source_counter() noexcept { + _LIBCPP_ASSERT( + __state_.load(std::memory_order_relaxed) <= static_cast<__state_t>(~(1 << __stop_source_counter_shift)), + "stop_source's counter reaches the maximum. Incrementing the counter will overflow"); + __state_.fetch_add(1 << __stop_source_counter_shift, std::memory_order_relaxed); + } + + // We are not destroying the object after counter decrements to zero, nor do we have + // operations depend on the ordering of decrementing the counter. relaxed is enough. + _LIBCPP_HIDE_FROM_ABI void __decrement_stop_source_counter() noexcept { + _LIBCPP_ASSERT(__state_.load(std::memory_order_relaxed) >= static_cast<__state_t>(1 << __stop_source_counter_shift), + "stop_source's counter is 0. Decrementing the counter will underflow"); + __state_.fetch_sub(1 << __stop_source_counter_shift, std::memory_order_relaxed); + } + + _LIBCPP_HIDE_FROM_ABI bool __stop_requested() const noexcept { + // acquire because [thread.stoptoken.intro] A call to request_stop that returns true + // synchronizes with a call to stop_requested on an associated stop_token or stop_source + // object that returns true. + // request_stop's compare_exchange_weak has release which syncs with this acquire + return (__state_.load(std::memory_order_acquire) & __stop_requested_bit) != 0; + } + + _LIBCPP_HIDE_FROM_ABI bool __stop_possible_for_stop_token() const noexcept { + // [stoptoken.mem] false if "a stop request was not made and there are no associated stop_source objects" + // Todo: Can this be std::memory_order_relaxed as the standard does not say anything except not to introduce data + // race? + __state_t __curent_state = __state_.load(std::memory_order_acquire); + return ((__curent_state & __stop_requested_bit) != 0) || ((__curent_state >> __stop_source_counter_shift) != 0); + } + + _LIBCPP_HIDE_FROM_ABI bool __request_stop() noexcept { + auto __cb_list_lock = __try_lock_for_request_stop(); + if (!__cb_list_lock.__owns_lock()) { + return false; + } + __requesting_thread_ = this_thread::get_id(); + + while (!__callback_list_.__empty()) { + auto __cb = __callback_list_.__pop_front(); + + // allow other callbacks to be removed while invoking the current callback + __cb_list_lock.__unlock(); + + bool __destroyed = false; + __cb->__destroyed_ = &__destroyed; + + __cb->__invoke(); + + // __cb's invoke function could potentially delete itself. We need to check before accessing __cb's member + if (!__destroyed) { + // needs to set __destroyed_ pointer to nullptr, otherwise it points to a local variable + // which is to be destroyed at the end of the loop + __cb->__destroyed_ = nullptr; + + // [stopcallback.cons] If callback is concurrently executing on another thread, then the return + // from the invocation of callback strongly happens before ([intro.races]) callback is destroyed. + // this release syncs with the acquire in the remove_callback + __cb->__completed_.store(true, std::memory_order_release); + __cb->__completed_.notify_all(); + } + + __cb_list_lock.__lock(); + } + + return true; + } + + _LIBCPP_HIDE_FROM_ABI bool __add_callback(__stop_callback_base* __cb) noexcept { + // If it is already stop_requested. Do not try to request it again. + const auto __give_up_trying_to_lock_condition = [__cb](__state_t __state) { + if ((__state & __stop_requested_bit) != 0) { + // already stop requested, synchronously run the callback and no need to lock the list again + __cb->__invoke(); + return true; + } + // no stop source. no need to lock the list to add the callback as it can never be invoked + return (__state >> __stop_source_counter_shift) == 0; + }; + + __callback_list_lock __cb_list_lock(__state_, __give_up_trying_to_lock_condition); + + if (!__cb_list_lock.__owns_lock()) { + return false; + } + + __callback_list_.__push_front(__cb); + + return true; + // unlock here: [thread.stoptoken.intro] Registration of a callback synchronizes with the invocation of + // that callback. + // Note: this release sync with the acquire in the request_stop' __try_lock_for_request_stop + } + + // called by the destructor of stop_callback + _LIBCPP_HIDE_FROM_ABI void __remove_callback(__stop_callback_base* __cb) noexcept { + __callback_list_lock __cb_list_lock(__state_); + + // under below condition, the request_stop call just popped __cb from the list and could execute it now + bool __potentially_executing_now = __cb->__prev_ == nullptr && !__callback_list_.__is_head(__cb); + + if (__potentially_executing_now) { + auto __requested_thread = __requesting_thread_; + __cb_list_lock.__unlock(); + + if (std::this_thread::get_id() != __requested_thread) { + // [stopcallback.cons] If callback is concurrently executing on another thread, then the return + // from the invocation of callback strongly happens before ([intro.races]) callback is destroyed. + __cb->__completed_.wait(false, std::memory_order_acquire); + } else { + // The destructor of stop_callback runs on the same thread of the thread that invokes the callback. + // The callback is potentially invoking its own destuctor. Set the flag to avoid accessing destroyed + // members on the invoking side + if (__cb->__destroyed_) { + *__cb->__destroyed_ = true; + } + } + } else { + __callback_list_.__remove(__cb); + } + } + +private: + _LIBCPP_HIDE_FROM_ABI __callback_list_lock __try_lock_for_request_stop() noexcept { + // If it is already stop_requested, do not try to request stop or lock the list again. + const auto __lock_fail_condition = [](__state_t __state) { return (__state & __stop_requested_bit) != 0; }; + + // set locked and requested bit at the same time + const auto __after_lock_state = [](__state_t __state) { + return __state | __callback_list_locked_bit | __stop_requested_bit; + }; + + // acq because [thread.stoptoken.intro] Registration of a callback synchronizes with the invocation of that + // callback. We are going to invoke the callback after getting the lock, acquire so that we can see the + // registration of a callback (and other writes that happens-before the add_callback) + // Note: the rel (unlock) in the add_callback syncs with this acq + // rel because [thread.stoptoken.intro] A call to request_stop that returns true synchronizes with a call + // to stop_requested on an associated stop_token or stop_source object that returns true. + // We need to make sure that all writes (including user code) before request_stop will be made visible + // to the threads that waiting for `stop_requested == true` + // Note: this rel syncs with the acq in `stop_requested` + const auto __locked_ordering = std::memory_order_acq_rel; + + return __callback_list_lock(__state_, __lock_fail_condition, __after_lock_state, __locked_ordering); + } + + template + friend struct __intrusive_shared_ptr_traits; +}; + +template +struct __intrusive_shared_ptr_traits; + +template <> +struct __intrusive_shared_ptr_traits<__stop_state> { + _LIBCPP_HIDE_FROM_ABI static atomic& __get_atomic_ref_count(__stop_state& __state) { + return __state.__ref_count_; + } +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_STOP_STATE_H diff --git a/libcxx/include/__stop_token/stop_token.h b/libcxx/include/__stop_token/stop_token.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stop_token/stop_token.h @@ -0,0 +1,64 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___STOP_TOKEN_STOP_TOKEN_H +#define _LIBCPP___STOP_TOKEN_STOP_TOKEN_H + +#include <__availability> +#include <__config> +#include <__stop_token/intrusive_shared_ptr.h> +#include <__stop_token/stop_state.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +class _LIBCPP_AVAILABILITY_SYNC stop_token { +public: + _LIBCPP_HIDE_FROM_ABI stop_token() noexcept = default; + + _LIBCPP_HIDE_FROM_ABI stop_token(const stop_token&) noexcept = default; + _LIBCPP_HIDE_FROM_ABI stop_token(stop_token&&) noexcept = default; + _LIBCPP_HIDE_FROM_ABI stop_token& operator=(const stop_token&) noexcept = default; + _LIBCPP_HIDE_FROM_ABI stop_token& operator=(stop_token&&) noexcept = default; + _LIBCPP_HIDE_FROM_ABI ~stop_token() = default; + + _LIBCPP_HIDE_FROM_ABI void swap(stop_token& __other) noexcept { __state_.swap(__other.__state_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_requested() const noexcept { + return __state_ != nullptr && __state_->__stop_requested(); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_possible() const noexcept { + return __state_ != nullptr && __state_->__stop_possible_for_stop_token(); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const stop_token&, const stop_token&) noexcept = default; + + _LIBCPP_HIDE_FROM_ABI friend void swap(stop_token& __lhs, stop_token& __rhs) noexcept { __lhs.swap(__rhs); } + +private: + __intrusive_shared_ptr<__stop_state> __state_; + + friend class stop_source; + template + friend class stop_callback; + + _LIBCPP_HIDE_FROM_ABI explicit stop_token(const __intrusive_shared_ptr<__stop_state>& __state) : __state_(__state) {} +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_STOP_TOKEN_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1454,11 +1454,17 @@ export * } module stop_token { + @requires_LIBCXX_ENABLE_THREADS@ + header "stop_token" export * module __stop_token { module atomic_unique_lock { private header "__stop_token/atomic_unique_lock.h" } module intrusive_list_view { private header "__stop_token/intrusive_list_view.h" } module intrusive_shared_ptr { private header "__stop_token/intrusive_shared_ptr.h" } + module stop_callback { private header "__stop_token/stop_callback.h" } + module stop_source { private header "__stop_token/stop_source.h" } + module stop_state { private header "__stop_token/stop_state.h" } + module stop_token { private header "__stop_token/stop_token.h" } } } module streambuf { diff --git a/libcxx/include/stop_token b/libcxx/include/stop_token new file mode 100644 --- /dev/null +++ b/libcxx/include/stop_token @@ -0,0 +1,49 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_STOP_TOKEN +#define _LIBCPP_STOP_TOKEN + +/* + +namespace std { + // [stoptoken], class stop_token + class stop_token; + + // [stopsource], class stop_source + class stop_source; + + // no-shared-stop-state indicator + struct nostopstate_t { + explicit nostopstate_t() = default; + }; + inline constexpr nostopstate_t nostopstate{}; + + // [stopcallback], class template stop_callback + template + class stop_callback; + +*/ + +#include <__assert> // all public C++ headers provide the assertion handler +#include <__config> +#include <__stop_token/stop_callback.h> +#include <__stop_token/stop_source.h> +#include <__stop_token/stop_token.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_THREADS +# error " is not supported since libc++ has been configured without support for threads." +#endif + +#endif // _LIBCPP_STOP_TOKEN diff --git a/libcxx/test/libcxx/clang_tidy.sh.cpp b/libcxx/test/libcxx/clang_tidy.sh.cpp --- a/libcxx/test/libcxx/clang_tidy.sh.cpp +++ b/libcxx/test/libcxx/clang_tidy.sh.cpp @@ -178,6 +178,9 @@ #include #include #include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif diff --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp --- a/libcxx/test/libcxx/double_include.sh.cpp +++ b/libcxx/test/libcxx/double_include.sh.cpp @@ -176,6 +176,9 @@ #include #include #include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif diff --git a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp --- a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp +++ b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp @@ -277,6 +277,10 @@ TEST_MACROS(); #include TEST_MACROS(); +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +TEST_MACROS(); +#endif #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include TEST_MACROS(); diff --git a/libcxx/test/libcxx/modules_include.sh.cpp b/libcxx/test/libcxx/modules_include.sh.cpp --- a/libcxx/test/libcxx/modules_include.sh.cpp +++ b/libcxx/test/libcxx/modules_include.sh.cpp @@ -644,232 +644,237 @@ // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_101 &' >> %t.sh // RUN: echo 'TEST_101=$!' >> %t.sh // RUN: echo "wait $TEST_85" >> %t.sh -#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -#include +#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_THREADS) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_102 &' >> %t.sh // RUN: echo 'TEST_102=$!' >> %t.sh // RUN: echo "wait $TEST_86" >> %t.sh -#if defined(TEST_102) -#include +#if defined(TEST_102) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_103 &' >> %t.sh // RUN: echo 'TEST_103=$!' >> %t.sh // RUN: echo "wait $TEST_87" >> %t.sh #if defined(TEST_103) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_104 &' >> %t.sh // RUN: echo 'TEST_104=$!' >> %t.sh // RUN: echo "wait $TEST_88" >> %t.sh #if defined(TEST_104) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_105 &' >> %t.sh // RUN: echo 'TEST_105=$!' >> %t.sh // RUN: echo "wait $TEST_89" >> %t.sh -#if defined(TEST_105) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -#include +#if defined(TEST_105) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_106 &' >> %t.sh // RUN: echo 'TEST_106=$!' >> %t.sh // RUN: echo "wait $TEST_90" >> %t.sh -#if defined(TEST_106) -#include +#if defined(TEST_106) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_107 &' >> %t.sh // RUN: echo 'TEST_107=$!' >> %t.sh // RUN: echo "wait $TEST_91" >> %t.sh #if defined(TEST_107) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_108 &' >> %t.sh // RUN: echo 'TEST_108=$!' >> %t.sh // RUN: echo "wait $TEST_92" >> %t.sh -#if defined(TEST_108) && !defined(_LIBCPP_HAS_NO_THREADS) -#include +#if defined(TEST_108) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_109 &' >> %t.sh // RUN: echo 'TEST_109=$!' >> %t.sh // RUN: echo "wait $TEST_93" >> %t.sh -#if defined(TEST_109) -#include +#if defined(TEST_109) && !defined(_LIBCPP_HAS_NO_THREADS) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_110 &' >> %t.sh // RUN: echo 'TEST_110=$!' >> %t.sh // RUN: echo "wait $TEST_94" >> %t.sh #if defined(TEST_110) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_111 &' >> %t.sh // RUN: echo 'TEST_111=$!' >> %t.sh // RUN: echo "wait $TEST_95" >> %t.sh #if defined(TEST_111) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_112 &' >> %t.sh // RUN: echo 'TEST_112=$!' >> %t.sh // RUN: echo "wait $TEST_96" >> %t.sh #if defined(TEST_112) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_113 &' >> %t.sh // RUN: echo 'TEST_113=$!' >> %t.sh // RUN: echo "wait $TEST_97" >> %t.sh #if defined(TEST_113) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_114 &' >> %t.sh // RUN: echo 'TEST_114=$!' >> %t.sh // RUN: echo "wait $TEST_98" >> %t.sh #if defined(TEST_114) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_115 &' >> %t.sh // RUN: echo 'TEST_115=$!' >> %t.sh // RUN: echo "wait $TEST_99" >> %t.sh #if defined(TEST_115) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_116 &' >> %t.sh // RUN: echo 'TEST_116=$!' >> %t.sh // RUN: echo "wait $TEST_100" >> %t.sh #if defined(TEST_116) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_117 &' >> %t.sh // RUN: echo 'TEST_117=$!' >> %t.sh // RUN: echo "wait $TEST_101" >> %t.sh #if defined(TEST_117) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_118 &' >> %t.sh // RUN: echo 'TEST_118=$!' >> %t.sh // RUN: echo "wait $TEST_102" >> %t.sh #if defined(TEST_118) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_119 &' >> %t.sh // RUN: echo 'TEST_119=$!' >> %t.sh // RUN: echo "wait $TEST_103" >> %t.sh #if defined(TEST_119) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_120 &' >> %t.sh // RUN: echo 'TEST_120=$!' >> %t.sh // RUN: echo "wait $TEST_104" >> %t.sh #if defined(TEST_120) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_121 &' >> %t.sh // RUN: echo 'TEST_121=$!' >> %t.sh // RUN: echo "wait $TEST_105" >> %t.sh -#if defined(TEST_121) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) -#include +#if defined(TEST_121) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_122 &' >> %t.sh // RUN: echo 'TEST_122=$!' >> %t.sh // RUN: echo "wait $TEST_106" >> %t.sh #if defined(TEST_122) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_123 &' >> %t.sh // RUN: echo 'TEST_123=$!' >> %t.sh // RUN: echo "wait $TEST_107" >> %t.sh -#if defined(TEST_123) && __cplusplus >= 201103L -#include +#if defined(TEST_123) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_124 &' >> %t.sh // RUN: echo 'TEST_124=$!' >> %t.sh // RUN: echo "wait $TEST_108" >> %t.sh #if defined(TEST_124) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_125 &' >> %t.sh // RUN: echo 'TEST_125=$!' >> %t.sh // RUN: echo "wait $TEST_109" >> %t.sh #if defined(TEST_125) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_126 &' >> %t.sh // RUN: echo 'TEST_126=$!' >> %t.sh // RUN: echo "wait $TEST_110" >> %t.sh #if defined(TEST_126) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_127 &' >> %t.sh // RUN: echo 'TEST_127=$!' >> %t.sh // RUN: echo "wait $TEST_111" >> %t.sh #if defined(TEST_127) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_128 &' >> %t.sh // RUN: echo 'TEST_128=$!' >> %t.sh // RUN: echo "wait $TEST_112" >> %t.sh #if defined(TEST_128) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_129 &' >> %t.sh // RUN: echo 'TEST_129=$!' >> %t.sh // RUN: echo "wait $TEST_113" >> %t.sh #if defined(TEST_129) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_130 &' >> %t.sh // RUN: echo 'TEST_130=$!' >> %t.sh // RUN: echo "wait $TEST_114" >> %t.sh -#if defined(TEST_130) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L -#include +#if defined(TEST_130) && __cplusplus >= 201103L +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_131 &' >> %t.sh // RUN: echo 'TEST_131=$!' >> %t.sh // RUN: echo "wait $TEST_115" >> %t.sh -#if defined(TEST_131) && __cplusplus >= 201103L -#include +#if defined(TEST_131) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_132 &' >> %t.sh // RUN: echo 'TEST_132=$!' >> %t.sh // RUN: echo "wait $TEST_116" >> %t.sh #if defined(TEST_132) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_133 &' >> %t.sh // RUN: echo 'TEST_133=$!' >> %t.sh // RUN: echo "wait $TEST_117" >> %t.sh #if defined(TEST_133) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_134 &' >> %t.sh // RUN: echo 'TEST_134=$!' >> %t.sh // RUN: echo "wait $TEST_118" >> %t.sh #if defined(TEST_134) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_135 &' >> %t.sh // RUN: echo 'TEST_135=$!' >> %t.sh // RUN: echo "wait $TEST_119" >> %t.sh #if defined(TEST_135) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_136 &' >> %t.sh // RUN: echo 'TEST_136=$!' >> %t.sh // RUN: echo "wait $TEST_120" >> %t.sh #if defined(TEST_136) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_137 &' >> %t.sh // RUN: echo 'TEST_137=$!' >> %t.sh // RUN: echo "wait $TEST_121" >> %t.sh #if defined(TEST_137) && __cplusplus >= 201103L -#include +#include #endif // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_138 &' >> %t.sh // RUN: echo 'TEST_138=$!' >> %t.sh // RUN: echo "wait $TEST_122" >> %t.sh #if defined(TEST_138) && __cplusplus >= 201103L -#include +#include #endif +// RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_139 &' >> %t.sh +// RUN: echo 'TEST_139=$!' >> %t.sh // RUN: echo "wait $TEST_123" >> %t.sh +#if defined(TEST_139) && __cplusplus >= 201103L +#include +#endif // RUN: echo "wait $TEST_124" >> %t.sh // RUN: echo "wait $TEST_125" >> %t.sh // RUN: echo "wait $TEST_126" >> %t.sh @@ -885,5 +890,6 @@ // RUN: echo "wait $TEST_136" >> %t.sh // RUN: echo "wait $TEST_137" >> %t.sh // RUN: echo "wait $TEST_138" >> %t.sh +// RUN: echo "wait $TEST_139" >> %t.sh // RUN: bash %t.sh // GENERATED-MARKER diff --git a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp --- a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp +++ b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp @@ -301,6 +301,9 @@ #include #include #include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif diff --git a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp --- a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp +++ b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp @@ -173,6 +173,9 @@ #include #include #include +#if !defined(_LIBCPP_HAS_NO_THREADS) +# include +#endif #if !defined(_LIBCPP_HAS_NO_LOCALIZATION) # include #endif diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -631,6 +631,10 @@ #include <__stop_token/atomic_unique_lock.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/atomic_unique_lock.h'}} #include <__stop_token/intrusive_list_view.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/intrusive_list_view.h'}} #include <__stop_token/intrusive_shared_ptr.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/intrusive_shared_ptr.h'}} +#include <__stop_token/stop_callback.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/stop_callback.h'}} +#include <__stop_token/stop_source.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/stop_source.h'}} +#include <__stop_token/stop_state.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/stop_state.h'}} +#include <__stop_token/stop_token.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/stop_token.h'}} #include <__string/char_traits.h> // expected-error@*:* {{use of private header from outside its module: '__string/char_traits.h'}} #include <__string/constexpr_c_functions.h> // expected-error@*:* {{use of private header from outside its module: '__string/constexpr_c_functions.h'}} #include <__string/extern_template_lists.h> // expected-error@*:* {{use of private header from outside its module: '__string/extern_template_lists.h'}} diff --git a/libcxx/test/libcxx/transitive_includes.sh.cpp b/libcxx/test/libcxx/transitive_includes.sh.cpp --- a/libcxx/test/libcxx/transitive_includes.sh.cpp +++ b/libcxx/test/libcxx/transitive_includes.sh.cpp @@ -409,136 +409,140 @@ #if defined(TEST_97) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_101 > /dev/null 2> %t/header.streambuf +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_101 > /dev/null 2> %t/header.stop_token #if defined(TEST_101) -#include +#include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_102 > /dev/null 2> %t/header.string +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_102 > /dev/null 2> %t/header.streambuf #if defined(TEST_102) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_103 > /dev/null 2> %t/header.string +#if defined(TEST_103) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_104 > /dev/null 2> %t/header.string_view -#if defined(TEST_104) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_105 > /dev/null 2> %t/header.string_view +#if defined(TEST_105) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_105 > /dev/null 2> %t/header.strstream -#if defined(TEST_105) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_106 > /dev/null 2> %t/header.strstream +#if defined(TEST_106) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_106 > /dev/null 2> %t/header.system_error -#if defined(TEST_106) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_107 > /dev/null 2> %t/header.system_error +#if defined(TEST_107) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_108 > /dev/null 2> %t/header.thread -#if defined(TEST_108) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_109 > /dev/null 2> %t/header.thread +#if defined(TEST_109) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_109 > /dev/null 2> %t/header.tuple -#if defined(TEST_109) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_110 > /dev/null 2> %t/header.tuple +#if defined(TEST_110) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_110 > /dev/null 2> %t/header.type_traits -#if defined(TEST_110) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_111 > /dev/null 2> %t/header.type_traits +#if defined(TEST_111) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_111 > /dev/null 2> %t/header.typeindex -#if defined(TEST_111) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_112 > /dev/null 2> %t/header.typeindex +#if defined(TEST_112) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_112 > /dev/null 2> %t/header.typeinfo -#if defined(TEST_112) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_113 > /dev/null 2> %t/header.typeinfo +#if defined(TEST_113) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_114 > /dev/null 2> %t/header.unordered_map -#if defined(TEST_114) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_115 > /dev/null 2> %t/header.unordered_map +#if defined(TEST_115) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_115 > /dev/null 2> %t/header.unordered_set -#if defined(TEST_115) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_116 > /dev/null 2> %t/header.unordered_set +#if defined(TEST_116) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_116 > /dev/null 2> %t/header.utility -#if defined(TEST_116) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_117 > /dev/null 2> %t/header.utility +#if defined(TEST_117) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_117 > /dev/null 2> %t/header.valarray -#if defined(TEST_117) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_118 > /dev/null 2> %t/header.valarray +#if defined(TEST_118) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_118 > /dev/null 2> %t/header.variant -#if defined(TEST_118) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_119 > /dev/null 2> %t/header.variant +#if defined(TEST_119) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_119 > /dev/null 2> %t/header.vector -#if defined(TEST_119) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_120 > /dev/null 2> %t/header.vector +#if defined(TEST_120) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_120 > /dev/null 2> %t/header.version -#if defined(TEST_120) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_121 > /dev/null 2> %t/header.version +#if defined(TEST_121) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_123 > /dev/null 2> %t/header.experimental_deque -#if defined(TEST_123) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_124 > /dev/null 2> %t/header.experimental_deque +#if defined(TEST_124) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_124 > /dev/null 2> %t/header.experimental_forward_list -#if defined(TEST_124) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_125 > /dev/null 2> %t/header.experimental_forward_list +#if defined(TEST_125) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_125 > /dev/null 2> %t/header.experimental_iterator -#if defined(TEST_125) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_126 > /dev/null 2> %t/header.experimental_iterator +#if defined(TEST_126) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_126 > /dev/null 2> %t/header.experimental_list -#if defined(TEST_126) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_127 > /dev/null 2> %t/header.experimental_list +#if defined(TEST_127) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_127 > /dev/null 2> %t/header.experimental_map -#if defined(TEST_127) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_128 > /dev/null 2> %t/header.experimental_map +#if defined(TEST_128) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_128 > /dev/null 2> %t/header.experimental_memory_resource -#if defined(TEST_128) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_129 > /dev/null 2> %t/header.experimental_memory_resource +#if defined(TEST_129) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_129 > /dev/null 2> %t/header.experimental_propagate_const -#if defined(TEST_129) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_130 > /dev/null 2> %t/header.experimental_propagate_const +#if defined(TEST_130) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_130 > /dev/null 2> %t/header.experimental_regex -#if defined(TEST_130) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_131 > /dev/null 2> %t/header.experimental_regex +#if defined(TEST_131) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_131 > /dev/null 2> %t/header.experimental_set -#if defined(TEST_131) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_132 > /dev/null 2> %t/header.experimental_set +#if defined(TEST_132) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_132 > /dev/null 2> %t/header.experimental_simd -#if defined(TEST_132) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_133 > /dev/null 2> %t/header.experimental_simd +#if defined(TEST_133) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_133 > /dev/null 2> %t/header.experimental_string -#if defined(TEST_133) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_134 > /dev/null 2> %t/header.experimental_string +#if defined(TEST_134) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_134 > /dev/null 2> %t/header.experimental_type_traits -#if defined(TEST_134) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_135 > /dev/null 2> %t/header.experimental_type_traits +#if defined(TEST_135) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_135 > /dev/null 2> %t/header.experimental_unordered_map -#if defined(TEST_135) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_136 > /dev/null 2> %t/header.experimental_unordered_map +#if defined(TEST_136) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_136 > /dev/null 2> %t/header.experimental_unordered_set -#if defined(TEST_136) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_137 > /dev/null 2> %t/header.experimental_unordered_set +#if defined(TEST_137) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_137 > /dev/null 2> %t/header.experimental_utility -#if defined(TEST_137) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_138 > /dev/null 2> %t/header.experimental_utility +#if defined(TEST_138) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_138 > /dev/null 2> %t/header.experimental_vector -#if defined(TEST_138) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_139 > /dev/null 2> %t/header.experimental_vector +#if defined(TEST_139) #include #endif // RUN: %{python} %S/transitive_includes_to_csv.py %t > %t/transitive_includes.csv diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -773,6 +773,11 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token limits +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -774,6 +774,11 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token limits +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -776,6 +776,11 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token limits +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -776,6 +776,11 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token limits +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -782,6 +782,11 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token limits +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -524,6 +524,11 @@ stack initializer_list stack version stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token limits +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// WARNING: This test was generated by generate_feature_test_macro_components.py +// and should not be edited manually. +// +// clang-format off + +// UNSUPPORTED: no-threads + +// + +// Test the feature test macros defined by + +/* Constant Value + __cpp_lib_jthread 201911L [C++20] +*/ + +#include +#include "test_macros.h" + +#if TEST_STD_VER < 14 + +# ifdef __cpp_lib_jthread +# error "__cpp_lib_jthread should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 14 + +# ifdef __cpp_lib_jthread +# error "__cpp_lib_jthread should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 17 + +# ifdef __cpp_lib_jthread +# error "__cpp_lib_jthread should not be defined before c++20" +# endif + +#elif TEST_STD_VER == 20 + +# if !defined(_LIBCPP_VERSION) +# ifndef __cpp_lib_jthread +# error "__cpp_lib_jthread should be defined in c++20" +# endif +# if __cpp_lib_jthread != 201911L +# error "__cpp_lib_jthread should have the value 201911L in c++20" +# endif +# else // _LIBCPP_VERSION +# ifdef __cpp_lib_jthread +# error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!" +# endif +# endif + +#elif TEST_STD_VER == 23 + +# if !defined(_LIBCPP_VERSION) +# ifndef __cpp_lib_jthread +# error "__cpp_lib_jthread should be defined in c++23" +# endif +# if __cpp_lib_jthread != 201911L +# error "__cpp_lib_jthread should have the value 201911L in c++23" +# endif +# else // _LIBCPP_VERSION +# ifdef __cpp_lib_jthread +# error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!" +# endif +# endif + +#elif TEST_STD_VER > 23 + +# if !defined(_LIBCPP_VERSION) +# ifndef __cpp_lib_jthread +# error "__cpp_lib_jthread should be defined in c++26" +# endif +# if __cpp_lib_jthread != 201911L +# error "__cpp_lib_jthread should have the value 201911L in c++26" +# endif +# else // _LIBCPP_VERSION +# ifdef __cpp_lib_jthread +# error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!" +# endif +# endif + +#endif // TEST_STD_VER > 23 + diff --git a/libcxx/test/std/thread/thread.stoptoken/nostopstate/cons.default.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/nostopstate/cons.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/nostopstate/cons.default.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// struct nostopstate_t { +// explicit nostopstate_t() = default; +// }; +// +// inline constexpr nostopstate_t nostopstate{}; + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_trivially_default_constructible_v); + +struct Empty {}; +static_assert(sizeof(Empty) == sizeof(std::nostopstate_t)); + +template +void conversionTest(T); + +template +concept ImplicitlyDefaultConstructible = requires { conversionTest({}); }; +static_assert(!ImplicitlyDefaultConstructible); + +int main(int, char**) { + [[maybe_unused]] std::same_as auto x = std::nostopstate; + [[maybe_unused]] auto y = std::nostopstate_t{}; + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.const.token.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.const.token.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.const.token.pass.cpp @@ -0,0 +1,236 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// template +// explicit stop_callback(const stop_token& st, C&& cb) +// noexcept(is_nothrow_constructible_v); + +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +struct Cb { + void operator()() const; +}; + +// Constraints: Callback and C satisfy constructible_from. +static_assert(std::is_constructible_v, const std::stop_token&, void (*)()>); +static_assert(!std::is_constructible_v, const std::stop_token&, void (*)(int)>); +static_assert(std::is_constructible_v, const std::stop_token&, Cb&>); +static_assert(std::is_constructible_v, const std::stop_token&, Cb&>); +static_assert(!std::is_constructible_v, const std::stop_token&, int>); + +// explicit +template +void conversion_test(T); + +template +concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test({std::forward(args)...}); }; +static_assert(ImplicitlyConstructible); +static_assert(!ImplicitlyConstructible, const std::stop_token&, Cb>); + +// noexcept +template +struct CbNoExcept { + CbNoExcept(int) noexcept(NoExceptCtor); + void operator()() const; +}; +static_assert(std::is_nothrow_constructible_v>, const std::stop_token&, int>); +static_assert(!std::is_nothrow_constructible_v>, const std::stop_token&, int>); + +int main(int, char**) { + // was requested + { + std::stop_source ss; + const std::stop_token st = ss.get_token(); + ss.request_stop(); + + bool called = false; + std::stop_callback sc(st, [&] { called = true; }); + assert(called); + } + + // was not requested + { + std::stop_source ss; + const std::stop_token st = ss.get_token(); + + bool called = false; + std::stop_callback sc(st, [&] { called = true; }); + assert(!called); + + ss.request_stop(); + assert(called); + } + + // token has no state + { + std::stop_token st; + bool called = false; + std::stop_callback sc(st, [&] { called = true; }); + assert(!called); + } + + // should not be called multiple times + { + std::stop_source ss; + const std::stop_token st = ss.get_token(); + + int calledTimes = 0; + std::stop_callback sc(st, [&] { ++calledTimes; }); + + std::vector threads; + for (auto i = 0; i < 10; ++i) { + threads.emplace_back(support::make_test_thread([&] { ss.request_stop(); })); + } + + for (auto& thread : threads) { + thread.join(); + } + assert(calledTimes == 1); + } + + // adding more callbacks during invoking other callbacks + { + std::stop_source ss; + const std::stop_token st = ss.get_token(); + + std::atomic startedFlag = false; + std::atomic finishFlag = false; + std::stop_callback sc(st, [&] { + startedFlag = true; + startedFlag.notify_all(); + finishFlag.wait(false); + }); + + auto thread = support::make_test_thread([&] { ss.request_stop(); }); + + startedFlag.wait(false); + + // first callback is still running, adding another one; + bool secondCallbackCalled = false; + std::stop_callback sc2(st, [&] { secondCallbackCalled = true; }); + + finishFlag = true; + finishFlag.notify_all(); + + thread.join(); + assert(secondCallbackCalled); + } + + // adding callbacks on different threads + { + std::stop_source ss; + const std::stop_token st = ss.get_token(); + + std::vector threads; + std::atomic callbackCalledTimes = 0; + std::atomic done = false; + for (auto i = 0; i < 10; ++i) { + threads.emplace_back(support::make_test_thread([&] { + std::stop_callback sc{st, [&] { callbackCalledTimes.fetch_add(1, std::memory_order_relaxed); }}; + done.wait(false); + })); + } + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + ss.request_stop(); + done = true; + done.notify_all(); + for (auto& thread : threads) { + thread.join(); + } + assert(callbackCalledTimes.load(std::memory_order_relaxed) == 10); + } + + // correct overload + { + struct CBWithTracking { + bool& lvalueCalled; + bool& lvalueConstCalled; + bool& rvalueCalled; + bool& rvalueConstCalled; + + void operator()() & { lvalueCalled = true; } + void operator()() const& { lvalueConstCalled = true; } + void operator()() && { rvalueCalled = true; } + void operator()() const&& { rvalueConstCalled = true; } + }; + + // RValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + const std::stop_token st = ss.get_token(); + ss.request_stop(); + + std::stop_callback sc( + st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}); + assert(rvalueCalled); + } + + // RValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + const std::stop_token st = ss.get_token(); + ss.request_stop(); + + std::stop_callback sc( + st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}); + assert(rvalueConstCalled); + } + + // LValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + const std::stop_token st = ss.get_token(); + ss.request_stop(); + CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}; + std::stop_callback sc(st, cb); + assert(lvalueCalled); + } + + // const LValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + const std::stop_token st = ss.get_token(); + ss.request_stop(); + CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}; + std::stop_callback sc(st, cb); + assert(lvalueConstCalled); + } + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp @@ -0,0 +1,227 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// template +// explicit stop_callback(stop_token&& st, C&& cb) +// noexcept(is_nothrow_constructible_v); + +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +struct Cb { + void operator()() const; +}; + +// Constraints: Callback and C satisfy constructible_from. +static_assert(std::is_constructible_v, std::stop_token&&, void (*)()>); +static_assert(!std::is_constructible_v, std::stop_token&&, void (*)(int)>); +static_assert(std::is_constructible_v, std::stop_token&&, Cb&>); +static_assert(std::is_constructible_v, std::stop_token&&, Cb&>); +static_assert(!std::is_constructible_v, std::stop_token&&, int>); + +// explicit +template +void conversion_test(T); + +template +concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test({std::forward(args)...}); }; +static_assert(ImplicitlyConstructible); +static_assert(!ImplicitlyConstructible, std::stop_token&&, Cb>); + +// noexcept +template +struct CbNoExcept { + CbNoExcept(int) noexcept(NoExceptCtor); + void operator()() const; +}; +static_assert(std::is_nothrow_constructible_v>, std::stop_token&&, int>); +static_assert(!std::is_nothrow_constructible_v>, std::stop_token&&, int>); + +int main(int, char**) { + // was requested + { + std::stop_source ss; + ss.request_stop(); + + bool called = false; + std::stop_callback sc(ss.get_token(), [&] { called = true; }); + assert(called); + } + + // was not requested + { + std::stop_source ss; + + bool called = false; + std::stop_callback sc(ss.get_token(), [&] { called = true; }); + assert(!called); + + ss.request_stop(); + assert(called); + } + + // token has no state + { + std::stop_token st; + bool called = false; + std::stop_callback sc(std::move(st), [&] { called = true; }); + assert(!called); + } + + // should not be called multiple times + { + std::stop_source ss; + + int calledTimes = 0; + std::stop_callback sc(ss.get_token(), [&] { ++calledTimes; }); + + std::vector threads; + for (auto i = 0; i < 10; ++i) { + threads.emplace_back(support::make_test_thread([&] { ss.request_stop(); })); + } + + for (auto& thread : threads) { + thread.join(); + } + assert(calledTimes == 1); + } + + // adding more callbacks during invoking other callbacks + { + std::stop_source ss; + + std::atomic startedFlag = false; + std::atomic finishFlag = false; + std::stop_callback sc(ss.get_token(), [&] { + startedFlag = true; + startedFlag.notify_all(); + finishFlag.wait(false); + }); + + auto thread = support::make_test_thread([&] { ss.request_stop(); }); + + startedFlag.wait(false); + + // first callback is still running, adding another one; + bool secondCallbackCalled = false; + std::stop_callback sc2(ss.get_token(), [&] { secondCallbackCalled = true; }); + + finishFlag = true; + finishFlag.notify_all(); + + thread.join(); + assert(secondCallbackCalled); + } + + // adding callbacks on different threads + { + std::stop_source ss; + + std::vector threads; + std::atomic callbackCalledTimes = 0; + std::atomic done = false; + for (auto i = 0; i < 10; ++i) { + threads.emplace_back(support::make_test_thread([&] { + std::stop_callback sc{ss.get_token(), [&] { callbackCalledTimes.fetch_add(1, std::memory_order_relaxed); }}; + done.wait(false); + })); + } + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + ss.request_stop(); + done = true; + done.notify_all(); + for (auto& thread : threads) { + thread.join(); + } + assert(callbackCalledTimes.load(std::memory_order_relaxed) == 10); + } + + // correct overload + { + struct CBWithTracking { + bool& lvalueCalled; + bool& lvalueConstCalled; + bool& rvalueCalled; + bool& rvalueConstCalled; + + void operator()() & { lvalueCalled = true; } + void operator()() const& { lvalueConstCalled = true; } + void operator()() && { rvalueCalled = true; } + void operator()() const&& { rvalueConstCalled = true; } + }; + + // RValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + ss.request_stop(); + + std::stop_callback sc( + ss.get_token(), CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}); + assert(rvalueCalled); + } + + // RValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + ss.request_stop(); + + std::stop_callback sc( + ss.get_token(), CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}); + assert(rvalueConstCalled); + } + + // LValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + ss.request_stop(); + CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}; + std::stop_callback sc(ss.get_token(), cb); + assert(lvalueCalled); + } + + // const LValue + { + bool lvalueCalled = false; + bool lvalueConstCalled = false; + bool rvalueCalled = false; + bool rvalueConstCalled = false; + std::stop_source ss; + ss.request_stop(); + CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}; + std::stop_callback sc(ss.get_token(), cb); + assert(lvalueConstCalled); + } + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopcallback/copy.move.compile.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopcallback/copy.move.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopcallback/copy.move.compile.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_callback(const stop_callback&) = delete; +// stop_callback(stop_callback&&) = delete; +// stop_callback& operator=(const stop_callback&) = delete; +// stop_callback& operator=(stop_callback&&) = delete; + +#include +#include + +struct Callback { + void operator()() const; +}; + +static_assert(!std::is_copy_constructible_v>); +static_assert(!std::is_move_constructible_v>); +static_assert(!std::is_copy_assignable_v>); +static_assert(!std::is_move_assignable_v>); diff --git a/libcxx/test/std/thread/thread.stoptoken/stopcallback/ctad.compile.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopcallback/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopcallback/ctad.compile.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// template +// stop_callback(stop_token, Callback) -> stop_callback; + +#include +#include +#include + +void test() { + std::stop_token st; + auto a = [] {}; + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + static_assert(std::is_same_v>); + static_assert( + std::is_same_v>); +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopcallback/dtor.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopcallback/dtor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopcallback/dtor.pass.cpp @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// ~stop_callback(); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +struct CallbackHolder; + +struct DeleteHolder { + CallbackHolder& holder_; + void operator()() const; +}; + +struct CallbackHolder { + std::unique_ptr> callback_; +}; + +void DeleteHolder::operator()() const { holder_.callback_.reset(); } + +int main(int, char**) { + // Unregisters the callback from the owned stop state, if any + { + std::stop_source ss; + bool called = false; + + { + std::stop_callback sc(ss.get_token(), [&] { called = true; }); + } + ss.request_stop(); + assert(!called); + } + + // The destructor does not block waiting for the execution of another + // callback registered by an associated stop_callback. + { + std::stop_source ss; + + std::atomic startedIndex = 0; + std::atomic callbackFinish = false; + + std::optional>> sc1(std::in_place, ss.get_token(), [&] { + startedIndex = 1; + startedIndex.notify_all(); + callbackFinish.wait(false); + }); + + std::optional>> sc2(std::in_place, ss.get_token(), [&] { + startedIndex = 2; + startedIndex.notify_all(); + callbackFinish.wait(false); + }); + + auto thread = support::make_test_thread([&] { ss.request_stop(); }); + + startedIndex.wait(0); + + // now one of the callback has started but not finished. + if (startedIndex == 1) { + sc2.reset(); // destructor should not block + } else if (startedIndex == 2) { + sc1.reset(); // destructor should not block + } else { + assert(false); // something is wrong + } + + callbackFinish = true; + callbackFinish.notify_all(); + thread.join(); + } + + // If callback is concurrently executing on another thread, then the + // return from the invocation of callback strongly happens before ([intro.races]) + // callback is destroyed. + { + struct Callback { + std::atomic& started_; + std::atomic& waitDone_; + std::atomic& finished_; + bool moved = false; + + Callback(std::atomic& started, std::atomic& waitDone, std::atomic& finished) + : started_(started), waitDone_(waitDone), finished_(finished) {} + Callback(Callback&& other) : started_(other.started_), waitDone_(other.waitDone_), finished_(other.finished_) { + other.moved = true; + } + + void operator()() const { + struct ScopedGuard { + std::atomic& g_finished_; + ~ScopedGuard() { g_finished_.store(true, std::memory_order_relaxed); } + }; + + started_ = true; + started_.notify_all(); + waitDone_.wait(false); + ScopedGuard g{finished_}; + } + + ~Callback() { + if (!moved) { + // destructor has to be called after operator() returns + assert(finished_.load(std::memory_order_relaxed)); + } + } + }; + + std::stop_source ss; + + std::atomic started = false; + std::atomic waitDone = false; + std::atomic finished = false; + + std::optional> sc{ + std::in_place, ss.get_token(), Callback{started, waitDone, finished}}; + + auto thread1 = support::make_test_thread([&] { ss.request_stop(); }); + started.wait(false); + + auto thread2 = support::make_test_thread([&] { + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + waitDone = true; + waitDone.notify_all(); + }); + + sc.reset(); // destructor should block until operator() returns, i.e. waitDone to be true + + thread1.join(); + thread2.join(); + } + + // If callback is executing on the current thread, then the destructor does not block ([defns.block]) + // waiting for the return from the invocation of callback. + { + std::stop_source ss; + + CallbackHolder holder; + holder.callback_ = std::make_unique>(ss.get_token(), DeleteHolder{holder}); + + assert(holder.callback_ != nullptr); + + ss.request_stop(); // the callbacks deletes itself. if the destructor blocks, it would be deadlock + assert(holder.callback_ == nullptr); + } +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopcallback/typedef.compile.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopcallback/typedef.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopcallback/typedef.compile.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// using callback_type = _Callback; + +#include +#include + +struct Callback { + void operator()() const; +}; + +static_assert(std::is_same_v::callback_type, Callback>); +static_assert(std::is_same_v::callback_type, const Callback>); +static_assert(std::is_same_v::callback_type, Callback&>); +static_assert(std::is_same_v::callback_type, const Callback&>); +static_assert(std::is_same_v::callback_type, Callback&&>); +static_assert(std::is_same_v::callback_type, const Callback&&>); diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/assign.copy.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/assign.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/assign.copy.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_source& operator=(const stop_source& rhs) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_copy_assignable_v); + +int main(int, char**) { + // have two different states + { + std::stop_source ss1; + std::stop_source ss2; + + assert(ss1 != ss2); + + ss2.request_stop(); + + assert(!ss1.stop_requested()); + assert(ss2.stop_requested()); + + std::same_as decltype(auto) ref = ss1 = ss2; + assert(&ref == &ss1); + + assert(ss1 == ss2); + assert(ss1.stop_requested()); + assert(ss2.stop_requested()); + } + + // this has no state + { + std::stop_source ss1{std::nostopstate}; + std::stop_source ss2; + + assert(ss1 != ss2); + + ss2.request_stop(); + + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(ss2.stop_requested()); + assert(ss2.stop_possible()); + + std::same_as decltype(auto) ref = ss1 = ss2; + assert(&ref == &ss1); + + assert(ss1 == ss2); + assert(ss1.stop_requested()); + assert(ss1.stop_possible()); + assert(ss2.stop_requested()); + assert(ss2.stop_possible()); + } + + // other has no state + { + std::stop_source ss1; + std::stop_source ss2{std::nostopstate}; + + assert(ss1 != ss2); + + ss1.request_stop(); + + assert(ss1.stop_requested()); + assert(ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + + std::same_as decltype(auto) ref = ss1 = ss2; + assert(&ref == &ss1); + + assert(ss1 == ss2); + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + } + + // both no state + { + std::stop_source ss1{std::nostopstate}; + std::stop_source ss2{std::nostopstate}; + + assert(ss1 == ss2); + + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + + std::same_as decltype(auto) ref = ss1 = ss2; + assert(&ref == &ss1); + + assert(ss1 == ss2); + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + } + + // self assignment + { + std::stop_source ss; + auto& self = ss; + + assert(!ss.stop_requested()); + + std::same_as decltype(auto) ref = ss = self; + assert(&ref == &ss); + + assert(!ss.stop_requested()); + + ss.request_stop(); + assert(ss.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.copy.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.copy.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_source(const stop_source&) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_copy_constructible_v); + +int main(int, char**) { + { + std::stop_source source; + std::stop_source copy{source}; + + assert(source == copy); + + assert(source.stop_possible()); + assert(!source.stop_requested()); + + assert(copy.stop_possible()); + assert(!copy.stop_requested()); + + source.request_stop(); + assert(source.stop_possible()); + assert(source.stop_requested()); + + assert(copy.stop_possible()); + assert(copy.stop_requested()); + } + + // source counter incremented + { + std::optional source(std::in_place); + auto st = source->get_token(); + assert(st.stop_possible()); + + std::optional copy{source}; + source.reset(); + + assert(st.stop_possible()); + + copy.reset(); + assert(!st.stop_possible()); + } + + // copy from empty + { + std::stop_source ss1{std::nostopstate}; + std::stop_source copy{ss1}; + assert(!copy.stop_possible()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.default.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.default.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_source(); + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_default_constructible_v); + +int main(int, char**) { + { + std::stop_source ss = {}; // implicit + assert(ss.stop_possible()); + assert(!ss.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.move.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.move.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_source(stop_source&&) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_move_constructible_v); + +int main(int, char**) { + { + std::stop_source source; + + assert(source.stop_possible()); + assert(!source.stop_requested()); + + std::stop_source source2{std::move(source)}; + + assert(!source.stop_possible()); + assert(!source.stop_requested()); + + assert(source2.stop_possible()); + assert(!source2.stop_requested()); + + source2.request_stop(); + + assert(!source.stop_possible()); + assert(!source.stop_requested()); + + assert(source2.stop_possible()); + assert(source2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.nostopstate.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.nostopstate.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/cons.nostopstate.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// explicit stop_source(nostopstate_t) noexcept; + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_constructible_v); +// explicit +static_assert(!std::is_convertible_v); + +int main(int, char**) { + { + std::stop_source ss(std::nostopstate); + assert(!ss.stop_possible()); + assert(!ss.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/equals.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/equals.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/equals.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool operator==(const stop_source& lhs, const stop_source& rhs) noexcept; +// Returns: true if lhs and rhs have ownership of the same stop state or if both lhs and rhs do not have ownership of a stop state; otherwise false. + +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept IsNoThrowEqualityComparable = requires(const T& t1, const T& t2) { + { t1 == t2 } noexcept; +}; + +static_assert(IsNoThrowEqualityComparable); + +int main(int, char**) { + // both no state + { + const std::stop_source ss1(std::nostopstate); + const std::stop_source ss2(std::nostopstate); + assert(ss1 == ss2); + assert(!(ss1 != ss2)); + } + + // only one has no state + { + const std::stop_source ss1(std::nostopstate); + const std::stop_source ss2; + assert(!(ss1 == ss2)); + assert(ss1 != ss2); + } + + // both has states. same state + { + const std::stop_source ss1; + const std::stop_source ss2(ss1); + assert(ss1 == ss2); + assert(!(ss1 != ss2)); + } + + // both has states. different states + { + const std::stop_source ss1; + const std::stop_source ss2; + assert(!(ss1 == ss2)); + assert(ss1 != ss2); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/get_token.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/get_token.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/get_token.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] stop_token get_token() const noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept IsGetTokenNoexcept = requires(const T& t) { + { t.get_token() } noexcept; +}; + +static_assert(IsGetTokenNoexcept); + +int main(int, char**) { + // no state + { + std::stop_source ss{std::nostopstate}; + std::same_as decltype(auto) st = ss.get_token(); + assert(!st.stop_possible()); + assert(!st.stop_requested()); + } + + // with state + { + std::stop_source ss; + std::same_as decltype(auto) st = ss.get_token(); + assert(st.stop_possible()); + assert(!st.stop_requested()); + + ss.request_stop(); + assert(st.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/move.copy.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/move.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/move.copy.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_source& operator=(stop_source&& rhs) noexcept; + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_move_assignable_v); + +int main(int, char**) { + // have two different states + { + std::stop_source ss1; + std::stop_source ss2; + + assert(ss1 != ss2); + + ss2.request_stop(); + + assert(!ss1.stop_requested()); + assert(ss2.stop_requested()); + + std::same_as decltype(auto) ref = ss1 = std::move(ss2); + assert(&ref == &ss1); + + assert(ss1.stop_requested()); + assert(!ss2.stop_possible()); + assert(!ss2.stop_requested()); + } + + // this has no state + { + std::stop_source ss1{std::nostopstate}; + std::stop_source ss2; + + assert(ss1 != ss2); + + ss2.request_stop(); + + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(ss2.stop_requested()); + assert(ss2.stop_possible()); + + std::same_as decltype(auto) ref = ss1 = std::move(ss2); + assert(&ref == &ss1); + + assert(ss1.stop_requested()); + assert(ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + } + + // other has no state + { + std::stop_source ss1; + std::stop_source ss2{std::nostopstate}; + + assert(ss1 != ss2); + + ss1.request_stop(); + + assert(ss1.stop_requested()); + assert(ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + + std::same_as decltype(auto) ref = ss1 = std::move(ss2); + assert(&ref == &ss1); + + assert(ss1 == ss2); + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + } + + // both no state + { + std::stop_source ss1{std::nostopstate}; + std::stop_source ss2{std::nostopstate}; + + assert(ss1 == ss2); + + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + + std::same_as decltype(auto) ref = ss1 = std::move(ss2); + assert(&ref == &ss1); + + assert(ss1 == ss2); + assert(!ss1.stop_requested()); + assert(!ss1.stop_possible()); + assert(!ss2.stop_requested()); + assert(!ss2.stop_possible()); + } + + // self assignment + { + std::stop_source ss; + auto& self = ss; + + assert(!ss.stop_requested()); + + std::same_as decltype(auto) ref = ss = std::move(self); + assert(&ref == &ss); + + assert(!ss.stop_requested()); + + ss.request_stop(); + assert(ss.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/nodiscard.verify.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/nodiscard.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/nodiscard.verify.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] stop_token get_token() const noexcept; +// [[nodiscard]] bool stop_possible() const noexcept; +// [[nodiscard]] bool stop_requested() const noexcept; +// [[nodiscard]] friend bool operator==(const stop_source& lhs, const stop_source& rhs) noexcept; + +#include + +void test() { + std::stop_source ss; + ss.get_token(); // expected-warning {{ignoring return value of function}} + ss.stop_requested(); // expected-warning {{ignoring return value of function}} + ss.stop_possible(); // expected-warning {{ignoring return value of function}} + operator==(ss, ss); // expected-warning {{ignoring return value of function}} +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/request_stop.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/request_stop.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/request_stop.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// bool request_stop() noexcept; + +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +concept IsRequestStopNoexcept = requires(T& t) { + { t.request_stop() } noexcept; +}; + +static_assert(IsRequestStopNoexcept); + +int main(int, char**) { + // If *this does not have ownership of a stop state, returns false + { + std::stop_source ss{std::nostopstate}; + auto ret = ss.request_stop(); + assert(!ret); + assert(!ss.stop_requested()); + } + + // Otherwise, atomically determines whether the owned stop state has received + // a stop request, and if not, makes a stop request + { + std::stop_source ss; + + auto ret = ss.request_stop(); + assert(ret); + assert(ss.stop_requested()); + } + + // already requested + { + std::stop_source ss; + ss.request_stop(); + assert(ss.stop_requested()); + + auto ret = ss.request_stop(); + assert(!ret); + assert(ss.stop_requested()); + } + + // If the request was made, the callbacks registered by + // associated stop_callback objects are synchronously called. + { + std::stop_source ss; + auto st = ss.get_token(); + + bool cb1Called = false; + bool cb2Called = false; + std::stop_callback sc1(st, [&] { cb1Called = true; }); + std::stop_callback sc2(st, [&] { cb2Called = true; }); + + ss.request_stop(); + assert(cb1Called); + assert(cb2Called); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/stop_possible.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/stop_possible.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/stop_possible.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool stop_possible() const noexcept; +// Returns: true if *this has ownership of a stop state; otherwise, false. + +#include +#include +#include + +#include "test_macros.h" + +template +concept IsStopPossibleNoexcept = requires(const T& t) { + { t.stop_possible() } noexcept; +}; + +static_assert(IsStopPossibleNoexcept); + +int main(int, char**) { + // no state + { + const std::stop_source st{std::nostopstate}; + assert(!st.stop_possible()); + } + + // with state + { + const std::stop_source st; + assert(st.stop_possible()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/stop_requested.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/stop_requested.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/stop_requested.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool stop_requested() const noexcept; +// true if *this has ownership of a stop state that has received a stop request; otherwise, false. + +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +concept IsStopRequestedNoexcept = requires(const T& t) { + { t.stop_requested() } noexcept; +}; + +static_assert(IsStopRequestedNoexcept); + +int main(int, char**) { + // no state + { + const std::stop_source ss{std::nostopstate}; + assert(!ss.stop_requested()); + } + + // has state + { + std::stop_source ss; + assert(!ss.stop_requested()); + + ss.request_stop(); + assert(ss.stop_requested()); + } + + // request from another instance with same state + { + std::stop_source ss1; + auto ss2 = ss1; + ss2.request_stop(); + assert(ss1.stop_requested()); + } + + // request from another instance with different state + { + std::stop_source ss1; + std::stop_source ss2; + + ss2.request_stop(); + assert(!ss1.stop_requested()); + } + + // multiple threads + { + std::stop_source ss; + + std::thread t = support::make_test_thread([&]() { ss.request_stop(); }); + + t.join(); + assert(ss.stop_requested()); + } + + // [thread.stopsource.intro] A call to request_stop that returns true + // synchronizes with a call to stop_requested on an associated stop_source + // or stop_source object that returns true. + { + std::stop_source ss; + + bool flag = false; + + std::thread t = support::make_test_thread([&]() { + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + + // happens-before request_stop + flag = true; + auto b = ss.request_stop(); + assert(b); + }); + + while (!ss.stop_requested()) { + std::this_thread::yield(); + } + + // write should be visible to the current thread + assert(flag == true); + + t.join(); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/swap.free.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/swap.free.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/swap.free.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// void swap(stop_source& rhs) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept IsNoThrowFreeSwappable = requires(T& t) { + { swap(t, t) } noexcept; +}; + +static_assert(IsNoThrowFreeSwappable); + +int main(int, char**) { + { + std::stop_source ss1; + std::stop_source ss2; + + assert(ss1 != ss2); + + ss2.request_stop(); + + assert(!ss1.stop_requested()); + assert(ss2.stop_requested()); + + swap(ss1, ss2); + + assert(ss1 != ss2); + assert(ss1.stop_requested()); + assert(!ss2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stopsource/swap.member.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stopsource/swap.member.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stopsource/swap.member.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// void swap(stop_source& rhs) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept IsNoThrowMemberSwappable = requires(T& t) { + { t.swap(t) } noexcept; +}; + +static_assert(IsNoThrowMemberSwappable); + +int main(int, char**) { + { + std::stop_source ss1; + std::stop_source ss2; + + assert(ss1 != ss2); + + ss2.request_stop(); + + assert(!ss1.stop_requested()); + assert(ss2.stop_requested()); + + ss1.swap(ss2); + + assert(ss1 != ss2); + assert(ss1.stop_requested()); + assert(!ss2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/assign.copy.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/assign.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/assign.copy.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_copy_assignable_v); + +int main(int, char**) { + { + std::stop_token st1; + + std::stop_source source; + auto st2 = source.get_token(); + + assert(st1 != st2); + + source.request_stop(); + + assert(!st1.stop_requested()); + assert(st2.stop_requested()); + + std::same_as decltype(auto) ref = st1 = st2; + assert(&ref == &st1); + + assert(st1 == st2); + assert(st1.stop_requested()); + assert(st2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/assign.move.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/assign.move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/assign.move.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_token& operator=(stop_token&& rhs) noexcept; + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_move_assignable_v); + +int main(int, char**) { + { + std::stop_token st1; + + std::stop_source source; + auto st2 = source.get_token(); + + assert(st1 != st2); + + source.request_stop(); + + assert(!st1.stop_requested()); + assert(st2.stop_requested()); + + std::same_as decltype(auto) ref = st1 = std::move(st2); + assert(&ref == &st1); + + assert(st1 != st2); + assert(st1.stop_requested()); + assert(!st2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.copy.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.copy.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_token(const stop_token&) noexcept; + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_copy_constructible_v); + +int main(int, char**) { + { + std::stop_source source; + auto st = source.get_token(); + std::stop_token copy{st}; + + assert(st == copy); + + assert(st.stop_possible()); + assert(!st.stop_requested()); + + assert(copy.stop_possible()); + assert(!copy.stop_requested()); + + source.request_stop(); + assert(st.stop_possible()); + assert(st.stop_requested()); + + assert(copy.stop_possible()); + assert(copy.stop_requested()); + + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.default.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.default.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_token() noexcept; + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_default_constructible_v); + +int main(int, char**) { + { + std::stop_token st = {}; // implicit + assert(!st.stop_possible()); + assert(!st.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.move.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/cons.move.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// stop_token(stop_token&&) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_nothrow_move_constructible_v); + +int main(int, char**) { + { + std::stop_source source; + auto st = source.get_token(); + + assert(st.stop_possible()); + assert(!st.stop_requested()); + + std::stop_token st2{std::move(st)}; + + assert(!st.stop_possible()); + assert(!st.stop_requested()); + + assert(st2.stop_possible()); + assert(!st2.stop_requested()); + + source.request_stop(); + + assert(!st.stop_possible()); + assert(!st.stop_requested()); + + assert(st2.stop_possible()); + assert(st2.stop_requested()); + + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/equals.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/equals.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/equals.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool operator==(const stop_token& lhs, const stop_token& rhs) noexcept; +// Returns: true if lhs and rhs have ownership of the same stop state or if both lhs and rhs do not have ownership of a stop state; otherwise false. + +// synthesized operator != also tested. + +#include +#include +#include +#include + +#include "test_macros.h" + +// LWG 3254 is related. +template +concept IsNoThrowEqualityComparable = requires(const T& t1, const T& t2) { + { t1 == t2 } noexcept; +}; + +template +concept IsNoThrowInequalityComparable = requires(const T& t1, const T& t2) { + { t1 != t2 } noexcept; +}; + +static_assert(IsNoThrowEqualityComparable); +static_assert(IsNoThrowInequalityComparable); + +int main(int, char**) { + // both no state + { + const std::stop_token st1; + const std::stop_token st2; + assert(st1 == st2); + assert(!(st1 != st2)); + } + + // only one has no state + { + std::stop_source ss; + const std::stop_token st1; + const auto st2 = ss.get_token(); + assert(!(st1 == st2)); + assert(st1 != st2); + } + + // both has states. same source + { + std::stop_source ss; + const auto st1 = ss.get_token(); + const auto st2 = ss.get_token(); + assert(st1 == st2); + assert(!(st1 != st2)); + } + + // both has states. different sources with same states + { + std::stop_source ss1; + auto ss2 = ss1; + const auto st1 = ss1.get_token(); + const auto st2 = ss2.get_token(); + assert(st1 == st2); + assert(!(st1 != st2)); + } + + // both has states. different sources with different states + { + std::stop_source ss1; + std::stop_source ss2; + const auto st1 = ss1.get_token(); + const auto st2 = ss2.get_token(); + assert(!(st1 == st2)); + assert(st1 != st2); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/nodiscard.verify.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/nodiscard.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/nodiscard.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool stop_requested() const noexcept; +// [[nodiscard]] bool stop_possible() const noexcept; +// [[nodiscard]] friend bool operator==(const stop_token& lhs, const stop_token& rhs) noexcept; + +#include + +void test() { + std::stop_token st; + st.stop_requested(); // expected-warning {{ignoring return value of function}} + st.stop_possible(); // expected-warning {{ignoring return value of function}} + operator==(st, st); // expected-warning {{ignoring return value of function}} +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/stop_possible.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/stop_possible.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/stop_possible.pass.cpp @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool stop_possible() const noexcept; +// Returns: false if: +// - *this does not have ownership of a stop state, or +// - a stop request was not made and there are no associated stop_source objects; +// otherwise, true. + +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +concept IsStopPossibleNoexcept = requires(const T& t) { + { t.stop_possible() } noexcept; +}; + +static_assert(IsStopPossibleNoexcept); + +int main(int, char**) { + // no state + { + const std::stop_token st; + assert(!st.stop_possible()); + } + + // a stop request was not made and there are no associated stop_source objects + { + std::optional ss{std::in_place}; + const auto st = ss->get_token(); + ss.reset(); + + assert(!st.stop_possible()); + } + + // a stop request was not made, but there is an associated stop_source objects + { + std::stop_source ss; + const auto st = ss.get_token(); + assert(st.stop_possible()); + } + + // a stop request was made and there are no associated stop_source objects + { + std::optional ss{std::in_place}; + const auto st = ss->get_token(); + ss->request_stop(); + ss.reset(); + + assert(st.stop_possible()); + } + + // a stop request was made and there is an associated stop_source objects + { + std::stop_source ss; + const auto st = ss.get_token(); + ss.request_stop(); + assert(st.stop_possible()); + } + + // a stop request was made on a different thread and + // there are no associated stop_source objects + { + std::optional ss{std::in_place}; + const auto st = ss->get_token(); + + std::thread t = support::make_test_thread([&]() { + ss->request_stop(); + ss.reset(); + }); + + assert(st.stop_possible()); + t.join(); + assert(st.stop_possible()); + + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/stop_requested.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/stop_requested.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/stop_requested.pass.cpp @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// [[nodiscard]] bool stop_requested() const noexcept; +// Returns: true if *this has ownership of a stop state that has received a stop request; otherwise, false. + +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +concept IsStopRequestedNoexcept = requires(const T& t) { + { t.stop_requested() } noexcept; +}; + +static_assert(IsStopRequestedNoexcept); + +int main(int, char**) { + // no state + { + const std::stop_token st; + assert(!st.stop_requested()); + } + + // has state + { + std::stop_source ss; + const auto st = ss.get_token(); + assert(!st.stop_requested()); + + ss.request_stop(); + assert(st.stop_requested()); + } + + // already requested before constructor + { + std::stop_source ss; + ss.request_stop(); + const auto st = ss.get_token(); + assert(st.stop_requested()); + } + + // stop_token should share the state + { + std::optional ss{std::in_place}; + ss->request_stop(); + const auto st = ss->get_token(); + + ss.reset(); + assert(st.stop_requested()); + } + + // single stop_source, multiple stop_token + { + std::stop_source ss; + const auto st1 = ss.get_token(); + const auto st2 = ss.get_token(); + assert(!st1.stop_requested()); + assert(!st2.stop_requested()); + + ss.request_stop(); + assert(st1.stop_requested()); + assert(st2.stop_requested()); + } + + // multiple stop_source, multiple stop_token + { + std::stop_source ss1; + std::stop_source ss2; + + const auto st1 = ss1.get_token(); + const auto st2 = ss2.get_token(); + assert(!st1.stop_requested()); + assert(!st2.stop_requested()); + + ss1.request_stop(); + assert(st1.stop_requested()); + assert(!st2.stop_requested()); + } + + // multiple threads + { + std::stop_source ss; + const auto st = ss.get_token(); + assert(!st.stop_requested()); + + std::thread t = support::make_test_thread([&]() { ss.request_stop(); }); + + t.join(); + assert(st.stop_requested()); + } + + // maybe concurrent calls + { + std::stop_source ss; + const auto st = ss.get_token(); + assert(!st.stop_requested()); + + std::thread t = support::make_test_thread([&]() { ss.request_stop(); }); + + while (!st.stop_requested()) { + // should eventually exit the loop + std::this_thread::yield(); + } + + t.join(); + } + + // [thread.stoptoken.intro] A call to request_stop that returns true + // synchronizes with a call to stop_requested on an associated stop_token + // or stop_source object that returns true. + { + std::stop_source ss; + const auto st = ss.get_token(); + assert(!st.stop_requested()); + + bool flag = false; + + std::thread t = support::make_test_thread([&]() { + using namespace std::chrono_literals; + std::this_thread::sleep_for(1ms); + + // happens-before request_stop + flag = true; + auto b = ss.request_stop(); + assert(b); + }); + + while (!st.stop_requested()) { + std::this_thread::yield(); + } + + // write should be visible to the current thread + assert(flag == true); + + t.join(); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/swap.free.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/swap.free.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/swap.free.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// friend void swap(stop_token& x, stop_token& y) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept IsNoThrowFreeSwappable = requires(T& t) { + { swap(t, t) } noexcept; +}; + +static_assert(IsNoThrowFreeSwappable); + +int main(int, char**) { + { + std::stop_token st1; + + std::stop_source source; + auto st2 = source.get_token(); + + assert(st1 != st2); + + source.request_stop(); + + assert(!st1.stop_requested()); + assert(st2.stop_requested()); + + swap(st1, st2); + + assert(st1 != st2); + assert(st1.stop_requested()); + assert(!st2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/test/std/thread/thread.stoptoken/stoptoken/swap.member.pass.cpp b/libcxx/test/std/thread/thread.stoptoken/stoptoken/swap.member.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/thread/thread.stoptoken/stoptoken/swap.member.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// UNSUPPORTED: no-threads +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// XFAIL: availability-synchronization_library-missing + +// void swap(stop_token& rhs) noexcept; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept IsNoThrowMemberSwappable = requires(T& t) { + { t.swap(t) } noexcept; +}; + +static_assert(IsNoThrowMemberSwappable); + +int main(int, char**) { + { + std::stop_token st1; + + std::stop_source source; + auto st2 = source.get_token(); + + assert(st1 != st2); + + source.request_stop(); + + assert(!st1.stop_requested()); + assert(st2.stop_requested()); + + st1.swap(st2); + + assert(st1 != st2); + assert(st1.stop_requested()); + assert(!st2.stop_requested()); + } + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1046,21 +1046,22 @@ # should be marked as UNSUPPORTED, because including # is a hard error in that case. lit_markup = { - "barrier": ["UNSUPPORTED: no-threads"], - "filesystem": ["UNSUPPORTED: no-filesystem"], - "iomanip": ["UNSUPPORTED: no-localization"], - "ios": ["UNSUPPORTED: no-localization"], - "iostream": ["UNSUPPORTED: no-localization"], - "istream": ["UNSUPPORTED: no-localization"], - "latch": ["UNSUPPORTED: no-threads"], - "locale": ["UNSUPPORTED: no-localization"], - "mutex": ["UNSUPPORTED: no-threads"], - "ostream": ["UNSUPPORTED: no-localization"], - "regex": ["UNSUPPORTED: no-localization"], - "semaphore": ["UNSUPPORTED: no-threads"], - "shared_mutex": ["UNSUPPORTED: no-threads"], - "stdatomic.h": ["UNSUPPORTED: no-threads"], - "thread": ["UNSUPPORTED: no-threads"], + "barrier": ["UNSUPPORTED: no-threads"], + "filesystem": ["UNSUPPORTED: no-filesystem"], + "iomanip": ["UNSUPPORTED: no-localization"], + "ios": ["UNSUPPORTED: no-localization"], + "iostream": ["UNSUPPORTED: no-localization"], + "istream": ["UNSUPPORTED: no-localization"], + "latch": ["UNSUPPORTED: no-threads"], + "locale": ["UNSUPPORTED: no-localization"], + "mutex": ["UNSUPPORTED: no-threads"], + "ostream": ["UNSUPPORTED: no-localization"], + "regex": ["UNSUPPORTED: no-localization"], + "semaphore": ["UNSUPPORTED: no-threads"], + "shared_mutex": ["UNSUPPORTED: no-threads"], + "stdatomic.h": ["UNSUPPORTED: no-threads"], + "stop_token": ["UNSUPPORTED: no-threads"], + "thread": ["UNSUPPORTED: no-threads"], } diff --git a/libcxx/utils/generate_header_inclusion_tests.py b/libcxx/utils/generate_header_inclusion_tests.py --- a/libcxx/utils/generate_header_inclusion_tests.py +++ b/libcxx/utils/generate_header_inclusion_tests.py @@ -75,6 +75,7 @@ "initializer_list": "11", "optional": "17", "ranges": "20", + "stop_token": "20", "string_view": "17", "syncstream": "20", "system_error": "11", diff --git a/libcxx/utils/libcxx/test/header_information.py b/libcxx/utils/libcxx/test/header_information.py --- a/libcxx/utils/libcxx/test/header_information.py +++ b/libcxx/utils/libcxx/test/header_information.py @@ -16,6 +16,7 @@ "semaphore": "!defined(_LIBCPP_HAS_NO_THREADS)", "shared_mutex": "!defined(_LIBCPP_HAS_NO_THREADS)", "stdatomic.h": "__cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)", + "stop_token": "!defined(_LIBCPP_HAS_NO_THREADS)", "thread": "!defined(_LIBCPP_HAS_NO_THREADS)", "filesystem": "!defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)", # TODO(LLVM-17): simplify this to __cplusplus >= 202002L