[libc++] Implement `stop_token`

Implement stop_token
http://eel.is/c++draft/thread.stoptoken

GitOrigin-RevId: b77e50e6aef5650d1ce0ce7ad1a41049395b9426
diff --git a/docs/Status/Cxx20.rst b/docs/Status/Cxx20.rst
index e0ffdc6..1ab0252 100644
--- a/docs/Status/Cxx20.rst
+++ b/docs/Status/Cxx20.rst
@@ -50,6 +50,7 @@
    .. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0.
    .. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet.
    .. [#note-P0408] P0408: Only `view()` members implemented.
+   .. [#note-P0660] P0660: Section 32.3 Stop Tokens is complete. ``jthread`` hasn't been implemented yet.
 
 .. _issues-status-cxx20:
 
diff --git a/docs/Status/Cxx20Issues.csv b/docs/Status/Cxx20Issues.csv
index d9f8711..c0d764d 100644
--- a/docs/Status/Cxx20Issues.csv
+++ b/docs/Status/Cxx20Issues.csv
@@ -210,7 +210,7 @@
 "`3250 <https://wg21.link/LWG3250>`__","``std::format``\ : ``#``\  (alternate form) for NaN and inf","Prague","|Complete|","14.0","|format|"
 "`3251 <https://wg21.link/LWG3251>`__","Are ``std::format``\  alignment specifiers applied to string arguments?","Prague","|Complete|","14.0","|format|"
 "`3252 <https://wg21.link/LWG3252>`__","Parse locale's aware modifiers for commands are not consistent with POSIX spec","Prague","","","|chrono|"
-"`3254 <https://wg21.link/LWG3254>`__","Strike ``stop_token``\ 's ``operator!=``\ ","Prague","",""
+"`3254 <https://wg21.link/LWG3254>`__","Strike ``stop_token``\ 's ``operator!=``\ ","Prague","|Complete|","17.0"
 "`3255 <https://wg21.link/LWG3255>`__","``span``\ 's ``array``\  constructor is too strict","Prague","|Complete|",""
 "`3260 <https://wg21.link/LWG3260>`__","``year_month*``\  arithmetic rejects durations convertible to years","Prague","","","|chrono|"
 "`3262 <https://wg21.link/LWG3262>`__","Formatting of negative durations is not specified","Prague","|Complete|","16.0","|chrono| |format|"
diff --git a/docs/Status/Cxx20Papers.csv b/docs/Status/Cxx20Papers.csv
index 62951f5..c3359bc 100644
--- a/docs/Status/Cxx20Papers.csv
+++ b/docs/Status/Cxx20Papers.csv
@@ -104,7 +104,7 @@
 "`P0553R4 <https://wg21.link/P0553R4>`__","LWG","Bit operations","Cologne","|Complete|","9.0"
 "`P0631R8 <https://wg21.link/P0631R8>`__","LWG","Math Constants","Cologne","|Complete|","11.0"
 "`P0645R10 <https://wg21.link/P0645R10>`__","LWG","Text Formatting","Cologne","|Complete| [#note-P0645]_","14.0"
-"`P0660R10 <https://wg21.link/P0660R10>`__","LWG","Stop Token and Joining Thread, Rev 10","Cologne","",""
+"`P0660R10 <https://wg21.link/P0660R10>`__","LWG","Stop Token and Joining Thread, Rev 10.","Cologne","|In Progress| [#note-P0660]_",""
 "`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0"
 "`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0"
 "`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0"
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 1e91e18..910e6b1 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -642,6 +642,10 @@
   __stop_token/atomic_unique_lock.h
   __stop_token/intrusive_list_view.h
   __stop_token/intrusive_shared_ptr.h
+  __stop_token/stop_callback.h
+  __stop_token/stop_source.h
+  __stop_token/stop_state.h
+  __stop_token/stop_token.h
   __string/char_traits.h
   __string/constexpr_c_functions.h
   __string/extern_template_lists.h
@@ -959,6 +963,7 @@
   stdint.h
   stdio.h
   stdlib.h
+  stop_token
   streambuf
   string
   string.h
diff --git a/include/__stop_token/atomic_unique_lock.h b/include/__stop_token/atomic_unique_lock.h
index f21c742..6c63a25 100644
--- a/include/__stop_token/atomic_unique_lock.h
+++ b/include/__stop_token/atomic_unique_lock.h
@@ -27,7 +27,7 @@
 // where State contains a lock bit and might contain other data,
 // and LockedBit is the value of State when the lock bit is set, e.g  1 << 2
 template <class _State, _State _LockedBit>
-class __atomic_unique_lock {
+class _LIBCPP_AVAILABILITY_SYNC __atomic_unique_lock {
   static_assert(std::popcount(_LockedBit) == 1, "LockedBit must be an integer where only one bit is set");
 
   std::atomic<_State>& __state_;
diff --git a/include/__stop_token/stop_callback.h b/include/__stop_token/stop_callback.h
new file mode 100644
index 0000000..2d94c10
--- /dev/null
+++ b/include/__stop_token/stop_callback.h
@@ -0,0 +1,99 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___STOP_TOKEN_STOP_CALLBACK_H
+#define _LIBCPP___STOP_TOKEN_STOP_CALLBACK_H
+
+#include <__availability>
+#include <__concepts/constructible.h>
+#include <__concepts/destructible.h>
+#include <__concepts/invocable.h>
+#include <__config>
+#include <__stop_token/intrusive_shared_ptr.h>
+#include <__stop_token/stop_state.h>
+#include <__stop_token/stop_token.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Callback>
+class _LIBCPP_AVAILABILITY_SYNC stop_callback : private __stop_callback_base {
+  static_assert(invocable<_Callback>,
+                "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that "
+                "satisfies invocable.");
+  static_assert(destructible<_Callback>,
+                "Mandates: stop_callback is instantiated with an argument for the template parameter Callback that "
+                "satisfies destructible.");
+
+public:
+  using callback_type = _Callback;
+
+  template <class _Cb>
+    requires constructible_from<_Callback, _Cb>
+  _LIBCPP_HIDE_FROM_ABI explicit stop_callback(const stop_token& __st,
+                                               _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
+      : stop_callback(__private_tag{}, __st.__state_, std::forward<_Cb>(__cb)) {}
+
+  template <class _Cb>
+    requires constructible_from<_Callback, _Cb>
+  _LIBCPP_HIDE_FROM_ABI explicit stop_callback(stop_token&& __st,
+                                               _Cb&& __cb) noexcept(is_nothrow_constructible_v<_Callback, _Cb>)
+      : stop_callback(__private_tag{}, std::move(__st.__state_), std::forward<_Cb>(__cb)) {}
+
+  _LIBCPP_HIDE_FROM_ABI ~stop_callback() {
+    if (__state_) {
+      __state_->__remove_callback(this);
+    }
+  }
+
+  stop_callback(const stop_callback&)            = delete;
+  stop_callback(stop_callback&&)                 = delete;
+  stop_callback& operator=(const stop_callback&) = delete;
+  stop_callback& operator=(stop_callback&&)      = delete;
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS _Callback __callback_;
+  __intrusive_shared_ptr<__stop_state> __state_;
+
+  friend __stop_callback_base;
+
+  struct __private_tag {};
+
+  template <class _StatePtr, class _Cb>
+  _LIBCPP_HIDE_FROM_ABI explicit stop_callback(__private_tag, _StatePtr&& __state, _Cb&& __cb) noexcept(
+      is_nothrow_constructible_v<_Callback, _Cb>)
+      : __stop_callback_base([](__stop_callback_base* __cb_base) noexcept {
+          // stop callback is supposed to only be called once
+          std::forward<_Callback>(static_cast<stop_callback*>(__cb_base)->__callback_)();
+        }),
+        __callback_(std::forward<_Cb>(__cb)),
+        __state_() {
+    if (__state && __state->__add_callback(this)) {
+      // st.stop_requested() was false and this is successfully added to the linked list
+      __state_ = std::forward<_StatePtr>(__state);
+    }
+  }
+};
+
+template <class Callback>
+_LIBCPP_AVAILABILITY_SYNC stop_callback(stop_token, Callback) -> stop_callback<Callback>;
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___STOP_TOKEN_STOP_TOKEN_H
diff --git a/include/__stop_token/stop_source.h b/include/__stop_token/stop_source.h
new file mode 100644
index 0000000..5dfc0f7
--- /dev/null
+++ b/include/__stop_token/stop_source.h
@@ -0,0 +1,92 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___STOP_TOKEN_STOP_SOURCE_H
+#define _LIBCPP___STOP_TOKEN_STOP_SOURCE_H
+
+#include <__availability>
+#include <__config>
+#include <__stop_token/intrusive_shared_ptr.h>
+#include <__stop_token/stop_state.h>
+#include <__stop_token/stop_token.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+struct nostopstate_t {
+  explicit nostopstate_t() = default;
+};
+
+inline constexpr nostopstate_t nostopstate{};
+
+class _LIBCPP_AVAILABILITY_SYNC stop_source {
+public:
+  _LIBCPP_HIDE_FROM_ABI stop_source() : __state_(new __stop_state()) { __state_->__increment_stop_source_counter(); }
+
+  _LIBCPP_HIDE_FROM_ABI explicit stop_source(nostopstate_t) noexcept : __state_(nullptr) {}
+
+  _LIBCPP_HIDE_FROM_ABI stop_source(const stop_source& __other) noexcept : __state_(__other.__state_) {
+    if (__state_) {
+      __state_->__increment_stop_source_counter();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI stop_source(stop_source&& __other) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI stop_source& operator=(const stop_source& __other) noexcept {
+    // increment `__other` first so that we don't hit 0 in case of self-assignment
+    if (__other.__state_) {
+      __other.__state_->__increment_stop_source_counter();
+    }
+    if (__state_) {
+      __state_->__decrement_stop_source_counter();
+    }
+    __state_ = __other.__state_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI stop_source& operator=(stop_source&&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI ~stop_source() {
+    if (__state_) {
+      __state_->__decrement_stop_source_counter();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void swap(stop_source& __other) noexcept { __state_.swap(__other.__state_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI stop_token get_token() const noexcept { return stop_token(__state_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_possible() const noexcept { return __state_ != nullptr; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_requested() const noexcept {
+    return __state_ != nullptr && __state_->__stop_requested();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI bool request_stop() noexcept { return __state_ && __state_->__request_stop(); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const stop_source&, const stop_source&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI friend void swap(stop_source& __lhs, stop_source& __rhs) noexcept { __lhs.swap(__rhs); }
+
+private:
+  __intrusive_shared_ptr<__stop_state> __state_;
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___STOP_TOKEN_STOP_SOURCE_H
diff --git a/include/__stop_token/stop_state.h b/include/__stop_token/stop_state.h
new file mode 100644
index 0000000..d880286
--- /dev/null
+++ b/include/__stop_token/stop_state.h
@@ -0,0 +1,234 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___STOP_TOKEN_STOP_STATE_H
+#define _LIBCPP___STOP_TOKEN_STOP_STATE_H
+
+#include <__availability>
+#include <__config>
+#include <__stop_token/atomic_unique_lock.h>
+#include <__stop_token/intrusive_list_view.h>
+#include <atomic>
+#include <thread>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+struct __stop_callback_base : __intrusive_node_base<__stop_callback_base> {
+  using __callback_fn_t = void(__stop_callback_base*) noexcept;
+  _LIBCPP_HIDE_FROM_ABI explicit __stop_callback_base(__callback_fn_t* __callback_fn) : __callback_fn_(__callback_fn) {}
+
+  _LIBCPP_HIDE_FROM_ABI void __invoke() noexcept { __callback_fn_(this); }
+
+  __callback_fn_t* __callback_fn_;
+  atomic<bool> __completed_ = false;
+  bool* __destroyed_        = nullptr;
+};
+
+class __stop_state {
+  static constexpr uint32_t __stop_requested_bit        = 1;
+  static constexpr uint32_t __callback_list_locked_bit  = 1 << 1;
+  static constexpr uint32_t __stop_source_counter_shift = 2;
+
+  // The "stop_source counter" is not used for lifetime reference counting.
+  // When the number of stop_source reaches 0, the remaining stop_tokens's
+  // stop_possible will return false. We need this counter to track this.
+  //
+  // The "callback list locked" bit implements the atomic_unique_lock to
+  // guard the operations on the callback list
+  //
+  //       31 - 2          |  1                   |    0           |
+  //  stop_source counter  | callback list locked | stop_requested |
+  atomic<uint32_t> __state_ = 0;
+
+  // Reference count for stop_token + stop_callback + stop_source
+  // When the counter reaches zero, the state is destroyed
+  // It is used by __intrusive_shared_ptr, but it is stored here for better layout
+  atomic<uint32_t> __ref_count_ = 0;
+
+  using __state_t            = uint32_t;
+  using __callback_list_lock = __atomic_unique_lock<__state_t, __callback_list_locked_bit>;
+  using __callback_list      = __intrusive_list_view<__stop_callback_base>;
+
+  __callback_list __callback_list_;
+  thread::id __requesting_thread_;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __stop_state() noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI void __increment_stop_source_counter() noexcept {
+    _LIBCPP_ASSERT(
+        __state_.load(std::memory_order_relaxed) <= static_cast<__state_t>(~(1 << __stop_source_counter_shift)),
+        "stop_source's counter reaches the maximum. Incrementing the counter will overflow");
+    __state_.fetch_add(1 << __stop_source_counter_shift, std::memory_order_relaxed);
+  }
+
+  // We are not destroying the object after counter decrements to zero, nor do we have
+  // operations depend on the ordering of decrementing the counter. relaxed is enough.
+  _LIBCPP_HIDE_FROM_ABI void __decrement_stop_source_counter() noexcept {
+    _LIBCPP_ASSERT(__state_.load(std::memory_order_relaxed) >= static_cast<__state_t>(1 << __stop_source_counter_shift),
+                   "stop_source's counter is 0. Decrementing the counter will underflow");
+    __state_.fetch_sub(1 << __stop_source_counter_shift, std::memory_order_relaxed);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI bool __stop_requested() const noexcept {
+    // acquire because [thread.stoptoken.intro] A call to request_stop that returns true
+    // synchronizes with a call to stop_requested on an associated stop_token or stop_source
+    // object that returns true.
+    // request_stop's compare_exchange_weak has release which syncs with this acquire
+    return (__state_.load(std::memory_order_acquire) & __stop_requested_bit) != 0;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI bool __stop_possible_for_stop_token() const noexcept {
+    // [stoptoken.mem] false if "a stop request was not made and there are no associated stop_source objects"
+    // Todo: Can this be std::memory_order_relaxed as the standard does not say anything except not to introduce data
+    // race?
+    __state_t __curent_state = __state_.load(std::memory_order_acquire);
+    return ((__curent_state & __stop_requested_bit) != 0) || ((__curent_state >> __stop_source_counter_shift) != 0);
+  }
+
+  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool __request_stop() noexcept {
+    auto __cb_list_lock = __try_lock_for_request_stop();
+    if (!__cb_list_lock.__owns_lock()) {
+      return false;
+    }
+    __requesting_thread_ = this_thread::get_id();
+
+    while (!__callback_list_.__empty()) {
+      auto __cb = __callback_list_.__pop_front();
+
+      // allow other callbacks to be removed while invoking the current callback
+      __cb_list_lock.__unlock();
+
+      bool __destroyed   = false;
+      __cb->__destroyed_ = &__destroyed;
+
+      __cb->__invoke();
+
+      // __cb's invoke function could potentially delete itself. We need to check before accessing __cb's member
+      if (!__destroyed) {
+        // needs to set __destroyed_ pointer to nullptr, otherwise it points to a local variable
+        // which is to be destroyed at the end of the loop
+        __cb->__destroyed_ = nullptr;
+
+        // [stopcallback.cons] If callback is concurrently executing on another thread, then the return
+        // from the invocation of callback strongly happens before ([intro.races]) callback is destroyed.
+        // this release syncs with the acquire in the remove_callback
+        __cb->__completed_.store(true, std::memory_order_release);
+        __cb->__completed_.notify_all();
+      }
+
+      __cb_list_lock.__lock();
+    }
+
+    return true;
+  }
+
+  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool __add_callback(__stop_callback_base* __cb) noexcept {
+    // If it is already stop_requested. Do not try to request it again.
+    const auto __give_up_trying_to_lock_condition = [__cb](__state_t __state) {
+      if ((__state & __stop_requested_bit) != 0) {
+        // already stop requested, synchronously run the callback and no need to lock the list again
+        __cb->__invoke();
+        return true;
+      }
+      // no stop source. no need to lock the list to add the callback as it can never be invoked
+      return (__state >> __stop_source_counter_shift) == 0;
+    };
+
+    __callback_list_lock __cb_list_lock(__state_, __give_up_trying_to_lock_condition);
+
+    if (!__cb_list_lock.__owns_lock()) {
+      return false;
+    }
+
+    __callback_list_.__push_front(__cb);
+
+    return true;
+    // unlock here: [thread.stoptoken.intro] Registration of a callback synchronizes with the invocation of
+    // that callback.
+    // Note: this release sync with the acquire in the request_stop' __try_lock_for_request_stop
+  }
+
+  // called by the destructor of stop_callback
+  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void __remove_callback(__stop_callback_base* __cb) noexcept {
+    __callback_list_lock __cb_list_lock(__state_);
+
+    // under below condition, the request_stop call just popped __cb from the list and could execute it now
+    bool __potentially_executing_now = __cb->__prev_ == nullptr && !__callback_list_.__is_head(__cb);
+
+    if (__potentially_executing_now) {
+      auto __requested_thread = __requesting_thread_;
+      __cb_list_lock.__unlock();
+
+      if (std::this_thread::get_id() != __requested_thread) {
+        // [stopcallback.cons] If callback is concurrently executing on another thread, then the return
+        // from the invocation of callback strongly happens before ([intro.races]) callback is destroyed.
+        __cb->__completed_.wait(false, std::memory_order_acquire);
+      } else {
+        // The destructor of stop_callback runs on the same thread of the thread that invokes the callback.
+        // The callback is potentially invoking its own destuctor. Set the flag to avoid accessing destroyed
+        // members on the invoking side
+        if (__cb->__destroyed_) {
+          *__cb->__destroyed_ = true;
+        }
+      }
+    } else {
+      __callback_list_.__remove(__cb);
+    }
+  }
+
+private:
+  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI __callback_list_lock __try_lock_for_request_stop() noexcept {
+    // If it is already stop_requested, do not try to request stop or lock the list again.
+    const auto __lock_fail_condition = [](__state_t __state) { return (__state & __stop_requested_bit) != 0; };
+
+    // set locked and requested bit at the same time
+    const auto __after_lock_state = [](__state_t __state) {
+      return __state | __callback_list_locked_bit | __stop_requested_bit;
+    };
+
+    // acq because [thread.stoptoken.intro] Registration of a callback synchronizes with the invocation of that
+    //     callback. We are going to invoke the callback after getting the lock, acquire so that we can see the
+    //     registration of a callback (and other writes that happens-before the add_callback)
+    //     Note: the rel (unlock) in the add_callback syncs with this acq
+    // rel because [thread.stoptoken.intro] A call to request_stop that returns true synchronizes with a call
+    //     to stop_requested on an associated stop_token or stop_source object that returns true.
+    //     We need to make sure that all writes (including user code) before request_stop will be made visible
+    //     to the threads that waiting for `stop_requested == true`
+    //     Note: this rel syncs with the acq in `stop_requested`
+    const auto __locked_ordering = std::memory_order_acq_rel;
+
+    return __callback_list_lock(__state_, __lock_fail_condition, __after_lock_state, __locked_ordering);
+  }
+
+  template <class _Tp>
+  friend struct __intrusive_shared_ptr_traits;
+};
+
+template <class _Tp>
+struct __intrusive_shared_ptr_traits;
+
+template <>
+struct __intrusive_shared_ptr_traits<__stop_state> {
+  _LIBCPP_HIDE_FROM_ABI static atomic<uint32_t>& __get_atomic_ref_count(__stop_state& __state) {
+    return __state.__ref_count_;
+  }
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___STOP_TOKEN_STOP_STATE_H
diff --git a/include/__stop_token/stop_token.h b/include/__stop_token/stop_token.h
new file mode 100644
index 0000000..55ce5be
--- /dev/null
+++ b/include/__stop_token/stop_token.h
@@ -0,0 +1,64 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___STOP_TOKEN_STOP_TOKEN_H
+#define _LIBCPP___STOP_TOKEN_STOP_TOKEN_H
+
+#include <__availability>
+#include <__config>
+#include <__stop_token/intrusive_shared_ptr.h>
+#include <__stop_token/stop_state.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+class _LIBCPP_AVAILABILITY_SYNC stop_token {
+public:
+  _LIBCPP_HIDE_FROM_ABI stop_token() noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI stop_token(const stop_token&) noexcept            = default;
+  _LIBCPP_HIDE_FROM_ABI stop_token(stop_token&&) noexcept                 = default;
+  _LIBCPP_HIDE_FROM_ABI stop_token& operator=(const stop_token&) noexcept = default;
+  _LIBCPP_HIDE_FROM_ABI stop_token& operator=(stop_token&&) noexcept      = default;
+  _LIBCPP_HIDE_FROM_ABI ~stop_token()                                     = default;
+
+  _LIBCPP_HIDE_FROM_ABI void swap(stop_token& __other) noexcept { __state_.swap(__other.__state_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_requested() const noexcept {
+    return __state_ != nullptr && __state_->__stop_requested();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool stop_possible() const noexcept {
+    return __state_ != nullptr && __state_->__stop_possible_for_stop_token();
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const stop_token&, const stop_token&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI friend void swap(stop_token& __lhs, stop_token& __rhs) noexcept { __lhs.swap(__rhs); }
+
+private:
+  __intrusive_shared_ptr<__stop_state> __state_;
+
+  friend class stop_source;
+  template <class _Tp>
+  friend class stop_callback;
+
+  _LIBCPP_HIDE_FROM_ABI explicit stop_token(const __intrusive_shared_ptr<__stop_state>& __state) : __state_(__state) {}
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___STOP_TOKEN_STOP_TOKEN_H
diff --git a/include/module.modulemap.in b/include/module.modulemap.in
index 38216f4..5e390d6 100644
--- a/include/module.modulemap.in
+++ b/include/module.modulemap.in
@@ -1462,11 +1462,17 @@
     export *
   }
   module stop_token {
+    @requires_LIBCXX_ENABLE_THREADS@
+    header "stop_token"
     export *
     module __stop_token {
       module atomic_unique_lock   { private header "__stop_token/atomic_unique_lock.h" }
       module intrusive_list_view  { private header "__stop_token/intrusive_list_view.h" }
       module intrusive_shared_ptr { private header "__stop_token/intrusive_shared_ptr.h" }
+      module stop_callback        { private header "__stop_token/stop_callback.h" }
+      module stop_source          { private header "__stop_token/stop_source.h" }
+      module stop_state           { private header "__stop_token/stop_state.h" }
+      module stop_token           { private header "__stop_token/stop_token.h" }
     }
   }
   module streambuf {
diff --git a/include/stop_token b/include/stop_token
new file mode 100644
index 0000000..dd43ac2
--- /dev/null
+++ b/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 Callback>
+    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 <version>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+#  error "<stop_token> is not supported since libc++ has been configured without support for threads."
+#endif
+
+#endif // _LIBCPP_STOP_TOKEN
diff --git a/test/libcxx/transitive_includes.sh.cpp b/test/libcxx/transitive_includes.sh.cpp
index 5ab2558..7a247be 100644
--- a/test/libcxx/transitive_includes.sh.cpp
+++ b/test/libcxx/transitive_includes.sh.cpp
@@ -409,136 +409,140 @@
 #if defined(TEST_97)
 #include <stdexcept>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_101 > /dev/null 2> %t/header.streambuf
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_101 > /dev/null 2> %t/header.stop_token
 #if defined(TEST_101)
+#include <stop_token>
+#endif
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_102 > /dev/null 2> %t/header.streambuf
+#if defined(TEST_102)
 #include <streambuf>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_102 > /dev/null 2> %t/header.string
-#if defined(TEST_102)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_103 > /dev/null 2> %t/header.string
+#if defined(TEST_103)
 #include <string>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_104 > /dev/null 2> %t/header.string_view
-#if defined(TEST_104)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_105 > /dev/null 2> %t/header.string_view
+#if defined(TEST_105)
 #include <string_view>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_105 > /dev/null 2> %t/header.strstream
-#if defined(TEST_105)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_106 > /dev/null 2> %t/header.strstream
+#if defined(TEST_106)
 #include <strstream>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_106 > /dev/null 2> %t/header.system_error
-#if defined(TEST_106)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_107 > /dev/null 2> %t/header.system_error
+#if defined(TEST_107)
 #include <system_error>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_108 > /dev/null 2> %t/header.thread
-#if defined(TEST_108)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_109 > /dev/null 2> %t/header.thread
+#if defined(TEST_109)
 #include <thread>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_109 > /dev/null 2> %t/header.tuple
-#if defined(TEST_109)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_110 > /dev/null 2> %t/header.tuple
+#if defined(TEST_110)
 #include <tuple>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_110 > /dev/null 2> %t/header.type_traits
-#if defined(TEST_110)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_111 > /dev/null 2> %t/header.type_traits
+#if defined(TEST_111)
 #include <type_traits>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_111 > /dev/null 2> %t/header.typeindex
-#if defined(TEST_111)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_112 > /dev/null 2> %t/header.typeindex
+#if defined(TEST_112)
 #include <typeindex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_112 > /dev/null 2> %t/header.typeinfo
-#if defined(TEST_112)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_113 > /dev/null 2> %t/header.typeinfo
+#if defined(TEST_113)
 #include <typeinfo>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_114 > /dev/null 2> %t/header.unordered_map
-#if defined(TEST_114)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_115 > /dev/null 2> %t/header.unordered_map
+#if defined(TEST_115)
 #include <unordered_map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_115 > /dev/null 2> %t/header.unordered_set
-#if defined(TEST_115)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_116 > /dev/null 2> %t/header.unordered_set
+#if defined(TEST_116)
 #include <unordered_set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_116 > /dev/null 2> %t/header.utility
-#if defined(TEST_116)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_117 > /dev/null 2> %t/header.utility
+#if defined(TEST_117)
 #include <utility>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_117 > /dev/null 2> %t/header.valarray
-#if defined(TEST_117)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_118 > /dev/null 2> %t/header.valarray
+#if defined(TEST_118)
 #include <valarray>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_118 > /dev/null 2> %t/header.variant
-#if defined(TEST_118)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_119 > /dev/null 2> %t/header.variant
+#if defined(TEST_119)
 #include <variant>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_119 > /dev/null 2> %t/header.vector
-#if defined(TEST_119)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_120 > /dev/null 2> %t/header.vector
+#if defined(TEST_120)
 #include <vector>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_120 > /dev/null 2> %t/header.version
-#if defined(TEST_120)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_121 > /dev/null 2> %t/header.version
+#if defined(TEST_121)
 #include <version>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_123 > /dev/null 2> %t/header.experimental_deque
-#if defined(TEST_123)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_124 > /dev/null 2> %t/header.experimental_deque
+#if defined(TEST_124)
 #include <experimental/deque>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_124 > /dev/null 2> %t/header.experimental_forward_list
-#if defined(TEST_124)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_125 > /dev/null 2> %t/header.experimental_forward_list
+#if defined(TEST_125)
 #include <experimental/forward_list>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_125 > /dev/null 2> %t/header.experimental_iterator
-#if defined(TEST_125)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_126 > /dev/null 2> %t/header.experimental_iterator
+#if defined(TEST_126)
 #include <experimental/iterator>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_126 > /dev/null 2> %t/header.experimental_list
-#if defined(TEST_126)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_127 > /dev/null 2> %t/header.experimental_list
+#if defined(TEST_127)
 #include <experimental/list>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_127 > /dev/null 2> %t/header.experimental_map
-#if defined(TEST_127)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_128 > /dev/null 2> %t/header.experimental_map
+#if defined(TEST_128)
 #include <experimental/map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_128 > /dev/null 2> %t/header.experimental_memory_resource
-#if defined(TEST_128)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_129 > /dev/null 2> %t/header.experimental_memory_resource
+#if defined(TEST_129)
 #include <experimental/memory_resource>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_129 > /dev/null 2> %t/header.experimental_propagate_const
-#if defined(TEST_129)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_130 > /dev/null 2> %t/header.experimental_propagate_const
+#if defined(TEST_130)
 #include <experimental/propagate_const>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_130 > /dev/null 2> %t/header.experimental_regex
-#if defined(TEST_130)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_131 > /dev/null 2> %t/header.experimental_regex
+#if defined(TEST_131)
 #include <experimental/regex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_131 > /dev/null 2> %t/header.experimental_set
-#if defined(TEST_131)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_132 > /dev/null 2> %t/header.experimental_set
+#if defined(TEST_132)
 #include <experimental/set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_132 > /dev/null 2> %t/header.experimental_simd
-#if defined(TEST_132)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_133 > /dev/null 2> %t/header.experimental_simd
+#if defined(TEST_133)
 #include <experimental/simd>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_133 > /dev/null 2> %t/header.experimental_string
-#if defined(TEST_133)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_134 > /dev/null 2> %t/header.experimental_string
+#if defined(TEST_134)
 #include <experimental/string>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_134 > /dev/null 2> %t/header.experimental_type_traits
-#if defined(TEST_134)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_135 > /dev/null 2> %t/header.experimental_type_traits
+#if defined(TEST_135)
 #include <experimental/type_traits>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_135 > /dev/null 2> %t/header.experimental_unordered_map
-#if defined(TEST_135)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_136 > /dev/null 2> %t/header.experimental_unordered_map
+#if defined(TEST_136)
 #include <experimental/unordered_map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_136 > /dev/null 2> %t/header.experimental_unordered_set
-#if defined(TEST_136)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_137 > /dev/null 2> %t/header.experimental_unordered_set
+#if defined(TEST_137)
 #include <experimental/unordered_set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_137 > /dev/null 2> %t/header.experimental_utility
-#if defined(TEST_137)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_138 > /dev/null 2> %t/header.experimental_utility
+#if defined(TEST_138)
 #include <experimental/utility>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_138 > /dev/null 2> %t/header.experimental_vector
-#if defined(TEST_138)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_139 > /dev/null 2> %t/header.experimental_vector
+#if defined(TEST_139)
 #include <experimental/vector>
 #endif
 // RUN: %{python} %S/transitive_includes_to_csv.py %t > %t/transitive_includes.csv
diff --git a/test/libcxx/transitive_includes/cxx03.csv b/test/libcxx/transitive_includes/cxx03.csv
index 117221e..61928bd 100644
--- a/test/libcxx/transitive_includes/cxx03.csv
+++ b/test/libcxx/transitive_includes/cxx03.csv
@@ -782,6 +782,11 @@
 stdexcept cstdlib
 stdexcept exception
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/libcxx/transitive_includes/cxx11.csv b/test/libcxx/transitive_includes/cxx11.csv
index 55f7699..d9e60a4 100644
--- a/test/libcxx/transitive_includes/cxx11.csv
+++ b/test/libcxx/transitive_includes/cxx11.csv
@@ -783,6 +783,11 @@
 stdexcept cstdlib
 stdexcept exception
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/libcxx/transitive_includes/cxx14.csv b/test/libcxx/transitive_includes/cxx14.csv
index b44b2c5..e1f632e 100644
--- a/test/libcxx/transitive_includes/cxx14.csv
+++ b/test/libcxx/transitive_includes/cxx14.csv
@@ -785,6 +785,11 @@
 stdexcept cstdlib
 stdexcept exception
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/libcxx/transitive_includes/cxx17.csv b/test/libcxx/transitive_includes/cxx17.csv
index b44b2c5..e1f632e 100644
--- a/test/libcxx/transitive_includes/cxx17.csv
+++ b/test/libcxx/transitive_includes/cxx17.csv
@@ -785,6 +785,11 @@
 stdexcept cstdlib
 stdexcept exception
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/libcxx/transitive_includes/cxx20.csv b/test/libcxx/transitive_includes/cxx20.csv
index 2ecf934..34171b0 100644
--- a/test/libcxx/transitive_includes/cxx20.csv
+++ b/test/libcxx/transitive_includes/cxx20.csv
@@ -791,6 +791,11 @@
 stdexcept cstdlib
 stdexcept exception
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/libcxx/transitive_includes/cxx23.csv b/test/libcxx/transitive_includes/cxx23.csv
index 776207f..eefe23e 100644
--- a/test/libcxx/transitive_includes/cxx23.csv
+++ b/test/libcxx/transitive_includes/cxx23.csv
@@ -533,6 +533,11 @@
 stack initializer_list
 stack version
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/libcxx/transitive_includes/cxx26.csv b/test/libcxx/transitive_includes/cxx26.csv
index 776207f..eefe23e 100644
--- a/test/libcxx/transitive_includes/cxx26.csv
+++ b/test/libcxx/transitive_includes/cxx26.csv
@@ -533,6 +533,11 @@
 stack initializer_list
 stack version
 stdexcept iosfwd
+stop_token atomic
+stop_token cstddef
+stop_token limits
+stop_token thread
+stop_token version
 streambuf cstdint
 streambuf ios
 streambuf iosfwd
diff --git a/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp b/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp
new file mode 100644
index 0000000..ad5fd94
--- /dev/null
+++ b/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+//
+// clang-format off
+
+// UNSUPPORTED: no-threads
+
+// <stop_token>
+
+// Test the feature test macros defined by <stop_token>
+
+/*  Constant             Value
+    __cpp_lib_jthread    201911L [C++20]
+*/
+
+#include <stop_token>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+# ifdef __cpp_lib_jthread
+#   error "__cpp_lib_jthread should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 14
+
+# ifdef __cpp_lib_jthread
+#   error "__cpp_lib_jthread should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifdef __cpp_lib_jthread
+#   error "__cpp_lib_jthread should not be defined before c++20"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_jthread
+#     error "__cpp_lib_jthread should be defined in c++20"
+#   endif
+#   if __cpp_lib_jthread != 201911L
+#     error "__cpp_lib_jthread should have the value 201911L in c++20"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_jthread
+#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
+#elif TEST_STD_VER == 23
+
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_jthread
+#     error "__cpp_lib_jthread should be defined in c++23"
+#   endif
+#   if __cpp_lib_jthread != 201911L
+#     error "__cpp_lib_jthread should have the value 201911L in c++23"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_jthread
+#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
+#elif TEST_STD_VER > 23
+
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_jthread
+#     error "__cpp_lib_jthread should be defined in c++26"
+#   endif
+#   if __cpp_lib_jthread != 201911L
+#     error "__cpp_lib_jthread should have the value 201911L in c++26"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_jthread
+#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
+#endif // TEST_STD_VER > 23
+
diff --git a/test/std/thread/thread.stoptoken/nostopstate/cons.default.pass.cpp b/test/std/thread/thread.stoptoken/nostopstate/cons.default.pass.cpp
new file mode 100644
index 0000000..400fb35
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/nostopstate/cons.default.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// struct nostopstate_t {
+//   explicit nostopstate_t() = default;
+// };
+//
+// inline constexpr nostopstate_t nostopstate{};
+
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_trivially_default_constructible_v<std::nostopstate_t>);
+
+struct Empty {};
+static_assert(sizeof(Empty) == sizeof(std::nostopstate_t));
+
+template <class T>
+void conversionTest(T);
+
+template <class T>
+concept ImplicitlyDefaultConstructible = requires { conversionTest<T>({}); };
+static_assert(!ImplicitlyDefaultConstructible<std::nostopstate_t>);
+
+int main(int, char**) {
+  [[maybe_unused]] std::same_as<std::nostopstate_t> auto x = std::nostopstate;
+  [[maybe_unused]] auto y                                  = std::nostopstate_t{};
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stopcallback/cons.const.token.pass.cpp b/test/std/thread/thread.stoptoken/stopcallback/cons.const.token.pass.cpp
new file mode 100644
index 0000000..9fdfdb8
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopcallback/cons.const.token.pass.cpp
@@ -0,0 +1,236 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// template<class C>
+// explicit stop_callback(const stop_token& st, C&& cb)
+//   noexcept(is_nothrow_constructible_v<Callback, C>);
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+struct Cb {
+  void operator()() const;
+};
+
+// Constraints: Callback and C satisfy constructible_from<Callback, C>.
+static_assert(std::is_constructible_v<std::stop_callback<void (*)()>, const std::stop_token&, void (*)()>);
+static_assert(!std::is_constructible_v<std::stop_callback<void (*)()>, const std::stop_token&, void (*)(int)>);
+static_assert(std::is_constructible_v<std::stop_callback<Cb>, const std::stop_token&, Cb&>);
+static_assert(std::is_constructible_v<std::stop_callback<Cb&>, const std::stop_token&, Cb&>);
+static_assert(!std::is_constructible_v<std::stop_callback<Cb>, const std::stop_token&, int>);
+
+// explicit
+template <class T>
+void conversion_test(T);
+
+template <class T, class... Args>
+concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
+static_assert(ImplicitlyConstructible<int, int>);
+static_assert(!ImplicitlyConstructible<std::stop_callback<Cb>, const std::stop_token&, Cb>);
+
+// noexcept
+template <bool NoExceptCtor>
+struct CbNoExcept {
+  CbNoExcept(int) noexcept(NoExceptCtor);
+  void operator()() const;
+};
+static_assert(std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<true>>, const std::stop_token&, int>);
+static_assert(!std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<false>>, const std::stop_token&, int>);
+
+int main(int, char**) {
+  // was requested
+  {
+    std::stop_source ss;
+    const std::stop_token st = ss.get_token();
+    ss.request_stop();
+
+    bool called = false;
+    std::stop_callback sc(st, [&] { called = true; });
+    assert(called);
+  }
+
+  // was not requested
+  {
+    std::stop_source ss;
+    const std::stop_token st = ss.get_token();
+
+    bool called = false;
+    std::stop_callback sc(st, [&] { called = true; });
+    assert(!called);
+
+    ss.request_stop();
+    assert(called);
+  }
+
+  // token has no state
+  {
+    std::stop_token st;
+    bool called = false;
+    std::stop_callback sc(st, [&] { called = true; });
+    assert(!called);
+  }
+
+  // should not be called multiple times
+  {
+    std::stop_source ss;
+    const std::stop_token st = ss.get_token();
+
+    int calledTimes = 0;
+    std::stop_callback sc(st, [&] { ++calledTimes; });
+
+    std::vector<std::thread> threads;
+    for (auto i = 0; i < 10; ++i) {
+      threads.emplace_back(support::make_test_thread([&] { ss.request_stop(); }));
+    }
+
+    for (auto& thread : threads) {
+      thread.join();
+    }
+    assert(calledTimes == 1);
+  }
+
+  // adding more callbacks during invoking other callbacks
+  {
+    std::stop_source ss;
+    const std::stop_token st = ss.get_token();
+
+    std::atomic<bool> startedFlag = false;
+    std::atomic<bool> finishFlag  = false;
+    std::stop_callback sc(st, [&] {
+      startedFlag = true;
+      startedFlag.notify_all();
+      finishFlag.wait(false);
+    });
+
+    auto thread = support::make_test_thread([&] { ss.request_stop(); });
+
+    startedFlag.wait(false);
+
+    // first callback is still running, adding another one;
+    bool secondCallbackCalled = false;
+    std::stop_callback sc2(st, [&] { secondCallbackCalled = true; });
+
+    finishFlag = true;
+    finishFlag.notify_all();
+
+    thread.join();
+    assert(secondCallbackCalled);
+  }
+
+  // adding callbacks on different threads
+  {
+    std::stop_source ss;
+    const std::stop_token st = ss.get_token();
+
+    std::vector<std::thread> threads;
+    std::atomic<int> callbackCalledTimes = 0;
+    std::atomic<bool> done               = false;
+    for (auto i = 0; i < 10; ++i) {
+      threads.emplace_back(support::make_test_thread([&] {
+        std::stop_callback sc{st, [&] { callbackCalledTimes.fetch_add(1, std::memory_order_relaxed); }};
+        done.wait(false);
+      }));
+    }
+    using namespace std::chrono_literals;
+    std::this_thread::sleep_for(1ms);
+    ss.request_stop();
+    done = true;
+    done.notify_all();
+    for (auto& thread : threads) {
+      thread.join();
+    }
+    assert(callbackCalledTimes.load(std::memory_order_relaxed) == 10);
+  }
+
+  // correct overload
+  {
+    struct CBWithTracking {
+      bool& lvalueCalled;
+      bool& lvalueConstCalled;
+      bool& rvalueCalled;
+      bool& rvalueConstCalled;
+
+      void operator()() & { lvalueCalled = true; }
+      void operator()() const& { lvalueConstCalled = true; }
+      void operator()() && { rvalueCalled = true; }
+      void operator()() const&& { rvalueConstCalled = true; }
+    };
+
+    // RValue
+    {
+      bool lvalueCalled      = false;
+      bool lvalueConstCalled = false;
+      bool rvalueCalled      = false;
+      bool rvalueConstCalled = false;
+      std::stop_source ss;
+      const std::stop_token st = ss.get_token();
+      ss.request_stop();
+
+      std::stop_callback<CBWithTracking> sc(
+          st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled});
+      assert(rvalueCalled);
+    }
+
+    // RValue
+    {
+      bool lvalueCalled      = false;
+      bool lvalueConstCalled = false;
+      bool rvalueCalled      = false;
+      bool rvalueConstCalled = false;
+      std::stop_source ss;
+      const std::stop_token st = ss.get_token();
+      ss.request_stop();
+
+      std::stop_callback<const CBWithTracking> sc(
+          st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled});
+      assert(rvalueConstCalled);
+    }
+
+    // LValue
+    {
+      bool lvalueCalled      = false;
+      bool lvalueConstCalled = false;
+      bool rvalueCalled      = false;
+      bool rvalueConstCalled = false;
+      std::stop_source ss;
+      const std::stop_token st = ss.get_token();
+      ss.request_stop();
+      CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled};
+      std::stop_callback<CBWithTracking&> sc(st, cb);
+      assert(lvalueCalled);
+    }
+
+    // const LValue
+    {
+      bool lvalueCalled      = false;
+      bool lvalueConstCalled = false;
+      bool rvalueCalled      = false;
+      bool rvalueConstCalled = false;
+      std::stop_source ss;
+      const std::stop_token st = ss.get_token();
+      ss.request_stop();
+      CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled};
+      std::stop_callback<const CBWithTracking&> sc(st, cb);
+      assert(lvalueConstCalled);
+    }
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp b/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp
new file mode 100644
index 0000000..49c97db
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp
@@ -0,0 +1,227 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// template<class C>
+// explicit stop_callback(stop_token&& st, C&& cb)
+//   noexcept(is_nothrow_constructible_v<Callback, C>);
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+struct Cb {
+  void operator()() const;
+};
+
+// Constraints: Callback and C satisfy constructible_from<Callback, C>.
+static_assert(std::is_constructible_v<std::stop_callback<void (*)()>, std::stop_token&&, void (*)()>);
+static_assert(!std::is_constructible_v<std::stop_callback<void (*)()>, std::stop_token&&, void (*)(int)>);
+static_assert(std::is_constructible_v<std::stop_callback<Cb>, std::stop_token&&, Cb&>);
+static_assert(std::is_constructible_v<std::stop_callback<Cb&>, std::stop_token&&, Cb&>);
+static_assert(!std::is_constructible_v<std::stop_callback<Cb>, std::stop_token&&, int>);
+
+// explicit
+template <class T>
+void conversion_test(T);
+
+template <class T, class... Args>
+concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
+static_assert(ImplicitlyConstructible<int, int>);
+static_assert(!ImplicitlyConstructible<std::stop_callback<Cb>, std::stop_token&&, Cb>);
+
+// noexcept
+template <bool NoExceptCtor>
+struct CbNoExcept {
+  CbNoExcept(int) noexcept(NoExceptCtor);
+  void operator()() const;
+};
+static_assert(std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<true>>, std::stop_token&&, int>);
+static_assert(!std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<false>>, 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<std::thread> 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<bool> startedFlag = false;
+    std::atomic<bool> 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<std::thread> threads;
+    std::atomic<int> callbackCalledTimes = 0;
+    std::atomic<bool> 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<CBWithTracking> 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<const CBWithTracking> 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<CBWithTracking&> 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<const CBWithTracking&> sc(ss.get_token(), cb);
+      assert(lvalueConstCalled);
+    }
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stopcallback/copy.move.compile.pass.cpp b/test/std/thread/thread.stoptoken/stopcallback/copy.move.compile.pass.cpp
new file mode 100644
index 0000000..2e66b64
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopcallback/copy.move.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+//  stop_callback(const stop_callback&) = delete;
+//  stop_callback(stop_callback&&) = delete;
+//  stop_callback& operator=(const stop_callback&) = delete;
+//  stop_callback& operator=(stop_callback&&) = delete;
+
+#include <stop_token>
+#include <type_traits>
+
+struct Callback {
+  void operator()() const;
+};
+
+static_assert(!std::is_copy_constructible_v<std::stop_callback<Callback>>);
+static_assert(!std::is_move_constructible_v<std::stop_callback<Callback>>);
+static_assert(!std::is_copy_assignable_v<std::stop_callback<Callback>>);
+static_assert(!std::is_move_assignable_v<std::stop_callback<Callback>>);
diff --git a/test/std/thread/thread.stoptoken/stopcallback/ctad.compile.pass.cpp b/test/std/thread/thread.stoptoken/stopcallback/ctad.compile.pass.cpp
new file mode 100644
index 0000000..9c1d4fe
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopcallback/ctad.compile.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+//   template<class Callback>
+//   stop_callback(stop_token, Callback) -> stop_callback<Callback>;
+
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+
+void test() {
+  std::stop_token st;
+  auto a = [] {};
+  static_assert(std::is_same_v<decltype(std::stop_callback(st, a)), std::stop_callback<decltype(a)>>);
+  static_assert(std::is_same_v<decltype(std::stop_callback(st, std::as_const(a))), std::stop_callback<decltype(a)>>);
+  static_assert(std::is_same_v<decltype(std::stop_callback(st, std::move(a))), std::stop_callback<decltype(a)>>);
+  static_assert(
+      std::is_same_v<decltype(std::stop_callback(st, std::move(std::as_const(a)))), std::stop_callback<decltype(a)>>);
+}
diff --git a/test/std/thread/thread.stoptoken/stopcallback/dtor.pass.cpp b/test/std/thread/thread.stoptoken/stopcallback/dtor.pass.cpp
new file mode 100644
index 0000000..62ef6ff
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopcallback/dtor.pass.cpp
@@ -0,0 +1,166 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// ~stop_callback();
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <functional>
+#include <optional>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+struct CallbackHolder;
+
+struct DeleteHolder {
+  CallbackHolder& holder_;
+  void operator()() const;
+};
+
+struct CallbackHolder {
+  std::unique_ptr<std::stop_callback<DeleteHolder>> callback_;
+};
+
+void DeleteHolder::operator()() const { holder_.callback_.reset(); }
+
+int main(int, char**) {
+  // Unregisters the callback from the owned stop state, if any
+  {
+    std::stop_source ss;
+    bool called = false;
+
+    {
+      std::stop_callback sc(ss.get_token(), [&] { called = true; });
+    }
+    ss.request_stop();
+    assert(!called);
+  }
+
+  // The destructor does not block waiting for the execution of another
+  // callback registered by an associated stop_callback.
+  {
+    std::stop_source ss;
+
+    std::atomic<int> startedIndex    = 0;
+    std::atomic<bool> callbackFinish = false;
+
+    std::optional<std::stop_callback<std::function<void()>>> sc1(std::in_place, ss.get_token(), [&] {
+      startedIndex = 1;
+      startedIndex.notify_all();
+      callbackFinish.wait(false);
+    });
+
+    std::optional<std::stop_callback<std::function<void()>>> 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<bool>& started_;
+      std::atomic<bool>& waitDone_;
+      std::atomic<bool>& finished_;
+      bool moved = false;
+
+      Callback(std::atomic<bool>& started, std::atomic<bool>& waitDone, std::atomic<bool>& 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<bool>& 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<bool> started  = false;
+    std::atomic<bool> waitDone = false;
+    std::atomic<bool> finished = false;
+
+    std::optional<std::stop_callback<Callback>> sc{
+        std::in_place, ss.get_token(), Callback{started, waitDone, finished}};
+
+    auto thread1 = support::make_test_thread([&] { ss.request_stop(); });
+    started.wait(false);
+
+    auto thread2 = support::make_test_thread([&] {
+      using namespace std::chrono_literals;
+      std::this_thread::sleep_for(1ms);
+      waitDone = true;
+      waitDone.notify_all();
+    });
+
+    sc.reset(); // destructor should block until operator() returns, i.e. waitDone to be true
+
+    thread1.join();
+    thread2.join();
+  }
+
+  // If callback is executing on the current thread, then the destructor does not block ([defns.block])
+  // waiting for the return from the invocation of callback.
+  {
+    std::stop_source ss;
+
+    CallbackHolder holder;
+    holder.callback_ = std::make_unique<std::stop_callback<DeleteHolder>>(ss.get_token(), DeleteHolder{holder});
+
+    assert(holder.callback_ != nullptr);
+
+    ss.request_stop(); // the callbacks deletes itself. if the destructor blocks, it would be deadlock
+    assert(holder.callback_ == nullptr);
+  }
+}
diff --git a/test/std/thread/thread.stoptoken/stopcallback/typedef.compile.pass.cpp b/test/std/thread/thread.stoptoken/stopcallback/typedef.compile.pass.cpp
new file mode 100644
index 0000000..d60bad9
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopcallback/typedef.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// using callback_type = _Callback;
+
+#include <stop_token>
+#include <type_traits>
+
+struct Callback {
+  void operator()() const;
+};
+
+static_assert(std::is_same_v<std::stop_callback<Callback>::callback_type, Callback>);
+static_assert(std::is_same_v<std::stop_callback<const Callback>::callback_type, const Callback>);
+static_assert(std::is_same_v<std::stop_callback<Callback&>::callback_type, Callback&>);
+static_assert(std::is_same_v<std::stop_callback<const Callback&>::callback_type, const Callback&>);
+static_assert(std::is_same_v<std::stop_callback<Callback&&>::callback_type, Callback&&>);
+static_assert(std::is_same_v<std::stop_callback<const Callback&&>::callback_type, const Callback&&>);
diff --git a/test/std/thread/thread.stoptoken/stopsource/assign.copy.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/assign.copy.pass.cpp
new file mode 100644
index 0000000..0684899
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/assign.copy.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_source& operator=(const stop_source& rhs) noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_copy_assignable_v<std::stop_source>);
+
+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<std::stop_source&> 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<std::stop_source&> 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<std::stop_source&> 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<std::stop_source&> 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<std::stop_source&> decltype(auto) ref = ss = self;
+    assert(&ref == &ss);
+
+    assert(!ss.stop_requested());
+
+    ss.request_stop();
+    assert(ss.stop_requested());
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stopsource/cons.copy.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/cons.copy.pass.cpp
new file mode 100644
index 0000000..b130e41
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/cons.copy.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_source(const stop_source&) noexcept;
+
+#include <cassert>
+#include <optional>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_copy_constructible_v<std::stop_source>);
+
+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<std::stop_source> source(std::in_place);
+    auto st = source->get_token();
+    assert(st.stop_possible());
+
+    std::optional<std::stop_source> 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/test/std/thread/thread.stoptoken/stopsource/cons.default.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/cons.default.pass.cpp
new file mode 100644
index 0000000..1dba0b6
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/cons.default.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_source();
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_default_constructible_v<std::stop_source>);
+
+int main(int, char**) {
+  {
+    std::stop_source ss = {}; // implicit
+    assert(ss.stop_possible());
+    assert(!ss.stop_requested());
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stopsource/cons.move.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/cons.move.pass.cpp
new file mode 100644
index 0000000..3eb73ce
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/cons.move.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_source(stop_source&&) noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_move_constructible_v<std::stop_source>);
+
+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/test/std/thread/thread.stoptoken/stopsource/cons.nostopstate.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/cons.nostopstate.pass.cpp
new file mode 100644
index 0000000..13067a5
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/cons.nostopstate.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// explicit stop_source(nostopstate_t) noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_constructible_v<std::stop_source, std::nostopstate_t>);
+// explicit
+static_assert(!std::is_convertible_v<std::nostopstate_t, std::stop_source>);
+
+int main(int, char**) {
+  {
+    std::stop_source ss(std::nostopstate);
+    assert(!ss.stop_possible());
+    assert(!ss.stop_requested());
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stopsource/equals.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/equals.pass.cpp
new file mode 100644
index 0000000..4aa3d53
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/equals.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool operator==(const stop_source& lhs, const stop_source& rhs) noexcept;
+// Returns: true if lhs and rhs have ownership of the same stop state or if both lhs and rhs do not have ownership of a stop state; otherwise false.
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T>
+concept IsNoThrowEqualityComparable = requires(const T& t1, const T& t2) {
+  { t1 == t2 } noexcept;
+};
+
+static_assert(IsNoThrowEqualityComparable<std::stop_source>);
+
+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/test/std/thread/thread.stoptoken/stopsource/get_token.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/get_token.pass.cpp
new file mode 100644
index 0000000..8cd5005
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/get_token.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] stop_token get_token() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T>
+concept IsGetTokenNoexcept = requires(const T& t) {
+  { t.get_token() } noexcept;
+};
+
+static_assert(IsGetTokenNoexcept<std::stop_source>);
+
+int main(int, char**) {
+  // no state
+  {
+    std::stop_source ss{std::nostopstate};
+    std::same_as<std::stop_token> decltype(auto) st = ss.get_token();
+    assert(!st.stop_possible());
+    assert(!st.stop_requested());
+  }
+
+  // with state
+  {
+    std::stop_source ss;
+    std::same_as<std::stop_token> 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/test/std/thread/thread.stoptoken/stopsource/move.copy.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/move.copy.pass.cpp
new file mode 100644
index 0000000..09b4796
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/move.copy.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_source& operator=(stop_source&& rhs) noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_move_assignable_v<std::stop_source>);
+
+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<std::stop_source&> 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<std::stop_source&> 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<std::stop_source&> 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<std::stop_source&> 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<std::stop_source&> 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/test/std/thread/thread.stoptoken/stopsource/nodiscard.verify.cpp b/test/std/thread/thread.stoptoken/stopsource/nodiscard.verify.cpp
new file mode 100644
index 0000000..4616f1c
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/nodiscard.verify.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] stop_token get_token() const noexcept;
+// [[nodiscard]] bool stop_possible() const noexcept;
+// [[nodiscard]] bool stop_requested() const noexcept;
+// [[nodiscard]] friend bool operator==(const stop_source& lhs, const stop_source& rhs) noexcept;
+
+#include <stop_token>
+
+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/test/std/thread/thread.stoptoken/stopsource/request_stop.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/request_stop.pass.cpp
new file mode 100644
index 0000000..8f91bea
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/request_stop.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// bool request_stop() noexcept;
+
+#include <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 IsRequestStopNoexcept = requires(T& t) {
+  { t.request_stop() } noexcept;
+};
+
+static_assert(IsRequestStopNoexcept<std::stop_source>);
+
+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/test/std/thread/thread.stoptoken/stopsource/stop_possible.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/stop_possible.pass.cpp
new file mode 100644
index 0000000..da4992f
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/stop_possible.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool stop_possible() const noexcept;
+// Returns: true if *this has ownership of a stop state; otherwise, false.
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T>
+concept IsStopPossibleNoexcept = requires(const T& t) {
+  { t.stop_possible() } noexcept;
+};
+
+static_assert(IsStopPossibleNoexcept<std::stop_source>);
+
+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/test/std/thread/thread.stoptoken/stopsource/stop_requested.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/stop_requested.pass.cpp
new file mode 100644
index 0000000..a33a035
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/stop_requested.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool stop_requested() const noexcept;
+// true if *this has ownership of a stop state that has received a stop request; otherwise, false.
+
+#include <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_source>);
+
+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/test/std/thread/thread.stoptoken/stopsource/swap.free.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/swap.free.pass.cpp
new file mode 100644
index 0000000..097dabf
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/swap.free.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void swap(stop_source& rhs) noexcept;
+
+#include <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_source>);
+
+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/test/std/thread/thread.stoptoken/stopsource/swap.member.pass.cpp b/test/std/thread/thread.stoptoken/stopsource/swap.member.pass.cpp
new file mode 100644
index 0000000..7c724ac
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stopsource/swap.member.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void swap(stop_source& rhs) noexcept;
+
+#include <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_source>);
+
+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/test/std/thread/thread.stoptoken/stoptoken/assign.copy.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/assign.copy.pass.cpp
new file mode 100644
index 0000000..753cf51
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/assign.copy.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_copy_assignable_v<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());
+
+    std::same_as<std::stop_token&> decltype(auto) ref = st1 = st2;
+    assert(&ref == &st1);
+
+    assert(st1 == st2);
+    assert(st1.stop_requested());
+    assert(st2.stop_requested());
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stoptoken/assign.move.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/assign.move.pass.cpp
new file mode 100644
index 0000000..2a94beb
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/assign.move.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_token& operator=(stop_token&& rhs) noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_move_assignable_v<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());
+
+    std::same_as<std::stop_token&> 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/test/std/thread/thread.stoptoken/stoptoken/cons.copy.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/cons.copy.pass.cpp
new file mode 100644
index 0000000..8794f70
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/cons.copy.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_token(const stop_token&) noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_copy_constructible_v<std::stop_token>);
+
+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/test/std/thread/thread.stoptoken/stoptoken/cons.default.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/cons.default.pass.cpp
new file mode 100644
index 0000000..389725b
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/cons.default.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_token() noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_default_constructible_v<std::stop_token>);
+
+int main(int, char**) {
+  {
+    std::stop_token st = {}; // implicit
+    assert(!st.stop_possible());
+    assert(!st.stop_requested());
+  }
+
+  return 0;
+}
diff --git a/test/std/thread/thread.stoptoken/stoptoken/cons.move.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/cons.move.pass.cpp
new file mode 100644
index 0000000..11cedc0
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/cons.move.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// stop_token(stop_token&&) noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_move_constructible_v<std::stop_token>);
+
+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/test/std/thread/thread.stoptoken/stoptoken/equals.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/equals.pass.cpp
new file mode 100644
index 0000000..4009988
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/equals.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool operator==(const stop_token& lhs, const stop_token& rhs) noexcept;
+// Returns: true if lhs and rhs have ownership of the same stop state or if both lhs and rhs do not have ownership of a stop state; otherwise false.
+
+// synthesized operator != also tested.
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// LWG 3254 is related.
+template <class T>
+concept IsNoThrowEqualityComparable = requires(const T& t1, const T& t2) {
+  { t1 == t2 } noexcept;
+};
+
+template <class T>
+concept IsNoThrowInequalityComparable = requires(const T& t1, const T& t2) {
+  { t1 != t2 } noexcept;
+};
+
+static_assert(IsNoThrowEqualityComparable<std::stop_token>);
+static_assert(IsNoThrowInequalityComparable<std::stop_token>);
+
+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/test/std/thread/thread.stoptoken/stoptoken/nodiscard.verify.cpp b/test/std/thread/thread.stoptoken/stoptoken/nodiscard.verify.cpp
new file mode 100644
index 0000000..b62ecdc
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/nodiscard.verify.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+//  [[nodiscard]] bool stop_requested() const noexcept;
+//  [[nodiscard]] bool stop_possible() const noexcept;
+//  [[nodiscard]] friend bool operator==(const stop_token& lhs, const stop_token& rhs) noexcept;
+
+#include <stop_token>
+
+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/test/std/thread/thread.stoptoken/stoptoken/stop_possible.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/stop_possible.pass.cpp
new file mode 100644
index 0000000..daa7f9d
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/stop_possible.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool stop_possible() const noexcept;
+// Returns: false if:
+//    - *this does not have ownership of a stop state, or
+//    - a stop request was not made and there are no associated stop_source objects;
+// otherwise, true.
+
+#include <cassert>
+#include <concepts>
+#include <optional>
+#include <stop_token>
+#include <type_traits>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+template <class T>
+concept IsStopPossibleNoexcept = requires(const T& t) {
+  { t.stop_possible() } noexcept;
+};
+
+static_assert(IsStopPossibleNoexcept<std::stop_token>);
+
+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<std::stop_source> 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<std::stop_source> 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<std::stop_source> 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/test/std/thread/thread.stoptoken/stoptoken/stop_requested.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/stop_requested.pass.cpp
new file mode 100644
index 0000000..acf986e
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/stop_requested.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool stop_requested() const noexcept;
+// Returns: true if *this has ownership of a stop state that has received a stop request; otherwise, false.
+
+#include <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;
+}
diff --git a/test/std/thread/thread.stoptoken/stoptoken/swap.free.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/swap.free.pass.cpp
new file mode 100644
index 0000000..90e95b1
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/swap.free.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// friend void swap(stop_token& x, stop_token& y) noexcept;
+
+#include <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;
+}
diff --git a/test/std/thread/thread.stoptoken/stoptoken/swap.member.pass.cpp b/test/std/thread/thread.stoptoken/stoptoken/swap.member.pass.cpp
new file mode 100644
index 0000000..9819c7d
--- /dev/null
+++ b/test/std/thread/thread.stoptoken/stoptoken/swap.member.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void swap(stop_token& rhs) noexcept;
+
+#include <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;
+}
diff --git a/utils/generate_feature_test_macro_components.py b/utils/generate_feature_test_macro_components.py
index abc833d..e71370a 100755
--- a/utils/generate_feature_test_macro_components.py
+++ b/utils/generate_feature_test_macro_components.py
@@ -1060,6 +1060,7 @@
     "semaphore": ["UNSUPPORTED: no-threads"],
     "shared_mutex": ["UNSUPPORTED: no-threads"],
     "stdatomic.h": ["UNSUPPORTED: no-threads"],
+    "stop_token": ["UNSUPPORTED: no-threads"],
     "thread": ["UNSUPPORTED: no-threads"],
 }
 
diff --git a/utils/libcxx/test/header_information.py b/utils/libcxx/test/header_information.py
index 026ec6a..20e3743 100644
--- a/utils/libcxx/test/header_information.py
+++ b/utils/libcxx/test/header_information.py
@@ -16,6 +16,7 @@
     "semaphore": "!defined(_LIBCPP_HAS_NO_THREADS)",
     "shared_mutex": "!defined(_LIBCPP_HAS_NO_THREADS)",
     "stdatomic.h": "__cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)",
+    "stop_token": "!defined(_LIBCPP_HAS_NO_THREADS)",
     "thread": "!defined(_LIBCPP_HAS_NO_THREADS)",
     "filesystem": "!defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)",
     # TODO(LLVM-17): simplify this to __cplusplus >= 202002L
@@ -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",