Thread-local storage

From Wikipedia, the free encyclopedia
  (Redirected from Thread local storage)
Jump to: navigation, search

Thread-local storage (TLS) is a computer programming method that uses static or global memory local to a thread.

This is sometimes needed because normally all threads in a process share the same address space, which is sometimes undesirable. In other words, data in a static or global variable is normally always located at the same memory location, when referred to by threads from the same process. Variables on the call stack however are local to threads, because each thread has its own stack, residing in a different memory location. The underlying problem is that, since all threads share an address space, no fixed memory location can be used to store the location of the storage.

Sometimes it is desirable that two threads referring to the same static or global variable are actually referring to different memory locations, thereby making the variable thread-local, a canonical example being the C error code variable errno.

If it is possible to make at least a memory address sized variable thread-local, it is in principle possible to make arbitrarily sized memory blocks thread-local, by allocating such a memory block and storing the memory address of that block in a thread-local variable.

Windows implementation[edit]

The application programming interface (API) function TlsAlloc can be used to obtain an unused TLS slot index; the TLS slot index will then be considered ‘used’.

The TlsGetValue and TlsSetValue functions are then used to read and write a memory address to a thread-local variable identified by the TLS slot index. TlsSetValue only affects the variable for the current thread. The TlsFree function can be called to release the TLS slot index.

There is a Win32 Thread Information Block for each thread. One of the entries in this block is the thread-local storage table for that thread.[1] TlsAlloc returns an index to this table, unique per address space, for each call. Each thread has its own copy of the thread-local storage table. Hence, each thread can independently use TlsSetValue(index) and obtain the same value via TlsGetValue(index), because these set and look up an entry in the thread's own table.

Apart from TlsXxx function family, Windows executables can define a section which is mapped to a different page for each thread of the executing process. Unlike TlsXxx values, these pages can contain arbitrary and valid addresses. These addresses, however, are different for each executing thread and therefore should not be passed to asynchronous functions (which may execute in a different thread) or otherwise passed to code which assume that a virtual address is unique within the whole process. TLS sections are managed using memory paging and its size is quantized to a page size (4kB on x86 machines). Such sections may only be defined inside a main executable of a program - DLLs should not contain such sections, because they are not correctly initialized when loading with LoadLibrary.

Pthreads implementation[edit]

TLS with POSIX Threads, thread-specific data in Pthreads nomenclature, is similar to TlsAlloc and related functionality for Windows. pthread_key_create creates a key, with an optional destructor, that can later be associated with thread-specific data via pthread_setspecific. The data can be retrieved using pthread_getspecific. If the thread-specific value is not NULL, the destructor will be called when the thread exits. Additionally, key must be destroyed with pthread_key_delete.

Language-specific implementation[edit]

Apart from relying on programmers to call the appropriate API functions, it is also possible to extend the programming language to support TLS.

C++[edit]

C++11 introduces the thread_local[2] keyword which can be used in the following cases

  • Namespace level (global) variables
  • File static variables
  • Function static variables
  • Static member variables

Aside from that, various C++ compiler implementations provide specific ways to declare thread-local variables:

On Windows versions before Vista and Server 2008, __declspec(thread) works in DLLs only when those DLLs are bound to the executable, and will not work for those loaded with LoadLibrary() (a protection fault or data corruption may occur).[9]

Common Lisp (and maybe other dialects)[edit]

Common Lisp provides a feature called dynamically scoped variables.

Dynamic variables have a binding which is private to the invocation of a function and all of the children called by that function.

This abstraction naturally maps to thread-specific storage, and Lisp implementations that provide threads do this. Common Lisp has numerous standard dynamic variables, and so threads cannot be sensibly added to an implementation of the language without these variables having thread-local semantics in dynamic binding.

For instance the standard variable *print-base* determines the default radix in which integers are printed. If this variable is overridden, then all enclosing code will print integers in an alternate radix:

;;; function foo and its children will print
;; in hexadecimal:
(let ((*print-base* 16)) (foo))

If functions can execute concurrently on different threads, this binding has to be properly thread-local, otherwise each thread will fight over who controls a global printing radix.

D[edit]

In D version 2, all static and global variables are thread-local by default and are declared with syntax similar to "normal" global and static variables in other languages. Global variables must be explicitly requested using the shared keyword:

int threadLocal;  // This is a thread-local variable.
shared int global;  // This is a global variable shared with all threads.

The shared keyword works both as the storage class, and as a type qualifiershared variables are subject to some restrictions which statically enforce data integrity.[10] To declare a "classic" global variable without these restrictions, the unsafe __gshared keyword must be used:[11]

__gshared int global;  // This is a plain old global variable.

Java[edit]

In Java, thread-local variables are implemented by the ThreadLocal class object. ThreadLocal holds variable of type T, which is accessible via get/set methods. For example ThreadLocal variable holding Integer value looks like this:

private static final ThreadLocal<Integer> myThreadLocalInteger = new ThreadLocal<Integer>();

.NET languages: C# and others[edit]

In .NET Framework languages such as C#, static fields can be marked with the ThreadStatic attribute:

class FooBar {
   [ThreadStatic] static int foo;
}

In .NET 4.0 the System.Threading.ThreadLocal<T> class is available for allocating and lazily loading thread-local variables.

class FooBar {
   private static System.Threading.ThreadLocal<int> foo;
}

Also an API is available for dynamically allocating thread-local variables.

Object Pascal[edit]

In Object Pascal (Delphi) or Free Pascal the threadvar reserved keyword can be used instead of 'var' to declare variables using the thread-local storage.

var
   mydata_process: integer;
threadvar
   mydata_threadlocal: integer;

Objective-C[edit]

In Cocoa, GNUstep, and OpenStep, each NSThread object has a thread-local dictionary that can be accessed through the thread's threadDictionary method.

NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
dict[@"A key"] = @"Some data";

Perl[edit]

In Perl threads were added late in the evolution of the language, after a large body of extant code was already present on the Comprehensive Perl Archive Network (CPAN). Thus, threads in Perl by default take their own local storage for all variables, to minimise the impact of threads on extant non-thread-aware code. In Perl, a thread-shared variable can be created using an attribute:

use threads;
use threads::shared;
 
my $localvar;
my $sharedvar :shared;

Python[edit]

In Python version 2.4 or later, local class in threading module can be used to create thread-local storage.

import threading
mydata = threading.local()
mydata.x = 1

Ruby[edit]

Ruby can create/access thread-local variables using []=/[] methods:

Thread.current[:user_id] = 1

References[edit]

  1. ^ Pietrek, Matt (May 2006). "Under the Hood". MSDN. Retrieved 6 April 2010. 
  2. ^ Section 3.7.2 in C++11 standard
  3. ^ IBM XL C/C++: Thread-local storage
  4. ^ GCC 3.3.1: Thread-Local Storage
  5. ^ Clang 2.0: release notes
  6. ^ Intel C++ Compiler 8.1 (linux) release notes: Thread-local Storage
  7. ^ Visual Studio 2003: Thread extended storage-class modifier
  8. ^ Intel C++ Compiler 10.0 (windows): Thread-local storage
  9. ^ "Rules and Limitations for TLS"
  10. ^ Alexandrescu, Andrei (July 6, 2010). "Chapter 13 - Concurrency". The D Programming Language. InformIT. p. 3. Retrieved January 3, 2014. 
  11. ^ Bright, Walter (May 12, 2009). "Migrating to Shared". dlang.org. Retrieved January 3, 2014. 

External links[edit]