#[cfg(test)] pub(crate) mod problems { use crate::MemPool; use crate::default::*; use crate::boxed::Pbox; use crate::cell::*; use crate::stm::*; use crate::stm::Journal; use crate::open_flags::*; #[test] fn vec_to_slice_test() { use crate::vec::Vec; use crate::alloc::heap::*; Heap::transaction(|j| { let mut vec = Vec::from_slice(&[1,2,3], j); let s = vec.as_slice_mut(j); s[1] = 0; assert_eq!(s, [1, 0, 3]); assert_eq!(vec, [1, 0, 3]); }).unwrap(); } #[test] fn abort_txn_test() { use crate::default::*; type P = Allocator; let vec = P::open::>>("abort_txn.pool", O_CFNE).unwrap(); let _ = P::transaction(|j| { let mut vec = vec.borrow_mut(j); println!("OLD: {:?}", vec); for i in 0..100 { vec.push(i, j); } println!("TMP: {:?}", vec); panic!("abort"); }); println!("NEW: {:?}", vec); P::print_info(); } #[test] #[ignore] fn challenge_mt() { use crate::sync::*; type P = Allocator; struct Root { v1: Parc, P>, } impl RootObj

for Root { fn init(j: &Journal

) -> Self { Root { v1: Parc::new(PMutex::new(0), j), } } } std::thread::spawn(|| { let root = P::open::("challenge_mt1.pool", O_CFNE).unwrap(); let v1 = root.v1.demote(); std::thread::spawn(move || { std::thread::sleep(std::time::Duration::from_secs(1)); P::transaction(|j| { if let Some(r) = v1.promote(j) { let mut r = r.lock(j); *r += 1; println!("root = {}", *r); } }) .unwrap(); }); }); std::thread::sleep(std::time::Duration::from_secs(2)); } #[test] fn test_vec_cons() { use crate::str::*; use crate::sync::*; use crate::vec::Vec; use std::thread; type P = Allocator; struct Root { v1: Parc, P>, P>, P>, } impl RootObj

for Root { fn init(j: &Journal

) -> Self { Root { v1: Parc::new(PMutex::new(Vec::new()), j), } } } let root = P::open::("test_vec_cons.pool", O_CFNE).unwrap(); let mut threads = vec![]; P::transaction(|j| { let mut v = root.v1.lock(j); v.clear(); }) .unwrap(); for i in 0..1 { let v1 = root.v1.demote(); threads.push(thread::spawn(move || { P::transaction(move |j| { if let Some(v1) = v1.promote(j) { let mut v = v1.lock(j); println!("{:?} old value = {:#?}", thread::current().id(), v); v.push( format!("{:?} pushed {}", thread::current().id(), i).to_pstring(j), j, ); println!("{:?} new value = {:#?}", thread::current().id(), v); if i == 6 { // std::process::exit(0); } } }) .unwrap(); })); } for thread in threads { let _ = thread.join().unwrap(); } println!("Memory usage = {}", P::used()); } #[test] fn paper() { use crate::default::*; type P = Allocator; struct Node { val: i32, next: PRefCell>> } impl RootObj

for Node { fn init(_j: &Journal) -> Self { Self{ val: 0, next: PRefCell::new(None) }} } fn append(n: &Node, v:i32, j: &Journal) { let mut t = n.next.borrow_mut(j); match &*t { Some(succ) => append(succ, v, j), None => *t = Some(Pbox::new( Node { val: v, next: PRefCell::new(None) }, j)) } } fn go(v: i32) { let head = Allocator::open::("list.pool",O_CFNE).unwrap(); transaction(|j| { append(&head, v, j); }).unwrap(); } fn print(n: &Node) { let t = n.next.borrow(); print!("{} ", n.val); match &*t { Some(succ) => print(succ), None => {} } } fn print_all() { let head = Allocator::open::("list.pool",O_CFNE).unwrap(); print(&head); println!(); } go(rand::random()); print_all(); } #[test] #[ignore] fn memory_leak_problem() { use crate::default::*; use std::time::Duration; type P = Allocator; let _pool = P::open_no_root("my_test.pool", O_CF).unwrap(); let _ = P::transaction(|j| { let a = Parc::new(42, j); let b = a.demote(); std::thread::spawn(move || { let _ = P::transaction(|j| { std::thread::sleep(Duration::from_millis(900)); if let Some(b) = b.promote(j) { println!("Exit {}", *b); std::process::exit(0); // Memory leak may happen here } }) .unwrap(); }); std::thread::sleep(Duration::from_millis(600)); println!("{}", *a); }); std::thread::sleep(Duration::from_millis(1000)); crate::tests::test::print_usage(0); } #[test] #[ignore] fn test_pack1() { use crate::alloc::*; use std::time::Duration; type P = Allocator; struct Root { data: Parc>>, } impl RootObj

for Root { fn init(j: &Journal

) -> Self { Root { data: Parc::new(PMutex::new(Parc::new(10, j)), j), } } } let root = P::open::("test_pack.pool", O_CFNE).unwrap(); crate::tests::test::print_usage(0); let b = root.data.demote(); std::thread::spawn(move || { P::transaction(|j| { std::thread::sleep(Duration::from_millis(900)); if let Some(b) = b.promote(j) { let mut b = b.lock(j); *b = Parc::new(**b + 1, j); println!("data1 {}", *b); // std::process::exit(0); // Memory leak may happen here } }) .unwrap(); }); std::thread::sleep(Duration::from_millis(1000)); crate::tests::test::print_usage(1); let b = root.data.demote(); P::transaction(|_| { std::thread::spawn(move || { P::transaction(|j| { std::thread::sleep(Duration::from_millis(900)); if let Some(b) = b.promote(j) { let mut b = b.lock(j); *b = Parc::new(**b + 1, j); println!("data2 {}", *b); // std::process::exit(0); // Memory leak may happen here } }) .unwrap(); }); }) .unwrap(); std::thread::sleep(Duration::from_millis(1000)); crate::tests::test::print_usage(2); } #[test] #[should_panic] fn test_pack2() { use crate::alloc::*; use std::time::Duration; type P = Allocator; let _img = P::open_no_root("nosb.pool", O_CF).unwrap(); crate::tests::test::print_usage(0); P::transaction(|j| { let root = Parc::new(10, j); let b = root.demote(); // Panics here because `pack` should be called // outside transaction std::thread::spawn(move || { std::thread::sleep(Duration::from_millis(900)); P::transaction(|j| { if let Some(b) = b.promote(j) { println!("data1 {}", *b); std::thread::sleep(Duration::from_millis(2000)); std::process::exit(0); // Memory leak may happen here } }).unwrap(); }); std::thread::sleep(Duration::from_millis(1000)); }).unwrap(); crate::tests::test::print_usage(1); } #[test] fn ref_cycle_mem_leak() { use crate::default::*; type P = Allocator; #[derive(Debug)] enum List { Cons(i32, PRefCell>), Nil, } use List::*; impl List { fn tail(&self) -> Option<&PRefCell>> { match self { Cons(_, item) => Some(item), Nil => None, } } } let _img = P::open_no_root("ref_cycle_mem_leak.pool", O_CF).unwrap(); println!("usage 1: {}", P::used()); P::transaction(|j| { let a = Prc::new(Cons(5, PRefCell::new(Prc::new(Nil, j))), j); println!("a initial rc count = {}", Prc::strong_count(&a)); println!("a next item = {:?}", a.tail()); let b = Prc::new(Cons(10, PRefCell::new(Prc::pclone(&a, j))), j); println!("a rc count after b creation = {}", Prc::strong_count(&a)); println!("b initial rc count = {}", Prc::strong_count(&b)); println!("b next item = {:?}", b.tail()); if let Some(link) = a.tail() { *link.borrow_mut(j) = Prc::pclone(&b, j); } println!("b rc count after changing a = {}", Prc::strong_count(&b)); println!("a rc count after changing a = {}", Prc::strong_count(&a)); }).unwrap(); println!("usage 2: {}", P::used()); } #[test] fn test_vweak() { use crate::default::*; type P = Allocator; struct Root { v: PRefCell>, } impl RootObj

for Root { fn init(j: &Journal) -> Self { Root { v: PRefCell::new(Prc::new(10, j)), } } } let ovp = { let root = P::open::("test_vweak.pool", O_CFNE).unwrap(); let vp = Prc::demote(&root.v.borrow()); P::transaction(|j| { if let Some(p) = vp.promote(j) { let _vp2 = Prc::demote(&p); // drop a recently created volatile reference let _vp2 = Prc::demote(&p); // drop a recently created volatile reference let _vp2 = Prc::demote(&p); // drop a recently created volatile reference let _vp2 = Prc::demote(&p); // drop a recently created volatile reference } else { println!("no data"); } // Dropping the old Prc let mut b = root.v.borrow_mut(j); *b = Prc::new(12, j); if let Some(p) = vp.promote(j) { println!("new data = {}", p); } else { println!("no new data"); } }) .unwrap(); let x = Prc::demote(&root.v.borrow()); P::transaction(|j| { // Trying to access a volatile pointer from the current session if let Some(p) = x.promote(j) { println!("data = {}", p); } else { println!("no data"); } }) .unwrap(); x }; println!("pool is closed"); // Reopening the pool let _root = P::open::("test_vweak.pool", O_CFNE).unwrap(); P::transaction(|j| { // Trying to access a volatile pointer from previous session if let Some(p) = ovp.promote(j) { println!("data = {}", p); } else { println!("no data"); } }) .unwrap(); } #[test] fn chaperon_swap() { use crate::cell::PRefCell as RefCell; use crate::boxed::Pbox; crate::pool!(pool1, P1); crate::pool!(pool2, P2); type P1 = pool1::P1; type P2 = pool2::P2; fn foo(root: &RefCell, v: i32) -> i32 { M::transaction(|j| { let mut root = root.borrow_mut(j); *root = v; *root }).unwrap() } let root1 = P1::open::, P1>>("pool5.pool", O_CFNE).unwrap(); let root2 = P2::open::, P2>>("pool6.pool", O_CFNE).unwrap(); println!("root1 = {}", root1); println!("root2 = {}", root2); let _res = Chaperon::session("foo1.pool", || { let root1_old = P1::transaction(|j| { let mut root = root1.borrow_mut(j); let old = *root; *root = *root2.borrow(); if *root == 0 { *root = 10; } old }).unwrap(); P2::transaction(|j| { // <-- Creates a Journal let mut root = root2.borrow_mut(j); *root = root1_old; if *root == 0 { *root = 20; } // std::process::exit(0); }).unwrap(); // let _other = foo::(&root1, 20); // <-- foo dose not commit here (postponed) because a trans is open // std::process::exit(0); }); println!("P1: {}", P1::used()); println!("P2: {}", P2::used()); } #[test] #[cfg(feature = "refcell_lifetime_change")] fn test_refcell_ownership() { use crate::default::*; type P = Allocator; let root = P::open::>>("test.pool", O_CF).unwrap(); P::transaction(|j| { let mut a = root.borrow_mut(j); *a = 10; let b = PRefMut::into_inner(a); let mut c = b.borrow_mut(j); *c = 20; let mut d = PRefMut::own(c); *d = 25; // let e = root.borrow_mut(j); <-- Error: multiple mutable borrows // let f = root.borrow(); <-- Error: already mutably borrowed let new = Pbox::new(PRefCell::pfrom(d, j), j); // A new `PRefCell` let mut e = new.borrow_mut(j); *e = 30; // The original PRefCell won't change // `d` is still available here }).unwrap(); assert_eq!(25, *root.borrow()); } #[test] fn test_concurrent() { use crate::alloc::*; use std::time::Instant; let _pool = Allocator::open_no_root("conc.pool", O_CF).unwrap(); let mut threads = vec![]; let start = Instant::now(); for _ in 0..4 { threads.push(std::thread::spawn(move || { Allocator::transaction(|j| { let mut v = vec![]; for _ in 0..100 { v.push(Pbox::new(0, j)); } }) .unwrap(); })); } for thread in threads { thread.join().unwrap(); } let duration = start.elapsed(); println!("Time elapsed in parallel execution is: {:?}", duration); println!("usage = {} bytes", Allocator::used()); let start = Instant::now(); for _ in 0..4 { Allocator::transaction(|j| { let mut v = vec![]; for _ in 0..100 { v.push(Pbox::new(0, j)); } }) .unwrap(); } let duration = start.elapsed(); println!("Time elapsed in serial execution is: {:?}", duration); println!("usage = {} bytes", Allocator::used()); } #[test] fn parc_two_threads() { use crate::default::*; type P = Allocator; let root = P::open::>("parc_two_threads.pool", O_CFNE).unwrap(); println!("usage: {}", P::used()); println!("strong = {}", Parc::strong_count(&root)); let c1 = root.demote(); let c2 = root.demote(); let t1 = std::thread::spawn(move || { let _=P::transaction(|j| { if let Some(p) = c2.promote(j) { let m = p.pclone(j); println!("strong(t1) = {}", Parc::strong_count(&m)); std::thread::sleep(std::time::Duration::from_millis(30)); println!("t1 is done"); } }); }); let t2 = std::thread::spawn(move || { std::thread::sleep(std::time::Duration::from_millis(25)); let _ = P::transaction(|j| { if let Some(p) = c1.promote(j) { let m = p.pclone(j); println!("strong(t2) = {}", Parc::strong_count(&m)); std::thread::sleep(std::time::Duration::from_millis(10)); // std::process::exit(0); panic!("abort"); } }); }); t1.join().unwrap(); t2.join().unwrap(); assert_eq!(Parc::strong_count(&root), 1); } } #[cfg(test)] pub(crate) mod test { use crate::alloc::heap::Heap; use crate::default::*; use crate::cell::*; use crate::prc::*; use crate::stm::*; use crate::stm::Journal; use crate::sync::PMutex; use crate::*; use crate::open_flags::*; type A = Allocator; #[test] #[ignore] fn test_mutex_deadlock() { // Race condition problem (cyclic locks) is still possible struct Root { a: PMutex, b: PMutex, } impl RootObj for Root { fn init(_j: &Journal) -> Self { Self { a: PMutex::new(0), b: PMutex::new(0), } } } let root = A::open::("test_mutex_deadlock.pool", O_CFNE).unwrap(); let t1 = { let root = root.clone(); thread::spawn(move || { A::transaction(|j| { let mut a = root.a.lock(j); *a += 1; let mut b = root.b.lock(j); *b += 1; }) .unwrap() }) }; let t2 = { let root = root.clone(); thread::spawn(move || { A::transaction(|j| { let mut b = root.b.lock(j); *b += 1; let mut a = root.a.lock(j); *a += 1; }) .unwrap() }) }; print_usage(0); t1.join().unwrap(); t2.join().unwrap(); } #[test] // #[ignore] fn test_string_mt() { use crate::str::String; struct ConsumerData { buf: String, } pub struct Consumer { pattern: String, data: PMutex, A>, } impl RootObj for Consumer { fn init(_j: &Journal) -> Self { Self { pattern: String::new(), data: PMutex::new(String::new()), } } } let root = A::open::>("consumer.pool", O_CFNE).unwrap(); let mut threads = vec![]; for i in 0..10 { let root = root.demote(); threads.push(thread::spawn(move || { A::transaction(|j| { if let Some(data) = root.promote(j) { let mut data = data.data.lock(j); let r = rand().to_string(); println!("Old persisted data: `{}`", data); println!("New data to be persisted: `{}`", r); *data = String::from_str(&r, j); if i == 6 { // std::process::exit(0); } } }) .unwrap() })) } for thread in threads { thread.join().unwrap() } } #[test] fn multiple_transactions() { let _image = A::open_no_root("nosb.pool", O_CF).unwrap(); A::transaction(|j1| { let b1 = Pbox::new(default::PCell::new(1), j1); b1.set( Heap::transaction(move |j2| { let b2 = Pbox::new(heap::PCell::new(1), j2); b2.get() }) .unwrap(), j1, ); }) .unwrap(); } #[test] #[ignore] fn multiple_open() { let mut threads = vec![]; for _ in 0..10 { threads.push(std::thread::spawn(move || { let _image = A::open_no_root("nosb.pool", O_CF).unwrap(); A::transaction(|j1| { let b1 = Pbox::new(default::PRefCell::new(1), j1); let mut b1 = b1.borrow_mut(j1); *b1 = Heap::transaction(move |j2| { let b2 = Pbox::new(heap::PRefCell::new(1), j2); b2.read() }) .unwrap(); }) .unwrap(); })); } for thread in threads { thread.join().unwrap(); } } #[test] fn test_maybe_null_drop() { struct D { val: i32, } impl Drop for D { fn drop(&mut self) { println!("Data {} dropped", self.val); } } struct MyData { data: [Option; 8], } let mut my_data = MyData { data: Default::default(), }; my_data.data[1] = Some(D { val: 12 }); } #[test] fn multiple_pools() { use crate::cell::PRefCell; crate::pool!(pool1, P1); crate::pool!(pool2, P2); type P1 = pool1::P1; type P2 = pool2::P2; struct Root { val: Pbox, P>, } impl RootObj for Root { fn init(j: &Journal) -> Self { Root { val: Pbox::new(PRefCell::new(0), j), } } } let root1 = P1::open::>("pool3.pool", O_CFNE).unwrap(); let root2 = P2::open::>("pool4.pool", O_CFNE).unwrap(); let _ = Chaperon::session("chaperon2.pool", || { let v = P2::transaction(|j| { let mut p2 = root2.val.borrow_mut(j); let old = *p2; *p2 += 1; // <-- should persist if both transactions commit // panic!("test"); // std::process::exit(0); old // <-- Send out p2's old data }) .unwrap(); // panic!("test"); // std::process::exit(0); P1::transaction(|j| { let mut p1 = root1.val.borrow_mut(j); *p1 += v; // panic!("test"); // std::process::exit(0); }) .unwrap(); // panic!("test"); // std::process::exit(0); }); let v1 = root1.val.read(); let v2 = root2.val.read(); println!("root1 = {} (usage = {})", v1, P1::used()); println!("root2 = {} (usage = {})", v2, P2::used()); assert_eq!(v1, calc(v2 - 1)); fn calc(n: i32) -> i32 { if n < 1 { 0 } else { n + calc(n - 1) } } } #[test] fn concat_test() { use crate::default::*; type P = Allocator; type Ptr = Option>>; struct Node { val: i32, next: Ptr, } fn remove_all(k: i32) { let root = P::open::>>("foo3.pool", 0).unwrap(); P::transaction(|j| { let mut curr = root.borrow().pclone(j); let mut prev = prc::PWeak::>::new(); while let Some(n) = curr { let p = n.borrow(); if p.val == k { if let Some(pr) = prev.upgrade(j) { let mut pr = pr.borrow_mut(j); pr.next = p.next.pclone(j); } else { *root.borrow_mut(j) = p.next.pclone(j); } } prev = Prc::downgrade(&n, j); curr = p.next.pclone(j); } }) .expect("Unsuccessful"); } } #[test] fn prc_test() { use crate::default::*; let _image = A::open_no_root("nosb.pool", O_CF).unwrap(); struct Cell { k: i32, next: Option>>, } impl Cell { pub fn new(x: i32) -> Self { Cell { k: x, next: None } } pub fn add(&mut self, v: i32, j: &Journal) { if let Some(next) = &self.next { let mut next = next.borrow_mut(j); next.add(v, j); } else { self.next = Some(Prc::new(PRefCell::new(Cell::new(v)), j)); } } pub fn sum(&self, j: &Journal) -> i32 { if let Some(next) = &self.next { self.k + next.borrow().sum(j) } else { self.k } } } transaction(|j| { let shared_root: Prc> = Prc::new(PRefCell::new(Cell::new(10)), j); // Create a new block to limit the scope of the dynamic borrow { let mut root = shared_root.borrow_mut(j); root.add(20, j); root.add(30, j); root.add(40, j); root.add(50, j); } // Note that if we had not let the previous borrow of the cache fall out // of scope then the subsequent borrow would cause a dynamic thread panic. // This is the major hazard of using `RefCell`. let total: i32 = shared_root.borrow().sum(j); println!("{}", total); }) .ok(); // std::thread::spawn(move || { // let total: i32 = shared_root.sum(); // println!("{}", total); // }).join().unwrap(); } #[test] fn test_nv_to_v() { use std::cell::RefCell; use crate::default::*; use crate::stm::Journal; let root = A::open::("nv2v.pool", O_CFNE).unwrap(); struct Cell { k: VCell>>>, next: PRefCell>>, } impl Default for Cell { fn default() -> Self { Cell::new(0) } } impl Cell { pub fn new(x: i32) -> Self { Cell { k: VCell::new(RefCell::new(Some(Box::new(x)))), next: PRefCell::new(None), } } pub fn add(&self, v: i32, j: &Journal) { if let Some(next) = &*self.next.borrow() { next.add(v, j); return; } *self.next.borrow_mut(j) = Some(Prc::new(Cell::new(v), j)); } pub fn sum(&self, j: &Journal) -> i32 { let mut k = if let Some(g) = &*self.k.borrow() { **g } else { -1 }; if k == -1 { println!("create a new pbox"); *self.k.borrow_mut() = Some(Box::new(0)); // self.k.replace(Some(Pbox::new(0, j))); k = 0; }; if let Some(next) = &*self.next.borrow() { k + next.sum(j) } else { k } } } let root = std::panic::AssertUnwindSafe(root); transaction(|j| { root.add(20, j); // root.add(30, j); // root.add(40, j); // root.add(50, j); // Note that if we had not let the previous borrow of the cache fall out // of scope then the subsequent borrow would cause a dynamic thread panic. // This is the major hazard of using `RefCell`. let total: i32 = root.sum(j); println!("{}", total); }) .ok(); print_usage(0); // std::thread::spawn(move || { // let total: i32 = shared_root.sum(); // println!("{}", total); // }).join().unwrap(); } pub fn print_usage(idx: i32) -> usize { let usage = A::used(); println!("{:>4}: {:>8} bytes used", idx, usage); usage } #[test] fn test_sort_insert_volatile() { use std::rc::*; use std::cell::*; struct Node { val: i32, next: Rc>> } impl Node { fn insert(&self, val: i32) { let mut next = self.next.borrow_mut(); if let Some(next) = &mut *next { if next.val > val { *next = Node { val, next: self.next.clone() } } else { next.insert(val); } } else { *next = Some(Node { val, next: Rc::new(RefCell::new(None)) }) } } } } fn test_sort_insert_pmem() { use crate::default::*; struct Node { val: i32, next: Prc>> } impl Node { fn insert(&self, val: i32) { transaction(|j|{ let mut next = self.next.borrow_mut(j); if let Some(n) = &*next { if n.val > val { *next = Some(Node { val, next: self.next.pclone(j) }) } else { n.insert(val); } } else { *next = Some(Node { val, next: Prc::new(PRefCell::new(None), j) }) } }).unwrap() } } } #[test] fn test_gadget_owners() { use crate::default::*; let _image = A::open_no_root("nosb.pool", O_CF); struct Owner { name: [u8; 8], gadgets: [Weak; 2], } struct Gadget { id: i32, owner: Prc>, } impl Owner { fn new(name: &str) -> Self { let vec: Vec = name.as_bytes().to_vec(); let mut name: [u8; 8] = [0; 8]; for i in 0..8 { name[i] = if i < vec.len() { vec[i] } else { 0 }; } Owner { name, gadgets: [Weak::::new(), Weak::::new()], } } fn get_name(&self) -> String { let mut vec = Vec::::new(); vec.extend_from_slice(&self.name); String::from_utf8(vec).unwrap() } } print_usage(0); transaction(|j| { // Create a reference-counted `Owner`. Note that we've put the `Owner`'s // vector of `Gadget`s inside a `RefCell` so that we can mutate it through // a shared reference. let gadget_owner: Prc> = Prc::new(PRefCell::new(Owner::new("Dany")), j); print_usage(1); // Create `Gadget`s belonging to `gadget_owner`, as before. let gadget1 = Prc::new( Gadget { id: 1, owner: gadget_owner.pclone(j), }, j, ); let gadget2 = Prc::new( Gadget { id: 2, owner: gadget_owner.pclone(j), }, j, ); // Add the `Gadget`s to their `Owner`. { let mut gadget_owner = gadget_owner.borrow_mut(j); gadget_owner.gadgets[0] = Prc::downgrade(&gadget1, j); gadget_owner.gadgets[1] = Prc::downgrade(&gadget2, j); } // Iterate over our `Gadget`s, printing their details out. for gadget_weak in gadget_owner.borrow().gadgets.iter() { // `gadget_weak` is a `Weak`. Since `Weak` pointers can't // guarantee the allocation still exists, we need to call // `upgrade`, which returns an `Option>`. // // In this case we know the allocation still exists, so we simply // `unwrap` the `Option`. In a more complicated program, you might // need graceful error handling for a `None` result. transaction(|j| { if let Some(gadget) = gadget_weak.upgrade(j) { println!( "Gadget {} owned by {}", gadget.id, gadget.owner.borrow().get_name() ); } else { println!("Gadget is already dropped"); } }) .unwrap(); } // At the end of the function, `gadget_owner`, `gadget1`, and `gadget2` // are destroyed. There are now no strong (`Rc`) pointers to the // gadgets, so they are destroyed. This zeroes the reference counter on // Gadget Man, so he gets destroyed as well. }) .unwrap(); print_usage(2); } pub fn rand() -> u8 { use std::fs::File; use std::io::Read; static mut BUF: [u8; 16] = [0u8; 16]; static mut IDX: usize = 15; unsafe { IDX += 1; if IDX == 16 { IDX = 0; let mut f = File::open("/dev/urandom").unwrap(); f.read_exact(&mut BUF).unwrap(); } BUF[IDX] } } fn may_panic() { if rand() > 200_u8 { panic!(); } } #[test] #[allow(clippy::comparison_chain)] fn test_dblist() { use crate::default::{*, prc::PWeak}; struct SB { root: Prc>>, } impl RootObj for SB { fn init(j: &Journal) -> Self { Self { root: Prc::new(PRefCell::new(Node::new(0)), j), } } } struct Node { value: T, next: Option>>>, prev: PWeak>>, } impl Node { fn new(x: T) -> Self { Self { value: x, next: None, prev: Weak::new(), } } } enum Operation { AddNewRndNum = 0, DelRndNum = 1, DelAll = 2, } use Operation::*; let r = rand() % 100; let cmd = if r < 20 { DelRndNum } else if r < 25 { DelAll } else { AddNewRndNum }; let sb = A::open::("sb11.pool", O_CFNE).unwrap(); println!(); print_usage(1); transaction(|j| { let mut sb = sb.root.borrow_mut(j); let value = (rand() % 10) as i32; match cmd { AddNewRndNum => { println!("adding new element {}", value); let new = Prc::new(PRefCell::new(Node::new(value)),j); if let Some(root) = &sb.next { let mut curr = Prc::downgrade(&root, j); let mut last = PWeak::>>::new(); let mut added = false; while let Some(pnode) = curr.upgrade(j) { let node = pnode.borrow_mut(j); if node.value > value { let mut new_node = (*new).borrow_mut(j); let mut element = node; if rand() % 2 == 0 { print_usage(2); println!("############################################################################################# CRASHED 1"); // std::process::exit(0); // 0 to make the test ok } new_node.next = Some(pnode.pclone(j)); if let Some(prev) = element.prev.upgrade(j) { (*prev).borrow_mut(j).next = Some(new.pclone(j)); new_node.prev = Prc::downgrade(&prev, j); } else { sb.next = Some(new.pclone(j)); } element.prev = Prc::downgrade(&new, j); added = true; break; } else if node.value == value { // new should drop added = true; break; } last = Prc::downgrade(&pnode, j); if let Some(next) = node.next.as_ref() { curr = Prc::downgrade(&next, j); } else { curr = Weak::new(); } } if !added { if let Some(tail) = last.upgrade(j) { new.borrow_mut(j).prev = Prc::downgrade(&tail, j); tail.borrow_mut(j).next = Some(new); } else { sb.next = Some(new); } } } else { sb.next = Some(new); } }, DelRndNum => { println!("deleting element {}", value); if let Some(root) = &sb.next { let mut curr = Prc::downgrade(&root, j); while let Some(pnode) = curr.upgrade(j) { let node = pnode.borrow(); if node.value == value { if let Some(prev) = node.prev.upgrade(j) { prev.borrow_mut(j).next = node.next.pclone(j); } if let Some(next) = node.next.as_ref() { next.borrow_mut(j).prev = node.prev.pclone(j); } break; } if let Some(next) = &node.next { curr = Prc::downgrade(&next, j); } else { curr = Weak::new(); } } } }, DelAll => { println!("deleting all elements"); sb.next = None; } } }).ok(); println!("strong_count = {}", RootCell::strong_count(&sb)); print_usage(2); let counter = transaction(|j| { let mut counter = 0; if let Some(root) = &sb.root.borrow().next { let mut curr = Prc::downgrade(&root, j); print!("[ "); while let Some(node) = curr.upgrade(j) { let node = node.borrow(); counter += 1; print!("{} ", node.value); if let Some(next) = &node.next { curr = Prc::downgrade(&next, j); } else { break; } } println!("]"); } counter }) .unwrap(); println!("total items = {}", counter); // assert_eq!(A::used(), 32840 + 64 * counter); print_usage(3); println!("#############################################################################################"); } // fn formal_verification() { // macro_rules! verify { // ($a:expr) => { // }; // } // pub trait Invariant { // fn invariant(&self) -> bool; // } // struct Node { // val: T, // next: Option>> // } // impl Invariant for Node { // fn invariant(&self) -> bool { // if let Some(n) = &self.next { // n.val > self.val // } else { // true // } // } // } // fn main() { // let root = A::open::>("foo.pool", 0).unwrap(); // A::transaction(|j| { // let mut n = root.next.borrow_mut(j); // n.val += 10; // verify!(n.invariant()); // }).unwrap(); // } // } #[allow(unused_assignments)] #[test] fn test_slice() { struct MyString<'a> { value: &'a [u8], } impl<'a> MyString<'a> { pub fn new(x: &'a str) -> Self { Self { value: x.as_bytes(), } } } impl<'a> std::ops::AddAssign<&'a str> for MyString<'a> { fn add_assign(&mut self, other: &'a str) { unsafe { let m: *const [u8] = self.value; let m: *mut [u8] = m as *mut [u8]; let m: &mut [u8] = &mut *m; let mut v = m.to_vec(); v.append(&mut other.as_bytes().to_vec()); } } } impl<'a> From<&'a str> for MyString<'a> { #[inline] fn from(v: &'a str) -> MyString<'a> { MyString { value: v.as_bytes(), } } } let mut str1 = MyString::new("first"); str1 += " second"; println!("str = {:?}", str1.value); let mut b1 = Box::new("first"); *b1 = " second"; println!("str = {:?}", b1); } #[test] fn test_address() { fn what_address(obj: &T) { println!("address = {:p}", obj); } fn test() { let d = Box::new(10); println!("address = {:p}", &d); what_address(&d); } test() } #[test] fn test_transaction() { use crate::default::*; let _heap = A::open_no_root("nosb.pool", O_CF); A::transaction(|j| { let data = Pbox::new(PRefCell::new(1), j); let mut data = data.borrow_mut(j); *data = 2; println!("data = {}", data); }) .unwrap(); } #[test] fn test_logs() { use crate::default::*; struct Root { head: Option>, } impl Default for Root { fn default() -> Self { Root { head: None } } } let root = A::open::>>("sb7.pool", O_CFNE).unwrap(); let data = A::transaction(|j| { let mut root = root.borrow_mut(j); if let Some(obj) = &root.head { let mut obj = obj.borrow_mut(j); *obj += 1; *obj } else { // let _root = root.borrow_mut(); // let _new_node = PRefCell::new(1); // std::process::exit(0); let new_node = PRefCell::new(1); //std::process::exit(0) root.head = Some(new_node); 1 } }) .unwrap(); println!("data = {}", data); } // Parc tests use crate::boxed::Pbox; use crate::sync::Parc; use std::thread; #[test] #[ignore] fn inner_tx() { use std::clone::Clone as StdClone; use std::sync::mpsc::channel; const N: usize = 10; Heap::transaction(|j| { // Spawn a few threads to increment a shared variable (non-atomically), and // let the main thread know once all increments are done. // // Here we're using an Arc to share memory among threads, and the data inside // the Arc is protected with a mutex. let data = Parc::new(PMutex::new(0), j); let (tx, rx) = channel(); for _ in 0..N { let (data, tx) = (data.demote(), tx.clone()); thread::spawn(move || { // The shared state can only be accessed once the lock is held. // Our non-atomic increment is safe because we're the only thread // which can access the shared state when the lock is held. // // We unwrap() the return value to assert that we are not expecting // threads to ever fail while holding the lock. let res = Heap::transaction(|j| { let data = data.promote(j).unwrap(); let mut data = data.lock(j); *data += 1; *data }) .unwrap(); // the lock is unlocked here when the transaction commits. if res == N { tx.send(()).unwrap(); } }); } rx.recv().unwrap(); }) .unwrap(); } #[test] fn parc_heap() { Heap::transaction(|j| { // the Arc is protected with a mutex. let _data = Parc::new(10, j); let _weak_five = Parc::downgrade(&_data, j); }).unwrap(); } #[test] fn test_parallel_alloc() { let mut threads = vec![]; struct Node { id: i32, next: Option>, } struct Root { root: Parc, A>, A>, } impl RootObj for Root { fn init(j: &Journal) -> Self { let node = Node { id: 0, next: None }; Self { root: Parc::new(PMutex::new(Some(node)), j), } } } let root = A::open::("sb8.pool", O_CFNE).unwrap(); print_usage(0); // let prev = A::used(); for i in 0..5 { let root = root.root.demote(); threads.push(thread::spawn(move || { A::transaction(|j| { if let Some(root) = root.promote(j) { let node = Node { id: i, next: None }; let mut root = root.lock(j); *root = Some(node); } }) .unwrap() })); } for thread in threads { thread.join().unwrap(); } print_usage(1); // assert_eq!(A::used(), prev); } #[test] fn test_mutex() { use crate::default::*; struct SB { root: Option>>, } impl Default for SB { fn default() -> Self { SB { root: None } } } let sb = A::open::>>("sb9.pool", O_CFNE).unwrap(); transaction(|j| { let mut sb = sb.borrow_mut(j); if sb.root.is_none() { sb.root = Some(Prc::new(PMutex::new(12), j)); } let mut val = sb.root.as_ref().unwrap().lock(j); println!("data is {}", val); *val = (rand() % 100) as i32; println!("data is {}", val); *val = (rand() % 100) as i32; println!("data is {}", val); }) .unwrap(); } #[test] fn test_mutex_mt() { struct SB { value: Parc, A>, } impl RootObj for SB { fn init(j: &stm::Journal) -> Self { SB { value: Parc::new(PMutex::new(0), j), } } } // for _ in 0..100 { let sb = A::open::("sb10.pool", O_CFNE).unwrap(); let mut threads = vec![]; print_usage(0); let mut v = vec![]; for _ in 0..10 { v.push((rand() % 100) as i32); } let mut sum = A::transaction(|j| *sb.value.lock(j)).unwrap(); println!("sum = {}", sum); // let mutex = std::sync::Arc::new(std::sync::Mutex::new(())); for i in 0..5 { let v = v.clone(); sum += v[i] + v[5 + i]; let sb = sb.clone(); let value = sb.value.demote(); threads.push(std::thread::spawn(move || { // let _lock = mutex.lock(); let delta1 = v[i]; let delta2 = v[5 + i]; let res = A::transaction(|j| { if let Some(val) = value.promote(j) { { // use std::io::Write; // std::io::stdout().flush().unwrap(); // std::process::exit(0); let mut val = val.lock(j); println!("{} + {} = {}", *val, delta1, *val + delta1); *val += delta1 - 10; *val += 10; } // no other thread can access sb.value here even the // fact that the lock guard is out of scope. { let mut val = val.lock(j); println!("{} + {} = {}", *val, delta2, *val + delta2); *val += delta2 - 10; // if i == 2 { // panic!("intentional {}", delta2); // // std::process::exit(0); // } *val += 10; } } }); if res.is_err() { // both changes are discarded delta1 + delta2 } else { 0 } })); } let mut missed: i32 = 0; for t in threads { match t.join() { Ok(delta) => missed += delta, Err(e) => println!("{:?}", e), } } println!("{:?}", v); let fin = A::transaction(|j| *sb.value.lock(j)).unwrap(); println!( "sum={}, fin={}, missed={}, fin+missed={}", sum, fin, missed, fin + missed ); assert_eq!(sum, fin + missed); print_usage(1); // } } #[test] fn test_tramutex_mt() { struct SB { value: Parc, A>, } impl RootObj for SB { fn init(j: &stm::Journal) -> Self { SB { value: Parc::new(PMutex::new(0), j), } } } // for _ in 0..100 { let sb = A::open::("sb20.pool", O_CFNE).unwrap(); let mut threads = vec![]; print_usage(0); let mut v = vec![]; for _ in 0..10 { v.push((rand() % 100) as i32); } let mut sum = A::transaction(|j| *sb.value.lock(j)).unwrap(); println!("sum = {}", sum); // let mutex = std::sync::Arc::new(std::sync::Mutex::new(())); for i in 0..3 { let v = v.clone(); sum += v[i] + v[5 + i]; let sb = sb.clone(); let value = sb.value.demote(); threads.push(std::thread::spawn(move || { // let _lock = mutex.lock(); let delta1 = v[i]; let delta2 = v[5 + i]; let res = A::transaction(|j| { if let Some(val) = value.promote(j) { { // use std::io::Write; // std::io::stdout().flush().unwrap(); // std::process::exit(0); let mut val = val.lock(j); println!("{} + {} = {}", *val, delta1, *val + delta1); *val += delta1 - 10; *val += 10; } { let mut val = val.lock(j); println!("{} + {} = {}", *val, delta2, *val + delta2); *val += delta2 - 10; // if i == 2 { // panic!("intentional {}", delta2); // // std::process::exit(0); // } *val += 10; } if i == 2 { panic!("intentional {}", delta2); // std::process::exit(0); } } }); if res.is_err() { // both changes are discarded delta1 + delta2 } else { 0 } })); } let mut missed: i32 = 0; for t in threads { match t.join() { Ok(delta) => missed += delta, Err(e) => println!("{:?}", e), } } println!("{:?}", v); let fin = A::transaction(|j| *sb.value.lock(j)).unwrap(); println!( "sum={}, fin={}, missed={}, fin+missed={}", sum, fin, missed, fin + missed ); assert_eq!(sum, fin + missed); print_usage(1); // } } // #[test] // fn inter_pool() { // crate::pool!(pool1); // crate::pool!(pool2); // type P1 = pool1::pool1; // type P2 = pool2::pool2; // type Root = Pbox>,P1>,P1>; // let root = P1::open::("interpool.pool", O_CFNE).unwrap(); // let _p = P2::open_no_root("interpool2.pool", O_CFNE).unwrap(); // let _ = P2::transaction(|j2| { // let mut root = root.borrow_mut(j2); // let b: Pbox>,P1>,P1>; // b = Pbox::new(PRefCell::new(None, j1), j1); // }); // } #[test] fn propagate_panic() { use crate::default::*; let _image = A::open_no_root("nosb.pool", O_CF); if A::transaction(|j| { let ptr = Parc::new(PMutex::new(1), j); print_usage(1); let _ = transaction(|j| { let _k = ptr.pclone(j); println!("panicking!"); panic!("yes"); }); println!("out 1!"); }) .is_err() { println!("panicking"); } // assert_eq!(A::used(), A::minimal_usage()); // let p = ptr.clone(); // transaction(|_| { // let _k = p.clone(); // // panic!("yes"); // }); } #[test] #[should_panic] fn outside_tx() { use crate::default::*; let _image = A::open_no_root("nosb.pool", O_CF); let b = PRefCell::new(10); let c = PCell::new(10); let m = PMutex::new(10); A::transaction(|j| { let mut b = b.borrow_mut(j); *b = 20; c.set(20, j); let mut m = m.lock(j); *m = 20; }).unwrap(); } #[test] fn assert_safe() { use crate::default::*; type P = Allocator; struct Obj { val: prc::VWeak } impl std::fmt::Display for Obj { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { transaction(AssertTxInSafe(|j| { writeln!(f, "{:?}", self.val.promote(j)) })).unwrap()?; Ok(()) } } } } #[cfg(test)] mod test_btree { //! btree.rs -- textbook implementation of btree /w preemptive splitting //! equivalent to [btree example] from PMDK. //! //! [btree example]: https://github.com/pmem/pmdk/blob/master/src/examples/libpmemobj/tree_map/btree_map.c use crate::default::*; use std::cell::RefCell; use std::fmt::{Display, Error, Formatter}; use std::rc::*; // use crate::cell::*; // use crate::Map; type P = Allocator; const N: usize = 5; #[derive(Clone)] pub struct NodeItem { key: K, val: V, } impl Display for NodeItem { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { write!(f, "{} ", self.key) } } impl Default for NodeItem { fn default() -> Self { Self { key: K::default(), val: V::default(), } } } #[derive(Clone)] pub struct LeafNode { len: usize, left: Weak>>, right: Weak>>, values: [Option>; N - 1], } impl LeafNode { fn key(&self) -> K { self.values[0].as_ref().unwrap().key } } impl LeafNode { fn add(&mut self, item: NodeItem) { for i in 0..self.len { if let Some(value) = &self.values[i] { if value.key > item.key { self.values[i..].rotate_right(1); self.values[i] = Some(item.clone()); self.len += 1; return; } } } self.values[self.len] = Some(item.clone()); self.len += 1; } } #[derive(Clone)] pub struct IntNode { len: usize, left: Weak>>, right: Weak>>, keys: [K; N - 1], slots: [Option>>>; N], } impl IntNode { fn key(&self) -> K { self.keys[0] } } impl IntNode { fn add(&mut self, node: Rc>>) { let key = node.borrow().key(); for i in 0..self.len { if self.keys[i] > key { self.keys[i..].rotate_right(1); self.slots[i..].rotate_right(1); self.keys[i] = key; self.slots[i] = Some(node); self.len += 1; return; } } self.slots[self.len] = Some(node); self.len += 1; } } #[derive(Clone)] pub enum BTreeNode { Leaf(LeafNode), Internal(IntNode), } use BTreeNode::*; impl BTreeNode { fn leaf() -> Self { Leaf(LeafNode { len: 0, left: Weak::new(), right: Weak::new(), values: Default::default(), }) } fn internal() -> Self { Internal(IntNode { len: 0, left: Weak::new(), right: Weak::new(), keys: Default::default(), slots: Default::default(), }) } } impl BTreeNode { fn len(&self) -> usize { match self { Leaf(this) => this.len, Internal(this) => this.len, } } fn insert( p: &Rc>>, item: NodeItem, ) -> Option> { // TODO: do not doublicate key (for publication) let mut this = p.borrow_mut(); match &mut *this { Leaf(this) => { for i in 0..this.len { if let Some(value) = &mut this.values[i] { if value.key == item.key { value.val = item.val; return None; } } } if this.len < N - 2 { this.add(item); None } else { if let Some(left) = this.left.upgrade() { let sib = left.borrow(); if sib.len() < N - 1 { let smallest = this.values[0].as_ref().unwrap().clone(); if item.key <= smallest.key { Self::insert(&left, item); } else { Self::insert(&left, smallest); this.values.rotate_left(1); this.values[N - 2] = None; this.len -= 1; this.add(item); } return None; } } if let Some(right) = this.right.upgrade() { let sib = right.borrow(); if sib.len() < N - 1 { let largest = this.values[N - 2].as_ref().unwrap().clone(); if item.key >= largest.key { Self::insert(&right, item); } else { Self::insert(&right, largest); this.values[N - 2] = None; this.len -= 1; this.add(item); } return None; } } // should split let mut new = LeafNode:: { len: 0, left: Weak::new(), right: Weak::new(), values: Default::default(), }; let mid = N / 2; let cmp = this.values[mid - 1].as_ref().unwrap().key; for i in 0..mid { new.values[i] = this.values[mid + i].clone(); this.values[mid + i] = None; } this.len = mid; new.len = mid; if item.key <= cmp { this.add(item); } else { new.add(item); } let mut parent = IntNode:: { len: 1, left: Weak::new(), right: Weak::new(), keys: Default::default(), slots: Default::default(), }; let new_key = new.key(); let this_key = this.key(); if this_key <= new_key { new.left = Rc::downgrade(p); new.right = this.right.clone(); } else { new.left = this.left.clone(); new.right = Rc::downgrade(p); } let new = Rc::new(RefCell::new(Leaf(new))); if this_key <= new_key { this.right = Rc::downgrade(&new); } else { this.left = Rc::downgrade(&new); } let slf = Rc::new(RefCell::new(Leaf(this.clone()))); parent.keys[0] = if this_key <= new_key { parent.slots[0] = Some(slf); parent.slots[1] = Some(new); new_key } else { parent.slots[0] = Some(new); parent.slots[1] = Some(slf); this_key }; Some(Internal(parent)) } } Internal(_) => None, } } } pub struct BTree { root: Rc>>, } impl Default for BTree { fn default() -> Self { Self { root: Rc::new(RefCell::new(BTreeNode::leaf())), } } } impl BTreeNode { fn key(&self) -> K { match self { Leaf(this) => this.key(), Internal(this) => this.key(), } } } impl Display for BTreeNode { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self { Leaf(this) => { for i in 0..N - 1 as usize { if let Some(value) = &this.values[i] { value.fmt(f)?; } else { break; } } } Internal(this) => { for i in 0..N - 1 as usize { if let Some(child) = &this.slots[i] { child.borrow().fmt(f)?; } else { break; } } } } Ok(()) } } impl BTree { pub fn is_empty(&self) -> bool { if let Leaf(root) = &*self.root.borrow() { root.values[0].is_none() } else { false } } } impl BTree { pub fn insert(&mut self, _key: K, _val: V) { // BTreeNode::insert(&self.root, key, val); } } impl Display for BTree { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { self.root.borrow().fmt(f) } } #[test] fn test_btree() { let mut btree = BTree::::default(); btree.insert(10, 0); btree.insert(30, 0); btree.insert(20, 0); btree.insert(25, 0); btree.insert(50, 0); println!("{}", btree); } }