#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include "utils.h"

#define NUM_THREADS ((int) 1)

static void init_locks( pthread_mutex_t *mu, pthread_cond_t *cv );
static void destroy_locks( pthread_mutex_t *mu, pthread_cond_t *cv );
static void th_proc( void );

static pthread_mutex_t mu;
static pthread_cond_t cv;
static pthread_t **th;

static int threads_remaining;

int
main( int argc, char *argv[] )
{
   int i, st, thread_count = NUM_THREADS;

   if( argc == 2 )
       thread_count = atoi( argv[1] );

   threads_remaining = thread_count;

   init_locks( &mu, &cv );

   /*
    *  --  Make a bunch of threads, each of which will block at the
    *      condition variable.
    */
   th = (pthread_t **) malloc( thread_count * sizeof( pthread_t * ));
   for(i = 0; i < thread_count; i++ )
   {
       th[i] = malloc( sizeof( pthread_t ));
       st = pthread_create( th[i], NULL, (thread_proc_t) th_proc, NULL );
       CHECK(st, "pthread_create()");
   }

   st = pthread_cond_broadcast( &cv );
   CHECK(st, "pthread_cond_signal()");

   destroy_locks( &mu, &cv );
   for(i = 0; i < thread_count; i++ )
       free( th[i] );

   free( th );
   printf(">> Threads remaining %d\n", threads_remaining );
   return( EXIT_SUCCESS );
}

static void 
th_proc( void )
{
   int st;

   st = pthread_mutex_lock( &mu );
   CHECK(st, "pthread_mutex_lock()");

   st = pthread_cond_wait( &cv, &mu );
   CHECK(st, "pthread_cond_wait()");

   threads_remaining -= 1;

   st = pthread_mutex_unlock( &mu );
   CHECK(st, "pthread_mutex_unlock()");

   pthread_exit( THREAD_SUCCESS );
}

static void
init_locks( pthread_mutex_t *mu, pthread_cond_t *cv )
{
   int st;

   st = pthread_cond_init( cv, NULL );
   CHECK( st, "pthread_cond_init()");

   st = pthread_mutex_init( mu, NULL );
   CHECK( st, "pthread_mutex_init()");
}

static void
destroy_locks( pthread_mutex_t *mu, pthread_cond_t *cv )
{
   int st;

   st = pthread_cond_destroy( cv );
   CHECK( st, "pthread_cond_destroy()");

   st = pthread_mutex_destroy( mu );
   CHECK( st, "pthread_mutex_destroy()");

   pthread_exit( THREAD_SUCCESS );
}
