use std::ops::Deref;
use std::mem;
use std::sync::atomic::{self, AtomicUsize, Ordering};
pub struct FromRawArc<T> {
_inner: *mut Inner<T>,
}
unsafe impl<T: Sync + Send> Send for FromRawArc<T> { }
unsafe impl<T: Sync + Send> Sync for FromRawArc<T> { }
#[repr(C)]
struct Inner<T> {
data: T,
cnt: AtomicUsize,
}
impl<T> FromRawArc<T> {
pub fn new(data: T) -> FromRawArc<T> {
let x = Box::new(Inner {
data: data,
cnt: AtomicUsize::new(1),
});
FromRawArc { _inner: unsafe { mem::transmute(x) } }
}
pub unsafe fn from_raw(ptr: *mut T) -> FromRawArc<T> {
FromRawArc { _inner: ptr as *mut Inner<T> }
}
}
impl<T> Clone for FromRawArc<T> {
fn clone(&self) -> FromRawArc<T> {
unsafe {
(*self._inner).cnt.fetch_add(1, Ordering::Relaxed);
}
FromRawArc { _inner: self._inner }
}
}
impl<T> Deref for FromRawArc<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &(*self._inner).data }
}
}
impl<T> Drop for FromRawArc<T> {
fn drop(&mut self) {
unsafe {
if (*self._inner).cnt.fetch_sub(1, Ordering::Release) != 1 {
return
}
atomic::fence(Ordering::Acquire);
drop(mem::transmute::<_, Box<T>>(self._inner));
}
}
}
#[cfg(test)]
mod tests {
use super::FromRawArc;
#[test]
fn smoke() {
let a = FromRawArc::new(1);
assert_eq!(*a, 1);
assert_eq!(*a.clone(), 1);
}
#[test]
fn drops() {
struct A<'a>(&'a mut bool);
impl<'a> Drop for A<'a> {
fn drop(&mut self) {
*self.0 = true;
}
}
let mut a = false;
{
let a = FromRawArc::new(A(&mut a));
a.clone();
assert!(!*a.0);
}
assert!(a);
}
}