// philo.c — los filósofos comensales con semáforos.
//
// Uso: ./philo naive     todos toman primero el tenedor izquierdo: deadlock
//      ./philo ordered   el filósofo 4 toma primero el derecho: sin ciclo
// Cada filósofo come MEALS veces. Un watchdog detecta el deadlock y
// muestra qué tenedor tiene y cuál espera cada filósofo.
#include <semaphore.h>
#include <string.h>
#include "trace.h"

#define N     5
#define MEALS 1000

static sem_t forks[N];
static int ordered;
static atomic_int holding[N];           // tenedor que tiene (-1: ninguno)
static atomic_int waiting[N];           // tenedor que espera (-1: ninguno)
static int meals[N];

static int left(int p) { return p; }
static int right(int p) { return (p + 1) % N; }

static void take(int p, int f) {
    atomic_store(&waiting[p], f);
    sem_wait(&forks[f]);
    atomic_store(&waiting[p], -1);
}

static void get_forks(int p) {
    int first = left(p), second = right(p);
    if (ordered && p == N - 1) {        // el último rompe el ciclo
        first = right(p);
        second = left(p);
    }
    take(p, first);
    atomic_store(&holding[p], first);
    if (meals[p] == 0)
        msleep(20);                     // la primera vez, todos a la vez
    take(p, second);
}

static void put_forks(int p) {
    sem_post(&forks[left(p)]);
    sem_post(&forks[right(p)]);
    atomic_store(&holding[p], -1);
}

static void *philosopher(void *arg) {
    int p = (int)(long)arg;
    for (int i = 0; i < MEALS; i++) {
        get_forks(p);
        meals[p]++;                     // comer
        progress_bump();
        put_forks(p);
    }
    return NULL;
}

static void report(void) {
    for (int p = 0; p < N; p++)
        printf("  filósofo %d: tiene el tenedor %d, espera el %d\n", p,
               atomic_load(&holding[p]), atomic_load(&waiting[p]));
}

int main(int argc, char *argv[]) {
    const char *mode = argc > 1 ? argv[1] : "";
    if (strcmp(mode, "naive") && strcmp(mode, "ordered")) {
        fprintf(stderr, "uso: %s naive|ordered\n", argv[0]);
        return 1;
    }
    ordered = strcmp(mode, "ordered") == 0;
    for (int i = 0; i < N; i++) {
        sem_init(&forks[i], 0, 1);
        holding[i] = waiting[i] = -1;
    }
    trace_start();
    watchdog_start(1, "Deadlock: espera circular.", report);

    pthread_t t[N];
    for (long i = 0; i < N; i++)
        pthread_create(&t[i], NULL, philosopher, (void *)i);
    for (int i = 0; i < N; i++)
        pthread_join(t[i], NULL);
    watchdog_done();
    TRACE("todos comieron %d veces: sin deadlock", MEALS);
    return 0;
}
