use core::cell::Cell;
use core::ptr;
use core::sync::atomic;
use std::borrow::Borrow;
use std::boxed::Box;
use std::ptr::NonNull;
use std::sync::atomic::Ordering;
struct MlspInner<T> {
atomic_count: atomic::AtomicUsize,
data: T,
}
impl<T> MlspInner<T> {
fn new(data: T) -> Self {
MlspInner {
atomic_count: atomic::AtomicUsize::new(1),
data,
}
}
unsafe fn increment(&self) {
self.atomic_count.fetch_add(1, Ordering::Release);
}
unsafe fn decrement(&mut self) {
let old = self.atomic_count.fetch_sub(1, Ordering::Release);
atomic::fence(Ordering::Acquire);
if old == 1 {
ptr::drop_in_place(self);
}
}
}
pub struct Mlsp<T> {
local_count: NonNull<Cell<usize>>,
inner_ptr: NonNull<MlspInner<T>>,
}
impl<T> Mlsp<T> {
pub fn new(data: T) -> Self {
let atomic_counter = Box::new(MlspInner::new(data));
let atomic_counter = Box::into_raw(atomic_counter);
let atomic_counter = NonNull::new(atomic_counter).unwrap();
Mlsp {
local_count: new_local_counter(),
inner_ptr: atomic_counter,
}
}
pub fn package(&self) -> MlspPackage<T> {
unsafe {
self.inner_ptr.as_ref().increment();
}
MlspPackage {
inner_ptr: self.inner_ptr,
}
}
}
impl<T> Borrow<T> for Mlsp<T> {
fn borrow(&self) -> &T {
unsafe { &self.inner_ptr.as_ref().data }
}
}
impl<T> AsRef<T> for Mlsp<T> {
fn as_ref(&self) -> &T {
unsafe { &self.inner_ptr.as_ref().data }
}
}
impl<T> Clone for Mlsp<T> {
fn clone(&self) -> Self {
let local_count = unsafe { self.local_count.as_ref() };
let count = local_count.get();
let count = count + 1;
local_count.set(count);
Mlsp {
local_count: self.local_count,
inner_ptr: self.inner_ptr,
}
}
}
impl<T> Drop for Mlsp<T> {
fn drop(&mut self) {
unsafe {
let local_count = self.local_count.as_mut();
let count = local_count.get();
let count = count - 1;
local_count.set(count);
if count > 0 {
return;
}
}
unsafe {
ptr::drop_in_place(self.local_count.as_mut());
self.inner_ptr.as_mut().decrement();
}
}
}
pub struct MlspPackage<T> {
inner_ptr: NonNull<MlspInner<T>>,
}
impl<T> MlspPackage<T> {
pub fn unpackage(self) -> Mlsp<T> {
Mlsp {
local_count: new_local_counter(),
inner_ptr: self.inner_ptr,
}
}
}
impl<T> Drop for MlspPackage<T> {
fn drop(&mut self) {
unsafe {
self.inner_ptr.as_mut().decrement();
}
}
}
unsafe impl<T: Sync + Send> Send for MlspPackage<T> {}
unsafe impl<T: Sync + Send> Sync for MlspPackage<T> {}
impl<T> Clone for MlspPackage<T> {
fn clone(&self) -> Self {
unsafe {
self.inner_ptr.as_ref().increment();
}
MlspPackage {
inner_ptr: self.inner_ptr,
}
}
}
fn new_local_counter() -> NonNull<Cell<usize>> {
let local_counter: Box<Cell<usize>> = Box::new(Cell::new(1));
let local_counter: *mut Cell<usize> = Box::into_raw(local_counter);
let local_counter: NonNull<Cell<usize>> = NonNull::new(local_counter).unwrap();
local_counter
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn local_sharing() {
let a = Mlsp::new(1u8);
let b = a.clone();
let c = b.clone();
let d = c.package();
let _d2 = d.unpackage();
let e = c.package();
let _e2 = e.unpackage();
drop(c);
drop(b);
drop(a);
}
#[test]
fn cross_thread_sharing() {
use std::thread;
let mlsp = Mlsp::new(1u8);
let mut children = vec![];
for _ in 0..10 {
let package = mlsp.package();
children.push(thread::spawn(move || {
let shared_mlsp = package.unpackage();
let shared_mlsp_clone = shared_mlsp.clone();
assert_eq!(1u8, *(shared_mlsp.borrow()));
assert_eq!(1u8, *(shared_mlsp_clone.borrow()));
}));
}
for child in children {
let _ = child.join();
}
}
}