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");
}
}