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 @@ -49,6 +49,7 @@ .. [#note-P0883.1] P0883: shared_ptr and floating-point changes weren't applied as they themselves aren't implemented yet. .. [#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-P0660] P0660: Section 32.3 Stop Tokens is complete. ``jthread`` haven'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 @@ -556,6 +556,12 @@ __ranges/zip_view.h __split_buffer __std_stream + __stop_token/atomic_unique_lock.h + __stop_token/intrusive_list.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 @@ -865,6 +871,7 @@ stdint.h stdio.h stdlib.h + stop_token streambuf string string.h diff --git a/libcxx/include/__stop_token/atomic_unique_lock.h b/libcxx/include/__stop_token/atomic_unique_lock.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stop_token/atomic_unique_lock.h @@ -0,0 +1,133 @@ +// -*- 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_ATOMIC_UNIQUE_GUARD_H +#define _LIBCPP___STOP_TOKEN_ATOMIC_UNIQUE_GUARD_H + +#include <__config> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 +/** + * This class implements an RAII unique_lock without a mutex. + * It uses std::atomic where State contains a lock bit + * and might contain other data. + */ +template +class __atomic_unique_lock { + std::atomic<_State>& __state_; + bool __is_locked_; + +public: + _LIBCPP_HIDE_FROM_ABI __atomic_unique_lock(std::atomic<_State>& __state) noexcept + : __state_(__state), __is_locked_(true) { + __lock(); + } + + template + _LIBCPP_HIDE_FROM_ABI __atomic_unique_lock(std::atomic<_State>& __state, _Pred&& __fail_to_lock) noexcept + : __state_(__state), __is_locked_(false) { + __is_locked_ = __lock_impl(__fail_to_lock, __set_locked_bit, std::memory_order_acquire); + } + + template + _LIBCPP_HIDE_FROM_ABI __atomic_unique_lock( + std::atomic<_State>& __state, + _Pred&& __fail_to_lock, + _UnaryFuncion&& __state_after_lock, + std::memory_order __locked_ordering) noexcept + : __state_(__state), __is_locked_(false) { + __is_locked_ = __lock_impl(__fail_to_lock, __state_after_lock, __locked_ordering); + } + + __atomic_unique_lock(const __atomic_unique_lock&) = delete; + __atomic_unique_lock(__atomic_unique_lock&&) = delete; + __atomic_unique_lock& operator=(const __atomic_unique_lock&) = delete; + __atomic_unique_lock& operator=(__atomic_unique_lock&&) = delete; + + _LIBCPP_HIDE_FROM_ABI ~__atomic_unique_lock() { + if (__is_locked_) { + __unlock(); + } + } + + _LIBCPP_HIDE_FROM_ABI bool __is_successfully_locked() const noexcept { return __is_locked_; } + + _LIBCPP_HIDE_FROM_ABI void __lock() noexcept { + const auto __never_fail_to_lock = [](_State) { return false; }; + // std::memory_order_acquire because we'd like to make sure that all the read operations after the lock can read the + // up-to-date values. + __lock_impl(__never_fail_to_lock, __set_locked_bit, std::memory_order_acquire); + __is_locked_ = true; + } + + _LIBCPP_HIDE_FROM_ABI void __unlock() noexcept { + // unset the _LockedBit. `memory_order_release` because we need to make sure all the write operations before calling + // `__unlock` will be made visible to other threads + __state_.fetch_and(~_LockedBit, std::memory_order_release); + __state_.notify_all(); + __is_locked_ = false; + } + +private: + template + _LIBCPP_HIDE_FROM_ABI bool __lock_impl( + _Pred&& __fail_to_lock, _UnaryFuncion&& __state_after_lock, std::memory_order __locked_ordering) noexcept { + // At this stage, until we exit the inner while loop, other than the atomic state, we are not reading any order + // dependent values that is written on other threads, or writing anything that needs to be seen on other threads. + // Therefore `memory_order_relaxed` is enough. + _State __current_state = __state_.load(std::memory_order_relaxed); + do { + while (true) { + if (__fail_to_lock(__current_state)) { + // user provided early return condition. fail to lock + return false; + } else if ((__current_state & _LockedBit) != 0) { + // another thread has locked the state, we need to wait + __state_.wait(__current_state, std::memory_order_relaxed); + // when it is woken up by notifyAll or spuriously, the __state_ + // might have changed. reload the state + // Note that the new state's _LockedBit may or may not equal to 0 + __current_state = __state_.load(std::memory_order_relaxed); + } else { + // at least for now, it is not locked. we can try `compare_exchange_weak` to lock it. + // Note that the variable `__current_state`'s lock bit has to be 0 at this point. + break; + } + } + } while (!__state_.compare_exchange_weak( + __current_state, // if __state_ has the same value of __current_state, lock bit must be zero before exchange and + // we are good to lock/exchange and return. If _state has a different value, because other + // threads locked it between the `break` statement above and this statement, exchange will fail + // and go back to the inner while loop above. + __state_after_lock(__current_state), // state after lock. Usually it should be __current_state | _LockedBit. + // Some use cases need to set other bits at the same time as an atomic + // operation therefore we accepts a function + __locked_ordering, // sucessful exchange order. Usually it should be std::memory_order_acquire. + // Some use cases need more strict ordering therefore we accept it as a parameter + std::memory_order_relaxed // fail to exchange order. We don't need any ordering as we are going back to the + // inner while loop + )); + return true; + } + + _LIBCPP_HIDE_FROM_ABI static constexpr auto __set_locked_bit = [](_State __state) { return __state | _LockedBit; }; +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_ATOMIC_UNIQUE_GUARD_H diff --git a/libcxx/include/__stop_token/intrusive_list.h b/libcxx/include/__stop_token/intrusive_list.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__stop_token/intrusive_list.h @@ -0,0 +1,74 @@ +// -*- 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_INTRUSIVE_LIST_H +#define _LIBCPP___STOP_TOKEN_INTRUSIVE_LIST_H + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +struct __instrusive_node_base { + _Derived* __next_ = nullptr; + _Derived* __prev_ = nullptr; +}; + +template +struct __instrusive_list { + _LIBCPP_HIDE_FROM_ABI bool __has_node() const noexcept { return __head_ != nullptr; } + + _LIBCPP_HIDE_FROM_ABI void __push_front(_Node* __node) noexcept { + __node->__next_ = __head_; + if (__head_) { + __head_->__prev_ = __node; + } + __head_ = __node; + } + + _LIBCPP_HIDE_FROM_ABI _Node* __pop_front() noexcept { + _Node* __front = __head_; + __head_ = __head_->__next_; + if (__head_) { + __head_->__prev_ = nullptr; + } + return __front; + } + + _LIBCPP_HIDE_FROM_ABI void __remove(_Node* __node) noexcept { + if (__node->__prev_) { + // prev exists, set its next to our next to skip __node + __node->__prev_->__next_ = __node->__next_; + if (__node->__next_) { + __node->__next_->__prev_ = __node->__prev_; + } + } else if (__node == __head_) { + __pop_front(); + } + } + + _LIBCPP_HIDE_FROM_ABI bool __is_head(_Node* __node) noexcept { + return __node ==__head_; + } + +private: + _Node* __head_ = nullptr; +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_INTRUSIVE_LIST_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,101 @@ +// -*- 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 <__concepts/constructible.h> +#include <__concepts/destructible.h> +#include <__concepts/invocable.h> +#include <__config> +#include <__stop_token/stop_state.h> +#include <__stop_token/stop_token.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__utility/forward.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 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 both invocable and destructible."); + static_assert(destructible<_Callback>, + "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that " + "satisfies both invocable and 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_base(&stop_callback::__callback_fn_impl), + __callback_(std::forward<_Cb>(__cb)), + __ref_counted_state_() { + if (__st.__ref_counted_state_.__has_state()) { + if (__st.__ref_counted_state_.__get()->__add_callback(this)) { + // st.stop_requested() was false and this is successfully added to the linked list + __ref_counted_state_ = __st.__ref_counted_state_; + } + } + } + + 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_base(&stop_callback::__callback_fn_impl), + __callback_(std::forward<_Cb>(__cb)), + __ref_counted_state_() { + if (__st.__ref_counted_state_.__has_state()) { + if (__st.__ref_counted_state_.__get()->__add_callback(this)) { + // st.stop_requested() was false and this is successfully added to the linked list + __st.__ref_counted_state_.__swap(__ref_counted_state_); + } + } + } + + _LIBCPP_HIDE_FROM_ABI ~stop_callback() { + if (__ref_counted_state_.__has_state()) { + __ref_counted_state_.__get()->__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_; + __ref_counted_stop_state __ref_counted_state_; + + friend __stop_callback_base; + + _LIBCPP_HIDE_FROM_ABI static void __callback_fn_impl(__stop_callback_base* __cb_base) noexcept { + std::forward<_Callback>(static_cast(__cb_base)->__callback_)(); + } +}; + +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,101 @@ +// -*- 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 <__config> +#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 stop_source { +public: + _LIBCPP_HIDE_FROM_ABI stop_source() + : __ref_counted_state_(__ref_counted_stop_state::__new_state_from_stop_source_tag{}) {} + + _LIBCPP_HIDE_FROM_ABI explicit stop_source(nostopstate_t) noexcept {} + + _LIBCPP_HIDE_FROM_ABI stop_source(const stop_source& __other) noexcept + : __ref_counted_state_(__other.__ref_counted_state_) { + if (__ref_counted_state_.__has_state()) { + __ref_counted_state_.__get()->__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 { + if (__ref_counted_state_ != __other.__ref_counted_state_) { + auto __tmp = std::move(__ref_counted_state_); + __ref_counted_state_ = __other.__ref_counted_state_; + if (__ref_counted_state_.__has_state()) { + __ref_counted_state_.__get()->__increment_stop_source_counter(); + } + if (__tmp.__has_state()) { + __tmp.__get()->__decrement_stop_source_counter(); + } + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI stop_source& operator=(stop_source&&) noexcept = default; + + _LIBCPP_HIDE_FROM_ABI ~stop_source() { + if (__ref_counted_state_.__has_state()) { + __ref_counted_state_.__get()->__decrement_stop_source_counter(); + } + } + + _LIBCPP_HIDE_FROM_ABI void swap(stop_source& __other) noexcept { + __ref_counted_state_.__swap(__other.__ref_counted_state_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI stop_token get_token() const noexcept { return stop_token(__ref_counted_state_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_possible() const noexcept { return __ref_counted_state_.__has_state(); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_requested() const noexcept { + return __ref_counted_state_.__has_state() && __ref_counted_state_.__get()->__stop_requested(); + } + + _LIBCPP_HIDE_FROM_ABI bool request_stop() noexcept { + if (!__ref_counted_state_.__has_state()) { + return false; + } + return __ref_counted_state_.__get()->__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: + __ref_counted_stop_state __ref_counted_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,257 @@ +// -*- 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 <__config> +#include <__stop_token/atomic_unique_lock.h> +#include <__stop_token/intrusive_list.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 : __instrusive_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_; + std::atomic __completed_ = false; +}; + +struct __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; + + // "stop_source counter" is not used for lifetime reference counter. + // When number of stop_source reaches 0, the remaining stop_tokens's + // stop_possible will return false. We need this counter to track this + // + // "callback list locked" bit is for implementing the pesudo-mutex to guard the operations + // on the callback list + // + // 31 - 2 | 1 | 0 | + // stop_source counter | callback list locked | stop_requested | + std::atomic __state_ = 0; + + // reference count for stop_token + stop_callback + stop_source + // When counter reaches zero, the state is destroyed + // It is used by __ref_counted_stop_state, put it here for better layout + std::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 = __instrusive_list<__stop_callback_base>; + + __callback_list __callback_list_; + std::thread::id __requesting_thread_; + + _LIBCPP_HIDE_FROM_ABI __stop_state(uint32_t __state, uint32_t __ref_count) noexcept + : __state_(__state), __ref_count_(__ref_count) {} + + _LIBCPP_HIDE_FROM_ABI void __increment_stop_source_counter() noexcept { + __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 { + __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_week 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" + __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.__is_successfully_locked()) { + return false; + } + __requesting_thread_ = std::this_thread::get_id(); + + while (__callback_list_.__has_node()) { + auto __cb = __callback_list_.__pop_front(); + + __cb_list_lock.__unlock(); + + __cb->__invoke(); + + __cb->__completed_.store(true, std::memory_order_release); + __cb->__completed_.notify_all(); + + __cb_list_lock.__lock(); + } + + return true; + } + + _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 + // 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 + const auto __locked_ordering = std::memory_order_acq_rel; + + return __callback_list_lock(__state_, __lock_fail_condition, __after_lock_state, __locked_ordering); + } + + _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 __lock_fail_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_, __lock_fail_condition); + + if (!__cb_list_lock.__is_successfully_locked()) { + 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. + } + + _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 poped __cb from the list and could execute it now + bool __potentially_executing_now = __cb->__prev_ == nullptr && !__callback_list_.__is_head(__cb); + + __callback_list_.__remove(__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); + } + } + } +}; + +struct __ref_counted_stop_state { + struct __new_state_from_stop_source_tag {}; + + _LIBCPP_HIDE_FROM_ABI __ref_counted_stop_state() = default; + + _LIBCPP_HIDE_FROM_ABI __ref_counted_stop_state(__new_state_from_stop_source_tag) + : __stop_state_(new __stop_state(/*state=*/1 << __stop_state::__stop_source_counter_shift, /*__ref_count=*/1)) {} + + _LIBCPP_HIDE_FROM_ABI __ref_counted_stop_state(const __ref_counted_stop_state& __other) noexcept + : __stop_state_(__other.__stop_state_) { + if (__stop_state_) { + __increment_ref_count(__stop_state_); + } + } + + _LIBCPP_HIDE_FROM_ABI __ref_counted_stop_state(__ref_counted_stop_state&& __other) noexcept + : __stop_state_(__other.__stop_state_) { + __other.__stop_state_ = nullptr; + } + + _LIBCPP_HIDE_FROM_ABI __ref_counted_stop_state& operator=(const __ref_counted_stop_state& __other) noexcept { + if (__other.__stop_state_ != __stop_state_) { + if (__other.__stop_state_) { + __increment_ref_count(__other.__stop_state_); + } + if (__stop_state_) { + __decrement_ref_count(__stop_state_); + } + __stop_state_ = __other.__stop_state_; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI __ref_counted_stop_state& operator=(__ref_counted_stop_state&& __other) noexcept { + __ref_counted_stop_state(std::move(__other)).__swap(*this); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI ~__ref_counted_stop_state() { + if (__stop_state_) { + __decrement_ref_count(__stop_state_); + } + } + + _LIBCPP_HIDE_FROM_ABI __stop_state* __get() const noexcept { return __stop_state_; } + + _LIBCPP_HIDE_FROM_ABI bool __has_state() const noexcept { return __stop_state_ != nullptr; } + + _LIBCPP_HIDE_FROM_ABI void __swap(__ref_counted_stop_state& __other) { + std::swap(__stop_state_, __other.__stop_state_); + } + + _LIBCPP_HIDE_FROM_ABI friend bool constexpr + operator==(const __ref_counted_stop_state&, const __ref_counted_stop_state&) = default; + +private: + __stop_state* __stop_state_ = nullptr; + + // the memory order for increment/decrement the counter is the same for shared_ptr + // increment is relaxed and decrement is acq_rel + _LIBCPP_HIDE_FROM_ABI void __increment_ref_count(__stop_state* __state) { + __state->__ref_count_.fetch_add(1, std::memory_order_relaxed); + } + + _LIBCPP_HIDE_FROM_ABI void __decrement_ref_count(__stop_state* __state) { + if (__state->__ref_count_.fetch_sub(1, std::memory_order_acq_rel) == 1) { + delete __state; + } + } +}; + +#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,65 @@ +// -*- 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 <__config> +#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 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 { + __ref_counted_state_.__swap(__other.__ref_counted_state_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_requested() const noexcept { + return __ref_counted_state_.__has_state() && __ref_counted_state_.__get()->__stop_requested(); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_possible() const noexcept { + return __ref_counted_state_.__has_state() && __ref_counted_state_.__get()->__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: + __ref_counted_stop_state __ref_counted_state_; + +private: + friend class stop_source; + template + friend class stop_callback; + + _LIBCPP_HIDE_FROM_ABI explicit stop_token(const __ref_counted_stop_state& __state) : __ref_counted_state_(__state) {} +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___STOP_TOKEN_STOP_TOKEN_H diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp --- a/libcxx/include/libcxx.imp +++ b/libcxx/include/libcxx.imp @@ -36,6 +36,7 @@ { include: [ "@<__numeric/.*>", "private", "", "public" ] }, { include: [ "@<__random/.*>", "private", "", "public" ] }, { include: [ "@<__ranges/.*>", "private", "", "public" ] }, + { include: [ "@<__stop_token/.*>", "private", "", "public" ] }, { include: [ "@<__string/.*>", "private", "", "public" ] }, { include: [ "@<__support/.*>", "private", "", "public" ] }, { include: [ "@<__thread/.*>", "private", "", "public" ] }, 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 @@ -1376,6 +1376,19 @@ header "stdexcept" export * } + module stop_token { + header "stop_token" + export * + + module __stop_token { + module atomic_unique_lock { private header "__stop_token/atomic_unique_lock.h" } + module intrusive_list { private header "__stop_token/intrusive_list.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 { @requires_LIBCXX_ENABLE_LOCALIZATION@ header "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/assertions/headers_declare_verbose_abort.sh.cpp b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp --- a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp +++ b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp @@ -555,211 +555,217 @@ #endif // RUN: %{build} -DTEST_100 -#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -# include +#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_THREADS) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_101 -#if defined(TEST_101) -# include +#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif -// RUN: %{build} -DTEST_103 -#if defined(TEST_103) -# include +// RUN: %{build} -DTEST_102 +#if defined(TEST_102) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_104 -#if defined(TEST_104) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -# include +#if defined(TEST_104) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_105 -#if defined(TEST_105) -# include +#if defined(TEST_105) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif -// RUN: %{build} -DTEST_107 -#if defined(TEST_107) && !defined(_LIBCPP_HAS_NO_THREADS) -# include +// RUN: %{build} -DTEST_106 +#if defined(TEST_106) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_108 -#if defined(TEST_108) -# include +#if defined(TEST_108) && !defined(_LIBCPP_HAS_NO_THREADS) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_109 #if defined(TEST_109) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_110 #if defined(TEST_110) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_111 #if defined(TEST_111) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif -// RUN: %{build} -DTEST_113 -#if defined(TEST_113) -# include +// RUN: %{build} -DTEST_112 +#if defined(TEST_112) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_114 #if defined(TEST_114) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_115 #if defined(TEST_115) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_116 #if defined(TEST_116) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_117 #if defined(TEST_117) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_118 #if defined(TEST_118) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_119 #if defined(TEST_119) -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif -// RUN: %{build} -DTEST_122 -#if defined(TEST_122) && __cplusplus >= 201103L -# include +// RUN: %{build} -DTEST_120 +#if defined(TEST_120) +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_123 #if defined(TEST_123) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_124 #if defined(TEST_124) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_125 #if defined(TEST_125) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_126 #if defined(TEST_126) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_127 #if defined(TEST_127) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_128 #if defined(TEST_128) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_129 -#if defined(TEST_129) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L -# include +#if defined(TEST_129) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_130 -#if defined(TEST_130) && __cplusplus >= 201103L -# include +#if defined(TEST_130) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_131 #if defined(TEST_131) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_132 #if defined(TEST_132) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_133 #if defined(TEST_133) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_134 #if defined(TEST_134) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_135 #if defined(TEST_135) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_136 #if defined(TEST_136) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_137 #if defined(TEST_137) && __cplusplus >= 201103L -# include +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_138 -#if defined(TEST_138) -# include +#if defined(TEST_138) && __cplusplus >= 201103L +# include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif // RUN: %{build} -DTEST_139 #if defined(TEST_139) +# include + using HandlerType = decltype(std::__libcpp_verbose_abort); +#endif + +// RUN: %{build} -DTEST_140 +#if defined(TEST_140) # include using HandlerType = decltype(std::__libcpp_verbose_abort); #endif 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 @@ -182,6 +182,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 @@ -183,6 +183,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 @@ -283,6 +283,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 @@ -648,244 +648,249 @@ // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_100 &' >> %t.sh // RUN: echo 'TEST_100=$!' >> %t.sh // RUN: echo "wait $TEST_84" >> %t.sh -#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -#include +#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_THREADS) +#include #endif // 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) -#include +#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +#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 +#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) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) -#include +#if defined(TEST_104) +#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) -#include +#if defined(TEST_105) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) +#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 +#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) && !defined(_LIBCPP_HAS_NO_THREADS) -#include +#if defined(TEST_107) +#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) -#include +#if defined(TEST_108) && !defined(_LIBCPP_HAS_NO_THREADS) +#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 +#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) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) -#include +#if defined(TEST_120) +#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 +#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) && __cplusplus >= 201103L -#include +#if defined(TEST_122) && !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_123 &' >> %t.sh // RUN: echo 'TEST_123=$!' >> %t.sh // RUN: echo "wait $TEST_107" >> %t.sh #if defined(TEST_123) && __cplusplus >= 201103L -#include +#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) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L -#include +#if defined(TEST_129) && __cplusplus >= 201103L +#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) && __cplusplus >= 201103L -#include +#if defined(TEST_130) && !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_131 &' >> %t.sh // RUN: echo 'TEST_131=$!' >> %t.sh // RUN: echo "wait $TEST_115" >> %t.sh #if defined(TEST_131) && __cplusplus >= 201103L -#include +#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) -#include +#if defined(TEST_138) && __cplusplus >= 201103L +#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) -#include +#include #endif +// RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_140 &' >> %t.sh +// RUN: echo 'TEST_140=$!' >> %t.sh // RUN: echo "wait $TEST_124" >> %t.sh +#if defined(TEST_140) +#include +#endif // RUN: echo "wait $TEST_125" >> %t.sh // RUN: echo "wait $TEST_126" >> %t.sh // RUN: echo "wait $TEST_127" >> %t.sh @@ -901,5 +906,6 @@ // RUN: echo "wait $TEST_137" >> %t.sh // RUN: echo "wait $TEST_138" >> %t.sh // RUN: echo "wait $TEST_139" >> %t.sh +// RUN: echo "wait $TEST_140" >> %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 @@ -307,6 +307,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 @@ -180,6 +180,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 @@ -587,6 +587,12 @@ #include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}} #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}} #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}} +#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.h> // expected-error@*:* {{use of private header from outside its module: '__stop_token/intrusive_list.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 @@ -413,144 +413,148 @@ #if defined(TEST_96) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_100 2> %t/header.streambuf +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_100 2> %t/header.stop_token #if defined(TEST_100) -#include +#include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_101 2> %t/header.string +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_101 2> %t/header.streambuf #if defined(TEST_101) +#include +#endif +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_102 2> %t/header.string +#if defined(TEST_102) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_103 2> %t/header.string_view -#if defined(TEST_103) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_104 2> %t/header.string_view +#if defined(TEST_104) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_104 2> %t/header.strstream -#if defined(TEST_104) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_105 2> %t/header.strstream +#if defined(TEST_105) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_105 2> %t/header.system_error -#if defined(TEST_105) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_106 2> %t/header.system_error +#if defined(TEST_106) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_107 2> %t/header.thread -#if defined(TEST_107) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_108 2> %t/header.thread +#if defined(TEST_108) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_108 2> %t/header.tuple -#if defined(TEST_108) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_109 2> %t/header.tuple +#if defined(TEST_109) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_109 2> %t/header.type_traits -#if defined(TEST_109) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_110 2> %t/header.type_traits +#if defined(TEST_110) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_110 2> %t/header.typeindex -#if defined(TEST_110) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_111 2> %t/header.typeindex +#if defined(TEST_111) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_111 2> %t/header.typeinfo -#if defined(TEST_111) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_112 2> %t/header.typeinfo +#if defined(TEST_112) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_113 2> %t/header.unordered_map -#if defined(TEST_113) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_114 2> %t/header.unordered_map +#if defined(TEST_114) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_114 2> %t/header.unordered_set -#if defined(TEST_114) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_115 2> %t/header.unordered_set +#if defined(TEST_115) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_115 2> %t/header.utility -#if defined(TEST_115) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_116 2> %t/header.utility +#if defined(TEST_116) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_116 2> %t/header.valarray -#if defined(TEST_116) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_117 2> %t/header.valarray +#if defined(TEST_117) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_117 2> %t/header.variant -#if defined(TEST_117) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_118 2> %t/header.variant +#if defined(TEST_118) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_118 2> %t/header.vector -#if defined(TEST_118) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_119 2> %t/header.vector +#if defined(TEST_119) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_119 2> %t/header.version -#if defined(TEST_119) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_120 2> %t/header.version +#if defined(TEST_120) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_122 2> %t/header.experimental_deque -#if defined(TEST_122) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_123 2> %t/header.experimental_deque +#if defined(TEST_123) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_123 2> %t/header.experimental_forward_list -#if defined(TEST_123) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_124 2> %t/header.experimental_forward_list +#if defined(TEST_124) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_124 2> %t/header.experimental_iterator -#if defined(TEST_124) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_125 2> %t/header.experimental_iterator +#if defined(TEST_125) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_125 2> %t/header.experimental_list -#if defined(TEST_125) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_126 2> %t/header.experimental_list +#if defined(TEST_126) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_126 2> %t/header.experimental_map -#if defined(TEST_126) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_127 2> %t/header.experimental_map +#if defined(TEST_127) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_127 2> %t/header.experimental_memory_resource -#if defined(TEST_127) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_128 2> %t/header.experimental_memory_resource +#if defined(TEST_128) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_128 2> %t/header.experimental_propagate_const -#if defined(TEST_128) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_129 2> %t/header.experimental_propagate_const +#if defined(TEST_129) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_129 2> %t/header.experimental_regex -#if defined(TEST_129) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_130 2> %t/header.experimental_regex +#if defined(TEST_130) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_130 2> %t/header.experimental_set -#if defined(TEST_130) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_131 2> %t/header.experimental_set +#if defined(TEST_131) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_131 2> %t/header.experimental_simd -#if defined(TEST_131) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_132 2> %t/header.experimental_simd +#if defined(TEST_132) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_132 2> %t/header.experimental_string -#if defined(TEST_132) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_133 2> %t/header.experimental_string +#if defined(TEST_133) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_133 2> %t/header.experimental_type_traits -#if defined(TEST_133) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_134 2> %t/header.experimental_type_traits +#if defined(TEST_134) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_134 2> %t/header.experimental_unordered_map -#if defined(TEST_134) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_135 2> %t/header.experimental_unordered_map +#if defined(TEST_135) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_135 2> %t/header.experimental_unordered_set -#if defined(TEST_135) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_136 2> %t/header.experimental_unordered_set +#if defined(TEST_136) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_136 2> %t/header.experimental_utility -#if defined(TEST_136) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_137 2> %t/header.experimental_utility +#if defined(TEST_137) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_137 2> %t/header.experimental_vector -#if defined(TEST_137) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_138 2> %t/header.experimental_vector +#if defined(TEST_138) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_138 2> %t/header.ext_hash_map -#if defined(TEST_138) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_139 2> %t/header.ext_hash_map +#if defined(TEST_139) #include #endif -// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_139 2> %t/header.ext_hash_set -#if defined(TEST_139) +// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes -fsyntax-only -DTEST_140 2> %t/header.ext_hash_set +#if defined(TEST_140) #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 @@ -745,6 +745,10 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +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 @@ -746,6 +746,10 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +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 @@ -755,6 +755,10 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +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 @@ -755,6 +755,10 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +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 @@ -761,6 +761,10 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +stop_token thread +stop_token version streambuf cstdint streambuf ios streambuf iosfwd diff --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv --- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv @@ -565,6 +565,10 @@ stdexcept cstdlib stdexcept exception stdexcept iosfwd +stop_token atomic +stop_token cstddef +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,74 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// 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 > 20 + +# if !defined(_LIBCPP_VERSION) +# ifndef __cpp_lib_jthread +# error "__cpp_lib_jthread should be defined in c++2b" +# endif +# if __cpp_lib_jthread != 201911L +# error "__cpp_lib_jthread should have the value 201911L in c++2b" +# 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 > 20 + 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,239 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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 auto 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 auto 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 auto 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 auto 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 auto 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 auto 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 auto 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 auto 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 auto 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,230 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,163 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// ~stop_callback(); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +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; + + struct Wrapper { + std::unique_ptr>> sc_; + }; + + Wrapper wrapper; + + auto ptr = + std::make_unique>>(ss.get_token(), [&] { wrapper.sc_ = nullptr; }); + + wrapper.sc_ = std::move(ptr); + assert(wrapper.sc_ != nullptr); + + ss.request_stop(); // the callbacks deletes itself. if the destructor blocks, it would be deadlock + assert(wrapper.sc_ == nullptr); + } +} 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,135 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,71 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,34 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,36 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,67 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,53 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,135 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,82 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,46 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,108 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// stop_token& operator=(const stop_token& rhs) noexcept; + +#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,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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,34 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,90 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,97 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,158 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// [[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,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// This test requires the dylib support introduced in D68480, which shipped in macOS 11.0. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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_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 @@ -73,6 +73,7 @@ "initializer_list": "11", "optional": "17", "ranges": "20", + "stop_token": "20", "string_view": "17", "syncstream": "20", "system_error": "11", diff --git a/libcxx/utils/generate_header_tests.py b/libcxx/utils/generate_header_tests.py --- a/libcxx/utils/generate_header_tests.py +++ b/libcxx/utils/generate_header_tests.py @@ -15,6 +15,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)",