/* vim: set expandtab sts=4: */
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#include <pthread.h>
#include <unistd.h>

typedef struct watchdog_s {
    int fd;
    int timeout;
} watchdog_t;

/* wait for file to be closed and then kill the current process */
void*
watchdog(void* param) {
    char buf[64];
    size_t n;
    sigset_t ss;
    struct timeval tv;
    watchdog_t *wdt = (watchdog_t*) param;
    int pid = getpid();

    sigfillset(&ss);
    pthread_sigmask(SIG_BLOCK, &ss, NULL);

    /* when file closed, send TERM to himself */
    for (;;) {
        n = read(wdt->fd, buf, 64);
        if (n <= 0) {
            kill(getpid(), SIGTERM);
            break;
        }
    }

    if (wdt->timeout) {
        /* if after timeout process is still running, just _exit(2) */
        tv.tv_sec = wdt->timeout;
        tv.tv_usec = 0;
        select(0, NULL, NULL, NULL, &tv);
        _exit(-1);
    }
}

MODULE = Parallel::Boss    PACKAGE = Parallel::Boss    PREFIX = boss_
PROTOTYPES: DISABLE

void
boss__start_watchdog(fd, timeout)
        int fd
        int timeout
    PREINIT:
        int s;
        watchdog_t *wdt;
        pthread_t pth;
        pthread_attr_t attr;
    CODE:
        Newx(wdt, 1, watchdog_t);
        wdt->fd = fd;
        wdt->timeout = timeout;
        s = pthread_attr_init(&attr);
        if (s != 0) {
            croak("Couldn't start watchdog, pthread_attr_init returned %d", s);
        }
        s = pthread_create(&pth, &attr, &watchdog, wdt);
        if (s != 0) {
            croak("Couldn't start watchdog, pthread_create returned %d", s);
        }