Skip to content

Commit 32d4201

Browse files
authored
Fix timeout not correctly applied (#4)
1 parent 685187a commit 32d4201

File tree

4 files changed

+90
-33
lines changed

4 files changed

+90
-33
lines changed

src/execution_profile.rs

+56-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::error::EngineError;
1414
/// execution_timeout: 1000,
1515
/// max_number_of_terms: 10,
1616
/// };
17-
///
17+
///
1818
/// // Store the settings on the current thread.
1919
/// ThreadLocalParams::init_profile(&execution_profile);
2020
/// ```
@@ -90,7 +90,7 @@ impl ExecutionProfile {
9090
/// Assert that `execution_timeout` is not exceeded.
9191
///
9292
/// Return empty if `execution_timeout` is not exceeded or if `start_execution_time` is not set.
93-
///
93+
///
9494
/// Return [`EngineError::OperationTimeOutError`] otherwise.
9595
pub fn assert_not_timed_out(&self) -> Result<(), EngineError> {
9696
if let Some(start) = self.start_execution_time {
@@ -110,13 +110,12 @@ impl ExecutionProfile {
110110
}
111111
}
112112

113-
114113
/// Hold [`ExecutionProfile`] on the current thread.
115-
///
114+
///
116115
/// The default [`ExecutionProfile`] is the following:
117116
/// ```
118117
/// use regexsolver::execution_profile::ExecutionProfile;
119-
///
118+
///
120119
/// ExecutionProfile {
121120
/// max_number_of_states: 8192,
122121
/// start_execution_time: None,
@@ -181,7 +180,7 @@ impl ThreadLocalParams {
181180

182181
#[cfg(test)]
183182
mod tests {
184-
use crate::regex::RegularExpression;
183+
use crate::{regex::RegularExpression, Term};
185184

186185
use super::*;
187186

@@ -205,4 +204,55 @@ mod tests {
205204

206205
Ok(())
207206
}
207+
208+
#[test]
209+
fn test_execution_timeout_generate_strings() -> Result<(), String> {
210+
let term = Term::from_regex(".*abc.*def.*qdsqd.*sqdsqd.*qsdsqdsqdz").unwrap();
211+
212+
let start_time = SystemTime::now();
213+
let execution_profile = ExecutionProfile {
214+
max_number_of_states: 8192,
215+
start_execution_time: Some(start_time),
216+
execution_timeout: 100,
217+
max_number_of_terms: 50,
218+
};
219+
ThreadLocalParams::init_profile(&execution_profile);
220+
221+
assert_eq!(EngineError::OperationTimeOutError, term.generate_strings(100).unwrap_err());
222+
223+
let run_duration = SystemTime::now()
224+
.duration_since(start_time)
225+
.expect("Time went backwards")
226+
.as_millis();
227+
228+
println!("{run_duration}");
229+
assert!(run_duration <= execution_profile.execution_timeout + 50);
230+
Ok(())
231+
}
232+
233+
#[test]
234+
fn test_execution_timeout_difference() -> Result<(), String> {
235+
let term1 = Term::from_regex(".*abc.*def.*qdqd.*qsdsqdsqdz").unwrap();
236+
let term2 = Term::from_regex(".*abc.*def.*qdsqd.*sqdsqd.*qsdsqdsqdz.*abc.*def.*qdsqd.*sqdsqd.*qsdsqdsqdz.*abc.*def.*qdsqd.*sqdsqd.*qsdsqdsqdz").unwrap();
237+
238+
let start_time = SystemTime::now();
239+
let execution_profile = ExecutionProfile {
240+
max_number_of_states: 8192,
241+
start_execution_time: Some(start_time),
242+
execution_timeout: 100,
243+
max_number_of_terms: 50,
244+
};
245+
ThreadLocalParams::init_profile(&execution_profile);
246+
247+
assert_eq!(EngineError::OperationTimeOutError, term1.difference(&term2).unwrap_err());
248+
249+
let run_duration = SystemTime::now()
250+
.duration_since(start_time)
251+
.expect("Time went backwards")
252+
.as_millis();
253+
254+
println!("{run_duration}");
255+
assert!(run_duration <= execution_profile.execution_timeout + 50);
256+
Ok(())
257+
}
208258
}

src/fast_automaton/convert/to_regex/transform.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ impl StateEliminationAutomaton<Range> {
1212
if self.cyclic {
1313
return self.convert_graph_to_regex(execution_profile);
1414
}
15+
execution_profile.assert_not_timed_out()?;
1516

1617
let mut regex_map: IntMap<usize, RegularExpression> = IntMap::with_capacity_and_hasher(
1718
self.get_number_of_states(),

src/fast_automaton/operation/intersection.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::error::EngineError;
1+
use crate::{error::EngineError, execution_profile::ThreadLocalParams};
22

33
use super::*;
44

@@ -11,6 +11,8 @@ impl FastAutomaton {
1111
} else if other.is_total() {
1212
return Ok(self.clone());
1313
}
14+
let execution_profile = ThreadLocalParams::get_execution_profile();
15+
1416
let new_spanning_set = self.spanning_set.merge(&other.spanning_set);
1517

1618
let mut new_automaton = FastAutomaton::new_empty();
@@ -29,6 +31,7 @@ impl FastAutomaton {
2931
new_states.insert((self.start_state, other.start_state), initial_pair);
3032

3133
while let Some(p) = worklist.pop_front() {
34+
execution_profile.assert_not_timed_out()?;
3235
if self.accept_states.contains(&p.1) && other.accept_states.contains(&p.2) {
3336
new_automaton.accept(p.0);
3437
}
@@ -67,6 +70,8 @@ impl FastAutomaton {
6770
} else if self.is_total() || other.is_total() {
6871
return Ok(true);
6972
}
73+
let execution_profile = ThreadLocalParams::get_execution_profile();
74+
7075
let new_spanning_set = self.spanning_set.merge(&other.spanning_set);
7176

7277
let mut new_automaton = FastAutomaton::new_empty();
@@ -85,6 +90,7 @@ impl FastAutomaton {
8590
new_states.insert((self.start_state, other.start_state), initial_pair);
8691

8792
while let Some(p) = worklist.pop_front() {
93+
execution_profile.assert_not_timed_out()?;
8894
if self.accept_states.contains(&p.1) && other.accept_states.contains(&p.2) {
8995
return Ok(true);
9096
}

src/regex/operation/concat.rs

+26-26
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,32 @@ impl RegularExpression {
165165
this: &RegularExpression,
166166
that: &RegularExpression,
167167
) -> Option<RegularExpression> {
168-
if let (
168+
if this == that {
169+
if let (
170+
RegularExpression::Repetition(this_regex, this_min, this_max_opt),
171+
RegularExpression::Repetition(_, that_min, that_max_opt),
172+
) = (this, that)
173+
{
174+
let new_min = this_min + that_min;
175+
let new_max_opt =
176+
if let (Some(this_max), Some(that_max)) = (this_max_opt, that_max_opt) {
177+
Some(this_max + that_max)
178+
} else {
179+
None
180+
};
181+
Some(RegularExpression::Repetition(
182+
this_regex.clone(),
183+
new_min,
184+
new_max_opt,
185+
))
186+
} else {
187+
Some(RegularExpression::Repetition(
188+
Box::new(this.clone()),
189+
2,
190+
Some(2),
191+
))
192+
}
193+
} else if let (
169194
RegularExpression::Repetition(this_regex, this_min, this_max_opt),
170195
RegularExpression::Repetition(that_regex, that_min, that_max_opt),
171196
) = (this, that)
@@ -197,31 +222,6 @@ impl RegularExpression {
197222
} else {
198223
return None;
199224
}
200-
} else if this == that {
201-
if let (
202-
RegularExpression::Repetition(this_regex, this_min, this_max_opt),
203-
RegularExpression::Repetition(_, that_min, that_max_opt),
204-
) = (this, that)
205-
{
206-
let new_min = this_min + that_min;
207-
let new_max_opt =
208-
if let (Some(this_max), Some(that_max)) = (this_max_opt, that_max_opt) {
209-
Some(this_max + that_max)
210-
} else {
211-
None
212-
};
213-
Some(RegularExpression::Repetition(
214-
this_regex.clone(),
215-
new_min,
216-
new_max_opt,
217-
))
218-
} else {
219-
Some(RegularExpression::Repetition(
220-
Box::new(this.clone()),
221-
2,
222-
Some(2),
223-
))
224-
}
225225
} else if let RegularExpression::Repetition(this_regex, this_min, this_max_opt) = this {
226226
if **this_regex == *that {
227227
let new_min = this_min + 1;

0 commit comments

Comments
 (0)