1. Revision History
1.1. Revision 0 - May 27th, 2025
-
Initial release. ✨
2. Introduction & Motivation
It is impossible to set the name, stack size, stack address + size, (scheduler) priority, processor affinity, and more of a thread in standard C or C++, despite this being both widely available and critical necessities in C and C++ programs. After Bruce Dawson pinged a few C++ standards committee members in circa 2019/2020 asking about thread names, C++ -- particularly, Corentin Jabot -- embarked on an adventure to provide such attributes to their version of C11’s threads,
. The effort was marred by platform incompatibilities, specific wording nits, and -- most glaringly -- ABI issues and API wars, resulting in no less than three (3) papers:
They take many approaches to the problem, trying to offer a varying level of ease of use alongside high type safety. The most mature of the papers -- P2019 -- has reached its eighth revision after first being published on March 2nd, 2020 and has not seen success. In that time:
-
Programming Languages and their standard libraries such as Swift, Java, C#, Haskell, C#, Rust, D, Perl, and many other languages either already supported or support using threads with an explicit stack size parameter and names;
-
For C and C++, augmentation/replacement standard libraries like Qt, Boost, POCO, Chromium, Bloomberg Basic Development Environment, Intel TBB, Folly, TensorFlow, KallistaOS (Dreamcast OS), tinycthread, and many replace C++11 and C11 threads with their own abstraction in order to do threading.
Importantly, in the case of C11 and C++11 threads, many of them need to rewrite the entire set of thread primitives because of (a) poor support for
until very recently (as of writing: only Apple does not support C11
; MSVC runtime, OpenBSD, FreeBSD, NetBSD, glibc, musl-libc, Bionic (Android), and more all support
); and, (b) many platforms do not support changing stack sizes, names, and more after thread creation. Therefore, an API cannot simply add
or
, or similar stack size parameters, with a thread handle parameter as shown by the POSIX Threads implementations. In fact, many implementations have wildly different getting and setting properties and most -- including Windows Threads -- mostly require you to set that information on thread creation, not after-the-fact. A table -- from P2019 -- is reproduced here:
Platform | At Creation | After | Query |
---|---|---|---|
Linux | 0
|
| |
QNX |
|
| |
NetBSD |
|
| |
Win32 | 1
| 1
| |
Darwin | 2
|
| |
Fuchsia |
| ||
Android | 3
| ||
FreeBSD |
| ||
OpenBSD |
| ||
RTEMS 4 |
|
| |
FreeRTOS |
|
| |
VxWorks |
| ||
eCos |
| ||
Plan 9 | 5
| 5
| |
Haiku |
|
|
|
Keil RTX |
|
| |
WebAssembly |
1 - Since Windows 10 1607 - In older versions, a name can be set only when a debugger is attached, by throwing an exception from the calling thread. See [https://stackoverflow.com/a/59490438/877556](Windows Documentation) and this article by Bruce Dawson.
2 - Can only be called from the new thread.
3 - See https://stackoverflow.com/a/59490438/877556.
4 - Since 2017.
5 - Can only be called from the new thread.
Similarly, a lot of APIs suffer from ABI issues;
is meant to be ABI-resistant and an opaque type, but implementation still have to contend with issues improving or widening the amount of information it can store without breaking things. C++
’s and P2019, in its attempts to be ABI-resistant, have met a ton of pushback from all kinds of people trying to simplify or reduce the API space and avoid the templated constructors, desperate for a simpler interface and -- in some cases -- ignoring ABI issues with structures similar in purpose to
. This has been a consistent theme even among early papers that simply just tried to standardize existing practice without addressing their deficiencies, such as C++'s even earlier papers [P0320] and [N2178].
For C11 threads, these same concerns were voiced during the 2019 Ithaca discussion of Kamil Ryatorwski’s paper [N2419].
It is clear that this area has strong vendor concerns around extensibility, and even strong constraints around the ability to do this both before thread startup and during thread running.
This paper provides an ABI that is standards-expandable, vendor-extensible, and resistant to Application Binary Interface (ABI) lock-in. It allows vendors to provide more direct ways to adding parameters without compromising current implementations and their structures.
3. Design
The design is based off an existing API that has proved successful in not replacing but supplementing existing implementations without causing undue burden or breaking the ABIs of existing interfaces. It has been shown to:
-
set name (in UTF-8/16/32, Execution, Wide Execution, and "direct" forms);
-
set detached state (to have a thread start in a detached state, (critical for MSVC));
-
set the stack size (taken as a parameter during startup for MSVC and for POSIX threads);
-
and, set the stack buffer (a pointer plus size, usable for POSIX threads but not necessarily for MSVC itself).
The API relies on 2 core language features to work:
-
the pointer to the first member of a structure is identical to the pointer of the structure itself;
-
and, casting from that first member to a pointer based on the value of that first member is well-defined behavior.
In particular, there is a
enumeration type that is the first member of a structure. It is a tag that tells the implementation what to cast a pointer of that type to, in order to interpret the full structure. It is also the first member of every thread attribute structure, which means it has the same address as the structure itself. This allows an array of
pointers to be passed to
that can be used as a standard and well-defined type-punning point and a sort of "tag" to determine which structure to cast to internally. The full enumeration and API looks as such:
#include <threads.h>#include <stdint.h>typedef enum thrd_attr_kind : int_least32_t { thrd_attr_kind_native_name = 0 , thrd_attr_kind_native_name_sized = 1 , thrd_attr_kind_mcname = 2 , thrd_attr_kind_mcname_sized = 3 , thrd_attr_kind_mwcname = 4 , thrd_attr_kind_mwcname_sized = 5 , thrd_attr_kind_c8name = 6 , thrd_attr_kind_c8name_sized = 7 , thrd_attr_kind_c16name = 8 , thrd_attr_kind_c16name_sized = 9 , thrd_attr_kind_c32name = 10 , thrd_attr_kind_c32name_sized = 11 , thrd_attr_kind_stack_size = 32 , thrd_attr_kind_detached = 256 , thrd_attr_kind_implementation_defined = 0xFFFF , } thrd_attr_kind ; typedef struct thrd_attr_native_name { thrd_attr_kind kind ; const void * name ; } thrd_attr_native_name ; typedef struct thrd_attr_native_name_sized { thrd_attr_kind kind ; size_t size ; const void * name ; } thrd_attr_native_name_sized ; typedef struct thrd_attr_mcname { thrd_attr_kind kind ; const char * name ; } thrd_attr_mcname ; typedef struct thrd_attr_mcname_sized { thrd_attr_kind kind ; size_t size ; const char * name ; } thrd_attr_mcname_sized ; typedef struct thrd_attr_mwcname { thrd_attr_kind kind ; const wchar_t * name ; } thrd_attr_mwcname ; typedef struct thrd_attr_mwcname_sized { thrd_attr_kind kind ; size_t size ; const wchar_t * name ; } thrd_attr_mwcname_sized ; typedef struct thrd_attr_c8name { thrd_attr_kind kind ; const char8_t * name ; } thrd_attr_c8name ; typedef struct thrd_attr_c8name_sized { thrd_attr_kind kind ; size_t size ; const char8_t * name ; } thrd_attr_c8name_sized ; typedef struct thrd_attr_c16name { thrd_attr_kind kind ; const char16_t * name ; } thrd_attr_c16name ; typedef struct thrd_attr_c16name_sized { thrd_attr_kind kind ; size_t size ; const char16_t * name ; } thrd_attr_c16name_sized ; typedef struct thrd_attr_c32name { thrd_attr_kind kind ; const char32_t * name ; } thrd_attr_c32name ; typedef struct thrd_attr_c32name_sized { thrd_attr_kind kind ; size_t size ; const char32_t * name ; } thrd_attr_c32name_sized ; typedef struct thrd_attr_stack_size { thrd_attr_kind kind ; size_t size ; } thrd_attr_stack_size ; typedef struct thrd_attr_detached { thrd_attr_kind kind ; bool detached ; } thrd_attr_detached ; typedef int ( thrd_attr_err_func_t )( const thrd_attr_kind [ static 1 ], int err , void * err_func_arg ); int thrd_create_attrs ( thrd_start_func func , void * arg , size_t n , const thrd_attr_kind * attrs [ static n ]); int thrd_create_attrs_err ( thrd_start_func func , void * arg , size_t n , const thrd_attr_kind * attrs [ static n ], thrd_attr_err_func_t * err_func , void * err_func_arg );
3.1. Thread Attribute Kind & ABI-Resistance
The thread attribute kind enumeration has 65,536 reserved values [0, 65’535] for the standard, and 2,147,418,112 reserved values [65’636, 2’147’483’647] for implementation-defined attributes. An implementation can add more attributes to cover more cases and information that the standard could not conceivably provide a unified interface for, such as:
-
stack address + stack size (POSIX Threads);
-
starting "Suspended" (Win32 Threads);
-
setting implementation-specific processor/core affinity (Win32 and POSIX Threads);
-
setting implementation-specific scheduler or thread priority (many different values for POSIX, fixed set of enumeration values for Win32);
-
setting commit vs. reserved stack size values (Win32 Threads);
and so much more. 65 thousand values for the standard more than covers the set of interfaces WG14 could possibly manage to standardize (one per standard structure), and 2.1 billion values (one per implementation-defined structure) gives implementations plenty of room to play around and not conflict with each other (hopefully they are nice and send each other e-mails so avoid potential conflicts, as they should in this day and age).
This design is also ABI-resistant. If structures need to change, rather than trying to change them in-line and suffering from a problem of adding members or otherwise disturbing how reads and writes of such structures will be treated, a new one can be added seamlessly and without pain both on the standard side and the implementation side.
Each enumeration is given a fixed value to prevent ABI disagreements for serialization purposes between platforms. The values themselves do not matter, and so any of them can be adjusted. The enumeration should fit a 32-bit signed integer, and so platforms with a 16 bit integer may need to use implementation-specific tricks or rely on C23’s enumeration specification abilities.
3.2. Thread Attributes
Thread attributes are structures paired with one of the enumeration names that represent a specific kind of transformation to a thread. The standard will define several thread attribute structures, though the specification says that an implementation can process -- but ignore -- the desired effects of each attribute. This is mostly to allow for embedded implementations that do not care for keeping this information around, and keeps the cost of this paper to 0 for implementations that do not care.
There are 14 different provided structures and 14 different enumeration constants to go with them:
-
: takes athrd_attr_stack_size
and communicates the size (in bytes) of the stack.size_t -
: takes athrd_attr_detached
and communicates that the thread should be detached at the very start.bool -
,thrd_attr_c8name
: takes athrd_attr_c8name_sized
or aconst char8_t *
+const char8_t *
, respectively. It represents a UTF-8-encoded name. The name is converted to the desired internal encoding of the thread name. Must be terminated by a nullsize_t
value, or not contain a nullchar8_t
value within the buffer denoted by the range, respectively. The pointer may be null.char8_t -
,thrd_attr_c16name
: takes athrd_attr_c16name_sized
or aconst char16_t *
+const char16_t *
, respectively. It represents a UTF-16-encoded name. The name is converted to the desired internal encoding of the thread name. Must be terminated by a nullsize_t
value, or not contain a nullchar16_t
value within the buffer denoted by the range, respectively. The pointer may be null.char16_t -
,thrd_attr_c32name
: takes athrd_attr_c32name_sized
or aconst char32_t *
+const char32_t *
, respectively. It represents a UTF-32-encoded name. The name is converted to the desired internal encoding of the thread name. Must be terminated by a nullsize_t
value, or not contain a nullchar32_t
value within the buffer denoted by the range, respectively. The pointer may be null.char32_t -
,thrd_attr_mcname
: takes athrd_attr_mcname_sized
or aconst char *
+const char *
, respectively. It represents an execution encoding-encoded name. The name is converted to the desired internal encoding of the thread name. Must be terminated by a nullsize_t
value, or not contain a nullchar
value within the buffer denoted by the range, respectively. The pointer may be null.char -
,thrd_attr_mwcname
: takes athrd_attr_mwcname_sized
or aconst wchar_t *
+const wchar_t *
, respectively. It represents a wide execution-encoded name. The name is converted to the desired internal encoding of the thread name. Must be terminated by a nullsize_t
value, or not contain a nullwchar_t
value within the buffer denoted by the range, respectively. The pointer may be null.wchar_t -
,thrd_attr_native_name
: takes athrd_attr_native_name_sized
or aconst void *
+const void *
, respectively, and has its value copied directly into the thread. It’s performs no transformations or permutations on the name. The pointed-to data must not have a sequence of zero bytes of a length chosen by the implementation (typically 1 on POSIX implementations and 2 on Windows implementations). This is explicitly for sending data directly to the threading API without any conversions.size_t
3.2.1. thrd_attr_stack_size
This attribute is necessary because most platforms have a way to set the stack size, but sometimes can only do it at the start of the thread. Therefore, it needs to be passed in at creation time and let the implementation decide how and when to apply it. There are additional APIs for controlling the entire stack (with both a pointer and a size), but this is not as universal so a
attribute with both a
and a
are not being proposed.
Note that the
passed by
is, more or less, a suggestion: many platforms will change or round the value up to a lower limit, or make sure it is a multiple of an internal implementation size or alignment (e.g., page-aligned or 64-byte aligned). Additionally, implementations may also implement guard pages around the stack as well. Therefore, even if the implementation honors it, the final size is still entirely implementation-defined, and the specification is written to reflect this.
3.2.2. thrd_attr_detached
This attribute is necessary because some platforms (e.g., Windows/MSVC) have issues with getting the proper thread control rights after creating the thread from a different thread in order to properly detach the thread. With this passed to the creation of the thread, a thread can be detached properly and follow POSIX semantics on creation, whereas otherwise it might not work from sinde the given
, as documented by Visual C++ Library Maintainer Charlie Barto|:
A key difference between our implementation and C11 threads implementations based on pthreads is that threads can not detach themselves using
and
thrd_current () . This is because of a fundamental difference in how threads work on Windows vs Unix descendants and we would require a shared datastructure that tracks thread handles to implement the typical behavior.
thrd_detach ()
Given this behavior, it’s better to provide this as an on-creation technique, where the original thread’s
is still present and this can be invoked properly.
3.2.3. thrd_attr_ { mwc / mc / c ( 8 | 16 | 32 | native ) name }
The name attributes are necessary because platforms differ wildly in how they can set the name. Some can only set the name inside of the thread after it has been started (many POSIX thread implementations); others can create a thread in suspended mode and then set all of the necessary information (Win32 Threads, IBM-styled POSIX threads).
Furthermore, the actual internal storage, encoding, and more of threads varies across implementations. Win32 stores 16-bit
/
internally, which is more suitable to a UTF-16 or Wide Literal/Execution Encoding string on their platforms, while POSIX platforms tend to simply absorb any byte string with no encoding except that \
is not part of it. Most of these APIs rely explicitly on null termination.
Thusly, to not unduly disadvantage any platform, we provide the 5 typical encodings that can be mapped to the internals of the implementation however they like. For example, POSIX platforms can take UTF-32, UTF-16, and Wide Execution encoded strings and convert them to UTF-8 or Execution encoded byte strings so that the null termination character is only a single byte and the whole name can be serialized properly. Windows can take UTF-32, UTF-8, and Execution encoded strings and convert them into 16-bit UTF-16/Wide Execution strings so that it has a proper 2-byte null terminator.
Finally, for individuals who feel confident that their name will closely match the platform’s semantics, there is a plain
, which holds a
. This can just be copied directly into an implementation, and the consequences will be whatever happens.
As a very, very important point: the strings passed in through these functions have no lifetime implications after
returns. They can be rewritten, changed, adjusted, or freed once the
function returns: all implementations tend to copy the names into some form of internal data anyways.
If there is appetite to provide a string just via pointer where the lifetime of that string must be kept alive for the duration of the thread, additional attribute types can be created to offer that to standard users. The goal would, of course, be for implementations that do not have any internal storage to hold onto the smallest possible string VIA pointer alone and simply let the user manage both the data and the lifetime. However, we have encountered no platforms that have requested this hypothetical optimization, so a special
structure has not been provided.
3.3. "Where is the get
-style API?"
We do not provide the
/
APIs in this paper. Much like thread creation, there are various APIs for getting a thread name, or its priority, or whether or not its detached. Some do not provide any API at all (a few of the BSDs). Getters also include the issue of synchronization, or possible Time Of Check vs. Time Of Use (TOCTOU) issues.
We leave getter/query APIs to future papers after ironing out this paper.
3.4. "How does this API handle errors?"
An important part of having thread attributes is recognizing that implementations have wildly different handling strategies, even for attributes that are spiritually the same on most platforms. For example,
is something that can be honored on all implementations we know about, including the Dreamcast Console implementation of threads. Unfortunately, the exact handling of the number actually differs on platforms.
Win32 thread implementations will round the value up as alignment or to meet a minimum stack size. But, POSIX thread implementations simply error and ignore the provided size, and in some versions of glibc it will round the value down for alignment purposes (despite that not being allowed by the specification). This fact means that a number passed in to
and propagated through the
function can and will silently fail on a POSIX implementation while working on a Win32 implementation.
The solution to this problem is the specification of a
function type, and taking a pointer to such a function in any given
. There will be cases where users do not care at all about attribute errors, either because they checked beforehand or because such failures don’t really concern them at all and the usage of such attributes are just nonchalant suggestions: in those cases, users can pass a function
to
, pass NULL
which will do the same thing, or simply call
which will do the same thing.
Similarly, users can pick and choose the kinds of errors they care about:
#include <threads.h>#include <stdcountof.h>#include <stdio.h?>inline static int thrd_main ( void * arg ) { ( void ) arg ; /* ... */ return 0 ; } int handle_attribute_errors ( const thrd_attr_kind * attr_kind , int err , void * userdata ) { ( void ) userdata ; if ( * attr_kind == thrd_attr_kind_c32name ) { // we don't care if name is unrecognized: not critical to us printf ( "We could not set the name of the thread " "with a UTF-32 string (%d)" , err ); return thrd_success ; } else if ( * attr_kind == thrd_attr_kind_stack_size ) { // this matters to us: pipe the error through return err ; } // attribute we either do not know or do not care about: just assume success return thrd_success ; } int main ( void ) { thrd_t t0 = {}; thrd_attr_c32name name_attr = { . kind = thrd_attr_kind_c32name , . name = U"meow?!" }; thrd_attr_stack_size stack_size_attr = { . kind = thrd_attr_kind_stack_size , . size = 1024 , // this is too small for POSIX threads // and will cause an error }; // some random attribute to illustrate // that we don't recognize it, // and that we don't care. const struct thrd_attr_priority { thrd_attr_kind kind ; int priority ; } priority_attr = { // some custom attribute or whatever . kind = ( ztdc_thrd_attr_kind ) 0x12345678 , . priority = INT_MAX , }; const ztdc_thrd_attr_kind * attrs [] = { & priority_attr . kind , & stack_size_attr . kind , & name_attr . kind , }; int create_err = thrd_create_attrs_err ( & t0 , thrd_main , NULL, countof ( attrs ), attrs , handle_attribute_errors , NULL); assert ( create_err == thrd_success ); int res0 = 0 ; thrd_join ( t0 , & res0 ); assert ( res0 == 0 ); return 0 ; }
This code will stop thread creation if
is not handled properly (e.g., the implementation does not recognize it OR the implementation tries to set the stack size but reports an error, like
would). It will explicitly acknowledge and ignore an issue with the thread name (but ignore it for the purposes of the implementation). This gives users the ultimate control over what is going on, in the form of a final say on whether or not thread creation should proceed.
The function passed in,
, is invoked on the same thread as the call to
. This is to prevent needing additional synchronization accesses and checks.
3.5. Other Designs?
There are other ways this API can be shaped. Unfortunately, most of those changes are not worth their bang or their buck: see the library documentation here.
4. Implementation Experience
This was implemented in [ztd.thread] as a proof of concept. It is tested on Win32, MacOS/Darwin, and Ubuntu/glibc in an automated fashion (GitHub CI) and by-hand on Win32/ARM64, Debian/glibc, Debian/musl-libc, and FreeBSD.
Documentation for the library is contained here.
5. Wording
The following wording is against the latest draft of the C standard.
5.1. Modify §7.29.1 "Introduction" to add the new structures and an enumeration
...
thrd_start_t which is the function pointer type
that is passed to
int ( * )( void * ) to create a new thread;
thrd_create thrd_attr_kind which is an enumeration type that is compatible with
and used to provide type associations for the functions
int_least32_t and
thrd_create_attrs ;
thrd_create_attrs_err thrd_attr_err_func_t which is the function pointer type
that is passed to
int ( const thrd_attr_kind * , int , void * ) to handle errors in thread attribute application;
thrd_create_attrs_err typedef struct thrd_attr_native_name { thrd_attr_kind kind ; const void * name ; } thrd_attr_native_name ; typedef struct thrd_attr_native_name_sized { thrd_attr_kind kind ; size_t size ; const void * name ; } thrd_attr_native_name_sized ; typedef struct thrd_attr_mcname { thrd_attr_kind kind ; const char * name ; } thrd_attr_mcname ; typedef struct thrd_attr_mcname_sized { thrd_attr_kind kind ; size_t size ; const char * name ; } thrd_attr_mcname_sized ; typedef struct thrd_attr_mwcname { thrd_attr_kind kind ; const wchar_t * name ; } thrd_attr_mwcname ; typedef struct thrd_attr_mwcname_sized { thrd_attr_kind kind ; size_t size ; const wchar_t * name ; } thrd_attr_mwcname_sized ; typedef struct thrd_attr_c8name { thrd_attr_kind kind ; const char8_t * name ; } thrd_attr_c8name ; typedef struct thrd_attr_c8name_sized { thrd_attr_kind kind ; size_t size ; const char8_t * name ; } thrd_attr_c8name_sized ; typedef struct thrd_attr_c16name { thrd_attr_kind kind ; const char16_t * name ; } thrd_attr_c16name ; typedef struct thrd_attr_c16name_sized { thrd_attr_kind kind ; size_t size ; const char16_t * name ; } thrd_attr_c16name_sized ; typedef struct thrd_attr_c32name { thrd_attr_kind kind ; const char32_t * name ; } thrd_attr_c32name ; typedef struct thrd_attr_c32name_sized { thrd_attr_kind kind ; size_t size ; const char32_t * name ; } thrd_attr_c32name_sized ; typedef struct thrd_attr_stack_size { thrd_attr_kind kind ; size_t size ; } thrd_attr_stack_size ; typedef struct thrd_attr_detached { thrd_attr_kind kind ; bool detached ; } thrd_attr_detached ; which represent standard thread attribute structures that are described in 7.29.2✨;
...
The enumeration constants part of
are
thrd_attr_kind thrd_attr_kind_native_name = 0 thrd_attr_kind_native_name_sized = 1 thrd_attr_kind_mcname = 2 thrd_attr_kind_mcname_sized = 3 thrd_attr_kind_mwcname = 4 thrd_attr_kind_mwcname_sized = 5 thrd_attr_kind_c8name = 6 thrd_attr_kind_c8name_sized = 7 thrd_attr_kind_c16name = 8 thrd_attr_kind_c16name_sized = 9 thrd_attr_kind_c32name = 10 thrd_attr_kind_c32name_sized = 11 thrd_attr_kind_stack_size = 32 thrd_attr_kind_detached = 256 thrd_attr_kind_implementation_defined = 0xFFFF and are set as the value of the
member field of thread attributes described in 7.29.2✨. They provide a way to uniquely identify each thread attribute, except for the
kind which has no associated structure.
thrd_attr_kind_implementation_defined
5.2. Add a new section §7.29.2✨ "Thread attributes" to make it rely on wording moved to other function
7.29.5.2✨Thread AttributesDescriptionThe thread attribute enumeration and structures listed in the preceding subclause correspond to specific modifications that can be applied to a thread when passed as part of the
array for
attrs and
thrd_create_attrs function calls. Standard thread attributes are the structures and enumerations mentioned in this and the preceding subclause. Any other thread attribute structures and enumeration constants defined by the implementation as prescribed by this subclause are implementation thread attributes. The standard thread attributes shall be available when
thrd_create_attrs_err is included, but the effects each has on any given thread are conditionally supported and implementation-defined. Implementation thread attributes may not be available when
< threads . h > is included and have implementation-defined effects, if any.
< threads . h > For
and
thrd_create_attrs function calls, the behavior is unspecified if more than one of
thrd_create_attrs_err ,
thrd_attr_native_name ,
thrd_attr_mcname ,
thrd_attr_mwcname ,
thrd_attr_c8name ,
thrd_attr_c16name , or any of their
thrd_attr_c32name counterparts, are used in the same
_sized array. The behavior is unspecified if more than one of any standard thread attribute is used in the
attrs array.
attrs With the exception of
, for every
thrd_attr_kind_implementation_defined enumeration constant defined in this document of the form
thrd_attr_kind , where
thrd_attr_kind_X is the rest of the identifier, there is a corresponding
X complete object of structure type. The
thrd_attr_X member must be set to the corresponding
kind enumeration constant’s value for every
thrd_attr_kind_X structure before use with the
thrd_attr_X and
thrd_create_attrs functions, otherwise the behavior is undefined.
thrd_create_attrs_with Each identifier of the form
, where
thrd_attr_kind_X is the rest of the identifier, is reserved for use by this document as a thread attribute enumeration constant whose value shall be less than
X . Each identifier of the form
thrd_attr_kind_implementation_defined for the corresponding structure is also reserved by this document as a thread attribute structure. Thread attribute enumeration constants and structures defined by this document shall not have
thrd_attr_X begin with the character
X (U+005F LOW LINE). An implementation may define subsequent
_ enumeration constants as part of the
thrd_attr_kind_Y enumerated type, where
thrd_attr_kind is the rest of the identifier, for use as implementation-defined thread attributes. The value of such an enumeration constant shall be greater than
Y .
thrd_attr_kind_implementation_defined The thread attributes
,
thrd_attr_mcname ,
thrd_attr_mwcname ,
thrd_attr_c8name , and
thrd_attr_c16name provide a thread with a name encoded in the execution, wide execution, UTF-8, UTF-16, and UTF-32 encodings (6.2.9), respectively, as a null terminated string. If the value of the
thrd_attr_c32name field is a null pointer, then no change occurs. Otherwise, the value pointed to by the
name field must be valid for the function call. The thread attributes of the same name but suffixed with
name provide a thread with the same information, except that the
_sized field can be a non-null terminated pointer and whose size is denoted by the field
name . For these
size thread attributes, the
_sized field may not contain an appropriately typed null terminator in the range denoted by the
name and
name fields. The range denoted by
size , or by
name and
name for the
size -suffixed thread attributes, are not accessed by the implementation after the
_sized or
thrd_create_attrs function calls return.
thrd_create_attrs_err The thread attribute
provides an pointer to
thrd_attr_native_name whose value is meant to be used directly by the implementation to set the name of the thread. It has an implementation-defined encoding. If the
const void field is a null pointer, there is no effect. Otherwise, the pointer shall be null terminated by a sequence of 0 bytes. The number of 0-valued bytes that make up the sequence of that null terminator is implementation-defined. The thread attribute
name is the same, except that the number of bytes in the
thrd_attr_native_name_sized field is equal to the value of the
name field. The
size field must not contain the implementation-defined null termination byte sequence in the range denoted by the
name and
name fields. The range denoted by
size , or by
name and
name for
size , are not accessed by the implementation after the
thrd_attr_native_name_sized or
thrd_create_attrs function calls return.
thrd_create_attrs_err Recommended PracticeThe thread attributes
,
thrd_attr_mcname ,
thrd_attr_mwcname ,
thrd_attr_c8name ,
thrd_attr_c16name , and their
thrd_attr_c32name -suffixed analogues, should have their names converted to any unspecified yet appropriate encoding, if necessary, and set as the name of the thread. The thread attributes
_sized and
thrd_attr_native_name should have their name set without any conversion, if possible, as the name of the thread. An implementation should make the name available when thread information, possibly obtained by unspecified or implementation-defined means, is inspected.
thrd_attr_native_name_sized The thread attribute
provides a recommendation for the size of the storage used for the thread with respect to automatic storage duration lifetime declarations for that thread.
thrd_attr_stack_size The thread attribute
provides a value which determines whether or not a thread should be created in the detached state, or should immediately be detached upon creation as-if by calling
thrd_attr_detached with the thread that is being created.
thrd_detach
5.3. Modify §7.29.5.1 "The thrd_create
function" to make it rely on wording moved to other function
7.29.5.1Thefunction
thrd_create Synopsis#include <threads.h>int thrd_create ( thrd_t * thr , thrd_start_t func , void * arg ); DescriptionThe
function creates a new thread executing func(arg). If the
thrd_create function succeeds, it sets the object pointed to
thrd_create to the identifier of the newly created thread. (A thread’s identifier can be reused for a different thread once the original thread has exited and either been detached or joined to another thread.) The completion of the
by thr function synchronizes with the beginning of the execution of the new thread.
thrd_create Returning from
has the same behavior as invoking
func with the value returned from
thrd_exit .
func ReturnsThe
function returns
thrd_create on success, or
thrd_success if no memory could be allocated for the thread requested, or
thrd_nomem if the request could not be honored.
thrd_error The
function returns and is equivalent to
thrd_create .
thrd_create_attrs_err ( thr , func , arg , 0 , nullptr , nullptr , nullptr )
5.4. Add a new sub-clause "§7.29.5.2✨ The thrd_create_attrs
function" after "The thrd_create
function"
7.29.5.2✨Thefunction
thrd_create_attrs Synopsis#include <threads.h>int thrd_create_attrs ( thrd_t * thr , thrd_start_t func , void * arg , size_t attrs_n , const thrd_attr_kind * attrs [ static attrs_n ]); DescriptionThe
function returns and is equivalent to
thrd_create_attrs .
thrd_create_attrs_err ( thr , func , arg , attrs_n , attrs , nullptr , nullptr )
5.5. Add a new sub-clause "§7.29.5.3✨ The thrd_create_attrs_err
function" after "The thrd_create_attrs
function"
7.29.5.3✨Thefunction
thrd_create_attrs_err Synopsis#include <threads.h>int thrd_create_attrs_err ( thrd_t * thr , thrd_start_t func , void * arg , size_t attrs_n , const thrd_attr_kind * attrs [ static attrs_n ] thrd_attr_err_func_t * err_func , void * err_func_arg ); DescriptionThe
function creates a new thread executing
thrd_create_attrs_err after applying any modifications to the thread through the thread attributes in the
func ( arg ) array, if any. If
attrs is a null pointer, this function behaves as if
err_func points to an appropriately typed function that returns
err_func no matter the input. If the
thrd_success function succeeds, it sets the object pointed to by
thrd_create_attrs_err to the identifier of the newly created thread. (A thread’s identifier can be reused for a different thread once the original thread has exited and either been detached or joined to another thread.)
thr If
is a null pointer, no thread attributes are processed. If
attrs is 0, no thread attributes are processed. Each pointer
attrs_n in the range
p must point to a thread attribute or be a null pointer. If
[ attrs , attrs + attrs_n ) is a null pointer, it is ignored and has no effect. Otherwise, if
p is a thread attribute that is not honored by the implementation,
* p is called with the arguments
err_func ,
p , and
thrd_error . Otherwise, the implementation processes the attribute and affects the thread according to the semantics in 7.29.5.2✨. If the implementation encounters an error during the processing,
err_func_arg is invoked with:
err_func
the first argument as
;
p the second argument as an unspecified value of one of
,
thrd_error ,
thrd_nomem , or
thrd_timedout ; and,
thrd_busy the third argument as
.
err_func_arg
returns one of
err_func ,
thrd_error ,
thrd_nomem , or
thrd_timeout . If
thrd_busy returns a value that is not
err_func ,
thrd_success returns that value and the thread is not created.
thrd_create_attrs_err Returning from
has the same behavior as invoking
func with the value returned from
thrd_exit . All thread attributes are processed before the completion of
func .
thrd_create_attrs_err is always executed on the same thread that invoked
err_func . The completion of the
thrd_create_attrs_err function synchronizes with the beginning of the execution of
thrd_create_attrs_err in the new thread.
func ReturnsThe
function returns
thrd_create_attrs_err on success, or
thrd_success if no memory could be allocated for the thread requested, or
thrd_nomem if the request could not be honored.
thrd_error Recommended PracticeImplementations should pass the closest analogous error code from the
,
thrd_error ,
thrd_nomem , or
thrd_timedout enumeration constants that is related to any implementation-specific errors that occur during thread creation and thread attribute processing.
thrd_busy
5.6. Modify "§7.35.20 Threads" of "Future library directions"
7.35.20Threads
< threads . h > Function names, type names, and enumeration constants that begin with either,
cnd_ ,
mtx_ , or
thrd_ , and a lowercase letter are potentially reserved identifiers and may be added to the declarations in the
tss_ header. Identifiers of the form
< threads . h > and
thrd_attr_X , where
thrd_attr_kind_X is the rest of the identifier, are either:
X
reserved by this document, if
does not begin with
X (U+005F LOW LINE); or
_ potentially reserved by the implementation, if
does begin with
X (U+005F LOW LINE).
_