use avr_oxide::alloc::boxed::Box;
use avr_oxide::concurrency::scheduler::{ThreadContext, ThreadState};
use avr_oxide::concurrency::stack::{ThreadStack, DynamicThreadStack};
use avr_oxide::concurrency::util::{ThreadId, ThreadSet};
use avr_oxide::concurrency::{interrupt, scheduler};
use avr_oxide::cpu;
use avr_oxide::deviceconsts::oxide::{DEFAULT_THREAD_STACK_SIZE};
use avr_oxide::hal::generic::cpu::ProcessorContext;
use avr_oxide::util::datatypes::{BitField, BitIndex};
use avr_oxide::hal::generic::cpu::Cpu;
pub struct JoinHandle {
thread: Thread
}
#[repr(C)]
#[derive(Clone,Copy)]
pub struct Thread {
thread_id: ThreadId
}
pub struct Builder {
stack_size: usize
}
pub fn spawn<F>(f: F) -> JoinHandle
where
F: FnOnce() -> u8,
F: Send + 'static
{
Builder::new().spawn(f)
}
pub fn yield_now() {
unsafe {
if cpu!().interrupts_enabled() && !cpu!().in_isr() {
scheduler::userland_schedule_and_switch();
} else {
avr_oxide::oserror::halt(avr_oxide::oserror::OsError::CannotYield)
}
}
}
pub(crate) fn spawn_with_stack(_isotoken: avr_oxide::concurrency::interrupt::token::Isolated, code: Box<dyn FnOnce() -> u8>, stack: Box<dyn ThreadStack>) -> JoinHandle {
unsafe {
let scheduler = scheduler::instance();
for i in ThreadId::MIN..ThreadId::MAX {
match scheduler.threads[i] {
None => {
let stack_top = stack.get_stack_top();
let thread = ThreadContext {
state: ThreadState::Schedulable,
entrypoint: Some(code),
returncode: 0,
waiting_threads: ThreadSet::new(),
stack: Some(stack),
cpu_context: ProcessorContext {
sreg: BitField::with_bits_set(&[BitIndex::bit_c(7)]), gpregs: [0x00; 32],
pc: scheduler::thread_entrypoint as u16,
sp: stack_top as u16,
tid: i,
#[cfg(feature="extended_addressing")]
rampx: 0,
#[cfg(feature="extended_addressing")]
rampy: 0,
#[cfg(feature="extended_addressing")]
rampz: 0,
#[cfg(feature="extended_addressing")]
eind: 0
},
guard: 0xf0.into()
};
scheduler.threads[i] = Some(thread);
return JoinHandle {
thread: Thread {
thread_id: i,
}
};
},
Some(_) => {}
}
}
avr_oxide::oserror::halt(avr_oxide::oserror::OsError::OutOfThreads);
}
}
impl JoinHandle {
pub fn join(self) -> u8 {
unsafe {
loop {
let return_code = interrupt::isolated(|isotoken|{
let target_thread = scheduler::get_thread_by_id(self.thread.thread_id);
if target_thread.state == ThreadState::Zombie {
scheduler::set_thread_state(isotoken, self.thread.thread_id, ThreadState::Dead);
Some(target_thread.returncode)
} else {
target_thread.waiting_threads.add_current_thread(isotoken);
scheduler::set_current_thread_state(isotoken, ThreadState::BlockedOnThread);
None
}
});
match return_code {
Some(value) => {
return value
},
None => {
yield_now();
}
}
}
}
}
pub fn thread(&self) -> &Thread {
&self.thread
}
}
impl Thread {
pub fn id(&self) -> ThreadId {
self.thread_id
}
}
impl Builder {
pub fn new() -> Builder {
Builder {
stack_size: DEFAULT_THREAD_STACK_SIZE
}
}
pub fn stack_size(mut self, size: usize) -> Builder {
self.stack_size = size;
self
}
pub fn spawn<F>(self, f: F) -> JoinHandle
where
F: FnOnce() -> u8,
F: Send + 'static
{
interrupt::isolated(|isotoken|{
scheduler::reap_dead_threads(isotoken);
let stack = Box::new(DynamicThreadStack::new(self.stack_size));
let code = Box::new(f);
spawn_with_stack(isotoken, code, stack)
})
}
}