/********************************************************************* * atomic_fail.c - Atomic operations demonstration * * This program demonstrates the importance of using mutexes, * semaphores, and atomic operations in multithreaded programs. * * The program will increment two global variables (allocated on the * heap). One will be incremented using atomic operations. The other * will not be. The non-atomic variable will result in an incorrect * answer, EVEN ON A SINGLE-PROCESSOR MACHINE. * * If the global variable is an int (rather than a pointer-to-int), * the program will calculate the correct value on a single processor * machine. On a multi-processor machine, the non-atomic variable * will still have a wrong answer.[1] * * This program requires: * + The GCC compiler because it uses its __sync_*() * extensions. * + A processor that is supported with the GCC * __sync_*() extensions. (If your isn't supported, * you should get a compile-time error.) * + pthreads library. * * Build and run this program like this: * * $ gcc -O0 -o atomic_fail atomic_fail.c -lpthread * $ ./atomic_fail * * Starting values for global ints: * atomic = 0 nonatomic = 0 * * Final values: * atomic = 20000000 nonatomic = 18011884 * * Expected values (for both): 20000000 * * Make sure that you use -O0, otherwise GCC might optimize all of our * increments into (*nonatomic) += ops !! * * Gabriel M. Beddingfield, 2009-Dec-12 * This program is placed in the public domain. * * [1] However, I don't have a multiprocessor machine to test with! :) ********************************************************************* */ #include #include #include #include /* Each thread will increment the global variable * 'ops' times. */ const unsigned long ops = 10000000UL; /* The integer (*atomic) will be incremented using * atomic operations. * * The integer (*nonatomic) will be incremented using * the normal (non-atomic) increment operator. */ unsigned long *atomic; unsigned long *nonatomic; /* This is thread uses atomic operations */ void* thread_atomic(void* arg) { unsigned long n; for(n=ops; n>0 ; --n) { __sync_fetch_and_add(atomic, 1); } return 0; } /* This thread does NOT use atomic operations. */ void* thread_nonatomic(void* arg) { unsigned long n; for(n=ops; n>0 ; --n) { ++(*nonatomic); } return 0; } int main(void) { pthread_t atomic_t1, atomic_t2; pthread_t nonatomic_t1, nonatomic_t2; int rv; atomic = (unsigned long*)malloc(sizeof(unsigned long)); *atomic = 0; nonatomic = (unsigned long*)malloc(sizeof(unsigned long)); *nonatomic = 0; printf("\n"); printf("Starting values for global ints:\n"); printf("atomic = %lu nonatomic = %lu\n", *atomic, *nonatomic); /* Spawn and run 4 threads. 2 for the atomic ops, 2 for * non-atomic. */ rv = pthread_create(&atomic_t1, 0, thread_atomic, 0); assert(rv == 0); rv = pthread_create(&atomic_t2, 0, thread_atomic, 0); assert(rv == 0); rv = pthread_create(&nonatomic_t1, 0, thread_nonatomic, 0); assert(rv == 0); rv = pthread_create(&nonatomic_t2, 0, thread_nonatomic, 0); assert(rv == 0); rv = pthread_join(atomic_t1, 0); assert(rv == 0); rv = pthread_join(atomic_t2, 0); assert(rv == 0); rv = pthread_join(nonatomic_t1, 0); assert(rv == 0); rv = pthread_join(nonatomic_t2, 0); assert(rv == 0); printf("\nFinal values:\n"); printf("atomic = %lu nonatomic = %lu\n", *atomic, *nonatomic); printf("\nExpected values (for both): %lu\n\n", ops*2); return 0; }