/* 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 */
