Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
// XFAIL: availability-synchronization_library-missing

// [[nodiscard]] bool stop_requested() const noexcept;
// Returns: true if *this has ownership of a stop state that has received a stop request; otherwise, false.

#include <cassert>
#include <chrono>
#include <concepts>
#include <optional>
#include <stop_token>
#include <type_traits>

#include "make_test_thread.h"
#include "test_macros.h"

template <class T>
concept IsStopRequestedNoexcept = requires(const T& t) {
{ t.stop_requested() } noexcept;
};

static_assert(IsStopRequestedNoexcept<std::stop_token>);

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<std::stop_source> 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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
// XFAIL: availability-synchronization_library-missing

// friend void swap(stop_token& x, stop_token& y) noexcept;

#include <cassert>
#include <concepts>
#include <stop_token>
#include <type_traits>

#include "test_macros.h"

template <class T>
concept IsNoThrowFreeSwappable = requires(T& t) {
{ swap(t, t) } noexcept;
};

static_assert(IsNoThrowFreeSwappable<std::stop_token>);

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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: no-threads
// UNSUPPORTED: c++03, c++11, c++14, c++17
// XFAIL: availability-synchronization_library-missing

// void swap(stop_token& rhs) noexcept;

#include <cassert>
#include <concepts>
#include <stop_token>
#include <type_traits>

#include "test_macros.h"

template <class T>
concept IsNoThrowMemberSwappable = requires(T& t) {
{ t.swap(t) } noexcept;
};

static_assert(IsNoThrowMemberSwappable<std::stop_token>);

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;
}
1 change: 1 addition & 0 deletions libcxx/utils/generate_feature_test_macro_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,7 @@ def add_version_header(tc):
"semaphore": ["UNSUPPORTED: no-threads"],
"shared_mutex": ["UNSUPPORTED: no-threads"],
"stdatomic.h": ["UNSUPPORTED: no-threads"],
"stop_token": ["UNSUPPORTED: no-threads"],
"thread": ["UNSUPPORTED: no-threads"],
}

Expand Down
2 changes: 2 additions & 0 deletions libcxx/utils/libcxx/test/header_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"semaphore": "!defined(_LIBCPP_HAS_NO_THREADS)",
"shared_mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
"stdatomic.h": "__cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)",
"stop_token": "!defined(_LIBCPP_HAS_NO_THREADS)",
"thread": "!defined(_LIBCPP_HAS_NO_THREADS)",
"filesystem": "!defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)",
# TODO(LLVM-17): simplify this to __cplusplus >= 202002L
Expand Down Expand Up @@ -61,6 +62,7 @@

lit_header_restrictions = {
"barrier": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"stop_token": "// UNSUPPORTED: no-threads, c++03, c++11, c++14, c++17",
"clocale": "// UNSUPPORTED: no-localization",
"codecvt": "// UNSUPPORTED: no-localization",
"coroutine": "// UNSUPPORTED: c++03, c++11, c++14, c++17",
Expand Down