-
Notifications
You must be signed in to change notification settings - Fork 1
Add generic interface for conditionally halting the interpreter #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,6 +99,11 @@ impl CraneFackError for ParserError { | |
| /// Runtime errors for interpreter invocations | ||
| #[derive(Debug)] | ||
| pub enum RuntimeError { | ||
| /// The interpreter's configured [`Limiter`][crate::limiters::Limiter] triggered | ||
| LimiterTriggered { | ||
| span: Range<usize>, | ||
| }, | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding a |
||
|
|
||
| /// The program tries to use a heap cell beyond the maximu allowed size | ||
| MaxHeapSizeReached { | ||
| span: Range<usize>, | ||
|
|
@@ -125,6 +130,7 @@ impl Error for RuntimeError { | |
| impl Display for RuntimeError { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
| match self { | ||
| RuntimeError::LimiterTriggered { .. } => write!(f, "Max number of cycles reached"), | ||
| RuntimeError::MaxHeapSizeReached { | ||
| max_heap_size, | ||
| required, | ||
|
|
@@ -142,6 +148,7 @@ impl Display for RuntimeError { | |
| impl CraneFackError for RuntimeError { | ||
| fn get_message(&self) -> (Option<Range<usize>>, String, Option<String>) { | ||
| match self { | ||
| RuntimeError::LimiterTriggered { span } => (Some(span.clone()), self.to_string(), None), | ||
| RuntimeError::MaxHeapSizeReached { span, .. } => { | ||
| (Some(span.clone()), self.to_string(), None) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,6 +55,7 @@ mod analyzer; | |
| mod backends; | ||
| mod errors; | ||
| mod ir; | ||
| pub mod limiters; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As of now all modules are implementation details and all types are explicitly exported. |
||
| mod optimizations; | ||
| mod parser; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| pub enum LimiterResult { | ||
| /// Continue executing | ||
| Continue, | ||
| /// Halt execution and return [`LimiterTriggered`][crate::errors::RuntimeError::LimiterTriggered] | ||
| Halt, | ||
| } | ||
|
|
||
| /// Generic interface for determining whether to continue or stop execution. | ||
| pub trait Limiter { | ||
| fn execute_op(&mut self) -> LimiterResult; | ||
| } | ||
|
|
||
| /// Always returns [`Continue`][LimiterResult::Continue]. | ||
| pub struct Unlimited; | ||
|
|
||
| impl Limiter for Unlimited { | ||
| fn execute_op(&mut self) -> LimiterResult { | ||
| LimiterResult::Continue | ||
| } | ||
| } | ||
|
|
||
| /// Limit execution to within the configured number of cycles. | ||
| pub struct CycleLimiter(usize); | ||
|
|
||
| impl CycleLimiter { | ||
| pub fn new(max_cycles: usize) -> Self { | ||
| CycleLimiter(max_cycles) | ||
| } | ||
| } | ||
|
|
||
| impl Limiter for CycleLimiter { | ||
| fn execute_op(&mut self) -> LimiterResult { | ||
| self.0 = self.0.saturating_sub(1); | ||
| if self.0 == 0 { | ||
| LimiterResult::Halt | ||
| } else { | ||
| LimiterResult::Continue | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Limits execution to within a given [`Duration`](std::time::Duration) of time. | ||
| pub struct TimeLimiter { | ||
| max_duration: std::time::Duration, | ||
| started_at: Option<std::time::Instant>, | ||
| } | ||
|
|
||
| impl TimeLimiter { | ||
| pub fn new(max_duration: std::time::Duration) -> Self { | ||
| TimeLimiter { | ||
| max_duration, | ||
| started_at: None, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Limiter for TimeLimiter { | ||
| fn execute_op(&mut self) -> LimiterResult { | ||
| let started_at = self.started_at.get_or_insert(std::time::Instant::now()); | ||
| if started_at.elapsed() > self.max_duration { | ||
| LimiterResult::Halt | ||
| } else { | ||
| LimiterResult::Continue | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of returning
LimiterResultit might be better if the limiter returns an Result<(), RuntimeError>.this would allow the limiter to control the
LimiterTriggerederror. In addition i think that passing more informations toLimiter::execute_opwould be usefull.Maybe soming like
fn execute_op(&mut self, span: &Range<usize>, op: &Op, heap: &[u8], pointer: usize) -> Result<(), RuntimeError>