N3554
Thread Attributes - Implementation Extensible and ABI-Resistant

Draft Proposal,

Previous Revisions:
None
Authors:
Paper Source:
GitHub
Issue Tracking:
GitHub
Project:
ISO/IEC 9899 Programming Languages — C, ISO/IEC JTC1/SC22/WG14
Proposal Category:
Change Request, Feature Request
Target:
C2y

Abstract

1. Revision History

1.1. Revision 0 - May 27th, 2025

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, std::thread. 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:

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 <threads.h> until very recently (as of writing: only Apple does not support C11 <threads.h>; MSVC runtime, OpenBSD, FreeBSD, NetBSD, glibc, musl-libc, Bionic (Android), and more all support <threads.h>); and, (b) many platforms do not support changing stack sizes, names, and more after thread creation. Therefore, an API cannot simply add set_threadname or get_threadname, 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 pthread_setname_np0 pthread_getname_np
QNX pthread_setname_np pthread_getname_np
NetBSD pthread_setname_np pthread_getname_np
Win32 SetThreadDescription1 GetThreadDescription1
Darwin pthread_setname_np2 pthread_getname_np
Fuchsia zx_thread_create
Android JavaVMAttachArgs3
FreeBSD pthread_setname_np
OpenBSD pthread_setname_np
RTEMS 4 pthread_setname_np pthread_getname_np
FreeRTOS xTaskCreate pcTaskGetName
VxWorks taskSpawn
eCos cyg_thread_create
Plan 9 threadsetname5 threadsetname5
Haiku spawn_thread rename_thread get_thread_info
Keil RTX osThreadNew osThreadGetName
WebAssembly


0 - GLIBC 2.12+, MUSL
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; pthread_attr_t 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++ std::thread’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 pthread_attr_t. 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:

The API relies on 2 core language features to work:

In particular, there is a thrd_attr_kind 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 thrd_attr_kind pointers to be passed to thrd_create_attrs 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:

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:

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 thrd_attr_stack_storage attribute with both a size_t and a void* are not being proposed.

Note that the size_t passed by thrd_attr_stack_size 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 thrd_t, 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 thrd_current() and thrd_detach(). 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.

Given this behavior, it’s better to provide this as an on-creation technique, where the original thread’s HANDLE 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 WCHAR/wchar_t 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 \0 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 thrd_attr_native_name, which holds a const void*. 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 thrd_create_with_attrs returns. They can be rewritten, changed, adjusted, or freed once the thrd_create_with_attrs 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 thrd_attr_static_name structure has not been provided.

3.3. "Where is the get-style API?"

We do not provide the get/query 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, stack_size 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 thrd_attr_stack_size and propagated through the thrd_create_attrs 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 thrd_attr_err_func function type, and taking a pointer to such a function in any given thrd_create_attrs_err(...). 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 accept_everything to thrd_create_attrs_err(...), pass NULL which will do the same thing, or simply call thrd_create_attrs 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 stack_size_attr 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 pthread_attr_setstacksize 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, handle_attribute_errors, is invoked on the same thread as the call to thrd_create_attrs_err. 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 int (*)(void*) that is passed to thrd_create to create a new thread;

thrd_attr_kind

which is an enumeration type that is compatible with int_least32_t and used to provide type associations for the functions thrd_create_attrs and thrd_create_attrs_err;

thrd_attr_err_func_t

which is the function pointer type int (const thrd_attr_kind*, int, void*) that is passed to thrd_create_attrs_err to handle errors in thread attribute application;

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 thrd_attr_kind are

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 kind member field of thread attributes described in 7.29.2✨. They provide a way to uniquely identify each thread attribute, except for the thrd_attr_kind_implementation_defined which has no associated structure.

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 Attributes
Description

The 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 attrs array for thrd_create_attrs and thrd_create_attrs_err 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 <threads.h> 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.

For thrd_create_attrs and thrd_create_attrs_err function calls, the behavior is unspecified if more than one of thrd_attr_native_name, thrd_attr_mcname, thrd_attr_mwcname, thrd_attr_c8name, thrd_attr_c16name, thrd_attr_c32name, or any of their _sized counterparts, are used in the same attrs array. The behavior is unspecified if more than one of any standard thread attribute is used in the attrs array.

With the exception of thrd_attr_kind_implementation_defined, for every thrd_attr_kind enumeration constant defined in this document of the form thrd_attr_kind_X, where X is the rest of the identifier, there is a corresponding thrd_attr_X complete object of structure type. The kind member must be set to the corresponding thrd_attr_kind_X enumeration constant’s value for every thrd_attr_X structure before use with the thrd_create_attrs and thrd_create_attrs_with functions, otherwise the behavior is undefined.

Each identifier of the form thrd_attr_kind_X, where 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 thrd_attr_kind_implementation_defined. Each identifier of the form thrd_attr_X 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 X begin with the character _ (U+005F LOW LINE). An implementation may define subsequent thrd_attr_kind_Y enumeration constants as part of the thrd_attr_kind enumerated type, where Y is the rest of the identifier, for use as implementation-defined thread attributes. The value of such an enumeration constant shall be greater than thrd_attr_kind_implementation_defined.

The thread attributes thrd_attr_mcname, thrd_attr_mwcname, thrd_attr_c8name, thrd_attr_c16name, and thrd_attr_c32name 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 name 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 _sized provide a thread with the same information, except that the name field can be a non-null terminated pointer and whose size is denoted by the field size. For these _sized thread attributes, the name field may not contain an appropriately typed null terminator in the range denoted by the name and size fields. The range denoted by name, or by name and size for the _sized-suffixed thread attributes, are not accessed by the implementation after the thrd_create_attrs or thrd_create_attrs_err function calls return.

The thread attribute thrd_attr_native_name provides an pointer to const void 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 name 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 thrd_attr_native_name_sized is the same, except that the number of bytes in the name field is equal to the value of the size field. The name field must not contain the implementation-defined null termination byte sequence in the range denoted by the name and size fields. The range denoted by name, or by name and size for thrd_attr_native_name_sized, are not accessed by the implementation after the thrd_create_attrs or thrd_create_attrs_err function calls return.

Recommended Practice

The thread attributes thrd_attr_mcname, thrd_attr_mwcname, thrd_attr_c8name, thrd_attr_c16name, thrd_attr_c32name, and their _sized-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 thrd_attr_native_name and thrd_attr_native_name_sized 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.

The thread attribute thrd_attr_stack_size provides a recommendation for the size of the storage used for the thread with respect to automatic storage duration lifetime declarations for that thread.

The thread attribute thrd_attr_detached 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_detach with the thread that is being created.

5.3. Modify §7.29.5.1 "The thrd_create function" to make it rely on wording moved to other function

7.29.5.1The thrd_create function
Synopsis
#include <threads.h>
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
Description

The thrd_create function creates a new thread executing func(arg). If the thrd_create function succeeds, it sets the object pointed to by thr 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 thrd_create function synchronizes with the beginning of the execution of the new thread.

Returning from func has the same behavior as invoking thrd_exit with the value returned from func.

Returns

The thrd_create function returns thrd_success on success, or thrd_nomem if no memory could be allocated for the thread requested, or thrd_error if the request could not be honored.

The thrd_create function returns and is equivalent to 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✨The thrd_create_attrs function
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]);
Description

The thrd_create_attrs function returns and is equivalent to 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✨The thrd_create_attrs_err function
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);
Description

The thrd_create_attrs_err function creates a new thread executing func(arg) after applying any modifications to the thread through the thread attributes in the attrs array, if any. If err_func is a null pointer, this function behaves as if err_func points to an appropriately typed function that returns thrd_success no matter the input. If the thrd_create_attrs_err function succeeds, it sets the object pointed to by thr 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.)

If attrs is a null pointer, no thread attributes are processed. If attrs_n is 0, no thread attributes are processed. Each pointer p in the range [attrs, attrs + attrs_n) must point to a thread attribute or be a null pointer. If p 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, err_func is called with the arguments p,thrd_error, and err_func_arg. 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 is invoked with:

  • the first argument as p;

  • the second argument as an unspecified value of one of thrd_error, thrd_nomem, thrd_timedout, or thrd_busy; and,

  • the third argument as err_func_arg.

err_func returns one of thrd_error, thrd_nomem, thrd_timeout, or thrd_busy. If err_func returns a value that is not thrd_success, thrd_create_attrs_err returns that value and the thread is not created.

Returning from func has the same behavior as invoking thrd_exit with the value returned from func. All thread attributes are processed before the completion of thrd_create_attrs_err. err_func is always executed on the same thread that invoked thrd_create_attrs_err. The completion of the thrd_create_attrs_err function synchronizes with the beginning of the execution of func in the new thread.

Returns

The thrd_create_attrs_err function returns thrd_success on success, or thrd_nomem if no memory could be allocated for the thread requested, or thrd_error if the request could not be honored.

Recommended Practice

Implementations should pass the closest analogous error code from the thrd_error, thrd_nomem, thrd_timedout, or thrd_busy enumeration constants that is related to any implementation-specific errors that occur during thread creation and thread attribute processing.

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_, thrd_, or tss_, and a lowercase letter are potentially reserved identifiers and may be added to the declarations in the <threads.h> header. Identifiers of the form thrd_attr_X and thrd_attr_kind_X, where X is the rest of the identifier, are either:
  • reserved by this document, if X does not begin with _ (U+005F LOW LINE); or

  • potentially reserved by the implementation, if X does begin with _ (U+005F LOW LINE).

References

Informative References

[MSVC-THREADS-H]
Charlie Barto; Microsoft. MSVC C11 threads.h. September 26th, 2023. URL: https://devblogs.microsoft.com/cppblog/c11-threads-in-visual-studio-2022-version-17-8-preview-2/
[N2178]
Peter Dimov. Proposed Text for Chapter 30, Thread Support Library [threads]. March 3rd, 2007. URL: https://wg21.link/n2178
[N2419]
Kamil Rytarowski. N2419 -. September 3rd, 2019. URL: https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2419.htm
[P0320]
Vicente J. Botet Escribá. Thread Constructor Attributes. October 12th, 2016. URL: https://wg21.link/p0320
[P2019]
Corentin Jabot. P2019 - Thread Attributes. September 9th, 2024. URL: https://wg21.link/p2019
[P3022]
David Sankel; Darius Santu. P3022 - A Boring Thread Attributes Interface. November 28th, 2023. URL: https://wg21.link/p3022
[P3072]
Zhihao Yuan. P3072 - Hassle-free Thread Attributes. March 15h, 2024. URL: https://wg21.link/p3072
[ZTD.THREAD]
Shepherd's Oasis, LLC; JeanHeyd Meneide. ztd.thread. March 27th, 2025. URL: https://github.com/soasis/thread