Skip to content

Commit

Permalink
proof-of-concept rayon support
Browse files Browse the repository at this point in the history
  • Loading branch information
dginev committed Mar 21, 2019
1 parent e2651c5 commit cffae71
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 29 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ exclude = [

[dependencies]
libc = "0.2"
rayon = "1.0.0"

[build-dependencies]
pkg-config = "0.3.2"
Expand Down
22 changes: 12 additions & 10 deletions src/tree/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::ptr;
use std::sync::{Arc, Weak};
use std::sync::{Arc, Weak, Mutex};
use std::str;

use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::node::Node;

pub(crate) type DocumentRef = Arc<RefCell<_Document>>;
pub(crate) type DocumentWeak = Weak<RefCell<_Document>>;
pub(crate) type DocumentRef = Arc<Mutex<RefCell<_Document>>>;
pub(crate) type DocumentWeak = Weak<Mutex<RefCell<_Document>>>;

#[derive(Debug)]
pub(crate) struct _Document {
Expand Down Expand Up @@ -68,14 +68,14 @@ impl Document {
doc_ptr,
nodes: HashMap::new(),
};
Ok(Document(Arc::new(RefCell::new(doc))))
Ok(Document(Arc::new(Mutex::new(RefCell::new(doc)))))
}
}
}

/// Obtain the underlying libxml2 `xmlDocPtr` for this Document
pub fn doc_ptr(&self) -> xmlDocPtr {
self.0.borrow().doc_ptr
self.0.lock().unwrap().borrow().doc_ptr
}

/// Creates a new `Document` from an existing libxml2 pointer
Expand All @@ -84,14 +84,14 @@ impl Document {
doc_ptr,
nodes: HashMap::new(),
};
Document(Arc::new(RefCell::new(doc)))
Document(Arc::new(Mutex::new(RefCell::new(doc))))
}

pub(crate) fn null_ref() -> DocumentRef {
Arc::new(RefCell::new(_Document {
Arc::new(Mutex::new(RefCell::new(_Document {
doc_ptr: ptr::null_mut(),
nodes: HashMap::new(),
}))
})))
}

/// Write document to `filename`
Expand Down Expand Up @@ -148,6 +148,8 @@ impl Document {
.get_docref()
.upgrade()
.unwrap()
.lock()
.unwrap()
.borrow_mut()
.forget_node(node.node_ptr());

Expand Down Expand Up @@ -243,7 +245,7 @@ impl Document {
doc_ptr,
nodes: HashMap::new(),
};
Ok(Document(Arc::new(RefCell::new(doc))))
Ok(Document(Arc::new(Mutex::new(RefCell::new(doc)))))
}
}

Expand All @@ -257,7 +259,7 @@ impl Document {
if doc_ptr.is_null() {
return Err(());
}
self.0.borrow_mut().doc_ptr = doc_ptr;
self.0.lock().unwrap().borrow_mut().doc_ptr = doc_ptr;
Ok(())
}
}
28 changes: 16 additions & 12 deletions src/tree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::ffi::{CStr, CString};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use std::str;

use crate::bindings::*;
Expand All @@ -29,7 +29,7 @@ pub fn set_node_rc_guard(value: usize) {
}
}

type NodeRef = Arc<RefCell<_Node>>;
type NodeRef = Arc<Mutex<RefCell<_Node>>>;

#[derive(Debug)]
struct _Node {
Expand All @@ -45,6 +45,9 @@ struct _Node {
#[derive(Clone, Debug)]
pub struct Node(NodeRef);

unsafe impl Sync for Node {}
unsafe impl Send for Node {}

impl Hash for Node {
/// Generates a hash value from the `node_ptr` value.
fn hash<H: Hasher>(&self, state: &mut H) {
Expand Down Expand Up @@ -103,7 +106,7 @@ impl Node {

/// Immutably borrows the underlying libxml2 `xmlNodePtr` pointer
pub fn node_ptr(&self) -> xmlNodePtr {
self.0.borrow().node_ptr
self.0.lock().unwrap().borrow().node_ptr
}

/// Mutably borrows the underlying libxml2 `xmlNodePtr` pointer
Expand All @@ -118,7 +121,7 @@ impl Node {
// correct check would be to have a weak count of 0 and a strong count <=2 (one for self, one for .nodes)
let guard_ok = unsafe { weak_count == 0 && strong_count <= NODE_RC_MAX_GUARD };
if guard_ok {
Ok(self.0.borrow_mut().node_ptr)
Ok(self.0.lock().unwrap().borrow_mut().node_ptr)
} else {
Err(format!(
"Can not mutably reference a shared Node {:?}! Rc: weak count: {:?}; strong count: {:?}",
Expand All @@ -132,7 +135,7 @@ impl Node {
/// Wrap a libxml node ptr with a Node
pub(crate) fn wrap(node_ptr: xmlNodePtr, document: &DocumentRef) -> Node {
// If already seen, return saved Node
if let Some(node) = document.borrow().get_node(node_ptr) {
if let Some(node) = document.lock().unwrap().borrow().get_node(node_ptr) {
return node.clone();
}
// If newly encountered pointer, wrap
Expand All @@ -141,8 +144,9 @@ impl Node {
document: Arc::downgrade(&document),
unlinked: false,
};
let wrapped_node = Node(Arc::new(RefCell::new(node)));
let wrapped_node = Node(Arc::new(Mutex::new(RefCell::new(node))));
document
.lock().unwrap()
.borrow_mut()
.insert_node(node_ptr, wrapped_node.clone());
wrapped_node
Expand All @@ -168,11 +172,11 @@ impl Node {

/// Create a mock node, used for a placeholder argument
pub fn null() -> Self {
Node(Arc::new(RefCell::new(_Node {
Node(Arc::new(Mutex::new(RefCell::new(_Node {
node_ptr: ptr::null_mut(),
document: Arc::downgrade(&Document::null_ref()),
unlinked: true,
})))
}))))
}

/// `libc::c_void` isn't hashable and cannot be made hashable
Expand All @@ -181,7 +185,7 @@ impl Node {
}

pub(crate) fn get_docref(&self) -> DocumentWeak {
self.0.borrow().document.clone()
self.0.lock().unwrap().borrow().document.clone()
}

/// Returns the next sibling if it exists
Expand Down Expand Up @@ -732,7 +736,7 @@ impl Node {

/// Checks if node is marked as unlinked
pub fn is_unlinked(&self) -> bool {
self.0.borrow().unlinked
self.0.lock().unwrap().borrow().unlinked
}

fn ptr_as_option(&self, node_ptr: xmlNodePtr) -> Option<Node> {
Expand All @@ -747,12 +751,12 @@ impl Node {

/// internal helper to ensure the node is marked as linked/imported/adopted in the main document tree
fn set_linked(&mut self) {
self.0.borrow_mut().unlinked = false;
self.0.lock().unwrap().borrow_mut().unlinked = false;
}

/// internal helper to ensure the node is marked as unlinked/removed from the main document tree
fn set_unlinked(&mut self) {
self.0.borrow_mut().unlinked = true;
self.0.lock().unwrap().borrow_mut().unlinked = true;
}

/// find nodes via xpath, at a specified node or the document root
Expand Down
14 changes: 7 additions & 7 deletions src/xpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use libc;
use libc::{c_char, c_void, size_t};
use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::sync::Arc;
use std::sync::{Mutex, Arc};
use std::str;

///Thinly wrapped libxml2 xpath context
pub(crate) type ContextRef = Arc<RefCell<_Context>>;
pub(crate) type ContextRef = Arc<Mutex<RefCell<_Context>>>;

#[derive(Debug)]
pub(crate) struct _Context(pub(crate) xmlXPathContextPtr);
Expand Down Expand Up @@ -49,26 +49,26 @@ impl Context {
Err(())
} else {
Ok(Context {
context_ptr: Arc::new(RefCell::new(_Context(ctxtptr))),
context_ptr: Arc::new(Mutex::new(RefCell::new(_Context(ctxtptr)))),
document: Arc::downgrade(&doc.0),
})
}
}
pub(crate) fn new_ptr(docref: &DocumentRef) -> Result<Context, ()> {
let ctxtptr = unsafe { xmlXPathNewContext(docref.borrow().doc_ptr) };
let ctxtptr = unsafe { xmlXPathNewContext(docref.lock().unwrap().borrow().doc_ptr) };
if ctxtptr.is_null() {
Err(())
} else {
Ok(Context {
context_ptr: Arc::new(RefCell::new(_Context(ctxtptr))),
document: Arc::downgrade(&docref),
context_ptr: Arc::new(Mutex::new(RefCell::new(_Context(ctxtptr)))),
document: Arc::downgrade(docref),
})
}
}

/// Returns the raw libxml2 context pointer behind the struct
pub fn as_ptr(&self) -> xmlXPathContextPtr {
self.context_ptr.borrow().0
self.context_ptr.lock().unwrap().borrow().0
}

/// Instantiate a new Context for the Document of a given Node.
Expand Down
27 changes: 27 additions & 0 deletions tests/rayon_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use libxml::parser::Parser;
use rayon::prelude::*;

#[test]
/// Root node and first child of root node are different
/// (There is a tiny chance this might fail for a correct program)
fn child_of_root_has_different_hash() {
let parser = Parser::default();
let doc_result = parser.parse_file("tests/resources/file01.xml");
assert!(doc_result.is_ok());
let doc = doc_result.unwrap();
let root = doc.get_root_element().unwrap();
assert!(!root.is_text_node());

root.get_child_nodes().into_par_iter().for_each(|mut child| {
assert!(root != child);
assert!(child.set_attribute("into_par_iter","true").is_ok());
});
let expected = r###"
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child attribute="value" into_par_iter="true">some text</child>
<child attribute="empty" into_par_iter="true">more text</child>
</root>
"###.trim();
assert_eq!(doc.to_string(true).trim(), expected);
}

0 comments on commit cffae71

Please sign in to comment.