[libcxx-commits] [libcxx] [libc++] Introduce make_test_jthread for jthread tests (PR #68837)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 11 14:42:45 PDT 2023


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/68837

This patch introduces the support::make_test_jthread utility which is basically the same as support::make_test_thread but for std::jthread. It allows vendors to maintain a downstream way to create threads for use within the test suite, which is especially useful for embedded platforms.

>From 995fcd34c3b5155d7bcf6c4c2019d88a940a61f3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 11 Oct 2023 14:39:10 -0700
Subject: [PATCH] [libc++] Introduce make_test_jthread for jthread tests

This patch introduces the support::make_test_jthread utility which
is basically the same as support::make_test_thread but for std::jthread.
It allows vendors to maintain a downstream way to create threads for
use within the test suite, which is especially useful for embedded
platforms.
---
 .../thread.jthread/assign.move.pass.cpp       | 37 ++++++++++---------
 .../thread/thread.jthread/cons.move.pass.cpp  |  9 +++--
 .../std/thread/thread.jthread/detach.pass.cpp |  7 ++--
 .../std/thread/thread.jthread/dtor.pass.cpp   | 10 +++--
 .../std/thread/thread.jthread/get_id.pass.cpp |  3 +-
 .../thread.jthread/get_stop_source.pass.cpp   |  3 +-
 .../thread.jthread/get_stop_token.pass.cpp    |  3 +-
 .../thread.jthread/join.deadlock.pass.cpp     |  5 ++-
 .../std/thread/thread.jthread/join.pass.cpp   | 11 +++---
 .../thread/thread.jthread/joinable.pass.cpp   |  7 ++--
 .../thread.jthread/request_stop.pass.cpp      |  5 ++-
 .../thread/thread.jthread/swap.free.pass.cpp  | 16 ++++----
 .../thread.jthread/swap.member.pass.cpp       | 16 ++++----
 libcxx/test/support/make_test_thread.h        | 21 +++++++++++
 14 files changed, 95 insertions(+), 58 deletions(-)

diff --git a/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp b/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp
index b932ac39d2f3773..89521ad7660a120 100644
--- a/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/assign.move.pass.cpp
@@ -23,6 +23,7 @@
 #include <utility>
 #include <vector>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(std::is_nothrow_move_assignable_v<std::jthread>);
@@ -30,10 +31,10 @@ 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);
+    std::jthread j = support::make_test_jthread([] {});
+    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);
   }
@@ -41,12 +42,12 @@ int main(int, char**) {
   // if joinable() is true, calls request_stop() and then join()
   // request_stop is called
   {
-    std::jthread j1([] {});
-    bool called = false;
+    std::jthread j1 = support::make_test_jthread([] {});
+    bool called     = false;
     std::stop_callback cb(j1.get_stop_token(), [&called] { called = true; });
 
-    std::jthread j2([] {});
-    j1 = std::move(j2);
+    std::jthread j2 = support::make_test_jthread([] {});
+    j1              = std::move(j2);
     assert(called);
   }
 
@@ -58,10 +59,10 @@ int main(int, char**) {
     constexpr auto numberOfThreads = 10u;
     jts.reserve(numberOfThreads);
     for (auto i = 0u; i < numberOfThreads; ++i) {
-      jts.emplace_back([&] {
+      jts.emplace_back(support::make_test_jthread([&] {
         std::this_thread::sleep_for(std::chrono::milliseconds(2));
         calledTimes.fetch_add(1, std::memory_order_relaxed);
-      });
+      }));
     }
 
     for (auto i = 0u; i < numberOfThreads; ++i) {
@@ -79,10 +80,10 @@ int main(int, char**) {
 
   // 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();
+    std::jthread j1 = support::make_test_jthread([] {});
+    std::jthread j2 = support::make_test_jthread([] {});
+    auto id2        = j2.get_id();
+    auto ssource2   = j2.get_stop_source();
 
     j1 = std::move(j2);
 
@@ -92,9 +93,9 @@ int main(int, char**) {
 
   // sets x to a default constructed state
   {
-    std::jthread j1([] {});
-    std::jthread j2([] {});
-    j1 = std::move(j2);
+    std::jthread j1 = support::make_test_jthread([] {});
+    std::jthread j2 = support::make_test_jthread([] {});
+    j1              = std::move(j2);
 
     assert(j2.get_id() == std::jthread::id());
     assert(!j2.get_stop_source().stop_possible());
@@ -103,7 +104,7 @@ int main(int, char**) {
   // joinable is false
   {
     std::jthread j1;
-    std::jthread j2([] {});
+    std::jthread j2 = support::make_test_jthread([] {});
 
     auto j2Id = j2.get_id();
 
diff --git a/libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp b/libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp
index 9eacf8971c2a58b..c3c04467703c96e 100644
--- a/libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/cons.move.pass.cpp
@@ -19,6 +19,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(std::is_nothrow_move_constructible_v<std::jthread>);
@@ -27,8 +28,8 @@ 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 j1 = support::make_test_jthread([] {});
+    auto id1        = j1.get_id();
 
     std::jthread j2(std::move(j1));
     assert(j1.get_id() == std::jthread::id());
@@ -38,8 +39,8 @@ int main(int, char**) {
   {
     // 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 j1 = support::make_test_jthread([] {});
+    auto ss1        = j1.get_stop_source();
 
     std::jthread j2(std::move(j1));
     assert(ss1 == j2.get_stop_source());
diff --git a/libcxx/test/std/thread/thread.jthread/detach.pass.cpp b/libcxx/test/std/thread/thread.jthread/detach.pass.cpp
index ee48d2691e68439..54fd5fd93bed6f3 100644
--- a/libcxx/test/std/thread/thread.jthread/detach.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/detach.pass.cpp
@@ -23,6 +23,7 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 int main(int, char**) {
@@ -30,10 +31,10 @@ int main(int, char**) {
   {
     std::atomic_bool start{false};
     std::atomic_bool done{false};
-    std::optional<std::jthread> jt{[&start, &done] {
+    std::optional<std::jthread> jt = support::make_test_jthread([&start, &done] {
       start.wait(false);
       done = true;
-    }};
+    });
 
     // If it blocks, it will deadlock here
     jt->detach();
@@ -49,7 +50,7 @@ int main(int, char**) {
 
   // Postconditions: get_id() == id().
   {
-    std::jthread jt{[] {}};
+    std::jthread jt = support::make_test_jthread([] {});
     assert(jt.get_id() != std::jthread::id());
     jt.detach();
     assert(jt.get_id() == std::jthread::id());
diff --git a/libcxx/test/std/thread/thread.jthread/dtor.pass.cpp b/libcxx/test/std/thread/thread.jthread/dtor.pass.cpp
index 47ee62023f62d94..35be0f6c0dd82c8 100644
--- a/libcxx/test/std/thread/thread.jthread/dtor.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/dtor.pass.cpp
@@ -20,6 +20,8 @@
 #include <thread>
 #include <type_traits>
 #include <vector>
+
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 int main(int, char**) {
@@ -32,8 +34,8 @@ int main(int, char**) {
   // If joinable() is true, calls request_stop() and then join().
   // request_stop is called
   {
-    std::optional<std::jthread> jt([] {});
-    bool called = false;
+    std::optional<std::jthread> jt = support::make_test_jthread([] {});
+    bool called                    = false;
     std::stop_callback cb(jt->get_stop_token(), [&called] { called = true; });
     jt.reset();
     assert(called);
@@ -48,10 +50,10 @@ int main(int, char**) {
     constexpr auto numberOfThreads = 10u;
     jts.reserve(numberOfThreads);
     for (auto i = 0u; i < numberOfThreads; ++i) {
-      jts.emplace_back([&calledTimes] {
+      jts.emplace_back(support::make_test_jthread([&calledTimes] {
         std::this_thread::sleep_for(std::chrono::milliseconds{2});
         calledTimes.fetch_add(1, std::memory_order_relaxed);
-      });
+      }));
     }
     jts.clear();
 
diff --git a/libcxx/test/std/thread/thread.jthread/get_id.pass.cpp b/libcxx/test/std/thread/thread.jthread/get_id.pass.cpp
index f92472d3d8dc649..b3a2beff9f416af 100644
--- a/libcxx/test/std/thread/thread.jthread/get_id.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/get_id.pass.cpp
@@ -18,6 +18,7 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(noexcept(std::declval<const std::jthread&>().get_id()));
@@ -32,7 +33,7 @@ int main(int, char**) {
 
   // Represents a thread
   {
-    const std::jthread jt{[] {}};
+    const std::jthread jt                                = support::make_test_jthread([] {});
     std::same_as<std::jthread::id> decltype(auto) result = jt.get_id();
     assert(result != std::jthread::id());
   }
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
index 41df2d894f45df0..8f35db297b74962 100644
--- a/libcxx/test/std/thread/thread.jthread/get_stop_source.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/get_stop_source.pass.cpp
@@ -19,6 +19,7 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(noexcept(std::declval<std::jthread&>().get_stop_source()));
@@ -26,7 +27,7 @@ static_assert(noexcept(std::declval<std::jthread&>().get_stop_source()));
 int main(int, char**) {
   // Represents a thread
   {
-    std::jthread jt{[] {}};
+    std::jthread jt                                      = support::make_test_jthread([] {});
     std::same_as<std::stop_source> decltype(auto) result = jt.get_stop_source();
     assert(result.stop_possible());
   }
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
index c65d39b3cdf4a77..070761e0a3ab883 100644
--- a/libcxx/test/std/thread/thread.jthread/get_stop_token.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/get_stop_token.pass.cpp
@@ -20,6 +20,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(noexcept(std::declval<const std::jthread&>().get_stop_token()));
@@ -27,7 +28,7 @@ static_assert(noexcept(std::declval<const std::jthread&>().get_stop_token()));
 int main(int, char**) {
   // Represents a thread
   {
-    std::jthread jt{[] {}};
+    std::jthread jt                                 = support::make_test_jthread([] {});
     auto ss                                         = jt.get_stop_source();
     std::same_as<std::stop_token> decltype(auto) st = std::as_const(jt).get_stop_token();
 
diff --git a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
index aa5cdf2783dba04..8e2f1e5f5d9d479 100644
--- a/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.deadlock.pass.cpp
@@ -31,6 +31,7 @@
 #include <type_traits>
 #include <vector>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 int main(int, char**) {
@@ -40,12 +41,12 @@ int main(int, char**) {
     std::atomic_bool start = false;
     std::atomic_bool done  = false;
 
-    std::jthread jt{[&] {
+    std::jthread jt = support::make_test_jthread([&] {
       start.wait(false);
       f();
       done = true;
       done.notify_all();
-    }};
+    });
 
     f = [&] {
       try {
diff --git a/libcxx/test/std/thread/thread.jthread/join.pass.cpp b/libcxx/test/std/thread/thread.jthread/join.pass.cpp
index 38986bdfed8d745..2bafd8633824752 100644
--- a/libcxx/test/std/thread/thread.jthread/join.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/join.pass.cpp
@@ -23,6 +23,7 @@
 #include <type_traits>
 #include <vector>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 int main(int, char**) {
@@ -33,10 +34,10 @@ int main(int, char**) {
     constexpr auto numberOfThreads = 10u;
     jts.reserve(numberOfThreads);
     for (auto i = 0u; i < numberOfThreads; ++i) {
-      jts.emplace_back([&] {
+      jts.emplace_back(support::make_test_jthread([&] {
         std::this_thread::sleep_for(std::chrono::milliseconds(2));
         calledTimes.fetch_add(1, std::memory_order_relaxed);
-      });
+      }));
     }
 
     for (auto i = 0u; i < numberOfThreads; ++i) {
@@ -55,15 +56,15 @@ int main(int, char**) {
   // 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; }};
+    bool flag       = false;
+    std::jthread jt = support::make_test_jthread([&] { 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{[] {}};
+    std::jthread jt = support::make_test_jthread([] {});
     assert(jt.get_id() != std::jthread::id());
     jt.join();
     assert(jt.get_id() == std::jthread::id());
diff --git a/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp b/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp
index 5c0fbece4c21e47..3a88100d934dbfc 100644
--- a/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/joinable.pass.cpp
@@ -19,6 +19,7 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(noexcept(std::declval<const std::jthread&>().joinable()));
@@ -33,7 +34,7 @@ int main(int, char**) {
 
   // Non-default constructed
   {
-    const std::jthread jt{[] {}};
+    const std::jthread jt                    = support::make_test_jthread([] {});
     std::same_as<bool> decltype(auto) result = jt.joinable();
     assert(result);
   }
@@ -41,8 +42,8 @@ int main(int, char**) {
   // Non-default constructed
   // the thread of execution has not finished
   {
-    std::atomic_bool done = false;
-    const std::jthread jt{[&done] { done.wait(false); }};
+    std::atomic_bool done                    = false;
+    const std::jthread jt                    = support::make_test_jthread([&done] { done.wait(false); });
     std::same_as<bool> decltype(auto) result = jt.joinable();
     done                                     = true;
     done.notify_all();
diff --git a/libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp b/libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp
index f1109561cf9f299..ccbea9f145e504e 100644
--- a/libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/request_stop.pass.cpp
@@ -19,6 +19,7 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 static_assert(noexcept(std::declval<std::jthread&>().request_stop()));
@@ -26,8 +27,8 @@ 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();
+    std::jthread jt = support::make_test_jthread([] {});
+    auto st         = jt.get_stop_token();
     assert(!st.stop_requested());
     std::same_as<bool> decltype(auto) result = jt.request_stop();
     assert(result);
diff --git a/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp b/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp
index 776537cdff48358..359dea2a17a9a91 100644
--- a/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/swap.free.pass.cpp
@@ -17,12 +17,14 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 template <class T>
-concept IsFreeSwapNoexcept = requires(T& a, T& b) {
-  { swap(a, b) } noexcept;
-};
+concept IsFreeSwapNoexcept =
+    requires(T& a, T& b) {
+      { swap(a, b) } noexcept;
+    };
 
 static_assert(IsFreeSwapNoexcept<std::jthread>);
 
@@ -30,7 +32,7 @@ int main(int, char**) {
   // x is default constructed
   {
     std::jthread t1;
-    std::jthread t2{[] {}};
+    std::jthread t2        = support::make_test_jthread([] {});
     const auto originalId2 = t2.get_id();
     swap(t1, t2);
 
@@ -40,7 +42,7 @@ int main(int, char**) {
 
   // y is default constructed
   {
-    std::jthread t1([] {});
+    std::jthread t1 = support::make_test_jthread([] {});
     std::jthread t2{};
     const auto originalId1 = t1.get_id();
     swap(t1, t2);
@@ -51,8 +53,8 @@ int main(int, char**) {
 
   // both not default constructed
   {
-    std::jthread t1([] {});
-    std::jthread t2{[] {}};
+    std::jthread t1        = support::make_test_jthread([] {});
+    std::jthread t2        = support::make_test_jthread([] {});
     const auto originalId1 = t1.get_id();
     const auto originalId2 = t2.get_id();
     swap(t1, t2);
diff --git a/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp b/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp
index 614e3ac8312dab7..b86cc35b9fdbb4e 100644
--- a/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp
+++ b/libcxx/test/std/thread/thread.jthread/swap.member.pass.cpp
@@ -17,12 +17,14 @@
 #include <thread>
 #include <type_traits>
 
+#include "make_test_thread.h"
 #include "test_macros.h"
 
 template <class T>
-concept IsMemberSwapNoexcept = requires(T& a, T& b) {
-  { a.swap(b) } noexcept;
-};
+concept IsMemberSwapNoexcept =
+    requires(T& a, T& b) {
+      { a.swap(b) } noexcept;
+    };
 
 static_assert(IsMemberSwapNoexcept<std::jthread>);
 
@@ -30,7 +32,7 @@ int main(int, char**) {
   // this is default constructed
   {
     std::jthread t1;
-    std::jthread t2{[] {}};
+    std::jthread t2        = support::make_test_jthread([] {});
     const auto originalId2 = t2.get_id();
     t1.swap(t2);
 
@@ -40,7 +42,7 @@ int main(int, char**) {
 
   // that is default constructed
   {
-    std::jthread t1([] {});
+    std::jthread t1 = support::make_test_jthread([] {});
     std::jthread t2{};
     const auto originalId1 = t1.get_id();
     t1.swap(t2);
@@ -51,8 +53,8 @@ int main(int, char**) {
 
   // both not default constructed
   {
-    std::jthread t1([] {});
-    std::jthread t2{[] {}};
+    std::jthread t1        = support::make_test_jthread([] {});
+    std::jthread t2        = support::make_test_jthread([] {});
     const auto originalId1 = t1.get_id();
     const auto originalId2 = t2.get_id();
     t1.swap(t2);
diff --git a/libcxx/test/support/make_test_thread.h b/libcxx/test/support/make_test_thread.h
index eaf967e2180ede8..6cbdf31c08edd22 100644
--- a/libcxx/test/support/make_test_thread.h
+++ b/libcxx/test/support/make_test_thread.h
@@ -12,13 +12,34 @@
 #include <thread>
 #include <utility>
 
+#include "test_macros.h"
+
 namespace support {
 
+// These functions are used to mock the creation of threads within the test suite.
+//
+// This provides a vendor-friendly way of making the test suite work even on platforms
+// where the standard thread constructors don't work (e.g. embedded environments where
+// creating a thread requires additional information like setting attributes).
+//
+// Vendors can keep a downstream diff in this file to create threads however they
+// need on their platform, and the majority of the test suite will work out of the
+// box. Of course, tests that exercise the standard thread constructors won't work,
+// but any other test that only creates threads as a side effect of testing should
+// work if they use the utilities in this file.
+
 template <class F, class ...Args>
 std::thread make_test_thread(F&& f, Args&& ...args) {
     return std::thread(std::forward<F>(f), std::forward<Args>(args)...);
 }
 
+#if TEST_STD_VER >= 20
+template <class F, class ...Args>
+std::jthread make_test_jthread(F&& f, Args&& ...args) {
+    return std::jthread(std::forward<F>(f), std::forward<Args>(args)...);
+}
+#endif
+
 } // end namespace support
 
 #endif // TEST_SUPPORT_MAKE_TEST_THREAD_H



More information about the libcxx-commits mailing list