#![cfg(crossbeam_loom)] use crossbeam_epoch as epoch; use loom_crate as loom; use epoch::*; use epoch::{Atomic, Owned}; use loom::sync::atomic::Ordering::{self, Acquire, Relaxed, Release}; use loom::sync::Arc; use loom::thread::spawn; use std::mem::ManuallyDrop; use std::ptr; #[test] fn it_works() { loom::model(|| { let collector = Collector::new(); let item: Atomic = Atomic::from(Owned::new(String::from("boom"))); let item2 = item.clone(); let collector2 = collector.clone(); let guard = collector.register().pin(); let jh = loom::thread::spawn(move || { let guard = collector2.register().pin(); guard.defer(move || { // this isn't really safe, since other threads may still have pointers to the // value, but in this limited test scenario it's okay, since we know the test won't // access item after all the pins are released. let mut item = unsafe { item2.into_owned() }; // mutate it as a second measure to make sure the assert_eq below would fail item.retain(|c| c == 'o'); drop(item); }); }); let item = item.load(Ordering::SeqCst, &guard); // we pinned strictly before the call to defer_destroy, // so item cannot have been dropped yet assert_eq!(*unsafe { item.deref() }, "boom"); drop(guard); jh.join().unwrap(); drop(collector); }) } #[test] fn treiber_stack() { /// Treiber's lock-free stack. /// /// Usable with any number of producers and consumers. #[derive(Debug)] pub struct TreiberStack { head: Atomic>, } #[derive(Debug)] struct Node { data: ManuallyDrop, next: Atomic>, } impl TreiberStack { /// Creates a new, empty stack. pub fn new() -> TreiberStack { TreiberStack { head: Atomic::null(), } } /// Pushes a value on top of the stack. pub fn push(&self, t: T) { let mut n = Owned::new(Node { data: ManuallyDrop::new(t), next: Atomic::null(), }); let guard = epoch::pin(); loop { let head = self.head.load(Relaxed, &guard); n.next.store(head, Relaxed); match self .head .compare_exchange(head, n, Release, Relaxed, &guard) { Ok(_) => break, Err(e) => n = e.new, } } } /// Attempts to pop the top element from the stack. /// /// Returns `None` if the stack is empty. pub fn pop(&self) -> Option { let guard = epoch::pin(); loop { let head = self.head.load(Acquire, &guard); match unsafe { head.as_ref() } { Some(h) => { let next = h.next.load(Relaxed, &guard); if self .head .compare_exchange(head, next, Relaxed, Relaxed, &guard) .is_ok() { unsafe { guard.defer_destroy(head); return Some(ManuallyDrop::into_inner(ptr::read(&(*h).data))); } } } None => return None, } } } /// Returns `true` if the stack is empty. pub fn is_empty(&self) -> bool { let guard = epoch::pin(); self.head.load(Acquire, &guard).is_null() } } impl Drop for TreiberStack { fn drop(&mut self) { while self.pop().is_some() {} } } loom::model(|| { let stack1 = Arc::new(TreiberStack::new()); let stack2 = Arc::clone(&stack1); // use 5 since it's greater than the 4 used for the sanitize feature let jh = spawn(move || { for i in 0..5 { stack2.push(i); assert!(stack2.pop().is_some()); } }); for i in 0..5 { stack1.push(i); assert!(stack1.pop().is_some()); } jh.join().unwrap(); assert!(stack1.pop().is_none()); assert!(stack1.is_empty()); }); }