160 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// UNSUPPORTED: no-threads10// UNSUPPORTED: c++03, c++11, c++14, c++1711 12// template<class F, class... Args>13// explicit jthread(F&& f, Args&&... args);14 15#include <cassert>16#include <stop_token>17#include <thread>18#include <type_traits>19#include <utility>20 21#include "test_macros.h"22 23template <class... Args>24struct Func {25 void operator()(Args...) const;26};27 28// Constraints: remove_cvref_t<F> is not the same type as jthread.29static_assert(std::is_constructible_v<std::jthread, Func<>>);30static_assert(std::is_constructible_v<std::jthread, Func<int>, int>);31static_assert(!std::is_constructible_v<std::jthread, std::jthread const&>);32 33// explicit34template <class T>35void conversion_test(T);36 37template <class T, class... Args>38concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };39 40static_assert(!ImplicitlyConstructible<std::jthread, Func<>>);41static_assert(!ImplicitlyConstructible<std::jthread, Func<int>, int>);42 43int main(int, char**) {44 // Effects: Initializes ssource45 // Postconditions: get_id() != id() is true and ssource.stop_possible() is true46 // and *this represents the newly started thread.47 {48 std::jthread jt{[] {}};49 assert(jt.get_stop_source().stop_possible());50 assert(jt.get_id() != std::jthread::id());51 }52 53 // The new thread of execution executes54 // invoke(auto(std::forward<F>(f)), get_stop_token(), auto(std::forward<Args>(args))...)55 // if that expression is well-formed,56 {57 int result = 0;58 std::jthread jt{[&result](std::stop_token st, int i) {59 assert(st.stop_possible());60 assert(!st.stop_requested());61 result += i;62 },63 5};64 jt.join();65 assert(result == 5);66 }67 68 // otherwise69 // invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...)70 {71 int result = 0;72 std::jthread jt{[&result](int i) { result += i; }, 5};73 jt.join();74 assert(result == 5);75 }76 77 // with the values produced by auto being materialized ([conv.rval]) in the constructing thread.78 {79 struct TrackThread {80 std::jthread::id threadId;81 bool copyConstructed = false;82 bool moveConstructed = false;83 84 TrackThread() : threadId(std::this_thread::get_id()) {}85 TrackThread(const TrackThread&) : threadId(std::this_thread::get_id()), copyConstructed(true) {}86 TrackThread(TrackThread&&) : threadId(std::this_thread::get_id()), moveConstructed(true) {}87 };88 89 auto mainThread = std::this_thread::get_id();90 91 TrackThread arg1;92 std::jthread jt1{[mainThread](const TrackThread& arg) {93 assert(arg.threadId == mainThread);94 assert(arg.threadId != std::this_thread::get_id());95 assert(arg.copyConstructed);96 },97 arg1};98 99 TrackThread arg2;100 std::jthread jt2{[mainThread](const TrackThread& arg) {101 assert(arg.threadId == mainThread);102 assert(arg.threadId != std::this_thread::get_id());103 assert(arg.moveConstructed);104 },105 std::move(arg2)};106 }107 108#if !defined(TEST_HAS_NO_EXCEPTIONS)109 // [Note 1: This implies that any exceptions not thrown from the invocation of the copy110 // of f will be thrown in the constructing thread, not the new thread. - end note]111 {112 struct Exception {113 std::jthread::id threadId;114 };115 struct ThrowOnCopyFunc {116 ThrowOnCopyFunc() = default;117 ThrowOnCopyFunc(const ThrowOnCopyFunc&) { throw Exception{std::this_thread::get_id()}; }118 void operator()() const {}119 };120 ThrowOnCopyFunc f1;121 try {122 std::jthread jt{f1};123 assert(false);124 } catch (const Exception& e) {125 assert(e.threadId == std::this_thread::get_id());126 }127 }128#endif // !defined(TEST_HAS_NO_EXCEPTIONS)129 130 // Synchronization: The completion of the invocation of the constructor131 // synchronizes with the beginning of the invocation of the copy of f.132 {133 int flag = 0;134 struct Arg {135 int& flag_;136 Arg(int& f) : flag_(f) {}137 138 Arg(const Arg& other) : flag_(other.flag_) { flag_ = 5; }139 };140 141 Arg arg(flag);142 std::jthread jt(143 [&flag](const auto&) {144 assert(flag == 5); // happens-after the copy-construction of arg145 },146 arg);147 }148 149 // Per https://eel.is/c++draft/thread.jthread.class#thread.jthread.cons-8:150 //151 // Throws: system_error if unable to start the new thread.152 // Error conditions:153 // resource_unavailable_try_again - the system lacked the necessary resources to create another thread,154 // or the system-imposed limit on the number of threads in a process would be exceeded.155 //156 // Unfortunately, this is extremely hard to test portably so we don't have a test for this error condition right now.157 158 return 0;159}160