use cpu::{CpuAffinityError, ThreadId, current_thread_id, for_each_online_cpu, set_cpu_affinity};
use std::{
    sync::mpsc::{self, SyncSender},
    thread::{self, JoinHandle},
};

fn main() -> Result<(), CpuAffinityError> {
    let cpu = first_online_cpu()?;
    let (thread_id, release_worker, worker) = spawn_waiting_thread();

    set_cpu_affinity(thread_id, [cpu])?;
    println!("pinned target thread {thread_id} to cpu {cpu}");

    release_worker.send(()).unwrap();
    worker.join().unwrap();

    Ok(())
}

fn first_online_cpu() -> Result<usize, CpuAffinityError> {
    let mut first = None;
    for_each_online_cpu(|cpu| {
        first.get_or_insert(cpu);
    })?;
    first.ok_or(CpuAffinityError::EmptyCpuList)
}

fn spawn_waiting_thread() -> (ThreadId, SyncSender<()>, JoinHandle<()>) {
    let (thread_id_sender, thread_id_receiver) = mpsc::sync_channel(1);
    let (release_sender, release_receiver) = mpsc::sync_channel(1);

    let worker = thread::spawn(move || {
        thread_id_sender.send(current_thread_id().unwrap()).unwrap();
        release_receiver.recv().unwrap();
    });

    let thread_id = thread_id_receiver.recv().unwrap();
    (thread_id, release_sender, worker)
}
