/* This really exists to check that Thrcheck behaves plausibly
   with pthread_once calls.  Which it appears to.

   The original source of this program is as shown below, although it
   has been modified somewhat.  See
   http://www.oreilly.com/pub/a/oreilly/ask_tim/2001/codepolicy.html
   for OReilly's policy on using bits of their code examples.
*/


/********************************************************
 * An example source module to accompany...
 *
 * "Using POSIX Threads: Programming with Pthreads"
 *     by Brad Nichols, Dick Buttlar, Jackie Farrell
 *     O'Reilly & Associates, Inc.
 *
 ********************************************************
 * once_exam.c
 *
 * An example of using the pthreads_once() call to execute an
 * initialization procedure.
 *
 * A program spawns multiple threads and each one tries to
 * execute the routine welcome() using the once call. Only
 * the first thread into the once routine will actually
 * execute welcome().
 *
 * The program's main thread synchronizes its exit with the
 * exit of the threads using the pthread_join() operation.
 *
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>

#include <pthread.h>

/* With more than 2 threads, the precise error reports vary between
   platforms, in terms of the number of races detected.  Make life
   simple and just have 2 threads and so just 1 race. */
#define  NUM_THREADS 2

static pthread_once_t welcome_once_block = PTHREAD_ONCE_INIT;

static int unprotected1 = 0;
static int unprotected2 = 0;

/* This is a hack: delay threads except the first enough so as to
   ensure threads[0] gets to the pthread_once call first.  This is so
   as to ensure that this test produces results which aren't
   scheduling sensitive.  (sigh) */
void maybe_stall ( int myid )
{
   assert(myid >= 0 && myid < NUM_THREADS);
   if (myid > 0)
      sleep(1);
}

void welcome(void) {
   printf("welcome: Welcome\n");
   unprotected1++; /* this is harmless */
}

void* child ( void* argV ) { 
   int r; 
   maybe_stall( *(int*)argV );
   r= pthread_once(&welcome_once_block, welcome); assert(!r);
   printf("child: Hi, I'm thread %d\n", *(int*)argV);
   unprotected2++; /* whereas this is a race */
   return NULL;
}

int main ( void ) {
   int       *id_arg, i, r;
   pthread_t threads[NUM_THREADS];

   id_arg = (int *)malloc(NUM_THREADS*sizeof(int));

   printf("main: Hello\n");
   for (i = 0; i < NUM_THREADS; i++) {
      id_arg[i] = i;
      r= pthread_create(&threads[i], NULL, child, &id_arg[i]);
      assert(!r);
   }

   for (i = 0; i < NUM_THREADS; i++) {
      pthread_join(threads[i], NULL);
      /* printf("main: joined to thread %d\n", i); */
   }
   printf("main: Goodbye\n");
   return 0;
}