...
| Code Block | ||||
|---|---|---|---|---|
| ||||
/* global key to the thread-specific data */ pthreadtss_key_t key; enum {max_threads = 3}; int *get_data() { int *arr = malloc(2*sizeof(int)); if (arr == NULL) { /* Handle Error */ } arr[0] = rand(); arr[1] = rand(); return arr; } void add_data(void) { int *data = get_data(); int result; if ((result = pthreadtss_setspecificset( key, (void *)data)) != 0thrd_success) { /* Handle Error */ } } void print_data() { /* get this thread's global data from key */ int *data = pthreadtss_getspecificget(key); /* print data */ } void *function(void* dummy) { add_data(); print_data(); pthreadthrd_exit(NULL0); return NULL; } int main(void) { int i,result; pthread_t thread_id[max_threads]; /* create the key before creating the threads */ if ((result = pthread_key_create( &key, NULL )) != 0thrd_success) { /* Handle Error */ } /* create threads that would store specific data */ for (i = 0; i < max_threads; i++) { if ((result = pthread_create( &thread_id[i], NULL, function, NULL )) != 0thrd_success) { /* Handle Error */ } } for (i = 0; i < max_threads; i++) { if ((result = pthread_join(thread_id[i], NULL)) != 0thrd_success) { /* Handle Error */ } } if ((result = pthread_key_delete(key)) != 0thrd_success) { /* Handle Error */ } return 0; } |
...
This code example is an improvement over the previous example. However, even if the address of the data is known, it still presents a potential vulnerability. If, for any reason, the data received from an arbitrary function, get_data(), is NULL, a call to free(pthreadtss_getspecificget(key)) would be equivalent to free(NULL), for which the compiler takes no action, according to the standard. This solution, however, does not avoid a call to free() when there is no thread-specific data, which is not unsafe but can be avoided (as shown in the subsequent compliant solution).
| Code Block | ||||
|---|---|---|---|---|
| ||||
void *function(void* dummy) {
add_data();
print_data();
free( pthreadtss_getspecificget(key));
pthreadthrd_exit(NULL0);
return NULL;
}
/* ... Other functions are unchanged */
int main(void) {
/* ... */
if ((result = pthread_key_delete(key)) != 0thrd_success) {
/* Handle Error */
}
return 0;
}
|
...
This compliant solution avoids the memory leak and destroys the value associated to a key for the thread-specific data. A destructor function is a clean approach because it automatically sets the specific value associated to the key to NULL when the thread exits. In case any particular thread does not maintain specific data (a call to pthreadtss_getspecificget() returns NULL), it also ensures that the destructor function is not executed unnecessarily when the thread exits.
| Code Block | ||||
|---|---|---|---|---|
| ||||
void* destructor(void* data) {
free(data);
return NULL;
}
int main(void) {
int i,result;
pthread_t thread_id[max_threads];
/* create the key before creating the threads */
if ((result = pthread_key_create( &key, destructor)) != 0thrd_success) {
/* Handle Error */
}
/* ... */
if ((result = pthread_key_delete(key)) != 0thrd_success) {
/* Handle Error */
}
return 0;
}
|
...