Change FileLockEntryHandle to operate on cached store objects

This commit is contained in:
Matthias Beyer 2017-01-21 18:38:09 +01:00
parent 64fc3749d0
commit 5e708b9720
4 changed files with 113 additions and 60 deletions

View file

@ -22,6 +22,7 @@ lazy_static = "0.2"
log = "0.3"
env_logger = "0.3"
toml = "0.2"
uuid = "0.3"
[dependencies.libimagerror]
path = "../libimagerror"

View file

@ -21,41 +21,15 @@ use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use uuid::Uuid;
use libimagstore::store::Store;
#[derive(Clone, Debug, Ord, Hash, Eq, PartialOrd, PartialEq)]
pub struct StoreHandle(Uuid);
lazy_static! {
/// A cache for FileLockEntry objects.
///
/// # Why do we need this?
///
/// "ruru" does us give the ability to move objects to the Ruby namespace by wrapping them into
/// a struct that can be handled by the Ruby VM. This is awesome.
/// Although, we cannot get the Object back into the Rust namespace (at least not as `Object`,
/// only as `&mut Object`).
///
/// Now, have a look at `Store::save_as()` for example. It has the type signature
/// `fn save_as(&self, FileLockEntry, StoreId) -> Result<()>;`.
/// This means that we need to _move_ a `FileLockEntry` into the Store.
///
/// But we cannot, if the FileLockEntry is in the Ruby namespace (we cannot get it back).
///
/// The solution is simple and complex at the same time: Do not move any object into the Ruby
/// namespace!
///
/// What we do: If the Ruby code wants us to get a `FileLockEntry`, we actually move the
/// `FileLockEntry` into this `FILE_LOCK_ENTRY_CACHE` and give the Ruby process a `Handle` for
/// the `FileLockEntry` object.
///
/// From the Ruby world, it looks like a `FileLockEntry`, but it is not. The implementations in
/// this very library fetch the `FileLockEntry` from this cache and operate on it, putting it
/// back into this cache afterwards.
///
/// # Performance?
///
/// I don't care right now. It is Ruby, it is slow anyways. If it works, I don't mind. And I
/// don't see why we couldn't change this implementation later under the hood...
pub static ref FILE_LOCK_ENTRY_CACHE: Arc<Mutex<BTreeMap<StoreId, FileLockEntry<'static>>>> = {
pub static ref RUBY_STORE_CACHE: Arc<Mutex<BTreeMap<StoreHandle, Store>>> = {
Arc::new(Mutex::new(BTreeMap::new()))
};
}

View file

@ -23,6 +23,7 @@ use std::ops::Deref;
use std::ops::DerefMut;
use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};
use uuid::Uuid;
use libimagstore::store::FileLockEntry as FLE;
use libimagstore::store::EntryHeader;
@ -34,13 +35,22 @@ use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;
use cache::FILE_LOCK_ENTRY_CACHE;
use cache::RUBY_STORE_CACHE;
use cache::StoreHandle;
pub struct FileLockEntryHandle(StoreId);
pub struct FileLockEntryHandle(StoreHandle, StoreId);
impl FileLockEntryHandle {
pub fn new(id: StoreId) -> FileLockEntryHandle {
FileLockEntryHandle(id)
pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle {
FileLockEntryHandle(sh, id)
}
pub fn store_handle(&self) -> &StoreHandle {
&self.0
}
pub fn fle_handle(&self) -> &StoreId {
&self.1
}
}
@ -48,13 +58,13 @@ impl Deref for FileLockEntryHandle {
type Target = StoreId;
fn deref(&self) -> &Self::Target {
&self.0
&self.1
}
}
impl DerefMut for FileLockEntryHandle {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
&mut self.1
}
}
@ -64,21 +74,21 @@ impl_unwrap!(RFileLockEntry, FileLockEntryHandle, FLE_WRAPPER);
impl_wrap!(FileLockEntryHandle, FLE_WRAPPER);
impl_verified_object!(RFileLockEntry);
/// Helper macro for operating on FILE_LOCK_ENTRY_CACHE object
/// Helper macro for operating on RUBY_STORE_CACHE object
///
/// This macro fetches an ARC from the FILE_LOCK_ENTRY_CACHE, then locks the Mutex inside it
/// This macro fetches an ARC from the RUBY_STORE_CACHE, then locks the Mutex inside it
/// and calls the $operation on the element inside the Mutex, this synchronizing the
/// operation on the FILE_LOCK_ENTRY_CACHE.
/// operation on the RUBY_STORE_CACHE.
///
/// Yes, this is performance-wise not very elegant, but we're working with Ruby, and we need
/// to cache objects (why, see documentation for FILE_LOCK_ENTRY_CACHE).
/// to cache objects (why, see documentation for RUBY_STORE_CACHE).
///
#[macro_export]
macro_rules! operate_on_fle_cache {
(mut |$name: ident| $operation: block) => {{
use cache::FILE_LOCK_ENTRY_CACHE;
use cache::RUBY_STORE_CACHE;
let arc = FILE_LOCK_ENTRY_CACHE.clone();
let arc = RUBY_STORE_CACHE.clone();
{
let lock = arc.lock();
match lock {
@ -91,9 +101,9 @@ macro_rules! operate_on_fle_cache {
}
}};
(|$name: ident| $operation: block) => {{
use cache::FILE_LOCK_ENTRY_CACHE;
use cache::RUBY_STORE_CACHE;
let arc = FILE_LOCK_ENTRY_CACHE.clone();
let arc = RUBY_STORE_CACHE.clone();
{
let lock = arc.lock();
match lock {
@ -114,8 +124,23 @@ methods!(
fn r_get_location() -> AnyObject {
operate_on_fle_cache!(|hm| {
match hm.get(itself.get_data(&*FLE_WRAPPER)) {
Some(el) => el.get_location().clone().wrap(),
let handle = itself.get_data(&*FLE_WRAPPER);
match hm.get(handle.store_handle()) {
Some(store) => {
match store.get(handle.fle_handle().clone()) {
Ok(Some(mut fle)) => {
fle.get_location().clone().wrap()
},
Ok(None) => {
VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
NilClass::new().to_any_object()
},
Err(e) => {
VM::raise(Class::from_existing("RuntimeError"), e.description());
NilClass::new().to_any_object()
},
}
},
None => {
VM::raise(Class::from_existing("RuntimeError"),
"Tried to operate on non-existing object");
@ -127,8 +152,23 @@ methods!(
fn r_get_header() -> AnyObject {
operate_on_fle_cache!(|hm| {
match hm.get(itself.get_data(&*FLE_WRAPPER)) {
Some(el) => el.get_header().clone().wrap(),
let handle = itself.get_data(&*FLE_WRAPPER);
match hm.get(handle.store_handle()) {
Some(store) => {
match store.get(handle.fle_handle().clone()) {
Ok(Some(mut fle)) => {
fle.get_header().clone().wrap()
},
Ok(None) => {
VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
NilClass::new().to_any_object()
},
Err(e) => {
VM::raise(Class::from_existing("RuntimeError"), e.description());
NilClass::new().to_any_object()
},
}
},
None => {
VM::raise(Class::from_existing("RuntimeError"),
"Tried to operate on non-existing object");
@ -153,9 +193,20 @@ methods!(
};
operate_on_fle_cache!(mut |hm| {
match hm.get_mut(itself.get_data(&*FLE_WRAPPER)) {
Some(mut el) => {
*el.get_header_mut() = entryheader;
let handle = itself.get_data(&*FLE_WRAPPER);
match hm.get_mut(handle.store_handle()) {
Some(mut store) => {
match store.get(handle.fle_handle().clone()) {
Ok(Some(mut fle)) => {
*fle.get_header_mut() = entryheader;
},
Ok(None) => {
VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
},
Err(e) => {
VM::raise(Class::from_existing("RuntimeError"), e.description());
},
}
},
None => {
VM::raise(Class::from_existing("RuntimeError"),
@ -170,8 +221,23 @@ methods!(
fn r_get_content() -> AnyObject {
operate_on_fle_cache!(|hm| {
match hm.get(itself.get_data(&*FLE_WRAPPER)) {
Some(el) => el.get_content().clone().wrap(),
let handle = itself.get_data(&*FLE_WRAPPER);
match hm.get(handle.store_handle()) {
Some(store) => {
match store.get(handle.fle_handle().clone()) {
Ok(Some(mut fle)) => {
fle.get_content().clone().wrap()
},
Ok(None) => {
VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
NilClass::new().to_any_object()
}
Err(e) => {
VM::raise(Class::from_existing("RuntimeError"), e.description());
NilClass::new().to_any_object()
},
}
}
None => NilClass::new().to_any_object()
}
})
@ -192,10 +258,21 @@ methods!(
};
operate_on_fle_cache!(mut |hm| {
match hm.get_mut(itself.get_data(&*FLE_WRAPPER)) {
Some(el) => {
*el.get_content_mut() = content;
},
let handle = itself.get_data(&*FLE_WRAPPER);
match hm.get_mut(handle.store_handle()) {
Some(store) => {
match store.get(handle.fle_handle().clone()) {
Ok(Some(mut fle)) => {
*fle.get_content_mut() = content;
},
Ok(None) => {
VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
}
Err(e) => {
VM::raise(Class::from_existing("RuntimeError"), e.description());
},
}
}
None => {
VM::raise(Class::from_existing("RuntimeError"),
"Tried to operate on non-existing object");

View file

@ -22,6 +22,7 @@
#[macro_use] extern crate log;
extern crate env_logger;
extern crate toml;
extern crate uuid;
#[macro_use] extern crate libimagerror;
extern crate libimagstore;