irid-std 0.3.1

A replacement for std when running without a filesystem on the irid kernel
Documentation
use core::{marker::PhantomData, sync::atomic::{AtomicUsize, Ordering}, time::Duration};

use alloc::{boxed::Box, format, string::String};
use irid_syscall::{SystemError, ffi::{io::IO_WATCH, thread::{ThreadID, syscall_join, syscall_spawn, syscall_yield}}, io::get_file_config};

use crate::{fs::File, io::{self, convert_error}, os::fd::AsRawFd, read_value};

pub fn spawn<T: Send + 'static>(f: impl FnOnce() -> T + Send + 'static) -> JoinHandle<T> {
    Builder::new().spawn(f).expect("failed to spawn thread")
}

pub struct Builder {
    name: String,
    stack_size: usize,
}

static THREAD_NUMBER: AtomicUsize = AtomicUsize::new(1);

impl Builder {
    pub fn new() -> Self {
        Self {
            name: format!("thread_{}", THREAD_NUMBER.fetch_add(1, Ordering::SeqCst)),
            stack_size: 0x30000,
        }
    }

    pub fn name(mut self, name: String) -> Self {
        self.name = name;

        self
    }
    
    pub fn stack_size(mut self, stack_size: usize) -> Self {
        self.stack_size = stack_size;

        self
    }

    pub fn spawn<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(self, f: F) -> Result<JoinHandle<T>, io::Error> {
        let data = Box::new(f);

        let result: Result<ThreadID, SystemError> = syscall_spawn(thread_entry::<T, F>, Box::into_raw(data) as *mut (), self.stack_size).into();

        let thread = result.map_err(convert_error)?;

        Ok(JoinHandle { thread, data: PhantomData  })
    }
}

unsafe extern "C" fn thread_entry<T, F: FnOnce() -> T + Send + 'static>(f: *mut ()) {
    let f = unsafe { Box::from_raw(f as *mut F)};

    let result = Box::new(f());

    irid_syscall::ffi::thread::syscall_thread_exit(-(Box::into_raw(result) as isize))
}

pub struct JoinHandle<T> {
    thread: ThreadID,
    data: PhantomData<T>
}

impl <T> JoinHandle<T> {
    pub fn join(self) -> Result<T, io::Error> {
        let result = syscall_join(self.thread);

        if result < 0 {
            let ptr = unsafe { Box::from_raw((-result) as *mut T) };

            Ok(*ptr)
        } else {
            Err(SystemError(result)).map_err(convert_error)
        }
    }
}

pub fn yield_now() {
    syscall_yield();
}

pub fn sleep(duration: Duration) {
    let mut timer = File::open("/dev/timer0").expect("failed to open timer file");

    let current: u64 = read_value(&mut timer).expect("failed to read current time");

    let target = u64::try_from(current as u128 + duration.as_nanos()).expect("total duration is too long for sleep");

    while read_value::<u64>(&mut timer).expect("failed to read time") < target {
        get_file_config::<()>(timer.as_raw_fd(), IO_WATCH).expect("failed to wait for timer");
    }
}