|
|
|
@ -1,5 +1,6 @@ |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
|
|
|
|
|
use std::thread::JoinHandle; |
|
|
|
|
use std::time::{Duration, Instant}; |
|
|
|
|
|
|
|
|
|
pub use info::ThreadInfo; |
|
|
|
@ -13,8 +14,6 @@ use crate::runtime::program::Program; |
|
|
|
|
use crate::runtime::run_thread::state::{CoroutineContext, CoroutineState}; |
|
|
|
|
use std::{mem, thread}; |
|
|
|
|
use crate::asm::instr::cond::Flag; |
|
|
|
|
use parking_lot::RwLock; |
|
|
|
|
use crate::asm::instr::Flatten; |
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)] |
|
|
|
|
pub struct ThreadToken(pub u32); |
|
|
|
@ -41,14 +40,12 @@ impl RunThread { |
|
|
|
|
pub fn new(params: ThreadParams) -> Self { |
|
|
|
|
let extensions = params.program.extensions.clone(); |
|
|
|
|
|
|
|
|
|
// TODO investigate if this division to 2 structs is still needed
|
|
|
|
|
|
|
|
|
|
let ti = Arc::new(ThreadInfo { |
|
|
|
|
id: params.id, |
|
|
|
|
uniq: params.uniq, |
|
|
|
|
program: params.program, |
|
|
|
|
cycle_time: params.cycle_time, |
|
|
|
|
scheduler_interval: RwLock::new(params.scheduler_interval), |
|
|
|
|
scheduler_interval: params.scheduler_interval, |
|
|
|
|
extensions, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
@ -63,8 +60,7 @@ impl RunThread { |
|
|
|
|
parked: Default::default(), |
|
|
|
|
global_regs: [0; REG_COUNT], |
|
|
|
|
ext_data: Default::default(), |
|
|
|
|
cr_deadline: None, |
|
|
|
|
critical_section: 0, |
|
|
|
|
cr_deadline: None |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
Self { |
|
|
|
@ -73,17 +69,21 @@ impl RunThread { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Spawn as a thread
|
|
|
|
|
pub fn spawn(self) -> JoinHandle<()> { |
|
|
|
|
std::thread::spawn(move || { |
|
|
|
|
self.run(); |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Start synchronously
|
|
|
|
|
pub fn run(mut self) { |
|
|
|
|
let mut loop_helper = spin_sleep::LoopHelper::builder() |
|
|
|
|
.build_with_target_rate(1.0/self.info.cycle_time.as_secs_f64()); |
|
|
|
|
|
|
|
|
|
loop_helper.loop_start(); |
|
|
|
|
let mut orig_pc; |
|
|
|
|
|
|
|
|
|
let fault = 'run: loop { |
|
|
|
|
'run: loop { |
|
|
|
|
let mut want_switch = false; |
|
|
|
|
orig_pc = self.state.cr.frame.pc; |
|
|
|
|
match self.eval_op() { |
|
|
|
|
Ok(EvalRes { cycles, advance, sched }) => { |
|
|
|
|
for _ in 0..cycles { |
|
|
|
@ -91,6 +91,7 @@ impl RunThread { |
|
|
|
|
loop_helper.loop_start(); |
|
|
|
|
} |
|
|
|
|
trace!("Step {}; Status = {}", advance, self.state.cr.frame.status); |
|
|
|
|
let orig_pc = self.state.cr.frame.pc; |
|
|
|
|
self.state.cr.frame.pc.advance(advance); |
|
|
|
|
|
|
|
|
|
if let Some(dl) = self.state.cr_deadline { |
|
|
|
@ -156,39 +157,22 @@ impl RunThread { |
|
|
|
|
// Do not advance, the last instruction will be re-tried
|
|
|
|
|
want_switch = true; |
|
|
|
|
} |
|
|
|
|
Err(Fault::Halt) => { |
|
|
|
|
// TODO implement coordinated shutdown when more threads are running!
|
|
|
|
|
break 'run; |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
break 'run e; |
|
|
|
|
error!("Fault: {:?}", e); |
|
|
|
|
error!("Core dump: {:?}", self.state); |
|
|
|
|
break 'run; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Resolve the next coroutine to run, or wait a bit...
|
|
|
|
|
'next: loop { |
|
|
|
|
if want_switch { |
|
|
|
|
let now = Instant::now(); |
|
|
|
|
|
|
|
|
|
if self.state.critical_section > 0 { |
|
|
|
|
match self.state.cr.cr_state { |
|
|
|
|
CoroutineState::Ready => {} |
|
|
|
|
CoroutineState::Sleep { due } => { |
|
|
|
|
let time = due.saturating_duration_since(now); |
|
|
|
|
trace!("Sleep in critical: {:?}", time); |
|
|
|
|
thread::sleep(time); |
|
|
|
|
continue 'run; |
|
|
|
|
} |
|
|
|
|
CoroutineState::Finished(_) | CoroutineState::YieldedValue(_) => { |
|
|
|
|
// This is not good
|
|
|
|
|
break 'run Fault::Deadlock; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This is either a bug, or waiting for IO.
|
|
|
|
|
// If it is the latter, try to give the OS a bit of breathing room..
|
|
|
|
|
std::thread::yield_now(); |
|
|
|
|
continue 'run; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
trace!("Switch requested"); |
|
|
|
|
|
|
|
|
|
let now = Instant::now(); |
|
|
|
|
|
|
|
|
|
let mut candidate = None; |
|
|
|
|
let mut closest_due = None; |
|
|
|
|
for _ in 0..self.state.parked.len() { |
|
|
|
@ -230,19 +214,11 @@ impl RunThread { |
|
|
|
|
// Do switch
|
|
|
|
|
let old = mem::replace(&mut self.state.cr, cr); |
|
|
|
|
self.state.parked.push_back(old); |
|
|
|
|
|
|
|
|
|
self.state.start_task_switching(); |
|
|
|
|
|
|
|
|
|
self.state.cr_deadline = Some(now + self.info.scheduler_interval); |
|
|
|
|
} else if let Some(due) = closest_due { |
|
|
|
|
let time = due.saturating_duration_since(now); |
|
|
|
|
trace!("No thread to switch to, sleep {:?}", time); |
|
|
|
|
thread::sleep(time); |
|
|
|
|
|
|
|
|
|
continue 'next; |
|
|
|
|
} else { |
|
|
|
|
// Nothing to run?
|
|
|
|
|
thread::yield_now(); |
|
|
|
|
trace!("No thread to switch to!"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let n_alive = self.state.parked.iter() |
|
|
|
@ -250,33 +226,11 @@ impl RunThread { |
|
|
|
|
|
|
|
|
|
if n_alive == 0 { |
|
|
|
|
trace!("Stop task switching, no parked threads are alive"); |
|
|
|
|
self.state.stop_task_switching(); // This should improve performance in single-threaded mode
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match self.state.cr.cr_state { |
|
|
|
|
CoroutineState::Finished(_) | CoroutineState::YieldedValue(_) => { |
|
|
|
|
break 'run Fault::RootReturned; |
|
|
|
|
self.state.cr_deadline = None; |
|
|
|
|
} |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
break 'next; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
match fault { |
|
|
|
|
Fault::Halt => { |
|
|
|
|
debug!("Thread ended."); |
|
|
|
|
} |
|
|
|
|
e => { |
|
|
|
|
if let Some(instr) = self.info.program.ops.get(orig_pc.0 as usize) { |
|
|
|
|
error!("Fault at {}: {}", instr.pos(), e); |
|
|
|
|
} else { |
|
|
|
|
error!("Fault at PC {}: {}", orig_pc, e); |
|
|
|
|
} |
|
|
|
|
warn!("Core dump: {:?}", self.state); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|