// join.c — el padre espera al hijo con una variable de condición.
//
// Uso: ./join ok        estado + lock + while: funciona siempre
//      ./join nostate   sin la variable done: si el hijo avisa antes,
//                       el aviso se pierde y el padre duerme para siempre
//      ./join nolock    el padre revisa done sin el lock: el hijo avisa
//                       justo entre la revisión y el wait
// Los retrasos (msleep) fuerzan la intercalación mala; un watchdog
// detecta que el padre no despierta.
#include <string.h>
#include "trace.h"

static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t c = PTHREAD_COND_INITIALIZER;
static int done = 0;
static const char *mode;

static void thr_exit(void) {
    if (strcmp(mode, "nolock") == 0) {
        done = 1;                       // sin lock
        TRACE("hijo:  done = 1, signal (sin lock)");
        pthread_cond_signal(&c);
    } else {
        pthread_mutex_lock(&m);
        if (strcmp(mode, "ok") == 0)
            done = 1;
        TRACE("hijo:  %ssignal", strcmp(mode, "ok") == 0 ? "done = 1, " : "");
        pthread_cond_signal(&c);
        pthread_mutex_unlock(&m);
    }
}

static void thr_join(void) {
    if (strcmp(mode, "ok") == 0) {
        pthread_mutex_lock(&m);
        while (done == 0) {
            TRACE("padre: done == 0, wait");
            pthread_cond_wait(&c, &m);
        }
        pthread_mutex_unlock(&m);
    } else if (strcmp(mode, "nostate") == 0) {
        pthread_mutex_lock(&m);
        TRACE("padre: wait (no hay estado que revisar)");
        pthread_cond_wait(&c, &m);
        pthread_mutex_unlock(&m);
    } else {                            // nolock
        if (done == 0) {                // revisa sin el lock...
            TRACE("padre: done == 0 (revisado sin lock)");
            msleep(100);                // ...y lo desalojan aquí
            pthread_mutex_lock(&m);
            TRACE("padre: wait");
            pthread_cond_wait(&c, &m);
            pthread_mutex_unlock(&m);
        }
    }
}

static void *child(void *arg) {
    (void)arg;
    if (strcmp(mode, "nolock") == 0)
        msleep(50);                     // llega en medio del padre
    TRACE("hijo:  trabajo terminado");
    thr_exit();
    return NULL;
}

int main(int argc, char *argv[]) {
    mode = argc > 1 ? argv[1] : "ok";
    if (strcmp(mode, "ok") && strcmp(mode, "nostate") && strcmp(mode, "nolock")) {
        fprintf(stderr, "uso: %s ok|nostate|nolock\n", argv[0]);
        return 1;
    }
    trace_start();
    watchdog_start(1, "El padre duerme: el aviso se perdió.", NULL);

    pthread_t p;
    pthread_create(&p, NULL, child, NULL);
    if (strcmp(mode, "nostate") == 0)
        msleep(50);                     // el hijo corre primero
    thr_join();
    TRACE("padre: el hijo terminó");
    watchdog_done();
    pthread_join(p, NULL);
    return 0;
}
