From 5911ecd010e5f88483ccfc8c210ae1d682207537 Mon Sep 17 00:00:00 2001 From: Eisenwave Date: Sun, 14 Jun 2026 17:29:57 +0200 Subject: [PATCH 1/4] P2019R9 Thread attributes Editorial notes: * Minor capitalisation fixes. * Used "namespace std" for all class synopses. * Added introductory word "Then:" before the list of notations in the constructor specifications. This is somewhat temporary, as the specification of ill-formed cases should probably move to one of the standard Elements. --- source/support.tex | 1 + source/threads.tex | 217 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 205 insertions(+), 13 deletions(-) diff --git a/source/support.tex b/source/support.tex index 3620c7ad55..5592746f4e 100644 --- a/source/support.tex +++ b/source/support.tex @@ -870,6 +870,7 @@ #define @\defnlibxname{cpp_lib_syncbuf}@ 201803L // also in \libheader{iosfwd}, \libheader{syncstream} #define @\defnlibxname{cpp_lib_task}@ 202506L // also in \libheader{execution} #define @\defnlibxname{cpp_lib_text_encoding}@ 202306L // also in \libheader{text_encoding} +#define @\defnlibxname{cpp_lib_thread_attributes}@ 202606L // also in \libheader{thread} #define @\defnlibxname{cpp_lib_three_way_comparison}@ 201907L // freestanding, also in \libheader{compare} #define @\defnlibxname{cpp_lib_to_address}@ 201711L // freestanding, also in \libheader{memory} #define @\defnlibxname{cpp_lib_to_array}@ 201907L // freestanding, also in \libheader{array} diff --git a/source/threads.tex b/source/threads.tex index 1094c68664..1ddcbf460b 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -1474,11 +1474,22 @@ public: // \ref{thread.thread.id}, class \tcode{thread::id} class id; + + // \ref{thread.attributes}, thread attributes + template<@\libconcept{same_as}@ T> + class name_hint; + template + name_hint(const T*) -> name_hint; + template + name_hint(basic_string) -> name_hint; + class stack_size_hint; + using native_handle_type = @\impdefnc@; // see~\ref{thread.req.native} // construct/copy/destroy thread() noexcept; - template explicit thread(F&& f, Args&&... args); + template + explicit thread(Args&&... args); ~thread(); thread(const thread&) = delete; thread(thread&&) noexcept; @@ -1499,6 +1510,101 @@ } \end{codeblock} +\rSec3[thread.attributes]{Thread attributes} + +\pnum +\defnx{Thread attributes}{thread attribute} +can be used to define additional implementation-defined behaviors +on a \tcode{thread} or \tcode{jthread} object. + +\pnum +The set of \defnadj{thread attribute}{types} contains +\tcode{thread::name_hint}, +\tcode{thread::stack_size_hint}, and +an \impldef{set of additional thread-attributes} set of additional thread-attributes. + +\pnum +When an object of a thread attribute type is passed to +a \tcode{thread} or \tcode{jthread} constructor, +it may affect the new thread's behavior. + +\indexlibraryglobal{thread::name_hint}% +\begin{codeblock} +namespace std { + template<@\libconcept{same_as}@ T> + class thread::name_hint { + public: + constexpr explicit name_hint(basic_string_view n) noexcept; + name_hint(name_hint&&) = delete; + name_hint(const name_hint&) = delete; + + private: + basic_string_view @\exposidnc{name}@; // \expos + }; +} +\end{codeblock} + +\pnum +The \tcode{name_hint} thread attribute can be used to set the name of a thread +for debugging purposes or platform-specific display mechanisms. + +\recommended +Implementations should not store the value of the \tcode{name_hint} attribute +in the \tcode{thread} or \tcode{jthread} object. + +\recommended +%FIXME: ordinary literal encoding? Literal encoding of T? What is "THE literal encoding"? +\exposid{name} is interpreted as a string in the literal encoding. + +\indexlibraryctor{thread::name_hint}% +\begin{itemdecl} +constexpr explicit name_hint(basic_string_view n) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{name} with \tcode{n}. +\end{itemdescr} + +\indexlibraryglobal{thread::stack_size_hint}% +\begin{codeblock} +namespace std { + class thread::stack_size_hint { + public: + constexpr explicit stack_size_hint(size_t s) noexcept; + + private: + size_t @\exposidnc{size}@; // \expos + }; +} +\end{codeblock} + +\pnum +\tcode{stack_size_hint} can be used to configure a desired size in bytes +for platform-specific storage required for a thread. +\begin{note} +Such storage is typically used for variables with automatic storage duration. +\end{note} + +\pnum +The stack size set by the implementation may be adjusted up or down +to meet platform-specific requirements. + +\pnum +If \exposid{size} is zero, the thread attribute is ignored. + +\indexlibraryctor{thread::stack_size_hint}% +\begin{itemdecl} +constexpr explicit stack_size_hint(size_t s) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{size} with \tcode{s}. +\end{itemdescr} + \rSec3[thread.thread.id]{Class \tcode{thread::id}} \indexlibraryglobal{thread::id}% @@ -1675,21 +1781,57 @@ \indexlibraryctor{thread}% \begin{itemdecl} -template explicit thread(F&& f, Args&&... args); +template explicit thread(Args&&... args); \end{itemdecl} \begin{itemdescr} \pnum \constraints -\tcode{remove_cvref_t} is not the same type as \tcode{thread}. +\begin{itemize} +\item +\tcode{Args} is not an empty pack, and +\item +\tcode{remove_cvref_t} is not the same type as \tcode{thread}. +\end{itemize} + +\pnum +Then: +\begin{itemize} +\item +Let $i$ be the smallest value such that +\tcode{decay_t} +is not a thread attribute type. +If no such $i$ exists, the program is ill-formed. +\item +Let \tcode{F} be \tcode{Args...[$i$]}. +\item +Let \tcode{f} be \tcode{args...[$i$]}. +\item +Let \tcode{Attrs} be a pack of the types +\tcode{Args...[$j$]} for each $j$ +such that $0 \leq j < i$. +\item +Let \tcode{attrs} be a pack of the expressions +\tcode{args...[$j$]} for each $j$ +such that $0 \leq j < i$. +\item +Let \tcode{FArgs} be a pack of the types +\tcode{Args...[$j$]} for each $j$ +such that $i < j < \tcode{sizeof...(args)}$. +\item +Let \tcode{fargs} be a pack of the expressions +\tcode{args...[$j$]} for each $j$ +such that $i < j < \tcode{sizeof...(args)}$. +\end{itemize} \pnum \mandates The following are all \tcode{true}: \begin{itemize} \item \tcode{is_constructible_v, F>}, -\item \tcode{(is_constructible_v, Args> \&\& ...)}, and -\item \tcode{is_invocable_v, decay_t...>}. +\item \tcode{(is_constructible_v, FArgs> \&\& ...)}, +\item \tcode{is_invocable_v, decay_t...>}, and +\item no type is present more than once in the pack \tcode{remove_cvref_t}. \end{itemize} \pnum @@ -1697,7 +1839,7 @@ The new thread of execution executes \begin{codeblock} invoke(auto(std::forward(f)), // for \tcode{invoke}, see \ref{func.invoke} - auto(std::forward(args))...) + auto(std::forward(fargs))...) \end{codeblock} with the values produced by \tcode{auto} being materialized\iref{conv.rval} in the constructing thread. @@ -1708,6 +1850,8 @@ \end{note} If the invocation of \tcode{invoke} terminates with an uncaught exception, \tcode{terminate} is invoked\iref{except.terminate}. +The parameters \tcode{attrs...} +can affect the behavior of the new thread\iref{thread.attributes}. \pnum \sync @@ -1951,9 +2095,14 @@ using id = thread::id; using native_handle_type = thread::native_handle_type; + template + using name_hint = thread::name_hint; + using stack_size_hint = thread::stack_size_hint; + // \ref{thread.jthread.cons}, constructors, move, and assignment jthread() noexcept; - template explicit jthread(F&& f, Args&&... args); + template + explicit jthread(Args&&... args); ~jthread(); jthread(const jthread&) = delete; jthread(jthread&&) noexcept; @@ -2006,21 +2155,58 @@ \indexlibraryctor{jthread}% \begin{itemdecl} -template explicit jthread(F&& f, Args&&... args); +template explicit jthread(Args&&... args); \end{itemdecl} \begin{itemdescr} \pnum \constraints -\tcode{remove_cvref_t} is not the same type as \tcode{jthread}. +\begin{itemize} +\item +\tcode{Args} is not an empty pack, and +\item +\tcode{remove_cvref_t} is not the same type as \tcode{jthread}. +\end{itemize} + +\pnum +Then: +\begin{itemize} +\item +Let $i$ be the smallest value such that +\tcode{decay_t} +is not a thread attribute type. +If no such $i$ exists, the program is ill-formed. +\item +Let \tcode{F} be \tcode{Args...[$i$]}. +\item +Let \tcode{f} be \tcode{args...[$i$]}. +\item +Let \tcode{Attrs} be a pack of the types +\tcode{Args...[$j$]} for each $j$ +such that $0 \leq j < i$. +\item +Let \tcode{attrs} be a pack of the expressions +\tcode{args...[$j$]} for each $j$ +such that $0 \leq j < i$. +\item +Let \tcode{FArgs} be a pack of the types +\tcode{Args...[$j$]} for each $j$ +such that $i < j < \tcode{sizeof...(args)}$. +\item +Let \tcode{fargs} be a pack of the expressions +\tcode{args...[$j$]} for each $j$ +such that $i < j < \tcode{sizeof...(args)}$. +\end{itemize} \pnum \mandates The following are all \tcode{true}: \begin{itemize} \item \tcode{is_constructible_v, F>}, -\item \tcode{(is_constructible_v, Args> \&\& ...)}, and -\item \tcode{is_invocable_v, decay_t...> ||} \\ \tcode{is_invocable_v, stop_token, decay_t...>}. +\item \tcode{(is_constructible_v, FArgs> \&\& ...)}, +\item \tcode{is_invocable_v, decay_t...> ||} \\ + \tcode{is_invocable_v, stop_token, decay_t...>}, and +\item no type is present more than once in the pack \tcode{remove_cvref_t}. \end{itemize} \pnum @@ -2029,12 +2215,12 @@ The new thread of execution executes \begin{codeblock} invoke(auto(std::forward(f)), get_stop_token(), // for \tcode{invoke}, see \ref{func.invoke} - auto(std::forward(args))...) + auto(std::forward(fargs))...) \end{codeblock} if that expression is well-formed, otherwise \begin{codeblock} -invoke(auto(std::forward(f)), auto(std::forward(args))...) +invoke(auto(std::forward(f)), auto(std::forward(fargs))...) \end{codeblock} with the values produced by \tcode{auto} being materialized\iref{conv.rval} in the constructing thread. @@ -2045,6 +2231,11 @@ \end{note} If the \tcode{invoke} expression exits via an exception, \tcode{terminate} is called. +The parameters \tcode{attrs...} +%FIXME: why do we have iref{thread.attributes} for thread for the same wording, +%but not for jthread? +%While we're at it, could this not be a note? +can affect the behavior of the new thread. \pnum \sync From 46acb3575919af2bf1e7e44ace6efc259d0333f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Thu, 16 Jul 2026 11:29:40 +0100 Subject: [PATCH 2/4] [thread.attributes] Clarify the character encoding --- source/threads.tex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/threads.tex b/source/threads.tex index 1ddcbf460b..e161a8ca72 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -1553,8 +1553,9 @@ in the \tcode{thread} or \tcode{jthread} object. \recommended -%FIXME: ordinary literal encoding? Literal encoding of T? What is "THE literal encoding"? -\exposid{name} is interpreted as a string in the literal encoding. +\exposid{name} is interpreted as a string +in the associated character encoding +of \tcode{T}~(\tref{lex.string.literal}). \indexlibraryctor{thread::name_hint}% \begin{itemdecl} From 1900bba26a6fb8a025935cdd8670e426df3d5046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Thu, 16 Jul 2026 11:23:58 +0100 Subject: [PATCH 3/4] [thread.attributes] Break into subclauses Even though the original subclause is already quite short, it contains synopses and specifications of multiple (nested) classes, and our method of specification assumes that a subclause only refers to one class (since itemdecls don't state their containing class). --- source/threads.tex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/threads.tex b/source/threads.tex index e161a8ca72..3d93eacf96 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -1512,6 +1512,8 @@ \rSec3[thread.attributes]{Thread attributes} +\rSec4[thread.attributes.general]{General} + \pnum \defnx{Thread attributes}{thread attribute} can be used to define additional implementation-defined behaviors @@ -1528,6 +1530,8 @@ a \tcode{thread} or \tcode{jthread} constructor, it may affect the new thread's behavior. +\rSec4[thread.attributes.hint]{Class \tcode{thread::name_hint}} + \indexlibraryglobal{thread::name_hint}% \begin{codeblock} namespace std { @@ -1568,6 +1572,8 @@ Initializes \exposid{name} with \tcode{n}. \end{itemdescr} +\rSec4[thread.attributes.size]{Class \tcode{thread::stack_size_hint}} + \indexlibraryglobal{thread::stack_size_hint}% \begin{codeblock} namespace std { From 6cb2e4daa49876dc968e1afa6529f0897ca0eeec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Thu, 16 Jul 2026 11:39:55 +0100 Subject: [PATCH 4/4] [thread.{,j}thread.constr] Turn notes about effects of attributes into notes. --- source/threads.tex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/threads.tex b/source/threads.tex index 3d93eacf96..5c5e477a63 100644 --- a/source/threads.tex +++ b/source/threads.tex @@ -1857,8 +1857,10 @@ \end{note} If the invocation of \tcode{invoke} terminates with an uncaught exception, \tcode{terminate} is invoked\iref{except.terminate}. +\begin{note} The parameters \tcode{attrs...} can affect the behavior of the new thread\iref{thread.attributes}. +\end{note} \pnum \sync @@ -2238,11 +2240,10 @@ \end{note} If the \tcode{invoke} expression exits via an exception, \tcode{terminate} is called. +\begin{note} The parameters \tcode{attrs...} -%FIXME: why do we have iref{thread.attributes} for thread for the same wording, -%but not for jthread? -%While we're at it, could this not be a note? -can affect the behavior of the new thread. +can affect the behavior of the new thread\iref{thread.attributes}. +\end{note} \pnum \sync