|  | /* BeginSourceFile tls.c | 
|  |  | 
|  | This file creates and deletes threads. It uses thread local storage | 
|  | variables too. */ | 
|  |  | 
|  | #include <unistd.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <pthread.h> | 
|  | #include <semaphore.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | #define N_THREADS 3 | 
|  |  | 
|  | /* Uncomment to turn on debugging output */ | 
|  | /*#define START_DEBUG*/ | 
|  |  | 
|  | /* Thread-local storage.  */ | 
|  | __thread int a_thread_local; | 
|  |  | 
|  | class K { | 
|  | public: | 
|  | static __thread int another_thread_local; | 
|  | }; | 
|  |  | 
|  | __thread int K::another_thread_local; | 
|  |  | 
|  | /* psymtabs->symtabs resolving check.  */ | 
|  | extern __thread int file2_thread_local; | 
|  |  | 
|  | /* Global variable just for info addr in gdb.  */ | 
|  | int a_global; | 
|  |  | 
|  | /* Print the results of thread-local storage.  */ | 
|  | int thread_local_val[ N_THREADS ]; | 
|  | int another_thread_local_val[ N_THREADS ]; | 
|  |  | 
|  | /* Semaphores to make sure the threads are alive when we print the TLS | 
|  | variables from gdb.  */ | 
|  | sem_t tell_main, tell_thread; | 
|  |  | 
|  |  | 
|  | void print_error () | 
|  | { | 
|  | switch (errno) | 
|  | { | 
|  | case EAGAIN: | 
|  | fprintf (stderr, "EAGAIN\n"); | 
|  | break; | 
|  | case EINTR: | 
|  | fprintf (stderr, "EINTR\n"); | 
|  | break; | 
|  | case EINVAL: | 
|  | fprintf (stderr, "EINVAL\n"); | 
|  | break; | 
|  | case ENOSYS: | 
|  | fprintf (stderr, "ENOSYS\n"); | 
|  | break; | 
|  | case ENOENT: | 
|  | fprintf (stderr, "ENOENT\n"); | 
|  | break; | 
|  | case EDEADLK: | 
|  | fprintf (stderr, "EDEADLK\n"); | 
|  | break; | 
|  | default: | 
|  | fprintf (stderr, "Unknown error\n"); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Routine for each thread to run, does nothing.  */ | 
|  | void *spin( void *vp ) | 
|  | { | 
|  | int me = (long) vp; | 
|  | int i; | 
|  |  | 
|  | /* Use a_global. */ | 
|  | a_global++; | 
|  |  | 
|  | a_thread_local = 0; | 
|  | K::another_thread_local = me; | 
|  | for( i = 0; i <= me; i++ ) { | 
|  | a_thread_local += i; | 
|  | } | 
|  |  | 
|  | another_thread_local_val[me] = K::another_thread_local; | 
|  | thread_local_val[ me ] = a_thread_local; /* here we know tls value */ | 
|  |  | 
|  | if (sem_post (&tell_main) == -1) | 
|  | { | 
|  | fprintf (stderr, "th %d post on sem tell_main failed\n", me); | 
|  | print_error (); | 
|  | return NULL; | 
|  | } | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "th %d post on tell main\n", me); | 
|  | #endif | 
|  |  | 
|  | while (1) | 
|  | { | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "th %d start wait on tell_thread\n", me); | 
|  | #endif | 
|  | if (sem_wait (&tell_thread) == 0) | 
|  | break; | 
|  |  | 
|  | if (errno == EINTR) | 
|  | { | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "th %d wait tell_thread got EINTR, rewaiting\n", me); | 
|  | #endif | 
|  | continue; | 
|  | } | 
|  | else | 
|  | { | 
|  | fprintf (stderr, "th %d wait on sem tell_thread failed\n", me); | 
|  | print_error (); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "th %d Wait on tell_thread\n", me); | 
|  | #endif | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void | 
|  | function_referencing_file2_thread_local (void) | 
|  | { | 
|  | file2_thread_local = file2_thread_local; | 
|  | } | 
|  |  | 
|  | void | 
|  | do_pass() | 
|  | { | 
|  | int i; | 
|  | pthread_t t[ N_THREADS ]; | 
|  | int err; | 
|  |  | 
|  | for( i = 0; i < N_THREADS; i++) | 
|  | { | 
|  | thread_local_val[i] = 0; | 
|  | another_thread_local_val[i] = 0; | 
|  | } | 
|  |  | 
|  | if (sem_init (&tell_main, 0, 0) == -1) | 
|  | { | 
|  | fprintf (stderr, "tell_main semaphore init failed\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (sem_init (&tell_thread, 0, 0) == -1) | 
|  | { | 
|  | fprintf (stderr, "tell_thread semaphore init failed\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Start N_THREADS threads, then join them so that they are terminated.  */ | 
|  | for( i = 0; i < N_THREADS; i++ ) | 
|  | { | 
|  | err = pthread_create( &t[i], NULL, spin, (void *) (long) i ); | 
|  | if( err != 0 ) { | 
|  | fprintf(stderr, "Error in thread %d create\n", i ); | 
|  | } | 
|  | } | 
|  |  | 
|  | for( i = 0; i < N_THREADS; i++ ) | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "main %d start wait on tell_main\n", i); | 
|  | #endif | 
|  | if (sem_wait (&tell_main) == 0) | 
|  | break; | 
|  |  | 
|  | if (errno == EINTR) | 
|  | { | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "main %d wait tell_main got EINTR, rewaiting\n", i); | 
|  | #endif | 
|  | continue; | 
|  | } | 
|  | else | 
|  | { | 
|  | fprintf (stderr, "main %d wait on sem tell_main failed\n", i); | 
|  | print_error (); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "main done waiting on tell_main\n"); | 
|  | #endif | 
|  |  | 
|  | i = 10;  /* Here all threads should be still alive. */ | 
|  |  | 
|  | for( i = 0; i < N_THREADS; i++ ) | 
|  | { | 
|  | if (sem_post (&tell_thread) == -1) | 
|  | { | 
|  | fprintf (stderr, "main %d post on sem tell_thread failed\n", i); | 
|  | print_error (); | 
|  | return; | 
|  | } | 
|  | #ifdef START_DEBUG | 
|  | fprintf (stderr, "main %d post on tell_thread\n", i); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | for( i = 0; i < N_THREADS; i++ ) | 
|  | { | 
|  | err = pthread_join(t[i], NULL ); | 
|  | if( err != 0 ) | 
|  | { | 
|  | fprintf (stderr, "error in thread %d join\n", i ); | 
|  | } | 
|  | } | 
|  |  | 
|  | i = 10;  /* Null line for setting bpts on. */ | 
|  |  | 
|  | } | 
|  |  | 
|  | int | 
|  | main() | 
|  | { | 
|  | do_pass (); | 
|  |  | 
|  | return 0;  /* Set breakpoint here before exit. */ | 
|  | } | 
|  |  | 
|  | /* EndSourceFile */ |