/***********************************************************************
Demonstrate multithreading using the Pthreads library, and show that
errno is handled correctly between threads.

This version does NOT use locks around output statements.  Tests have been
run on

	FreeBSD (IA-32)
	GNU/Linux (Alpha, AMD64, IA-32, IA-64, PowerPC-32, PowerPC-64, SPARC)
	IRIX (MIPS)
	Mac OS X (IA-32 and PowerPC-32)
	MirBSD (IA-32)
	NetBSD (IA-32)
	OpenBSD (IA-32)
	Solaris (AMD64, IA-32, SPARC)

with both -lm and -lmcw.  With -lm, output is always correct.  With
-lmcw, serious output mixing occurs on IRIX, and minor mixing on
FreeBSD.

For a tutorial on Pthreads, see

	POSIX Threads Programming
	Blaise Barney, Lawrence Livermore National Laboratory
	https://computing.llnl.gov/tutorials/pthreads/

The standard is available here:

	IEEE Std 1003.1, 2004 Edition
	http://www.unix.org/version3/ieee_std.html

Usage:
	c99  [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread1.c -lpthread -lm && ./a.out
	cc   [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread1.c -lpthread -lm && ./a.out
	gcc  [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread1.c -lpthread -lm && ./a.out

	dgcc [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread1.c -lpthread ../libmcw.a && ./a.out
	gcc  [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread1.c -lpthread ../libmcw.a && ./a.out

Typical default output:

	------------------------------------------------------------------------
	The only output should be two begin/end lines from each thread.
	Any assertion failure means that errno has received an unexpected value.
	------------------------------------------------------------------------

	Begin normal thread 0
			Begin underflow thread 2
		Begin overflow thread 1
	End normal thread 0
	Begin normal thread 3
	End normal thread 3
		Begin overflow thread 4
			Begin underflow thread 5
	Begin normal thread 6
		Begin overflow thread 7
	End normal thread 6
			End underflow thread 5
			End underflow thread 2
		End overflow thread 1
		End overflow thread 4
		End overflow thread 7

[19-Aug-2010]
***********************************************************************/

#include <assert.h>
#include <errno.h>
#include <float.h>
#include <math.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#if !defined(MAXTEST)
#define MAXTEST	1000000
#endif

#if !defined(MAXTHREAD)
#define MAXTHREAD	8
#endif

void
work_normal(long int id)
{
    int k;

    (void)printf("Begin normal thread %ld\n", id);
    (void)fflush(stdout);

    for (k = 0; k < MAXTEST; ++k)
    {
	volatile double x, y;

	x = (double)k;
	errno = 0;
	y = sqrt(x);
	assert(errno == 0);
    }

    (void)printf("End normal thread %ld\n", id);
    (void)fflush(stdout);
}

void
work_overflow(long int id)
{
    int k;

    (void)printf("\tBegin overflow thread %ld\n", id);
    (void)fflush(stdout);

    for (k = 0; k < MAXTEST; ++k)
    {
	volatile double x, y;

	x = DBL_MAX;
	errno = 0;
	y = exp(x);

	if ( (errno != ERANGE) && (errno != 0) )
	{
	    (void)fprintf(stderr, "overflow thread sets errno = %d\t", errno);
	    perror("perror() says errno means");
	    (void)fflush(stderr);

	    assert((errno == ERANGE) || (errno == 0));
	}
    }

    (void)printf("\tEnd overflow thread %ld\n", id);
    (void)fflush(stdout);
}

void
work_underflow(long int id)
{
    int k;

    (void)printf("\t\tBegin underflow thread %ld\n", id);
    (void)fflush(stdout);

    for (k = 0; k < MAXTEST; ++k)
    {
	volatile double x, y;

	x = DBL_MIN;
	errno = 0;
	y = exp(x);

	if ( (errno != ERANGE) && (errno != 0) )
	{
	    (void)fprintf(stderr, "underflow thread sets errno = %d\t", errno);
	    perror("perror() says errno means");
	    (void)fflush(stderr);

	    assert((errno == ERANGE) || (errno == 0));
	}
    }

    (void)printf("\t\tEnd underflow thread %ld\n", id);
    (void)fflush(stdout);
}

void *
thread_main(void *thread_id)
{
    long int id;

    id = (long int)thread_id;

    switch (id % 3)
    {
    default:				/* FALL THROUGH */
    case 0:	work_normal(id);    break;
    case 1:	work_overflow(id);  break;
    case 2:	work_underflow(id); break;
    }

    pthread_exit(NULL);

    return ((void *)NULL);		/* NOT REACHED */
}

int
main(void)
{
    pthread_t threads[MAXTHREAD];
    int k, rc;

    (void)printf("------------------------------------------------------------------------\n");
    (void)printf("The only output should be two begin/end lines from each thread.\n");
    (void)printf("Any assertion failure means that errno has received an unexpected value.\n");
    (void)printf("------------------------------------------------------------------------\n\n");

    for (k = 0; k < MAXTHREAD; ++k)
    {
	/*
	** Ignore harmless compiler warnings
	**	"cast to pointer from integer of different size"
	** for last argument to pthread_create().
	*/

	rc = pthread_create(&threads[k], NULL, thread_main, (void *)k);

	if (rc != 0)
	{
	    (void)printf("ERROR: return code for thread %d [%ld] from pthread_create() = %d\n",
			 k, (long int)threads[k], rc);
	    return (EXIT_FAILURE);
	}
    }

    pthread_exit(NULL);

    (void)printf("Unexpected return from pthread_exit()\n"); /* NOT REACHED */

    return (EXIT_SUCCESS);		/* NOT REACHED */
}
