Saturday, November 1, 2014

Unix Prog: Thread Specific Data

1. Thread specific data
Thread specific data, is a mechanism for storing and finding data associated with a particular thread. Since all threads in a process have access to the entire address space of the process, in order to manage thread-specific data, system calls are provided to promote data separation.

Firstly, we need to create a key to associate with the data.
 ubuntu@ip-172-31-23-227:~$ less /usr/include/pthread.h  
 ......  
 /* Create a key value identifying a location in the thread-specific  
   data area. Each thread maintains a distinct thread-specific data  
   area. DESTR_FUNCTION, if non-NULL, is called with the value  
   associated to that key when the key is destroyed.  
   DESTR_FUNCTION is not called if the value associated is NULL when  
   the key is destroyed. */  
 extern int pthread_key_create (pthread_key_t *__key,  
                 void (*__destr_function) (void *))  
    __THROW __nonnull ((1));  
   
 /* Destroy KEY. */  
 extern int pthread_key_delete (pthread_key_t __key) __THROW;  
 ......  

For destructor function, if the thread exits normally, calling pthread_exit or by returning, the destructor function will be called. If the thread calls, exit, _exist, or abort to exit abnormally, the destructor function will not be called. Since it usually calls the malloc to allocate memory for thread-specific data, destructor can help free the memory after thread exits.

The same key can be used by all threads, but each thread will associate a different specific data address with the key.
A thread can allocate multiple keys for thread-specific data, each key can have a destructor associated with it. There can be a different destructor function for each key, or they can all use the same function.

pthread_key_delete will break the association of a key with the thread-specific data for all thread. But it will not call destructor.

 ubuntu@ip-172-31-23-227:~$ less /usr/include/pthread.h  
 ......  
 /* Return current value of the thread-specific data slot identified by KEY. */  
 extern void *pthread_getspecific (pthread_key_t __key) __THROW;  
   
 /* Store POINTER in the thread-specific data slot identified by KEY. */  
 extern int pthread_setspecific (pthread_key_t __key,  
                 const void *__pointer) __THROW ;  
 ......  

After creating the key, we can pthread_getspecific and pthread_setspecific to get and setup the thread-specific data. After the thread normally exits, destructor will be called with the address of thread specific data. If the thread associates with different keys, then each key's destructor will be called one by one.

2. pthread_once

System Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/pthread.h  
 ......  
 /* Guarantee that the initialization function INIT_ROUTINE will be called  
   only once, even if pthread_once is executed several times with the  
   same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or  
   extern variable initialized to PTHREAD_ONCE_INIT.  
   
   The initialization functions might throw exception which is why  
   this function is not marked with __THROW. */  
 extern int pthread_once (pthread_once_t *__once_control,  
              void (*__init_routine) (void)) __nonnull ((1, 2));  
 ......  

3. Example of thread-specific data
thread.c
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
 #include<string.h>  
 #include<pthread.h>  
   
 #define ARG_MAX 4096  
   
 static pthread_key_t key;  
 static pthread_once_t init_done = PTHREAD_ONCE_INIT;  
 pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;  
   
 extern char **environ;  
   
 static void thread_init(void)  
 {  
  // Use "free" as the destructor when thread exits,  
  // free will be called with the thread-specific data address  
  pthread_key_create(&key, free);  
 }  
   
 char *getenv(const char* name)  
 {  
  int i, len;  
  char *envbuf;  
   
  // pthread_once could make sure only one key is created within the process  
  // even if getenv is called multiple times, thread_init is only called once  
  pthread_once(&init_done, thread_init);  
   
  pthread_mutex_lock(&env_mutex);  
  envbuf = (char *)pthread_getspecific(key);  
  if(envbuf == NULL) {  
   envbuf = malloc(ARG_MAX);  
   if(envbuf == NULL) {  
    pthread_mutex_unlock(&env_mutex);  
    return NULL;  
   }  
   pthread_setspecific(key, envbuf);  
  }  
   
  len = strlen(name);  
  for(i = 0; environ[i] != NULL; i++) {  
   if((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '=')) {  
    strcpy(envbuf, &environ[i][len+1]);  
    pthread_mutex_unlock(&env_mutex);  
    return(envbuf);  
   }  
  }  
   
  pthread_mutex_unlock(&env_mutex);  
  return NULL;  
 }  
   
 int main(int argc, char* argv[])  
 {  
  printf("SHELL = %s\n", getenv("SHELL"));  
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./thread.out  
 SHELL = /bin/bash  

No comments:

Post a Comment