diff options
Diffstat (limited to 'src/exec.rs')
-rw-r--r-- | src/exec.rs | 41 |
1 files changed, 31 insertions, 10 deletions
diff --git a/src/exec.rs b/src/exec.rs index e1aae87..3d5a52b 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::collections::HashMap; +use std::panic::AssertUnwindSafe; use std::sync::Arc; #[cfg(feature = "perf-literal")] @@ -9,7 +10,6 @@ use syntax::hir::Hir; use syntax::ParserBuilder; use backtrack; -use cache::{Cached, CachedGuard}; use compile::Compiler; #[cfg(feature = "perf-dfa")] use dfa; @@ -17,6 +17,7 @@ use error::Error; use input::{ByteInput, CharInput}; use literal::LiteralSearcher; use pikevm; +use pool::{Pool, PoolGuard}; use prog::Program; use re_builder::RegexOptions; use re_bytes; @@ -34,8 +35,15 @@ use utf8::next_utf8; pub struct Exec { /// All read only state. ro: Arc<ExecReadOnly>, - /// Caches for the various matching engines. - cache: Cached<ProgramCache>, + /// A pool of reusable values for the various matching engines. + /// + /// Note that boxing this value is not strictly necessary, but it is an + /// easy way to ensure that T does not bloat the stack sized used by a pool + /// in the case where T is big. And this turns out to be the case at the + /// time of writing for regex's use of this pool. At the time of writing, + /// the size of a Regex on the stack is 856 bytes. Boxing this value + /// reduces that size to 16 bytes. + pool: Box<Pool<ProgramCache>>, } /// `ExecNoSync` is like `Exec`, except it embeds a reference to a cache. This @@ -46,7 +54,7 @@ pub struct ExecNoSync<'c> { /// All read only state. ro: &'c Arc<ExecReadOnly>, /// Caches for the various matching engines. - cache: CachedGuard<'c, ProgramCache>, + cache: PoolGuard<'c, ProgramCache>, } /// `ExecNoSyncStr` is like `ExecNoSync`, but matches on &str instead of &[u8]. @@ -302,7 +310,8 @@ impl ExecBuilder { ac: None, match_type: MatchType::Nothing, }); - return Ok(Exec { ro: ro, cache: Cached::new() }); + let pool = ExecReadOnly::new_pool(&ro); + return Ok(Exec { ro: ro, pool }); } let parsed = self.parse()?; let mut nfa = Compiler::new() @@ -342,7 +351,8 @@ impl ExecBuilder { ro.match_type = ro.choose_match_type(self.match_type); let ro = Arc::new(ro); - Ok(Exec { ro: ro, cache: Cached::new() }) + let pool = ExecReadOnly::new_pool(&ro); + Ok(Exec { ro, pool }) } #[cfg(feature = "perf-literal")] @@ -1254,10 +1264,9 @@ impl Exec { /// Get a searcher that isn't Sync. #[cfg_attr(feature = "perf-inline", inline(always))] pub fn searcher(&self) -> ExecNoSync { - let create = || RefCell::new(ProgramCacheInner::new(&self.ro)); ExecNoSync { ro: &self.ro, // a clone is too expensive here! (and not needed) - cache: self.cache.get_or(create), + cache: self.pool.get(), } } @@ -1309,7 +1318,8 @@ impl Exec { impl Clone for Exec { fn clone(&self) -> Exec { - Exec { ro: self.ro.clone(), cache: Cached::new() } + let pool = ExecReadOnly::new_pool(&self.ro); + Exec { ro: self.ro.clone(), pool } } } @@ -1442,6 +1452,13 @@ impl ExecReadOnly { let lcs_len = self.suffixes.lcs().char_len(); lcs_len >= 3 && lcs_len > self.dfa.prefixes.lcp().char_len() } + + fn new_pool(ro: &Arc<ExecReadOnly>) -> Box<Pool<ProgramCache>> { + let ro = ro.clone(); + Box::new(Pool::new(Box::new(move || { + AssertUnwindSafe(RefCell::new(ProgramCacheInner::new(&ro))) + }))) + } } #[derive(Clone, Copy, Debug)] @@ -1500,7 +1517,11 @@ enum MatchNfaType { /// `ProgramCache` maintains reusable allocations for each matching engine /// available to a particular program. -pub type ProgramCache = RefCell<ProgramCacheInner>; +/// +/// We declare this as unwind safe since it's a cache that's only used for +/// performance purposes. If a panic occurs, it is (or should be) always safe +/// to continue using the same regex object. +pub type ProgramCache = AssertUnwindSafe<RefCell<ProgramCacheInner>>; #[derive(Debug)] pub struct ProgramCacheInner { |