/*********************************************************************** Demonstrate multithreading using the Pthreads library, and show that errno is handled correctly between threads. This version uses 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. Locking solves the problems that occurs without lock: 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] thread2.c -lpthread -lm && ./a.out cc [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread2.c -lpthread -lm && ./a.out gcc [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread2.c -lpthread -lm && ./a.out dgcc [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread2.c -lpthread ../libmcw.a && ./a.out gcc [-DMAXTEST=nnnnnn] [-DMAXTHREADS=nn] thread2.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 #include #include #include #include #include #include #if !defined(MAXTEST) #define MAXTEST 1000000 #endif #if !defined(MAXTHREAD) #define MAXTHREAD 8 #endif pthread_mutex_t the_lock; #define LOCK() pthread_mutex_lock(&the_lock) #define UNLOCK() pthread_mutex_unlock(&the_lock) void work_normal(long int id) { int k; LOCK(); (void)printf("Begin normal thread %ld\n", id); (void)fflush(stdout); UNLOCK(); for (k = 0; k < MAXTEST; ++k) { volatile double x, y; x = (double)k; errno = 0; y = sqrt(x); assert(errno == 0); } LOCK(); (void)printf("End normal thread %ld\n", id); (void)fflush(stdout); UNLOCK(); } void work_overflow(long int id) { int k; LOCK(); (void)printf("\tBegin overflow thread %ld\n", id); (void)fflush(stdout); UNLOCK(); for (k = 0; k < MAXTEST; ++k) { volatile double x, y; x = DBL_MAX; errno = 0; y = exp(x); if ( (errno != ERANGE) && (errno != 0) ) { LOCK(); (void)fprintf(stderr, "overflow thread sets errno = %d\t", errno); perror("perror() says errno means"); (void)fflush(stderr); UNLOCK(); assert((errno == ERANGE) || (errno == 0)); } } LOCK(); (void)printf("\tEnd overflow thread %ld\n", id); (void)fflush(stdout); UNLOCK(); } void work_underflow(long int id) { int k; LOCK(); (void)printf("\t\tBegin underflow thread %ld\n", id); (void)fflush(stdout); UNLOCK(); for (k = 0; k < MAXTEST; ++k) { volatile double x, y; x = DBL_MIN; errno = 0; y = exp(x); if ( (errno != ERANGE) && (errno != 0) ) { LOCK(); (void)fprintf(stderr, "underflow thread sets errno = %d\t", errno); perror("perror() says errno means"); (void)fflush(stderr); UNLOCK(); assert((errno == ERANGE) || (errno == 0)); } } LOCK(); (void)printf("\t\tEnd underflow thread %ld\n", id); (void)fflush(stdout); UNLOCK(); } 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) { LOCK(); (void)printf("ERROR: return code for thread %d [%ld] from pthread_create() = %d\n", k, (long int)threads[k], rc); UNLOCK(); return (EXIT_FAILURE); } } pthread_exit(NULL); (void)printf("Unexpected return from pthread_exit()\n"); /* NOT REACHED */ return (EXIT_SUCCESS); /* NOT REACHED */ }