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