// race.c — condición de carrera en un contador compartido, con y sin mutex.
//
// Uso: ./race <modo> <hilos> <iteraciones por hilo>
//   modo = nolock   cada hilo hace counter++ sin protección (incorrecto)
//          mutex    cada counter++ va entre lock y unlock (correcto, lento)
//          local    cada hilo suma en una variable propia y al final
//                   agrega su total al contador con un solo lock (correcto, rápido)
//
// Ejemplo: ./race nolock 4 1000000
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

static volatile long counter = 0;   // dato compartido por todos los hilos
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static long loops;

static void *worker_nolock(void *arg) {
    (void)arg;
    for (long i = 0; i < loops; i++)
        counter++;                    // leer, sumar, escribir: no es atómico
    return NULL;
}

static void *worker_mutex(void *arg) {
    (void)arg;
    for (long i = 0; i < loops; i++) {
        pthread_mutex_lock(&lock);    // inicio de la sección crítica
        counter++;
        pthread_mutex_unlock(&lock);  // fin de la sección crítica
    }
    return NULL;
}

static void *worker_local(void *arg) {
    (void)arg;
    long mine = 0;                    // vive en la pila de este hilo: no se comparte
    for (long i = 0; i < loops; i++)
        mine++;
    pthread_mutex_lock(&lock);        // una sola sección crítica por hilo
    counter += mine;
    pthread_mutex_unlock(&lock);
    return NULL;
}

static double now(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec + ts.tv_nsec / 1e9;
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        fprintf(stderr, "uso: race nolock|mutex|local <hilos> <iteraciones>\n");
        return 1;
    }
    void *(*fn)(void *);
    if (strcmp(argv[1], "nolock") == 0)
        fn = worker_nolock;
    else if (strcmp(argv[1], "mutex") == 0)
        fn = worker_mutex;
    else if (strcmp(argv[1], "local") == 0)
        fn = worker_local;
    else {
        fprintf(stderr, "modo desconocido: %s\n", argv[1]);
        return 1;
    }
    int n = atoi(argv[2]);
    loops = atol(argv[3]);
    if (n < 1 || n > 64 || loops < 0) {
        fprintf(stderr, "hilos entre 1 y 64, iteraciones >= 0\n");
        return 1;
    }

    pthread_t th[64];
    double t0 = now();
    for (int i = 0; i < n; i++)
        if (pthread_create(&th[i], NULL, fn, NULL) != 0) {
            fprintf(stderr, "pthread_create falló\n");
            return 1;
        }
    for (int i = 0; i < n; i++)
        pthread_join(th[i], NULL);    // esperar a que todos terminen
    double t1 = now();

    long expected = n * loops;
    printf("%-6s  hilos=%d  esperado=%ld  obtenido=%ld  %s  %.3f s\n",
           argv[1], n, expected, counter,
           counter == expected ? "OK " : "MAL", t1 - t0);
    return 0;
}
