mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 00:16:11 +00:00
175 lines
3.6 KiB
C
175 lines
3.6 KiB
C
/*
|
|
* 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;
|
|
}
|