guest_cell/
lib.rs

1//! A container which provides typed scratch storage for external owners.
2
3// Copyright 2023 Eric Michael Sumner
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use std::collections::{HashMap,HashSet};
18use std::hash::{Hash,Hasher};
19use std::borrow::Borrow;
20
21use parking_lot::Mutex;
22
23#[cfg(test)] mod tests;
24pub mod id;
25use id::{Id,UniqueId};
26
27/// A typed slot for `Guest`s to store private values.
28/// Stores multiple values, one per guest.
29#[derive(Debug)]
30pub struct GuestCell<T:?Sized> {
31    map: Mutex<HashMap<Id, *mut T>>,
32    id: UniqueId
33}
34
35impl<T:?Sized> Default for GuestCell<T> {
36    fn default()->Self {
37        GuestCell {
38            map: Mutex::new(HashMap::new()),
39            id: UniqueId::default()
40        }
41    }
42}
43
44impl<T:?Sized> GuestCell<T> {
45    pub fn new()->Self { Self::default() }
46}
47
48// GuestCell acts much likd Mutex<T>
49unsafe impl<T:?Sized + Send> Send for GuestCell<T> {}
50unsafe impl<T:?Sized + Send> Sync for GuestCell<T> {}
51
52/// Accessor for a `GuestCell`.
53/// Safety: `borrow` must always return the same `GuestCell`
54pub unsafe trait IndirectGuestCell: Borrow<GuestCell<Self::Inner>> {
55    type Inner: ?Sized;
56}
57
58unsafe impl<T:?Sized> IndirectGuestCell for &'_ GuestCell<T> { type Inner = T; }
59unsafe impl<T:?Sized> IndirectGuestCell for std::sync::Arc<GuestCell<T>> { type Inner = T; }
60unsafe impl<T:?Sized> IndirectGuestCell for std::rc::Rc<GuestCell<T>> { type Inner = T; }
61
62trait ErasedGuestCell {
63    /// Drop the stored value placed here by `guest`.
64    fn guest_drop(&self, guest: &mut UniqueId);
65
66    /// Returns an identifier that represents this cell
67    fn id(&self)->&UniqueId;
68
69    /// Returns the number of guests that have stored value in this cell.
70    ///
71    /// Note: This is provided for diagnostic use; beware TOCTOU issues if
72    /// using this to make actual decisions.
73    fn len(&self)->usize;
74}
75
76impl<T:?Sized> ErasedGuestCell for GuestCell<T> {
77    fn guest_drop(&self, guest_id: &mut UniqueId) {
78        let ptr = self.map.lock().remove(&guest_id).unwrap();
79
80        // Safety: This pointer was generated via `Box::into_raw`
81        //         and is not currently aliased, due to the &mut UniqueId
82        let _ = unsafe { Box::from_raw(ptr) };
83    }
84
85    fn id(&self)->&UniqueId { &self.id }
86
87    fn len(&self)->usize { self.map.lock().len() }
88}
89
90impl<Ptr:IndirectGuestCell> ErasedGuestCell for Ptr {
91    fn guest_drop(&self, guest: &mut UniqueId) {
92        self.borrow().guest_drop(guest)
93    }
94
95    fn id(&self)->&UniqueId {
96        self.borrow().id()
97    }
98
99    fn len(&self)->usize { self.borrow().len() }
100}
101
102impl PartialEq for dyn ErasedGuestCell + '_ {
103    fn eq(&self, rhs: &dyn ErasedGuestCell)->bool {
104        **self.id() == **rhs.id()
105    }
106}
107
108impl Eq for dyn ErasedGuestCell + '_ {}
109
110impl Hash for dyn ErasedGuestCell + '_ {
111    fn hash<H:Hasher>(&self, h:&mut H) {
112        self.id().hash(h);
113    }
114}
115
116/// An owner for private values stored in various `GuestCell`s.
117#[derive(Default)]
118pub struct Guest<'a> {
119    id: UniqueId,
120    cells: HashSet<Box<dyn ErasedGuestCell + 'a>>,
121}
122
123impl Drop for Guest<'_> {
124    fn drop(&mut self) {
125        for map in &self.cells {
126            map.guest_drop(&mut self.id);
127        }
128    }
129}
130
131impl std::fmt::Debug for Guest<'_> {
132    fn fmt(&self, f:&mut std::fmt::Formatter<'_>)->Result<(), std::fmt::Error> {
133        f.debug_struct("Guest")
134            .field("id", &self.id)
135            .field("cells", &self.cells.iter().map(|x| x.id()).collect::<Vec<_>>())
136            .finish()
137    }
138}
139
140impl<'a> Guest<'a> {
141    /// Alias for Default::default()
142    pub fn new()->Self { Self::default() }
143
144    pub fn get<T:?Sized>(&self, cell: impl 'a + IndirectGuestCell<Inner=T>)->Option<&T> {
145        let guard = cell.borrow().map.lock();
146        let ptr:*mut T = *guard.get(&self.id)?;
147
148        // Safety: Access to this box is mediated through self.id 
149        Some(unsafe { &*ptr })
150    }
151
152    pub fn get_mut<T:?Sized>(&mut self, cell: impl 'a + IndirectGuestCell<Inner=T>)->Option<&mut T> {
153        let guard = cell.borrow().map.lock();
154        let ptr:*mut T = *guard.get(&self.id)?;
155
156        // Safety: Access to this box is mediated through self.id 
157        Some(unsafe { &mut *ptr })
158    }
159
160    pub fn get_or_init_mut<T:?Sized>(&mut self,
161                                 cell: impl 'a + IndirectGuestCell<Inner=T>,
162                                 init: impl FnOnce()->Box<T>)->&mut T {
163        let ptr:*mut T = {
164            let mut guard = cell.borrow().map.lock();
165            *guard.entry(*self.id).or_insert_with(|| Box::into_raw(init()))
166        };
167
168        self.cells.insert(Box::new(cell));
169        // Safety: Access to this box is mediated through self.id 
170        unsafe { &mut *ptr }
171    }
172
173    pub fn set<T:?Sized>(&mut self, cell: impl 'a + IndirectGuestCell<Inner=T>, val:Box<T>)->Option<Box<T>> {
174        let result = cell.borrow().map.lock().insert(*self.id, Box::into_raw(val));
175        self.cells.insert(Box::new(cell));
176
177        // Safety: This value was generated via `Box::into_raw`, and has no live references
178        Some(unsafe { Box::from_raw(result?) })
179    }
180
181    pub fn take<T:?Sized>(&mut self, cell: impl 'a + IndirectGuestCell<Inner=T>)->Option<Box<T>> {
182        let result = cell.borrow().map.lock().remove(&self.id);
183        self.cells.remove(&cell as &dyn ErasedGuestCell);
184
185        // Safety: This value was generated via `Box::into_raw`, and has no live references
186        Some(unsafe { Box::from_raw(result?) })
187    }
188}