#define NITERS 100

/* Sample code demonstrating reader-writers using queue based
 * synchronization..  Create NITERS agents, numbered from 1 to NITERS.
 * Each agent is randomly chosen to be a reader (probability 80%) or a
 * writer (probability 20%).  Writers assign their ID to the global
 * value.  Readers read the global value.  Also insert random delays
 * in agent generation so that writers can catch up to the readers.
 */
#include "rwqueue.h"

/* Uniform random number in [0.0,1.0] */
static double uniform() {
    return (double) random() / RAND_MAX;
}

bool choose_with_probability(double prob) {
    return uniform() <=  prob;
}


/* Global variables */
rw_queue_t q;

void init() {
    rw_queue_init(&q);
}

/* Get read access to data and read */
int ireader(int *buf) 
{
    rw_token_t tok;
    rw_queue_request_read(&q, &tok);

    /* Critical section: Read only */
    int v = *buf;

    rw_queue_release(&q);
    return v;
}

/* Get write access to data and write */
void iwriter(int *buf, int v) 
{
    rw_token_t tok;
    rw_queue_request_write(&q, &tok);

    /* Critical section: Writing happens */
    *buf = v;

    rw_queue_release(&q);
}

int global_value = 0;

void *rthread(void *vargp) {
    int id = *(int *) vargp;
    Free(vargp);
    int v = ireader(&global_value);
    printf("Reader %d read value %d\n", id, v);
    return NULL;
}

void *wthread(void *vargp) {
    int id = *(int *) vargp;
    Free(vargp);
    iwriter(&global_value, id);
    printf("Writer %d wrote value %d\n", id, id);
    return NULL;
}


int main(int argc, char *argv[]) {
    int niters = NITERS;
    if (argc > 1)
	niters = atoi(argv[1]);
    pthread_t tid[niters];
    init();
    int id;
    for (id = 1; id <= niters; id++) {
	bool doread = choose_with_probability(0.8);
	void *vargp = Malloc(sizeof(int));
	*(int *) vargp = id;
	Pthread_create(&tid[id-1], NULL,
		       doread ? rthread : wthread,
		       vargp);
    }
    for (id = 1; id <= niters; id++) {
	Pthread_join(tid[id-1], NULL);
    }
    return 0;
}
				
