Merge pull request #370 from matthiasbeyer/libimagstore/hook-errors-not-aborting

Libimagstore/hook errors not aborting
This commit is contained in:
Matthias Beyer 2016-05-28 22:36:29 +02:00
commit ac89856e1c
3 changed files with 124 additions and 71 deletions

View file

@ -1,3 +1,5 @@
use libimagerror::trace::trace_error;
use store::FileLockEntry; use store::FileLockEntry;
use storeid::StoreId; use storeid::StoreId;
use hook::Hook; use hook::Hook;
@ -38,34 +40,32 @@ impl Aspect {
impl StoreIdAccessor for Aspect { impl StoreIdAccessor for Aspect {
fn access(&self, id: &StoreId) -> HookResult<()> { fn access(&self, id: &StoreId) -> HookResult<()> {
use crossbeam;
let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect(); let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
if !accessors.iter().all(|a| is_match!(*a, HDA::StoreIdAccess(_))) { if !accessors.iter().all(|a| is_match!(*a, HDA::StoreIdAccess(_))) {
return Err(HE::new(HEK::AccessTypeViolation, None)); return Err(HE::new(HEK::AccessTypeViolation, None));
} }
let threads : Vec<HookResult<()>> = accessors accessors
.iter() .iter()
.map(|accessor| { .fold(Ok(()), |acc, accessor| {
crossbeam::scope(|scope| { acc.and_then(|_| {
scope.spawn(|| { let res = match accessor {
match *accessor { &HDA::StoreIdAccess(accessor) => accessor.access(id),
HDA::StoreIdAccess(accessor) => accessor.access(id), _ => unreachable!(),
_ => unreachable!(), };
}
.map_err(|_| ()) // TODO: We're losing the error cause here
})
})
})
.map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
.collect();
threads match res {
.into_iter() Ok(res) => Ok(res),
.fold(Ok(()), |acc, elem| { Err(e) => {
acc.and_then(|a| { if !e.is_aborting() {
elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None)) trace_error(&e);
// ignore error if it is not aborting, as we printed it already
Ok(())
} else {
Err(e)
}
}
}
}) })
}) })
} }
@ -83,52 +83,63 @@ impl MutableHookDataAccessor for Aspect {
return Err(HE::new(HEK::AccessTypeViolation, None)); return Err(HE::new(HEK::AccessTypeViolation, None));
} }
for accessor in accessors { // TODO: Naiive implementation.
match accessor { // More sophisticated version would check whether there are _chunks_ of
HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)), // NonMutableAccess accessors and execute these chunks in parallel. We do not have
// performance concerns yet, so this is okay.
accessors.iter().fold(Ok(()), |acc, accessor| {
acc.and_then(|_| {
let res = match accessor {
&HDA::MutableAccess(ref accessor) => accessor.access_mut(fle),
&HDA::NonMutableAccess(ref accessor) => accessor.access(fle),
_ => unreachable!(),
};
// TODO: Naiive implementation. match res {
// More sophisticated version would check whether there are _chunks_ of Ok(res) => Ok(res),
// NonMutableAccess accessors and execute these chunks in parallel. We do not have Err(e) => {
// performance concerns yet, so this is okay. if !e.is_aborting() {
HDA::NonMutableAccess(accessor) => try!(accessor.access(fle)), trace_error(&e);
_ => unreachable!(), // ignore error if it is not aborting, as we printed it already
} Ok(())
} } else {
Ok(()) Err(e)
}
}
}
})
})
} }
} }
impl NonMutableHookDataAccessor for Aspect { impl NonMutableHookDataAccessor for Aspect {
fn access(&self, fle: &FileLockEntry) -> HookResult<()> { fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
use crossbeam;
let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect(); let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
if !accessors.iter().all(|a| is_match!(*a, HDA::NonMutableAccess(_))) { if !accessors.iter().all(|a| is_match!(*a, HDA::NonMutableAccess(_))) {
return Err(HE::new(HEK::AccessTypeViolation, None)); return Err(HE::new(HEK::AccessTypeViolation, None));
} }
let threads : Vec<HookResult<()>> = accessors accessors
.iter() .iter()
.map(|accessor| { .fold(Ok(()), |acc, accessor| {
crossbeam::scope(|scope| { acc.and_then(|_| {
scope.spawn(|| { let res = match accessor {
match *accessor { &HDA::NonMutableAccess(accessor) => accessor.access(fle),
HDA::NonMutableAccess(accessor) => accessor.access(fle), _ => unreachable!(),
_ => unreachable!(), };
}
.map_err(|_| ()) // TODO: We're losing the error cause here
})
})
})
.map(|i| i.join().map_err(|_| HE::new(HEK::HookExecutionError, None)))
.collect();
threads match res {
.into_iter() Ok(res) => Ok(res),
.fold(Ok(()), |acc, elem| { Err(e) => {
acc.and_then(|a| { if !e.is_aborting() {
elem.map(|_| a).map_err(|_| HE::new(HEK::HookExecutionError, None)) trace_error(&e);
// ignore error if it is not aborting, as we printed it already
Ok(())
} else {
Err(e)
}
}
}
}) })
}) })
} }

View file

@ -1,10 +1,23 @@
generate_error_module!( use std::convert::Into;
generate_error_types!(HookError, HookErrorKind, generate_error_imports!();
HookExecutionError => "Hook exec error",
AccessTypeViolation => "Hook access type violation" generate_custom_error_types!(HookError, HookErrorKind, CustomData,
); HookExecutionError => "Hook exec error",
AccessTypeViolation => "Hook access type violation"
); );
pub use self::error::HookError; #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Copy)]
pub use self::error::HookErrorKind; pub struct CustomData {
aborting: bool,
}
impl HookError {
pub fn is_aborting(&self) -> bool {
match self.custom_data {
Some(b) => b.aborting,
None => true
}
}
}

View file

@ -28,10 +28,13 @@ use storeid::{IntoStoreId, StoreId, StoreIdIterator};
use lazyfile::LazyFile; use lazyfile::LazyFile;
use hook::aspect::Aspect; use hook::aspect::Aspect;
use hook::error::HookErrorKind;
use hook::result::HookResult;
use hook::accessor::{ MutableHookDataAccessor, use hook::accessor::{ MutableHookDataAccessor,
StoreIdAccessor}; StoreIdAccessor};
use hook::position::HookPosition; use hook::position::HookPosition;
use hook::Hook; use hook::Hook;
use libimagerror::trace::trace_error;
use libimagerror::into::IntoError; use libimagerror::into::IntoError;
@ -301,7 +304,13 @@ impl Store {
pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> { pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
let id = self.storify_id(id.into_storeid()); let id = self.storify_id(id.into_storeid());
if let Err(e) = self.execute_hooks_for_id(self.pre_create_aspects.clone(), &id) { if let Err(e) = self.execute_hooks_for_id(self.pre_create_aspects.clone(), &id) {
return Err(e); if e.is_aborting() {
return Err(e)
.map_err(Box::new)
.map_err(|e| SEK::PreHookExecuteError.into_error_with_cause(e))
} else {
trace_error(&e);
}
} }
let mut hsmap = match self.entries.write() { let mut hsmap = match self.entries.write() {
@ -332,7 +341,13 @@ impl Store {
pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> { pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
let id = self.storify_id(id.into_storeid()); let id = self.storify_id(id.into_storeid());
if let Err(e) = self.execute_hooks_for_id(self.pre_retrieve_aspects.clone(), &id) { if let Err(e) = self.execute_hooks_for_id(self.pre_retrieve_aspects.clone(), &id) {
return Err(e); if e.is_aborting() {
return Err(e)
.map_err(Box::new)
.map_err(|e| SEK::PreHookExecuteError.into_error_with_cause(e))
} else {
trace_error(&e);
}
} }
self.entries self.entries
@ -426,7 +441,13 @@ impl Store {
/// Return the `FileLockEntry` and write to disk /// Return the `FileLockEntry` and write to disk
pub fn update<'a>(&'a self, mut entry: FileLockEntry<'a>) -> Result<()> { pub fn update<'a>(&'a self, mut entry: FileLockEntry<'a>) -> Result<()> {
if let Err(e) = self.execute_hooks_for_mut_file(self.pre_update_aspects.clone(), &mut entry) { if let Err(e) = self.execute_hooks_for_mut_file(self.pre_update_aspects.clone(), &mut entry) {
return Err(e); if e.is_aborting() {
return Err(e)
.map_err(Box::new)
.map_err(|e| SEK::PreHookExecuteError.into_error_with_cause(e))
} else {
trace_error(&e);
}
} }
if let Err(e) = self._update(&entry) { if let Err(e) = self._update(&entry) {
@ -434,6 +455,7 @@ impl Store {
} }
self.execute_hooks_for_mut_file(self.post_update_aspects.clone(), &mut entry) self.execute_hooks_for_mut_file(self.post_update_aspects.clone(), &mut entry)
.map_err(|e| SE::new(SEK::PreHookExecuteError, Some(Box::new(e))))
} }
/// Internal method to write to the filesystem store. /// Internal method to write to the filesystem store.
@ -482,7 +504,13 @@ impl Store {
pub fn delete<S: IntoStoreId>(&self, id: S) -> Result<()> { pub fn delete<S: IntoStoreId>(&self, id: S) -> Result<()> {
let id = self.storify_id(id.into_storeid()); let id = self.storify_id(id.into_storeid());
if let Err(e) = self.execute_hooks_for_id(self.pre_delete_aspects.clone(), &id) { if let Err(e) = self.execute_hooks_for_id(self.pre_delete_aspects.clone(), &id) {
return Err(e); if e.is_aborting() {
return Err(e).map_err(|e| {
SE::new(SEK::PreHookExecuteError, Some(Box::new(e)))
})
} else {
trace_error(&e);
}
} }
let mut entries = match self.entries.write() { let mut entries = match self.entries.write() {
@ -502,6 +530,7 @@ impl Store {
} }
self.execute_hooks_for_id(self.post_delete_aspects.clone(), &id) self.execute_hooks_for_id(self.post_delete_aspects.clone(), &id)
.map_err(|e| SE::new(SEK::PreHookExecuteError, Some(Box::new(e))))
} }
/// Gets the path where this store is on the disk /// Gets the path where this store is on the disk
@ -566,29 +595,29 @@ impl Store {
fn execute_hooks_for_id(&self, fn execute_hooks_for_id(&self,
aspects: Arc<Mutex<Vec<Aspect>>>, aspects: Arc<Mutex<Vec<Aspect>>>,
id: &StoreId) id: &StoreId)
-> Result<()> -> HookResult<()>
{ {
match aspects.lock() { match aspects.lock() {
Err(_) => return Err(SE::new(SEK::PreHookExecuteError, None)), Err(_) => return Err(HookErrorKind::HookExecutionError.into()),
Ok(g) => g Ok(g) => g
}.iter().fold(Ok(()), |acc, aspect| { }.iter().fold(Ok(()), |acc, aspect| {
debug!("[Aspect][exec]: {:?}", aspect); debug!("[Aspect][exec]: {:?}", aspect);
acc.and_then(|_| (aspect as &StoreIdAccessor).access(id)) acc.and_then(|_| (aspect as &StoreIdAccessor).access(id))
}).map_err(|e| SE::new(SEK::PreHookExecuteError, Some(Box::new(e)))) }).map_err(Box::new).map_err(|e| HookErrorKind::HookExecutionError.into_error_with_cause(e))
} }
fn execute_hooks_for_mut_file(&self, fn execute_hooks_for_mut_file(&self,
aspects: Arc<Mutex<Vec<Aspect>>>, aspects: Arc<Mutex<Vec<Aspect>>>,
fle: &mut FileLockEntry) fle: &mut FileLockEntry)
-> Result<()> -> HookResult<()>
{ {
match aspects.lock() { match aspects.lock() {
Err(_) => return Err(SE::new(SEK::PreHookExecuteError, None)), Err(_) => return Err(HookErrorKind::HookExecutionError.into()),
Ok(g) => g Ok(g) => g
}.iter().fold(Ok(()), |acc, aspect| { }.iter().fold(Ok(()), |acc, aspect| {
debug!("[Aspect][exec]: {:?}", aspect); debug!("[Aspect][exec]: {:?}", aspect);
acc.and_then(|_| aspect.access_mut(fle)) acc.and_then(|_| aspect.access_mut(fle))
}).map_err(|e| SE::new(SEK::PreHookExecuteError, Some(Box::new(e)))) }).map_err(Box::new).map_err(|e| HookErrorKind::HookExecutionError.into_error_with_cause(e))
} }
} }