/* * Test whether all data races are detected in a multithreaded program with * user-annotated barriers. See also pth_barrier.c. */ #define _GNU_SOURCE #include <pthread.h> /* pthread_create() */ #include <stdio.h> /* fprintf() */ #include <stdlib.h> /* atoi() */ #include <string.h> /* memset() */ #include <unistd.h> /* usleep() */ #include "../../drd/drd.h" #include "../../config.h" #define BARRIER_SERIAL_THREAD -1 /* Local datatypes. */ typedef struct { /* * number of threads that must call barrier_wait() before any of them * successfully return from the call. */ unsigned thread_count; /* number of barrier_wait() calls since last barrier. */ volatile unsigned wait_count; /* * barrier count. Only the least significant bit matters -- a single bit * counter would be sufficient. */ volatile unsigned barrier_count; } barrier_t; struct threadinfo { int thread_num; barrier_t* b; pthread_t tid; int* array; int iterations; }; /* Local variables. */ static int s_silent; /* Local functions. */ static void barrier_init(barrier_t* b, unsigned count) { b->thread_count = count; b->wait_count = 0; b->barrier_count = 0; ANNOTATE_BARRIER_INIT(b, count, 0); } static void barrier_destroy(barrier_t* b) { ANNOTATE_BARRIER_DESTROY(b); memset(b, 0, sizeof(*b)); } static int barrier_wait(barrier_t* b) { int res; unsigned barrier_count; res = 0; ANNOTATE_BARRIER_WAIT_BEFORE(b); barrier_count = b->barrier_count; if (__sync_add_and_fetch(&b->wait_count, 1) == b->thread_count) { __sync_sub_and_fetch(&b->wait_count, b->thread_count); __sync_add_and_fetch(&b->barrier_count, 1); res = BARRIER_SERIAL_THREAD; } else { while (b->barrier_count == barrier_count) { #ifndef HAVE_PTHREAD_YIELD /* Darwin doesn't have an implementation of pthread_yield(). */ usleep(100 * 1000); #else pthread_yield(); #endif } } ANNOTATE_BARRIER_WAIT_AFTER(b); return res; } /* * Single thread, which touches p->iterations elements of array p->array. * Each modification of an element of p->array is a data race. */ static void* threadfunc(struct threadinfo* p) { int i; int* const array = p->array; barrier_t* const b = p->b; if (! s_silent) printf("thread %d iteration 0\n", p->thread_num); barrier_wait(b); for (i = 0; i < p->iterations; i++) { if (! s_silent) printf("thread %d iteration %d; writing to %p\n", p->thread_num, i + 1, &array[i]); array[i] = i; barrier_wait(b); } return 0; } /* Actual test, consisting of nthread threads. */ static void barriers_and_races(const int nthread, const int iterations) { const struct timespec delay = { 0, 100 * 1000 * 1000 }; int i; struct threadinfo* t; barrier_t b; int* array; t = malloc(nthread * sizeof(struct threadinfo)); array = malloc(iterations * sizeof(array[0])); if (! s_silent) printf("&array[0] = %p\n", array); barrier_init(&b, nthread); for (i = 0; i < nthread; i++) { t[i].thread_num = i + 1; t[i].b = &b; t[i].array = array; t[i].iterations = iterations; pthread_create(&t[i].tid, 0, (void*(*)(void*))threadfunc, &t[i]); nanosleep(&delay, 0); } for (i = 0; i < nthread; i++) pthread_join(t[i].tid, 0); barrier_destroy(&b); free(array); free(t); } int main(int argc, char** argv) { int nthread; int iterations; nthread = (argc > 1) ? atoi(argv[1]) : 2; iterations = (argc > 2) ? atoi(argv[2]) : 3; s_silent = (argc > 3) ? atoi(argv[3]) : 0; barriers_and_races(nthread, iterations); fprintf(stderr, "Done.\n"); return 0; }