// pin.c — ¿en qué CPU corre un proceso? Afinidad con sched_setaffinity.
//
// Uso: ./pin          (el kernel elige el CPU)
//      ./pin <cpu>    (fija el proceso a ese CPU)
//
// Muestrea sched_getcpu() durante un trabajo de CPU con varios hilos
// compitiendo y cuenta las migraciones (cambios de CPU).
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define SAMPLES 2000

static volatile int stop = 0;

// Hilos de relleno: mantienen ocupados a los demás CPUs.
static void *burn(void *arg) {
    (void)arg;
    while (!stop)
        ;
    return NULL;
}

static void spin_us(long us) {
    struct timespec t0, t;
    clock_gettime(CLOCK_MONOTONIC, &t0);
    do {
        clock_gettime(CLOCK_MONOTONIC, &t);
    } while ((t.tv_sec - t0.tv_sec) * 1000000L + (t.tv_nsec - t0.tv_nsec) / 1000 < us);
}

int main(int argc, char *argv[]) {
    long ncpu = sysconf(_SC_NPROCESSORS_ONLN);
    if (argc > 1) {
        int cpu = atoi(argv[1]);
        cpu_set_t set;
        CPU_ZERO(&set);
        CPU_SET(cpu, &set);
        if (sched_setaffinity(0, sizeof(set), &set) < 0) {
            perror("sched_setaffinity");
            return 1;
        }
        printf("fijado al CPU %d\n", cpu);
    }

    // El doble de hilos de relleno que CPUs: hay más trabajo que CPUs.
    // Se crean después de fijar la afinidad, así que la heredan.
    pthread_t th[64];
    int nth = 2 * ncpu < 64 ? 2 * (int)ncpu : 64;
    for (int i = 0; i < nth; i++)
        pthread_create(&th[i], NULL, burn, NULL);

    int seen[1024] = {0};
    int last = -1, migrations = 0;
    for (int i = 0; i < SAMPLES; i++) {
        spin_us(500);
        int c = sched_getcpu();
        if (c >= 0 && c < 1024)
            seen[c] = 1;
        if (last != -1 && c != last)
            migrations++;
        last = c;
    }
    stop = 1;
    for (int i = 0; i < nth; i++)
        pthread_join(th[i], NULL);

    printf("CPUs en línea: %ld\nCPUs usados:", ncpu);
    for (int c = 0; c < 1024; c++)
        if (seen[c])
            printf(" %d", c);
    printf("\nmigraciones en %d muestras: %d\n", SAMPLES, migrations);
    return 0;
}
