#include	<errno.h>
#include	<malloc.h>
#include	<pthread.h>
#include	<stdio.h>
#include	<string.h>
#include	<unistd.h>

// Calculation of greatest common divisor (GCD), multithreaded
// gcc -g -o thread1 thread1.c -lpthread

struct	gcd {
	unsigned int	a,b;
};

unsigned int
gcd( unsigned int a, unsigned int b )	// BKR recursive
{
	if ( b == 0 )
		return a;
	else
		return gcd( b, a % b );
}

// Note the dynamic memory covenant:  The parent thread allocates the parameter
// struct, the child thread extracts data from the struct and frees it.  For
// return values, the child allocates and the joining thread does the free().

void *
gcd_calculator( void * p )		// BKR why is this not recursive?
{
	struct gcd *	gptr = (struct gcd *)p;	// BKR covenant:  parent thread
	unsigned int *	rptr;

	rptr = (unsigned int *)malloc( sizeof( unsigned int ) );
	*rptr = gcd( gptr->a, gptr->b );
	free( gptr );
	return rptr;			// Note malloc/free covenant for threads.
}

int
main( int argc, char ** argv )
{
	pthread_t	tid;
	pthread_attr_t	attr;
	struct gcd *	gcdptr;
	unsigned int *	rptr;
	unsigned int	error;

	if ( argc < 3 )
	{
		printf( "Must specify two integer args on command line\n" );
		_exit( 1 );	// crash and burn
	}
	else if ( (gcdptr = (struct gcd *)malloc( sizeof(struct gcd) )) == 0 )
	{
		printf( "malloc() croaked in %s line %d: %s\n",
			__FILE__, __LINE__, strerror( errno ) );	// BKR
		_exit( 1 );	// crash and burn
	}
	else if ( sscanf( argv[1], " %d", &gcdptr->a ) < 1 )
	{
		printf( "Must specify two integer args on command line\n" );
		_exit( 1 );	// crash and burn
	}
	else if ( sscanf( argv[2], " %d", &gcdptr->b ) < 1 )
	{
		printf( "Must specify two integer args on command line\n" );
		_exit( 1 );	// crash and burn
	}
	else if ( (error = pthread_attr_init( &attr )) != 0 )
	{
		printf( "pthread_attr_init() croaked in %s line %d: %s\n",
			__FILE__, __LINE__, strerror( error ) );
		_exit( 1 );	// crash and burn
	}
	else if ( (error = pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM )) != 0 )	// BKR try PROCESS
	{
		printf( "pthread_attr_setscope() croaked in %s line %d: %s\n",
			__FILE__, __LINE__, strerror( error ) );
		_exit( 1 );	// crash and burn
	}
	else if ( (error = pthread_create( &tid, &attr, gcd_calculator, gcdptr )) != 0 )
	{
		printf( "pthread_create() croaked in %s line %d: %s\n",
			__FILE__, __LINE__, strerror( error ) );
		_exit( 1 );	// crash and burn
	}
	else if ( (error = pthread_join( tid, (void **)&rptr )) != 0 )
	{
		printf( "pthread_join() croaked in %s line %d: %s\n",
			__FILE__, __LINE__, strerror( error ) );
		_exit( 1 );	// crash and burn
	}
	else
	{
		printf( "Joined thread returned GCD %d\n", *rptr );
		free( rptr );			// BKR note malloc/free covenant
		_exit( 0 );
	}	
}
