[libcxx-commits] [libcxx] 695138c - [libc++] implement std::`jthread`
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Sep 16 11:55:38 PDT 2023
Author: Hui
Date: 2023-09-16T19:54:19+01:00
New Revision: 695138ca8405779c2b7756cc31d887aa54f56bb8
URL: https://github.com/llvm/llvm-project/commit/695138ca8405779c2b7756cc31d887aa54f56bb8
DIFF: https://github.com/llvm/llvm-project/commit/695138ca8405779c2b7756cc31d887aa54f56bb8.diff
LOG: [libc++] implement std::`jthread`
Added:
libcxx/include/__thread/jthread.h
libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp
libcxx/test/std/thread/thread.jthread/cons.default.pass.cpp
libcxx/test/std/thread/thread.jthread/cons.func.token.pass.cpp
libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp
libcxx/test/std/thread/thread.jthread/copy.delete.compile.pass.cpp
libcxx/test/std/thread/thread.jthread/detach.pass.cpp
libcxx/test/std/thread/thread.jthread/dtor.pass.cpp
libcxx/test/std/thread/thread.jthread/get_id.pass.cpp
libcxx/test/std/thread/thread.jthread/get_stop_source.pass.cpp
libcxx/test/std/thread/thread.jthread/get_stop_token.pass.cpp
libcxx/test/std/thread/thread.jthread/hardware_concurrency.pass.cpp
libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
libcxx/test/std/thread/thread.jthread/join.pass.cpp
libcxx/test/std/thread/thread.jthread/joinable.pass.cpp
libcxx/test/std/thread/thread.jthread/nodiscard.verify.cpp
libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp
libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp
libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp
libcxx/test/std/thread/thread.jthread/type.compile.pass.cpp
Modified:
libcxx/docs/UsingLibcxx.rst
libcxx/include/CMakeLists.txt
libcxx/include/__stop_token/atomic_unique_lock.h
libcxx/include/__stop_token/stop_callback.h
libcxx/include/__stop_token/stop_state.h
libcxx/include/module.modulemap.in
libcxx/include/thread
libcxx/modules/std/thread.inc
libcxx/test/libcxx/transitive_includes/cxx03.csv
libcxx/test/libcxx/transitive_includes/cxx11.csv
libcxx/test/libcxx/transitive_includes/cxx14.csv
libcxx/test/libcxx/transitive_includes/cxx17.csv
libcxx/test/libcxx/transitive_includes/cxx20.csv
libcxx/test/libcxx/transitive_includes/cxx23.csv
libcxx/test/libcxx/transitive_includes/cxx26.csv
Removed:
################################################################################
diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index 9db25f12a6a7898..0b517c0f8f7860c 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -48,6 +48,7 @@ when ``-fexperimental-library`` is passed:
* The parallel algorithms library (``<execution>`` and the associated algorithms)
* ``std::stop_token``, ``std::stop_source`` and ``std::stop_callback``
+* ``std::jthread``
* ``std::chrono::tzdb`` and related time zone functionality
* ``std::ranges::join_view``
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 7af9b85b974211f..70db7c721a6158e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -677,6 +677,7 @@ set(files
__system_error/system_error.h
__thread/formatter.h
__thread/id.h
+ __thread/jthread.h
__thread/poll_with_backoff.h
__thread/this_thread.h
__thread/thread.h
diff --git a/libcxx/include/__stop_token/atomic_unique_lock.h b/libcxx/include/__stop_token/atomic_unique_lock.h
index 6c63a254eab9d9a..13e59f9f0dce005 100644
--- a/libcxx/include/__stop_token/atomic_unique_lock.h
+++ b/libcxx/include/__stop_token/atomic_unique_lock.h
@@ -28,7 +28,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// and LockedBit is the value of State when the lock bit is set, e.g 1 << 2
template <class _State, _State _LockedBit>
class _LIBCPP_AVAILABILITY_SYNC __atomic_unique_lock {
- static_assert(std::popcount(_LockedBit) == 1, "LockedBit must be an integer where only one bit is set");
+ static_assert(std::__libcpp_popcount(static_cast<unsigned long long>(_LockedBit)) == 1,
+ "LockedBit must be an integer where only one bit is set");
std::atomic<_State>& __state_;
bool __is_locked_;
diff --git a/libcxx/include/__stop_token/stop_callback.h b/libcxx/include/__stop_token/stop_callback.h
index c9dcad3deb911ea..e52f0350dacc5bb 100644
--- a/libcxx/include/__stop_token/stop_callback.h
+++ b/libcxx/include/__stop_token/stop_callback.h
@@ -26,6 +26,9 @@
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
@@ -96,4 +99,6 @@ _LIBCPP_AVAILABILITY_SYNC stop_callback(stop_token, _Callback) -> stop_callback<
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
diff --git a/libcxx/include/__stop_token/stop_state.h b/libcxx/include/__stop_token/stop_state.h
index 9103bb1961599fe..0e43292faae8819 100644
--- a/libcxx/include/__stop_token/stop_state.h
+++ b/libcxx/include/__stop_token/stop_state.h
@@ -14,9 +14,9 @@
#include <__config>
#include <__stop_token/atomic_unique_lock.h>
#include <__stop_token/intrusive_list_view.h>
+#include <__thread/id.h>
#include <atomic>
#include <cstdint>
-#include <thread>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -63,7 +63,7 @@ class __stop_state {
using __callback_list = __intrusive_list_view<__stop_callback_base>;
__callback_list __callback_list_;
- thread::id __requesting_thread_;
+ __thread_id __requesting_thread_;
public:
_LIBCPP_HIDE_FROM_ABI __stop_state() noexcept = default;
diff --git a/libcxx/include/__thread/jthread.h b/libcxx/include/__thread/jthread.h
new file mode 100644
index 000000000000000..fc86b13afb13431
--- /dev/null
+++ b/libcxx/include/__thread/jthread.h
@@ -0,0 +1,130 @@
+// -*- 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___THREAD_JTHREAD_H
+#define _LIBCPP___THREAD_JTHREAD_H
+
+#include <__availability>
+#include <__config>
+#include <__functional/invoke.h>
+#include <__stop_token/stop_source.h>
+#include <__stop_token/stop_token.h>
+#include <__thread/thread.h>
+#include <__threading_support>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class _LIBCPP_AVAILABILITY_SYNC jthread {
+public:
+ // types
+ using id = thread::id;
+ using native_handle_type = thread::native_handle_type;
+
+ // [thread.jthread.cons], constructors, move, and assignment
+ _LIBCPP_HIDE_FROM_ABI jthread() noexcept : __stop_source_(std::nostopstate) {}
+
+ template <class _Fun, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI explicit jthread(_Fun&& __fun, _Args&&... __args)
+ requires(!std::is_same_v<remove_cvref_t<_Fun>, jthread>)
+ : __stop_source_(),
+ __thread_(__init_thread(__stop_source_, std::forward<_Fun>(__fun), std::forward<_Args>(__args)...)) {
+ static_assert(is_constructible_v<decay_t<_Fun>, _Fun>);
+ static_assert((is_constructible_v<decay_t<_Args>, _Args> && ...));
+ static_assert(is_invocable_v<decay_t<_Fun>, decay_t<_Args>...> ||
+ is_invocable_v<decay_t<_Fun>, stop_token, decay_t<_Args>...>);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI ~jthread() {
+ if (joinable()) {
+ request_stop();
+ join();
+ }
+ }
+
+ jthread(const jthread&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI jthread(jthread&&) noexcept = default;
+
+ jthread& operator=(const jthread&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI jthread& operator=(jthread&& __other) noexcept {
+ if (this != &__other) {
+ if (joinable()) {
+ request_stop();
+ join();
+ }
+ __stop_source_ = std::move(__other.__stop_source_);
+ __thread_ = std::move(__other.__thread_);
+ }
+
+ return *this;
+ }
+
+ // [thread.jthread.mem], members
+ _LIBCPP_HIDE_FROM_ABI void swap(jthread& __other) noexcept {
+ std::swap(__stop_source_, __other.__stop_source_);
+ std::swap(__thread_, __other.__thread_);
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool joinable() const noexcept { return get_id() != id(); }
+
+ _LIBCPP_HIDE_FROM_ABI void join() { __thread_.join(); }
+
+ _LIBCPP_HIDE_FROM_ABI void detach() { __thread_.detach(); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI id get_id() const noexcept { return __thread_.get_id(); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return __thread_.native_handle(); }
+
+ // [thread.jthread.stop], stop token handling
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI stop_source get_stop_source() noexcept { return __stop_source_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI stop_token get_stop_token() const noexcept { return __stop_source_.get_token(); }
+
+ _LIBCPP_HIDE_FROM_ABI bool request_stop() noexcept { return __stop_source_.request_stop(); }
+
+ // [thread.jthread.special], specialized algorithms
+ _LIBCPP_HIDE_FROM_ABI friend void swap(jthread& __lhs, jthread& __rhs) noexcept { __lhs.swap(__rhs); }
+
+ // [thread.jthread.static], static members
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static unsigned int hardware_concurrency() noexcept {
+ return thread::hardware_concurrency();
+ }
+
+private:
+ template <class _Fun, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI static thread __init_thread(const stop_source& __ss, _Fun&& __fun, _Args&&... __args) {
+ if constexpr (is_invocable_v<decay_t<_Fun>, stop_token, decay_t<_Args>...>) {
+ return thread(std::forward<_Fun>(__fun), __ss.get_token(), std::forward<_Args>(__args)...);
+ } else {
+ return thread(std::forward<_Fun>(__fun), std::forward<_Args>(__args)...);
+ }
+ }
+
+ stop_source __stop_source_;
+ thread __thread_;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+
+#endif // _LIBCPP___THREAD_JTHREAD_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index e349386e0fba133..6d9bb8653fcb5e9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1789,6 +1789,10 @@ module std_private_system_error_system_error [system] { header "__system_erro
module std_private_thread_formatter [system] { header "__thread/formatter.h" }
module std_private_thread_id [system] { header "__thread/id.h" }
+module std_private_thread_jthread [system] {
+ header "__thread/jthread.h"
+ export *
+}
module std_private_thread_poll_with_backoff [system] { header "__thread/poll_with_backoff.h" }
module std_private_thread_this_thread [system] { header "__thread/this_thread.h" }
module std_private_thread_thread [system] {
diff --git a/libcxx/include/thread b/libcxx/include/thread
index 4ddcf3ec79cb0e6..943085b7af03529 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -90,6 +90,7 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
#include <__availability>
#include <__config>
#include <__thread/formatter.h>
+#include <__thread/jthread.h>
#include <__thread/this_thread.h>
#include <__thread/thread.h>
#include <__threading_support>
diff --git a/libcxx/modules/std/thread.inc b/libcxx/modules/std/thread.inc
index 43594d1a29cf305..6504a39a7aeaee1 100644
--- a/libcxx/modules/std/thread.inc
+++ b/libcxx/modules/std/thread.inc
@@ -15,7 +15,9 @@ export namespace std {
using std::swap;
// [thread.jthread.class], class jthread
- // using std::jthread;
+# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+ using std::jthread;
+# endif
// [thread.thread.this], namespace this_thread
namespace this_thread {
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index f78345656175f56..57420b9a3795d41 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -805,9 +805,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token type_traits
stop_token version
streambuf cstdint
@@ -867,6 +867,7 @@ system_error string
system_error type_traits
system_error version
thread array
+thread atomic
thread cerrno
thread chrono
thread compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 2518c2bcafc99de..464c0ef892c6195 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -811,9 +811,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token type_traits
stop_token version
streambuf cstdint
@@ -873,6 +873,7 @@ system_error string
system_error type_traits
system_error version
thread array
+thread atomic
thread cerrno
thread chrono
thread compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 4191094251f0633..eb698e29d922ddc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -813,9 +813,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token type_traits
stop_token version
streambuf cstdint
@@ -875,6 +875,7 @@ system_error string
system_error type_traits
system_error version
thread array
+thread atomic
thread cerrno
thread chrono
thread compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 4191094251f0633..eb698e29d922ddc 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -813,9 +813,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token type_traits
stop_token version
streambuf cstdint
@@ -875,6 +875,7 @@ system_error string
system_error type_traits
system_error version
thread array
+thread atomic
thread cerrno
thread chrono
thread compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 2cfb6e9e3861cc6..9ba59c10734b3ec 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -818,9 +818,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token type_traits
stop_token version
streambuf cstdint
@@ -880,6 +880,7 @@ system_error string
system_error type_traits
system_error version
thread array
+thread atomic
thread cerrno
thread compare
thread cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 37095662bc30bc7..baaf19a002b0eb1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -582,9 +582,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token version
streambuf cstdint
streambuf ios
@@ -627,6 +627,7 @@ system_error stdexcept
system_error string
system_error version
thread array
+thread atomic
thread cerrno
thread compare
thread cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 37095662bc30bc7..baaf19a002b0eb1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -582,9 +582,9 @@ stop_token cstddef
stop_token cstdint
stop_token cstring
stop_token ctime
+stop_token iosfwd
stop_token limits
stop_token ratio
-stop_token thread
stop_token version
streambuf cstdint
streambuf ios
@@ -627,6 +627,7 @@ system_error stdexcept
system_error string
system_error version
thread array
+thread atomic
thread cerrno
thread compare
thread cstddef
diff --git a/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp b/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp
new file mode 100644
index 000000000000000..b932ac39d2f3773
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+// ADDITIONAL_COMPILE_FLAGS: -Wno-self-move
+
+// jthread& operator=(jthread&&) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_move_assignable_v<std::jthread>);
+
+int main(int, char**) {
+ // If &x == this is true, there are no effects.
+ {
+ std::jthread j([] {});
+ auto id = j.get_id();
+ auto ssource = j.get_stop_source();
+ j = std::move(j);
+ assert(j.get_id() == id);
+ assert(j.get_stop_source() == ssource);
+ }
+
+ // if joinable() is true, calls request_stop() and then join()
+ // request_stop is called
+ {
+ std::jthread j1([] {});
+ bool called = false;
+ std::stop_callback cb(j1.get_stop_token(), [&called] { called = true; });
+
+ std::jthread j2([] {});
+ j1 = std::move(j2);
+ assert(called);
+ }
+
+ // if joinable() is true, calls request_stop() and then join()
+ // join is called
+ {
+ std::atomic_int calledTimes = 0;
+ std::vector<std::jthread> jts;
+ constexpr auto numberOfThreads = 10u;
+ jts.reserve(numberOfThreads);
+ for (auto i = 0u; i < numberOfThreads; ++i) {
+ jts.emplace_back([&] {
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ calledTimes.fetch_add(1, std::memory_order_relaxed);
+ });
+ }
+
+ for (auto i = 0u; i < numberOfThreads; ++i) {
+ jts[i] = std::jthread{};
+ }
+
+ // If join was called as expected, calledTimes must equal to numberOfThreads
+ // If join was not called, there is a chance that the check below happened
+ // before test threads incrementing the counter, thus calledTimed would
+ // be less than numberOfThreads.
+ // This is not going to catch issues 100%. Creating more threads to increase
+ // the probability of catching the issue
+ assert(calledTimes.load(std::memory_order_relaxed) == numberOfThreads);
+ }
+
+ // then assigns the state of x to *this
+ {
+ std::jthread j1([] {});
+ std::jthread j2([] {});
+ auto id2 = j2.get_id();
+ auto ssource2 = j2.get_stop_source();
+
+ j1 = std::move(j2);
+
+ assert(j1.get_id() == id2);
+ assert(j1.get_stop_source() == ssource2);
+ }
+
+ // sets x to a default constructed state
+ {
+ std::jthread j1([] {});
+ std::jthread j2([] {});
+ j1 = std::move(j2);
+
+ assert(j2.get_id() == std::jthread::id());
+ assert(!j2.get_stop_source().stop_possible());
+ }
+
+ // joinable is false
+ {
+ std::jthread j1;
+ std::jthread j2([] {});
+
+ auto j2Id = j2.get_id();
+
+ j1 = std::move(j2);
+
+ assert(j1.get_id() == j2Id);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/cons.default.pass.cpp b/libcxx/test/std/thread/thread.jthread/cons.default.pass.cpp
new file mode 100644
index 000000000000000..88585eaef599e52
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/cons.default.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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// jthread() noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_default_constructible_v<std::jthread>);
+
+int main(int, char**) {
+ {
+ std::jthread jt = {}; // implicit
+ assert(!jt.get_stop_source().stop_possible());
+ assert(jt.get_id() == std::jthread::id());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/cons.func.token.pass.cpp b/libcxx/test/std/thread/thread.jthread/cons.func.token.pass.cpp
new file mode 100644
index 000000000000000..2d029271d75ce84
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/cons.func.token.pass.cpp
@@ -0,0 +1,160 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// template<class F, class... Args>
+// explicit jthread(F&& f, Args&&... args);
+
+#include <cassert>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class... Args>
+struct Func {
+ void operator()(Args...) const;
+};
+
+// Constraints: remove_cvref_t<F> is not the same type as jthread.
+static_assert(std::is_constructible_v<std::jthread, Func<>>);
+static_assert(std::is_constructible_v<std::jthread, Func<int>, int>);
+static_assert(!std::is_constructible_v<std::jthread, std::jthread const&>);
+
+// 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<std::jthread, Func<>>);
+static_assert(!ImplicitlyConstructible<std::jthread, Func<int>, int>);
+
+int main(int, char**) {
+ // Effects: Initializes ssource
+ // Postconditions: get_id() != id() is true and ssource.stop_possible() is true
+ // and *this represents the newly started thread.
+ {
+ std::jthread jt{[] {}};
+ assert(jt.get_stop_source().stop_possible());
+ assert(jt.get_id() != std::jthread::id());
+ }
+
+ // The new thread of execution executes
+ // invoke(auto(std::forward<F>(f)), get_stop_token(), auto(std::forward<Args>(args))...)
+ // if that expression is well-formed,
+ {
+ int result = 0;
+ std::jthread jt{[&result](std::stop_token st, int i) {
+ assert(st.stop_possible());
+ assert(!st.stop_requested());
+ result += i;
+ },
+ 5};
+ jt.join();
+ assert(result == 5);
+ }
+
+ // otherwise
+ // invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...)
+ {
+ int result = 0;
+ std::jthread jt{[&result](int i) { result += i; }, 5};
+ jt.join();
+ assert(result == 5);
+ }
+
+ // with the values produced by auto being materialized ([conv.rval]) in the constructing thread.
+ {
+ struct TrackThread {
+ std::jthread::id threadId;
+ bool copyConstructed = false;
+ bool moveConstructed = false;
+
+ TrackThread() : threadId(std::this_thread::get_id()) {}
+ TrackThread(const TrackThread&) : threadId(std::this_thread::get_id()), copyConstructed(true) {}
+ TrackThread(TrackThread&&) : threadId(std::this_thread::get_id()), moveConstructed(true) {}
+ };
+
+ auto mainThread = std::this_thread::get_id();
+
+ TrackThread arg1;
+ std::jthread jt1{[mainThread](const TrackThread& arg) {
+ assert(arg.threadId == mainThread);
+ assert(arg.threadId != std::this_thread::get_id());
+ assert(arg.copyConstructed);
+ },
+ arg1};
+
+ TrackThread arg2;
+ std::jthread jt2{[mainThread](const TrackThread& arg) {
+ assert(arg.threadId == mainThread);
+ assert(arg.threadId != std::this_thread::get_id());
+ assert(arg.moveConstructed);
+ },
+ std::move(arg2)};
+ }
+
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ // [Note 1: This implies that any exceptions not thrown from the invocation of the copy
+ // of f will be thrown in the constructing thread, not the new thread. - end note]
+ {
+ struct Exception {
+ std::jthread::id threadId;
+ };
+ struct ThrowOnCopyFunc {
+ ThrowOnCopyFunc() = default;
+ ThrowOnCopyFunc(const ThrowOnCopyFunc&) { throw Exception{std::this_thread::get_id()}; }
+ void operator()() const {}
+ };
+ ThrowOnCopyFunc f1;
+ try {
+ std::jthread jt{f1};
+ assert(false);
+ } catch (const Exception& e) {
+ assert(e.threadId == std::this_thread::get_id());
+ }
+ }
+#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
+
+ // Synchronization: The completion of the invocation of the constructor
+ // synchronizes with the beginning of the invocation of the copy of f.
+ {
+ int flag = 0;
+ struct Arg {
+ int& flag_;
+ Arg(int& f) : flag_(f) {}
+
+ Arg(const Arg& other) : flag_(other.flag_) { flag_ = 5; }
+ };
+
+ Arg arg(flag);
+ std::jthread jt(
+ [&flag](const auto&) {
+ assert(flag == 5); // happens-after the copy-construction of arg
+ },
+ arg);
+ }
+
+ // Per https://eel.is/c++draft/thread.jthread.class#thread.jthread.cons-8:
+ //
+ // Throws: system_error if unable to start the new thread.
+ // Error conditions:
+ // resource_unavailable_try_again - the system lacked the necessary resources to create another thread,
+ // or the system-imposed limit on the number of threads in a process would be exceeded.
+ //
+ // Unfortunately, this is extremely hard to test portably so we don't have a test for this error condition right now.
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp b/libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp
new file mode 100644
index 000000000000000..9eacf8971c2a58b
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// jthread(jthread&& x) noexcept;
+
+#include <cassert>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+static_assert(std::is_nothrow_move_constructible_v<std::jthread>);
+
+int main(int, char**) {
+ {
+ // x.get_id() == id() and get_id() returns the value of x.get_id() prior
+ // to the start of construction.
+ std::jthread j1{[] {}};
+ auto id1 = j1.get_id();
+
+ std::jthread j2(std::move(j1));
+ assert(j1.get_id() == std::jthread::id());
+ assert(j2.get_id() == id1);
+ }
+
+ {
+ // ssource has the value of x.ssource prior to the start of construction
+ // and x.ssource.stop_possible() is false.
+ std::jthread j1{[] {}};
+ auto ss1 = j1.get_stop_source();
+
+ std::jthread j2(std::move(j1));
+ assert(ss1 == j2.get_stop_source());
+ assert(!j1.get_stop_source().stop_possible());
+ assert(j2.get_stop_source().stop_possible());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/copy.delete.compile.pass.cpp b/libcxx/test/std/thread/thread.jthread/copy.delete.compile.pass.cpp
new file mode 100644
index 000000000000000..e1c34afca9d4791
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/copy.delete.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// jthread(const jthread&) = delete;
+// jthread& operator=(const jthread&) = delete;
+
+#include <thread>
+#include <type_traits>
+
+static_assert(!std::is_copy_constructible_v<std::jthread>);
+static_assert(!std::is_copy_assignable_v<std::jthread>);
diff --git a/libcxx/test/std/thread/thread.jthread/detach.pass.cpp b/libcxx/test/std/thread/thread.jthread/detach.pass.cpp
new file mode 100644
index 000000000000000..ee48d2691e68439
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/detach.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void detach();
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <concepts>
+#include <functional>
+#include <optional>
+#include <system_error>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+ // Effects: The thread represented by *this continues execution without the calling thread blocking.
+ {
+ std::atomic_bool start{false};
+ std::atomic_bool done{false};
+ std::optional<std::jthread> jt{[&start, &done] {
+ start.wait(false);
+ done = true;
+ }};
+
+ // If it blocks, it will deadlock here
+ jt->detach();
+
+ jt.reset();
+
+ // The other thread continues execution
+ start = true;
+ start.notify_all();
+ while (!done) {
+ }
+ }
+
+ // Postconditions: get_id() == id().
+ {
+ std::jthread jt{[] {}};
+ assert(jt.get_id() != std::jthread::id());
+ jt.detach();
+ assert(jt.get_id() == std::jthread::id());
+ }
+
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ // Throws: system_error when an exception is required ([thread.req.exception]).
+ // invalid_argument - if the thread is not joinable.
+ {
+ std::jthread jt;
+ try {
+ jt.detach();
+ assert(false);
+ } catch (const std::system_error& err) {
+ assert(err.code() == std::errc::invalid_argument);
+ }
+ }
+#endif
+
+ std::this_thread::sleep_for(std::chrono::milliseconds{2});
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/dtor.pass.cpp b/libcxx/test/std/thread/thread.jthread/dtor.pass.cpp
new file mode 100644
index 000000000000000..47ee62023f62d94
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/dtor.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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// ~jthread();
+
+#include <atomic>
+#include <cassert>
+#include <optional>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+#include <vector>
+#include "test_macros.h"
+
+int main(int, char**) {
+ // !joinable()
+ {
+ std::jthread jt;
+ assert(!jt.joinable());
+ }
+
+ // If joinable() is true, calls request_stop() and then join().
+ // request_stop is called
+ {
+ std::optional<std::jthread> jt([] {});
+ bool called = false;
+ std::stop_callback cb(jt->get_stop_token(), [&called] { called = true; });
+ jt.reset();
+ assert(called);
+ }
+
+ // If joinable() is true, calls request_stop() and then join().
+ // join is called
+ {
+ std::atomic_int calledTimes = 0;
+ std::vector<std::jthread> jts;
+
+ constexpr auto numberOfThreads = 10u;
+ jts.reserve(numberOfThreads);
+ for (auto i = 0u; i < numberOfThreads; ++i) {
+ jts.emplace_back([&calledTimes] {
+ std::this_thread::sleep_for(std::chrono::milliseconds{2});
+ calledTimes.fetch_add(1, std::memory_order_relaxed);
+ });
+ }
+ jts.clear();
+
+ // If join was called as expected, calledTimes must equal to numberOfThreads
+ // If join was not called, there is a chance that the check below happened
+ // before test threads incrementing the counter, thus calledTimed would
+ // be less than numberOfThreads.
+ // This is not going to catch issues 100%. Creating more threads would increase
+ // the probability of catching the issue
+ assert(calledTimes.load(std::memory_order_relaxed) == numberOfThreads);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/get_id.pass.cpp b/libcxx/test/std/thread/thread.jthread/get_id.pass.cpp
new file mode 100644
index 000000000000000..f92472d3d8dc649
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/get_id.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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] id get_id() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(noexcept(std::declval<const std::jthread&>().get_id()));
+
+int main(int, char**) {
+ // Does not represent a thread
+ {
+ const std::jthread jt;
+ std::same_as<std::jthread::id> decltype(auto) result = jt.get_id();
+ assert(result == std::jthread::id());
+ }
+
+ // Represents a thread
+ {
+ const std::jthread jt{[] {}};
+ std::same_as<std::jthread::id> decltype(auto) result = jt.get_id();
+ assert(result != std::jthread::id());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/get_stop_source.pass.cpp b/libcxx/test/std/thread/thread.jthread/get_stop_source.pass.cpp
new file mode 100644
index 000000000000000..41df2d894f45df0
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/get_stop_source.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] stop_source get_stop_source() noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(noexcept(std::declval<std::jthread&>().get_stop_source()));
+
+int main(int, char**) {
+ // Represents a thread
+ {
+ std::jthread jt{[] {}};
+ std::same_as<std::stop_source> decltype(auto) result = jt.get_stop_source();
+ assert(result.stop_possible());
+ }
+
+ // Does not represents a thread
+ {
+ std::jthread jt{};
+ std::same_as<std::stop_source> decltype(auto) result = jt.get_stop_source();
+ assert(!result.stop_possible());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/get_stop_token.pass.cpp b/libcxx/test/std/thread/thread.jthread/get_stop_token.pass.cpp
new file mode 100644
index 000000000000000..c65d39b3cdf4a77
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/get_stop_token.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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] stop_token get_stop_token() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+static_assert(noexcept(std::declval<const std::jthread&>().get_stop_token()));
+
+int main(int, char**) {
+ // Represents a thread
+ {
+ std::jthread jt{[] {}};
+ auto ss = jt.get_stop_source();
+ std::same_as<std::stop_token> decltype(auto) st = std::as_const(jt).get_stop_token();
+
+ assert(st.stop_possible());
+ assert(!st.stop_requested());
+ ss.request_stop();
+ assert(st.stop_requested());
+ }
+
+ // Does not represent a thread
+ {
+ const std::jthread jt{};
+ std::same_as<std::stop_token> decltype(auto) st = jt.get_stop_token();
+
+ assert(!st.stop_possible());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/hardware_concurrency.pass.cpp b/libcxx/test/std/thread/thread.jthread/hardware_concurrency.pass.cpp
new file mode 100644
index 000000000000000..669d72f1fd5f5fa
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/hardware_concurrency.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] static unsigned int hardware_concurrency() noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(noexcept(std::jthread::hardware_concurrency()));
+
+int main(int, char**) {
+ std::same_as<unsigned int> decltype(auto) result = std::jthread::hardware_concurrency();
+ assert(result == std::thread::hardware_concurrency());
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
new file mode 100644
index 000000000000000..aa5cdf2783dba04
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Windows cannot detect the deadlock. Instead of throwing system_error,
+// it would dead lock the test
+// UNSUPPORTED: windows
+
+// TSAN bug: https://github.com/llvm/llvm-project/issues/66537
+// UNSUPPORTED: tsan
+
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: no-exceptions
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void join();
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <concepts>
+#include <functional>
+#include <system_error>
+#include <thread>
+#include <type_traits>
+#include <vector>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+ // resource_deadlock_would_occur - if deadlock is detected or get_id() == this_thread::get_id().
+ {
+ std::function<void()> f;
+ std::atomic_bool start = false;
+ std::atomic_bool done = false;
+
+ std::jthread jt{[&] {
+ start.wait(false);
+ f();
+ done = true;
+ done.notify_all();
+ }};
+
+ f = [&] {
+ try {
+ jt.join();
+ assert(false);
+ } catch (const std::system_error& err) {
+ assert(err.code() == std::errc::resource_deadlock_would_occur);
+ }
+ };
+ start = true;
+ start.notify_all();
+ done.wait(false);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/join.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.pass.cpp
new file mode 100644
index 000000000000000..38986bdfed8d745
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/join.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void join();
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <concepts>
+#include <functional>
+#include <system_error>
+#include <thread>
+#include <type_traits>
+#include <vector>
+
+#include "test_macros.h"
+
+int main(int, char**) {
+ // Effects: Blocks until the thread represented by *this has completed.
+ {
+ std::atomic_int calledTimes = 0;
+ std::vector<std::jthread> jts;
+ constexpr auto numberOfThreads = 10u;
+ jts.reserve(numberOfThreads);
+ for (auto i = 0u; i < numberOfThreads; ++i) {
+ jts.emplace_back([&] {
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ calledTimes.fetch_add(1, std::memory_order_relaxed);
+ });
+ }
+
+ for (auto i = 0u; i < numberOfThreads; ++i) {
+ jts[i].join();
+ }
+
+ // If join did block, calledTimes must equal to numberOfThreads
+ // If join did not block, there is a chance that the check below happened
+ // before test threads incrementing the counter, thus calledTimed would
+ // be less than numberOfThreads.
+ // This is not going to catch issues 100%. Creating more threads to increase
+ // the probability of catching the issue
+ assert(calledTimes.load(std::memory_order_relaxed) == numberOfThreads);
+ }
+
+ // Synchronization: The completion of the thread represented by *this synchronizes with
+ // ([intro.multithread]) the corresponding successful join() return.
+ {
+ bool flag = false;
+ std::jthread jt{[&] { flag = true; }};
+ jt.join();
+ assert(flag); // non atomic write is visible to the current thread
+ }
+
+ // Postconditions: The thread represented by *this has completed. get_id() == id().
+ {
+ std::jthread jt{[] {}};
+ assert(jt.get_id() != std::jthread::id());
+ jt.join();
+ assert(jt.get_id() == std::jthread::id());
+ }
+
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+ // Throws: system_error when an exception is required ([thread.req.exception]).
+ // invalid_argument - if the thread is not joinable.
+ {
+ std::jthread jt;
+ try {
+ jt.join();
+ assert(false);
+ } catch (const std::system_error& err) {
+ assert(err.code() == std::errc::invalid_argument);
+ }
+ }
+
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp b/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp
new file mode 100644
index 000000000000000..8d9619bdefd8cef
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: no-threads
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool joinable() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(noexcept(std::declval<const std::jthread&>().joinable()));
+
+int main(int, char**) {
+ // Default constructed
+ {
+ const std::jthread jt;
+ std::same_as<bool> decltype(auto) result = jt.joinable();
+ assert(!result);
+ }
+
+ // Non-default constructed
+ {
+ const std::jthread jt{[] {}};
+ std::same_as<bool> decltype(auto) result = jt.joinable();
+ assert(result);
+ }
+
+ // Non-default constructed
+ // the thread of execution has not finished
+ {
+ std::atomic_bool done = false;
+ const std::jthread jt{[&done] { done.wait(false); }};
+ std::same_as<bool> decltype(auto) result = jt.joinable();
+ done = true;
+ assert(result);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/nodiscard.verify.cpp b/libcxx/test/std/thread/thread.jthread/nodiscard.verify.cpp
new file mode 100644
index 000000000000000..d0fbef733860cd2
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/nodiscard.verify.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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool joinable() const noexcept;
+// [[nodiscard]] id get_id() const noexcept;
+// [[nodiscard]] native_handle_type native_handle();
+// [[nodiscard]] stop_source get_stop_source() noexcept;
+// [[nodiscard]] stop_token get_stop_token() const noexcept;
+// [[nodiscard]] static unsigned int hardware_concurrency() noexcept;
+
+#include <thread>
+
+void test() {
+ std::jthread jt;
+ jt.joinable(); // expected-warning {{ignoring return value of function}}
+ jt.get_id(); // expected-warning {{ignoring return value of function}}
+ jt.native_handle(); // expected-warning {{ignoring return value of function}}
+ jt.get_stop_source(); // expected-warning {{ignoring return value of function}}
+ jt.get_stop_token(); // expected-warning {{ignoring return value of function}}
+ jt.hardware_concurrency(); // expected-warning {{ignoring return value of function}}
+}
diff --git a/libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp b/libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp
new file mode 100644
index 000000000000000..f1109561cf9f299
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/request_stop.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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// [[nodiscard]] bool request_stop() noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <stop_token>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(noexcept(std::declval<std::jthread&>().request_stop()));
+
+int main(int, char**) {
+ // Represents a thread
+ {
+ std::jthread jt{[] {}};
+ auto st = jt.get_stop_token();
+ assert(!st.stop_requested());
+ std::same_as<bool> decltype(auto) result = jt.request_stop();
+ assert(result);
+ assert(st.stop_requested());
+ }
+
+ // Does not represent a thread
+ {
+ std::jthread jt{};
+ std::same_as<bool> decltype(auto) result = jt.request_stop();
+ assert(!result);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp b/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp
new file mode 100644
index 000000000000000..776537cdff48358
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// friend void swap(jthread& x, jthread& y) noexcept;
+
+#include <cassert>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T>
+concept IsFreeSwapNoexcept = requires(T& a, T& b) {
+ { swap(a, b) } noexcept;
+};
+
+static_assert(IsFreeSwapNoexcept<std::jthread>);
+
+int main(int, char**) {
+ // x is default constructed
+ {
+ std::jthread t1;
+ std::jthread t2{[] {}};
+ const auto originalId2 = t2.get_id();
+ swap(t1, t2);
+
+ assert(t1.get_id() == originalId2);
+ assert(t2.get_id() == std::jthread::id());
+ }
+
+ // y is default constructed
+ {
+ std::jthread t1([] {});
+ std::jthread t2{};
+ const auto originalId1 = t1.get_id();
+ swap(t1, t2);
+
+ assert(t1.get_id() == std::jthread::id());
+ assert(t2.get_id() == originalId1);
+ }
+
+ // both not default constructed
+ {
+ std::jthread t1([] {});
+ std::jthread t2{[] {}};
+ const auto originalId1 = t1.get_id();
+ const auto originalId2 = t2.get_id();
+ swap(t1, t2);
+
+ assert(t1.get_id() == originalId2);
+ assert(t2.get_id() == originalId1);
+ }
+
+ // both default constructed
+ {
+ std::jthread t1;
+ std::jthread t2;
+ swap(t1, t2);
+
+ assert(t1.get_id() == std::jthread::id());
+ assert(t2.get_id() == std::jthread::id());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp b/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp
new file mode 100644
index 000000000000000..614e3ac8312dab7
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// void swap(jthread& x) noexcept;
+
+#include <cassert>
+#include <thread>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class T>
+concept IsMemberSwapNoexcept = requires(T& a, T& b) {
+ { a.swap(b) } noexcept;
+};
+
+static_assert(IsMemberSwapNoexcept<std::jthread>);
+
+int main(int, char**) {
+ // this is default constructed
+ {
+ std::jthread t1;
+ std::jthread t2{[] {}};
+ const auto originalId2 = t2.get_id();
+ t1.swap(t2);
+
+ assert(t1.get_id() == originalId2);
+ assert(t2.get_id() == std::jthread::id());
+ }
+
+ // that is default constructed
+ {
+ std::jthread t1([] {});
+ std::jthread t2{};
+ const auto originalId1 = t1.get_id();
+ t1.swap(t2);
+
+ assert(t1.get_id() == std::jthread::id());
+ assert(t2.get_id() == originalId1);
+ }
+
+ // both not default constructed
+ {
+ std::jthread t1([] {});
+ std::jthread t2{[] {}};
+ const auto originalId1 = t1.get_id();
+ const auto originalId2 = t2.get_id();
+ t1.swap(t2);
+
+ assert(t1.get_id() == originalId2);
+ assert(t2.get_id() == originalId1);
+ }
+
+ // both default constructed
+ {
+ std::jthread t1;
+ std::jthread t2;
+ t1.swap(t2);
+
+ assert(t1.get_id() == std::jthread::id());
+ assert(t2.get_id() == std::jthread::id());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.jthread/type.compile.pass.cpp b/libcxx/test/std/thread/thread.jthread/type.compile.pass.cpp
new file mode 100644
index 000000000000000..496f793896757d6
--- /dev/null
+++ b/libcxx/test/std/thread/thread.jthread/type.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-experimental-stop_token
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: availability-synchronization_library-missing
+
+// using id = thread::id;
+// using native_handle_type = thread::native_handle_type;
+
+#include <thread>
+#include <type_traits>
+
+static_assert(std::is_same_v<std::jthread::id, std::thread::id>);
+static_assert(std::is_same_v<std::jthread::native_handle_type, std::thread::native_handle_type>);
More information about the libcxx-commits
mailing list