Doc. No.: WG14/N1329
Date: 2008-08-06
Reply to: Clark Nelson
Phone: +1-503-712-8433
Email: clark.nelson@intel.com

Thread-local storage

Background

At its 2008-06 meeting in Sophia Antipolis, WG21 accepted N2659 as a specification of thread-local storage. The motivation for this feature is clear and compelling; I am personally unaware of any hosted implementation of C that doesn't already provide it, using some spelling or other.

The changes to the standard necessary to add this feature to C are fairly limited and straightforward. (The changes to C++ are more extensive mainly because they address dynamic initialization and destruction. It should be noted that allowing dynamic initialization and destruction for thread-local objects in C++ is a fairly significant extension of existing practice.) I have retained the keyword spelling and terminology adopted into the C++ standard for consistency, not because I necessarily believe them to be optimal.

It is perhaps worth pointing out that this specification requires that a thread-specific instance of a thread-local variable has a distinct (non-constant) address, and that it is possible for a thread to indirectly access a thread-specific instance corresponding to a different thread.

Standard changes

6.2.4 Storage durations of objects

Change p1:

An object has a storage duration that determines its lifetime. There are three four storage durations: static, thread, automatic, and allocated. Allocated storage is described in 7.20.3.

Change p3:

An object whose identifier is declared without the storage-class specifier thread_local, and either with external or internal linkage, or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

Insert new paragraph after p3:

An object whose identifier is declared with the storage-class specifier thread_local has thread storage duration. Its lifetime is the entire execution of the thread for which it is created, and its stored value is initialized when the thread is started. There is a distinct object per thread, and use of the declared name in an expression refers to the object associated with the thread evaluating the expression.

6.4.1 Keywords

To p1, add thread_local.

6.7.1 Storage-class specifiers

Change p1:

storage-class-specifier:
typedef
extern
static
thread_local
auto
register

Change p2:

At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that thread_local may appear with static or extern.102)

NOTE: Allowing thread_local with either static or extern is the only obvious way to control the linkage of a thread-local object declared at file-scope.

Add a new Constraints paragraph following p2:

In the declaration of a block-scope object, if the declaration specifiers include thread_local, they shall also include either static or extern. If thread_local appears in any declaration of an object, it shall be present in every declaration of that object.

NOTE: The current words in the C++ standard prohibit block-scope extern declarations of thread-local objects, but that was an oversight.

6.7.4 Function specifiers

Change p3:

An inline definition of a function with external linkage shall not contain a definition of a modifiable object with static or thread storage duration, and shall not contain a reference to an identifier with internal linkage.

6.7.5.2 Array declarators

Change p2:

An ordinary identifier (as defined in 6.2.3) that has a variably modified type shall have either block scope and no linkage or function prototype scope. If an identifier is declared to be an object with static or thread storage duration, it shall not have a variable length array type.

Change p10:

EXAMPLE 4 All declarations of variably modified (VM) types have to be at either block scope or function prototype scope. Array objects declared with the thread_local, static or extern storage-class specifier cannot have a variable length array (VLA) type. However, an object declared with the static storage-class specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.

6.7.8 Initialization

Change p4:

All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

Change p10:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static or thread storage duration is not initialized explicitly, then:

7.1.4 Use of library functions

Change p4:

The functions in the standard library are not guaranteed to be reentrant and may modify objects with static or thread storage duration.164)

7.14.1.1 The signal function

Change p5:

If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.220)

F.7.4 Constant expressions

Change p1:

An arithmetic constant expression of floating type, other than one in an initializer for an object that has static or thread storage duration, is evaluated (as if) during execution; thus, it is affected by any operative floating-point control modes and raises floating-point exceptions as required by IEC 60559 (provided the state for the FENV_ACCESS pragma is “on”).315)

F.7.5 Initialization

Change p1:

All computation for automatic initialization is done (as if) at execution time; thus, it is affected by any operative modes and raises floating-point exceptions as required by IEC 60559 (provided the state for the FENV_ACCESS pragma is “on”). All computation for initialization of objects that have static or thread storage duration is done (as if) at translation time.