1 /* Making a library function that uses static variables thread-safe. 2 Illustrates: thread-specific data, pthread_once(). */ 3 4 #include <stddef.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <pthread.h> 9 10 /* This is a typical example of a library function that uses 11 static variables to accumulate results between calls. 12 Here, it just returns the concatenation of all string arguments 13 that were given to it. */ 14 15 #if 0 16 17 char * str_accumulate(char * s) 18 { 19 static char accu[1024] = { 0 }; 20 strcat(accu, s); 21 return accu; 22 } 23 24 #endif 25 26 /* Of course, this cannot be used in a multi-threaded program 27 because all threads store "accu" at the same location. 28 So, we'll use thread-specific data to have a different "accu" 29 for each thread. */ 30 31 /* Key identifying the thread-specific data */ 32 static pthread_key_t str_key; 33 /* "Once" variable ensuring that the key for str_alloc will be allocated 34 exactly once. */ 35 static pthread_once_t str_alloc_key_once = PTHREAD_ONCE_INIT; 36 37 /* Forward functions */ 38 static void str_alloc_key(void); 39 static void str_alloc_destroy_accu(void * accu); 40 41 /* Thread-safe version of str_accumulate */ 42 43 char * str_accumulate(const char * s) 44 { 45 char * accu; 46 47 /* Make sure the key is allocated */ 48 pthread_once(&str_alloc_key_once, str_alloc_key); 49 /* Get the thread-specific data associated with the key */ 50 accu = (char *) pthread_getspecific(str_key); 51 /* It's initially NULL, meaning that we must allocate the buffer first. */ 52 if (accu == NULL) { 53 accu = malloc(1024); 54 if (accu == NULL) return NULL; 55 accu[0] = 0; 56 /* Store the buffer pointer in the thread-specific data. */ 57 pthread_setspecific(str_key, (void *) accu); 58 printf("Thread %lx: allocating buffer at %p\n", pthread_self(), accu); 59 } 60 /* Now we can use accu just as in the non thread-safe code. */ 61 strcat(accu, s); 62 return accu; 63 } 64 65 /* Function to allocate the key for str_alloc thread-specific data. */ 66 67 static void str_alloc_key(void) 68 { 69 pthread_key_create(&str_key, str_alloc_destroy_accu); 70 printf("Thread %lx: allocated key %d\n", pthread_self(), str_key); 71 } 72 73 /* Function to free the buffer when the thread exits. */ 74 /* Called only when the thread-specific data is not NULL. */ 75 76 static void str_alloc_destroy_accu(void * accu) 77 { 78 printf("Thread %lx: freeing buffer at %p\n", pthread_self(), accu); 79 free(accu); 80 } 81 82 /* Test program */ 83 84 static void *process(void * arg) 85 { 86 char *res; 87 res = str_accumulate("Result of "); 88 res = str_accumulate((char *) arg); 89 res = str_accumulate(" thread"); 90 printf("Thread %lx: \"%s\"\n", pthread_self(), res); 91 return NULL; 92 } 93 94 int libc_ex4() 95 { 96 char * res; 97 pthread_t th1, th2; 98 99 // res = str_accumulate("Result of "); 100 pthread_create(&th1, NULL, process, (void *) "first"); 101 pthread_create(&th2, NULL, process, (void *) "second"); 102 // res = str_accumulate("initial thread"); 103 printf("Thread %lx: \"%s\"\n", pthread_self(), res); 104 pthread_join(th1, NULL); 105 pthread_join(th2, NULL); 106 } 107 #include <finsh.h> 108 FINSH_FUNCTION_EXPORT(libc_ex4, example 4 for libc); 109