use std::ops;
use std::sync::atomic;
use {hazard, local};
#[must_use]
#[derive(Debug)]
pub struct Guard<T: 'static> {
hazard: hazard::Writer,
pointer: &'static T,
}
impl<T> Guard<T> {
pub fn try_new<F, E>(ptr: F) -> Result<Guard<T>, E>
where F: FnOnce() -> Result<&'static T, E> {
let hazard = local::get_hazard();
atomic::fence(atomic::Ordering::SeqCst);
match ptr() {
Ok(ptr) => {
hazard.set(hazard::State::Protect(ptr as *const T as *const u8));
Ok(Guard {
hazard: hazard,
pointer: ptr,
})
},
Err(err) => {
hazard.set(hazard::State::Free);
Err(err)
}
}
}
pub fn new<F>(ptr: F) -> Guard<T>
where F: FnOnce() -> &'static T {
Guard::try_new::<_, ()>(|| Ok(ptr())).unwrap()
}
pub fn maybe_new<F>(ptr: F) -> Option<Guard<T>>
where F: FnOnce() -> Option<&'static T> {
Guard::try_new(|| ptr().ok_or(())).ok()
}
pub fn map<U, F>(self, f: F) -> Guard<U>
where F: FnOnce(&T) -> &U {
Guard {
hazard: self.hazard,
pointer: f(self.pointer),
}
}
pub fn as_raw(&self) -> *const T {
self.pointer
}
}
impl<T> ops::Deref for Guard<T> {
type Target = T;
fn deref(&self) -> &T {
self.pointer
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn panic_during_guard_creation() {
let _ = Guard::new(|| -> &'static u8 { panic!() });
}
}