Rules and Limitations for TLS
The following guidelines must be observed when declaring statically bound thread local objects and variables:
The thread attribute can be applied only to data declarations and definitions. It cannot be used on function declarations or definitions. For example, the following code will generate a compiler error:
#define Thread __declspec( thread ) Thread void func(); // This will generate an error.
The thread modifier may be specified only on data items with static extent. This includes global data objects (both static and extern), local static objects, and static data members of C++ classes. Automatic data objects may not be declared with the thread attribute. The following code will generate compiler errors:
#define Thread __declspec( thread ) void func1() { Thread int tls_i; // This will generate an error. } int func2( Thread int tls_i ) // This will generate an error. { return tls_i; }
The declarations and the definition of a thread local object must all specify the thread attribute. For example, the following code will generate an error:
#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int Thread tls_i; // declaration and definition differ.
The thread attribute cannot be used as a type modifier. For example, the following code will generate a compiler error:
char __declspec( thread ) *ch; // Error
C++ classes cannot use the thread attribute. However, C++ class objects may be instantiated with the thread attribute. For example, the following code will generate a compiler error:
#define Thread __declspec( thread ) class Thread C // Error: classes cannot be declared Thread. { // Code }; C CObject;
Because the declaration of C++ objects that utilize the thread attribute is permitted, the following two examples are semantically equivalent:
#define Thread __declspec( thread ) Thread class B { // Code } BObject; // OK--BObject is declared thread local. class B { // Code }; Thread B BObject; // OK--BObject is declared thread local.
Because C++ objects with constructors and destructors (as well as any object that utilizes some form of initialization semantics) may be allocated as thread local objects, an associated initialization routine (such as the constructor) is called to initialize that object. For example:
class tlsClass { private: int x; public: tlsClass() { x = 1; } ; ~tlsClass(); } __declspec( thread ) tlsClass tlsObject; extern int func(); __declspec( thread ) int y = func();
In this case, data or objects initialized by the
func
routine do not necessarily belong to the same thread into whichtlsObject
is instantiated.The address of a thread local object is not considered constant, and any expression involving such an address is not considered a constant expression. In standard C, the effect of this is to forbid the use of the address of a thread local variable as an initializer for an object or pointer. For example, the following code will be flagged as an error by the C compiler:
#define Thread __declspec( thread ) Thread int tls_i; int *p = &tls_i; //This will generate an error in C.
This restriction does not apply in C++, however. Because C++ permits dynamic initialization of all objects, you can initialize an object with an expression that uses the address of a thread local variable. This is accomplished in the same way as the construction of thread local objects. For example, the code shown above will not generate an error when compiled as a C++ source file. Note that the address of a thread local variable is valid only as long as the thread in which the address was taken still exists.
Standard C permits the initialization of an object or variable with an expression involving a reference to itself, but only for objects of nonstatic extent. Although C++ normally permits such dynamic initialization of objects with an expression involving a reference to itself, this type of initialization is not permitted with thread local objects. For example:
#define Thread __declspec( thread ) Thread int tls_i = tls_i; // Error in C and C++ int j = j; // OK in C++, error in C Thread int tls_i = sizeof( tls_i ) // Legal in C and C++
Note that a
sizeof
expression that includes the object being initialized does not constitute a reference to itself, and is legal in both C and C++.C++ does not allow such dynamic initialization of thread data because of possible future enhancements to the thread local storage facility.
If a DLL declares any nonlocal data or object as __declspec( thread ), it can cause a protection fault if dynamically loaded. After the DLL is loaded with , it causes system failure whenever the code references the nonlocal __declspec( thread ) data. Because the global variable space for a thread is allocated at run time, the size of this space is based on a calculation of the requirements of the application plus the requirements of all of the DLLs that are statically linked. When you use LoadLibrary, there is no way to extend this space to allow for the thread local variables declared with __declspec( thread ). Use the TLS APIs, such as , in your DLL to allocate TLS if the DLL might be loaded with LoadLibrary.