1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* 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<T: SharedMemCast> {
    address: SharedAddressRange,
    marker: PhantomData<T>,
}

impl<T: SharedMemCast> SharedBox<T> {
    pub(crate) fn new_in(data: T, alloc: &ShmemAllocator) -> Option<SharedBox<T>> {
        let size = mem::size_of::<T>();
        let address = alloc.alloc_bytes(size)?;
        let bytes = alloc.get_bytes(address)?;
        let volatile = Volatile::<T>::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<T>> {
        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<T>> {
        SharedBox::new_in(data, &ALLOCATOR)
    }

    /// Allocates a new box in shared memory, panicing if allocation failed.
    pub fn new(data: T) -> SharedBox<T> {
        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<T>> {
        self.get_in(&ALLOCATOR)
    }

    /// Accesses a box in shared memory, panicing if the box refers to inaccessible memory.
    pub fn get(&self) -> &Volatile<T> {
        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<T> {
        SharedBox {
            address,
            marker: PhantomData,
        }
    }
}

impl<T: SharedMemCast> TryFrom<SharedAddressRange> for SharedBox<T> {
    type Error = ();
    fn try_from(address: SharedAddressRange) -> Result<SharedBox<T>, ()> {
        if mem::size_of::<T>() <= address.object_size().to_usize().ok_or(())? {
            Ok(SharedBox::unchecked_from_address(address))
        } else {
            Err(())
        }
    }
}

impl<T: SharedMemCast> From<SharedBox<T>> for SharedAddressRange {
    fn from(boxed: SharedBox<T>) -> SharedAddressRange {
        let address = boxed.address;
        mem::forget(boxed);
        address
    }
}

impl<T: SharedMemCast + SharedMemRef> Deref for SharedBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.get().deref()
    }
}

impl<T: SharedMemCast> Drop for SharedBox<T> {
    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<AtomicUsize> = SharedBox::new(AtomicUsize::new(37));
    let val = boxed.load(Ordering::SeqCst);
    assert_eq!(val, 37);
}

#[test]
fn test_five_boxes() {
    let boxed: [SharedBox<AtomicUsize>; 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);
    }
}