/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::SharedAddressRange; use crate::SharedMemRef; use crate::ShmemAllocator; use crate::Volatile; use crate::ALLOCATOR; use num_traits::ToPrimitive; use shared_memory::SharedMemCast; use std::convert::From; use std::convert::TryFrom; use std::marker::PhantomData; use std::mem; use std::ops::Deref; /// An owned pointer into shared memory. pub struct SharedBox { address: SharedAddressRange, marker: PhantomData, } impl SharedBox { pub(crate) fn new_in(data: T, alloc: &ShmemAllocator) -> Option> { let size = mem::size_of::(); let address = alloc.alloc_bytes(size)?; let bytes = alloc.get_bytes(address)?; let volatile = Volatile::::from_volatile_bytes(bytes)?; let marker = PhantomData; volatile.write_volatile(data); Some(SharedBox { address, marker }) } pub(crate) fn get_in<'a>(&'a self, alloc: &'a ShmemAllocator) -> Option<&'a Volatile> { let bytes = alloc.get_bytes(self.address)?; Volatile::from_volatile_bytes(bytes) } /// Allocates a new box in shared memory, returning `None` if allocation failed. pub fn try_new(data: T) -> Option> { SharedBox::new_in(data, &ALLOCATOR) } /// Allocates a new box in shared memory, panicing if allocation failed. pub fn new(data: T) -> SharedBox { SharedBox::try_new(data).expect("Failed to allocate shared box") } /// Accesses a box in shared memory, returning `None` if the box refers to inaccessible memory. pub fn try_get(&self) -> Option<&Volatile> { self.get_in(&ALLOCATOR) } /// Accesses a box in shared memory, panicing if the box refers to inaccessible memory. pub fn get(&self) -> &Volatile { self.try_get().expect("Failed to deref shared box") } /// The shared address of the box. pub fn address(&self) -> SharedAddressRange { self.address } /// Create a box from a shared address. pub(crate) fn unchecked_from_address(address: SharedAddressRange) -> SharedBox { SharedBox { address, marker: PhantomData, } } } impl TryFrom for SharedBox { type Error = (); fn try_from(address: SharedAddressRange) -> Result, ()> { if mem::size_of::() <= address.object_size().to_usize().ok_or(())? { Ok(SharedBox::unchecked_from_address(address)) } else { Err(()) } } } impl From> for SharedAddressRange { fn from(boxed: SharedBox) -> SharedAddressRange { let address = boxed.address; mem::forget(boxed); address } } impl Deref for SharedBox { type Target = T; fn deref(&self) -> &T { self.get().deref() } } impl Drop for SharedBox { fn drop(&mut self) { // TODO: make it possible to use drop_in_place if let Some(volatile) = self.try_get() { volatile.read_volatile(); } ALLOCATOR.free_bytes(self.address); } } #[cfg(test)] use std::sync::atomic::AtomicUsize; #[cfg(test)] use std::sync::atomic::Ordering; #[test] fn test_one_box() { let boxed: SharedBox = SharedBox::new(AtomicUsize::new(37)); let val = boxed.load(Ordering::SeqCst); assert_eq!(val, 37); } #[test] fn test_five_boxes() { let boxed: [SharedBox; 5] = [ SharedBox::new(AtomicUsize::new(1)), SharedBox::new(AtomicUsize::new(2)), SharedBox::new(AtomicUsize::new(3)), SharedBox::new(AtomicUsize::new(4)), SharedBox::new(AtomicUsize::new(5)), ]; for i in 0..5 { let val = boxed[i].load(Ordering::SeqCst); assert_eq!(val, i + 1); } }