diff --git a/README.md b/README.md index 78305ec..100ebdb 100644 --- a/README.md +++ b/README.md @@ -545,6 +545,9 @@ Jumping to a label is always safer than a manual skip. ; The label can be a numeric or string label, its sole purpose is tying the two together. They must be unique in the program. (barrier-open LABEL) (barrier-close LABEL) + +; Set coroutine scheduler timeslice (in microseconds). Set to zero to disable preemption. +(rt-opt RT_TIMESLICE Rd'usec) ``` ## Arithmetic Module diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 4424a15..20a4253 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -613,7 +613,7 @@ mod test { global_regs: [0; REG_COUNT], ext_data: Default::default(), cr_deadline: None, - critical_section: false, + critical_section: 0, }, const_eval_ti: ti.clone(), parsing_expr: false, diff --git a/crsn/src/runtime/run_thread.rs b/crsn/src/runtime/run_thread.rs index f16c781..7ed8f1d 100644 --- a/crsn/src/runtime/run_thread.rs +++ b/crsn/src/runtime/run_thread.rs @@ -230,7 +230,9 @@ impl RunThread { // Do switch let old = mem::replace(&mut self.state.cr, cr); self.state.parked.push_back(old); - self.state.cr_deadline = Some(now + *self.info.scheduler_interval.read()); + + self.state.start_task_switching(); + } else if let Some(due) = closest_due { let time = due.saturating_duration_since(now); trace!("No thread to switch to, sleep {:?}", time); @@ -248,7 +250,7 @@ impl RunThread { if n_alive == 0 { trace!("Stop task switching, no parked threads are alive"); - self.state.cr_deadline = None; + self.state.stop_task_switching(); // This should improve performance in single-threaded mode } match self.state.cr.cr_state { diff --git a/crsn/src/runtime/run_thread/state.rs b/crsn/src/runtime/run_thread/state.rs index e6c52ea..81f8185 100644 --- a/crsn/src/runtime/run_thread/state.rs +++ b/crsn/src/runtime/run_thread/state.rs @@ -12,7 +12,7 @@ use nudge::{likely}; use crate::asm::instr::cond::Flag; use std::fmt::{Debug, Formatter}; use std::fmt; -use std::time::Instant; +use std::time::{Instant, Duration}; pub struct RunState { pub thread_info: Arc, @@ -95,10 +95,24 @@ impl RunState { }); if self.cr_deadline.is_none() { // start context switching - self.cr_deadline = Some(Instant::now() + *self.thread_info.scheduler_interval.read()); + self.start_task_switching(); } handle } + + pub(crate) fn start_task_switching(&mut self) { + let ival = *self.thread_info.scheduler_interval.read(); + if ival > Duration::default() { + self.cr_deadline = Some(Instant::now() + ival); + } else { + // Disabled + self.cr_deadline = None; + } + } + + pub(crate) fn stop_task_switching(&mut self) { + self.cr_deadline = None; + } } impl Debug for RunState { diff --git a/examples/coroutines3-crit.csn b/examples/coroutines3-crit.csn index eed6d59..bcb0aa3 100644 --- a/examples/coroutines3-crit.csn +++ b/examples/coroutines3-crit.csn @@ -1,16 +1,21 @@ ( ; This example shows the use of critical sections. + ; Set short timeslice (50us) to make the effect more pronounced + (rt-opt RT_TIMESLICE 50) + (spawn _ unsafe 'A' 'Z') - (spawn _ safe '0' '9') - (ssleep 2) + (spawn _ unsafe 'a' 'z') + (spawn _ safe '0' '9') ; Notice that the sequence 0-9 is always printed in its entirety - because it is in a critical section + (msleep 200) + (halt) (proc unsafe start end ; This can be interrupted any time (:x) + (yield) (ld r0 start) (:l) - (msleep 5) (ld @cout r0) (cmp r0 end) (j.eq :x)