Linux Thread Local Storage

Thread-local storage (TLS) is a mechanism by which variables are allocated such that there is one instance of the variable per extant thread.

Linux ACPI

There are two ways to use TLS.

#Use the keyword “_thread” provided by compiler

In the following example, “_thread” keyword which is supported by gcc compiler to tell the two integer variables “TLS_data1” and TLS_data2” are TLS varaibles.

#define _MULTI_THREADED
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void foo(void);  /* Functions that use the TLS data */
void bar(void);
 
#define checkResults(string, val) {             \
 if (val) {                                     \
   printf("Failed with %d at %s", val, string); \
   exit(1);                                     \
 }                                              \
}
 
/* 
   Use the keyword provided by pthread.h to delcare the following variable
   is thread specific, i.e. it is only visible to a specific thread, 
   not shared/common to all thread.
   These variables are stored in thread local storage (TLS) area.
 */
__thread int TLS_data1;
__thread int TLS_data2;
 
#define  NUMTHREADS   2 

typedef struct {
   int   data1;
   int   data2;
} threadparm_t; 

void *thread_run(void *parm)
{
   int               rc;
   threadparm_t     *gData;

   printf("Thread %.16llx: Entered\n", pthread_self());

   gData = (threadparm_t *)parm;

   /* Assign the value from global variable to thread specific variable*/
   TLS_data1 = gData->data1;
   TLS_data2 = gData->data2;

   foo();
   return NULL;
}
 
void foo() {
   printf("Thread %.16lx: foo(), TLS data=%d %d\n",
          pthread_self(), TLS_data1, TLS_data2);
   bar();
}
 
void bar() {
   printf("Thread %.16lx: bar(), TLS data=%d %d\n",
          pthread_self(), TLS_data1, TLS_data2);
   return;
}
 

int main(int argc, char **argv)
{
  pthread_t             thread[NUMTHREADS];
  int                   rc=0;
  int                   i;
  threadparm_t          gData[NUMTHREADS];
 
  printf("Enter Testcase - %s\n", argv[0]);
 
  printf("Create/start %d threads\n", NUMTHREADS);
  for (i=0; i < NUMTHREADS; i++) { 
     /* Create per-thread TLS data and pass it to the thread */
     gData[i].data1 = i;
     gData[i].data2 = (i+1)*2;
     rc = pthread_create(&thread[i], NULL, thread_run, &gData[i]);
     checkResults("pthread_create()\n", rc);
  }
 
  printf("Wait for all threads to complete, and release their resources\n");
  for (i=0; i < NUMTHREADS; i++) {
     rc = pthread_join(thread[i], NULL);
     checkResults("pthread_join()\n", rc);
  }

  printf("Main completed\n");
  return 0;
}

Test and verify it:

weng@weng-u1604:~/codeEx$ gcc -o tls-keyword tls-keyword.c -lpthread
weng@weng-u1604:~/codeEx$ ./tls-keyword 
Enter Testcase - ./tls-keyword
Create/start 2 threads
Wait for all threads to complete, and release their resources
Thread 00007f0670791700: Entered
Thread 00007f0670791700: foo(), TLS data=0 2
Thread 00007f0670791700: bar(), TLS data=0 2
Thread 00007f066ff90700: Entered
Thread 00007f066ff90700: foo(), TLS data=1 4
Thread 00007f066ff90700: bar(), TLS data=1 4
Main completed
weng@weng-u1604:~/codeEx$ 

#Use “pthread_create_key() / pthread_delete_key()” to for bug chunk of data

Since the storage area for TLS is limited, and if there is a big chunk of datas/pbjects are to be stored in TLS, it can be organized into a struct and use “pthread_create_key() /pthread_delete_key()”.

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

typedef struct TLS_Big_data_ {
  int thread_id;
  int data[2048];
} TLS_Big_data;

void * thread_run(void *);
void show_my_tls();

pthread_key_t thr_id_key;

void main(int argc, void ** argv)
{
    pthread_t thread;
    TLS_Big_data * tls_data;
    int i;

    pthread_key_create(&thr_id_key, NULL);

    for(i = 1; i <= 10; i++)
    {
        tls_data = (TLS_Big_data *)malloc(sizeof(TLS_Big_data));
        tls_data->thread_id = i;
        pthread_create(&thread, NULL,
                       thread_run, (void *)(tls_data));
    }

    pthread_exit(NULL);
}

void * thread_run(void * param)
{
    void *tls_val = param;
  
    pthread_setspecific(thr_id_key, tls_val);

    show_my_tls();

    /* release the meory held by TLS*/
    TLS_Big_data * tls_data;
    tls_data = (TLS_Big_data *)pthread_getspecific(thr_id_key);

    if (tls_data) free(tls_data);
    
    return NULL;
}

void show_my_tls()
{
   TLS_Big_data * tls_data;
   tls_data = (TLS_Big_data *)pthread_getspecific(thr_id_key);

   if (tls_data) {
     printf("Thread %lx holds TLS has %d\n", pthread_self(),
	    tls_data->thread_id);
   } else {
     printf("Thread %lx TLS data not found\n", pthread_self());
   }
}

Compile and run it to verify:

weng@weng-u1604:~/codeEx$ gcc -o tls-use-key tls-use-key.c -lpthread

weng@weng-u1604:~/codeEx$ ./tls-use-key 
Thread 7fb46fdab700 holds TLS has 3
Thread 7fb46f5aa700 holds TLS has 4
Thread 7fb46eda9700 holds TLS has 5
Thread 7fb46e5a8700 holds TLS has 6
Thread 7fb46dda7700 holds TLS has 7
Thread 7fb46d5a6700 holds TLS has 8
Thread 7fb46cda5700 holds TLS has 9
Thread 7fb46c5a4700 holds TLS has 10
Thread 7fb470dad700 holds TLS has 1
Thread 7fb4705ac700 holds TLS has 2
weng@weng-u1604:~/codeEx$