Skip to content

Commit

Permalink
Add timeout function to holmes
Browse files Browse the repository at this point in the history
  • Loading branch information
maurer committed Oct 18, 2017
1 parent 5eeebd9 commit ea91224
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 9 deletions.
44 changes: 35 additions & 9 deletions src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,34 @@ impl Signal {
}
}

fn go_dormant(&self) {
// We went idle, let anyone waiting for this know
for task in self.referents.borrow().iter() {
task.notify();
}

// They'll wake up from the notify, and so can let us
// know if they need to be woken up again.
self.referents.borrow_mut().truncate(0);
}

fn done(&self) -> FutureResult<(), ()> {
trace!("Done with work loop");
if self.state.get() == RuleState::Running {
trace!("And no new work arrived, going idle");
self.state.set(RuleState::Idle);

// We went idle, let anyone waiting for this know
for task in self.referents.borrow().iter() {
task.notify();
}

// They'll wake up from the notify, and so can let us
// know if they need to be woken up again.
self.referents.borrow_mut().truncate(0);
self.go_dormant();
}
result(Ok(()))
}

fn stop(&self) -> FutureResult<(), ()> {
trace!("Work loop being terminated");
self.state.set(RuleState::ShutDown);
self.go_dormant();
result(Ok(()))
}

fn dormant(&self) -> bool {
(self.state.get() == RuleState::Idle) || (self.state.get() == RuleState::ShutDown)
}
Expand Down Expand Up @@ -205,6 +215,8 @@ pub struct Engine {
rule_profiles: Vec<Rc<RefCell<RuleProfile>>>,
signals: Vec<Signal>,
event_loop: Handle,
start_time: Instant,
limiter: Option<Duration>,
}

#[allow(missing_docs)]
Expand Down Expand Up @@ -261,6 +273,8 @@ impl Engine {
signals: Vec::new(),
rule_profiles: Vec::new(),
event_loop: handle,
start_time: Instant::now(),
limiter: None,
}
}

Expand All @@ -273,6 +287,12 @@ impl Engine {
conn.batch_execute(&sql).unwrap();
}

/// For correct operation, limit_time must be called before the installation of
/// any rules
pub fn limit_time(&mut self, limiter: Duration) {
self.limiter = Some(limiter)
}

/// Dump profiling information for how much time was spent in each rule
pub fn dump_profile(&self) -> Vec<RuleProfile> {
self.rule_profiles.iter().map(|x| x.borrow().clone()).collect()
Expand Down Expand Up @@ -447,8 +467,14 @@ impl Engine {
let buddies = self.get_dep_rules(&rule.head.pred_name);
let rule = rule.clone();
let out_signal = signal.clone();
let start_time = self.start_time.clone();
let limiter = self.limiter.clone();
signal.for_each(move |_| {
let rule_start = Instant::now();
match (start_time.elapsed(), limiter) {
(run_time, Some(limit_time)) if run_time > limit_time => return out_signal.stop(),
_ => ()
}
trace!("Activating rule: {:?}", rule.name);
let mut productive: usize = 0;
let pre_db = Instant::now();
Expand Down
29 changes: 29 additions & 0 deletions tests/timeout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#[macro_use]
extern crate holmes;
use holmes::simple::*;
use std::time::{Duration, Instant};

// The whole point of this test is to take an excessively long execution
// and make sure it terminates about on time if a limiter is provided
#[test]
pub fn infinity() {
single(&|holmes: &mut Engine, core: &mut Core| {
// Amount of time holmes gets
let limit = Duration::new(2, 0);
// Wiggle room to shut things down
let wiggle = Duration::new(1, 0);
holmes.limit_time(limit.clone());
let start = Instant::now();
try!(holmes_exec!(holmes, {
predicate!(count(uint64));
fact!(count(0));
func!(let inc: uint64 -> uint64 = |i: &u64| *i + 1);
rule!(inc: count(n_plus_one) <= count(n), {
let n_plus_one = {inc([n])}
})
}));
core.run(holmes.quiesce()).unwrap();
assert!(start.elapsed() < limit + wiggle);
Ok(())
})
}

0 comments on commit ea91224

Please sign in to comment.