intern_arc/
ref_count.rs

1/*
2 * Copyright 2021 Actyx AG
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16use crate::loom::*;
17use parking_lot::lock_api::RawMutex;
18use std::{
19    cell::UnsafeCell,
20    fmt::{Formatter, Pointer, Result},
21    ops::Deref,
22    ptr::NonNull,
23    sync::{Arc, Weak},
24};
25
26/// sealed trait pattern for trait Interner
27mod private {
28    pub trait Sealed {}
29
30    impl Sealed for () {}
31    impl<T: ?Sized + Eq + std::hash::Hash> Sealed for crate::hash::Hash<T> {}
32    impl<T: ?Sized + Ord> Sealed for crate::tree::Ord<T> {}
33}
34
35/// This is an internal trait that is not meant to be implemented outside this crate.
36pub trait Interner: private::Sealed + Sized {
37    type T: ?Sized;
38    fn remove(&self, value: &Interned<Self>) -> (bool, Option<Interned<Self>>);
39}
40/// This is a bogus shim impl used only for being able to compute the size of a RefCounted structure.
41impl Interner for () {
42    type T = ();
43    fn remove(&self, _value: &Interned<Self>) -> (bool, Option<Interned<Self>>) {
44        (false, None)
45    }
46}
47
48struct State<I> {
49    // inlining the raw mutex manually here to bring overhead down from 24 to 16 bytes
50    // on 64bit platforms (which unfortunately implies writing our own `struct Guard`)
51    mutex: parking_lot::RawMutex,
52    /// 4 billion refs ought to be enough for anybody, plus this allows the RawMutex
53    /// to live inside the same word on 64bit architectures.
54    refs: UnsafeCell<u32>,
55    cleanup: UnsafeCell<Option<Weak<I>>>,
56}
57impl<I: Interner> State<I> {
58    pub fn new() -> Self {
59        Self {
60            mutex: parking_lot::RawMutex::INIT,
61            refs: UnsafeCell::new(1),
62            cleanup: UnsafeCell::new(None),
63        }
64    }
65    pub fn lock(&self) -> Guard<I> {
66        self.mutex.lock();
67        Guard(self)
68    }
69}
70/// Safety: having the Guard is equivalent to owning the mutex lock, which holds a
71/// reference to the wrapped data, so dereferencing those pointers is safe.
72struct Guard<'a, I>(&'a State<I>);
73impl<'a, I> Guard<'a, I> {
74    pub fn refs(&self) -> u32 {
75        unsafe { *self.0.refs.get() }
76    }
77    pub fn refs_mut(&mut self) -> &mut u32 {
78        unsafe { &mut *self.0.refs.get() }
79    }
80    pub fn cleanup(&mut self) -> &mut Option<Weak<I>> {
81        unsafe { &mut *self.0.cleanup.get() }
82    }
83}
84impl<'a, I> Drop for Guard<'a, I> {
85    fn drop(&mut self) {
86        unsafe { self.0.mutex.unlock() };
87    }
88}
89
90// repr(C) is needed so that we can determine the correct allocation layout without
91// knowning the layout of `value`; this is needed to compute the combined layout from
92// these two pieces
93#[repr(C)]
94struct RefCounted<I: Interner> {
95    state: State<I>,
96    value: I::T,
97}
98
99impl<I: Interner> RefCounted<I> {
100    fn from_box(value: Box<I::T>) -> NonNull<Self> {
101        // figure out the needed allocation size — this requires #[repr(C)]
102        let layout = Layout::new::<RefCounted<()>>()
103            .extend(Layout::for_value(value.as_ref()))
104            .unwrap() // fails only on address range overflow
105            .0
106            .pad_to_align();
107        unsafe {
108            // allocate using global allocator
109            let ptr = alloc(layout);
110            // get value pointer with the right metadata (e.g. string length)
111            // while making sure to NOT DROP THE BOX
112            let b = Box::leak(value) as *mut I::T;
113            // construct correct (fat) pointer from allocation result:
114            //  - make a copy of the passed-in Box pointer
115            //  - overwrite the first sizeof::<usize>() bytes with the new address
116            // this keeps the metadata (second machine word) intact
117            let ptr = {
118                let mut temp = b as *mut Self;
119                // unfortunately <*mut>::set_ptr_value is still experimental, but this is what it does:
120                std::ptr::write(&mut temp as *mut _ as *mut *mut u8, ptr);
121                temp
122            };
123            // write the fields
124            std::ptr::write(&mut (*ptr).state, State::new());
125            let num_bytes = std::mem::size_of_val(&*b);
126            if num_bytes > 0 {
127                std::ptr::copy_nonoverlapping(
128                    // copy payload value byte-wise, because what else can we do?
129                    b as *const u8,
130                    &mut (*ptr).value as *mut _ as *mut u8,
131                    num_bytes,
132                );
133                // free the memory of the ex-Box; global allocator does not allow zero-sized allocations
134                // so this must be guarded by num_bytes > 0
135                #[cfg(not(loom))]
136                dealloc(b as *mut u8, Layout::for_value(&*b));
137                #[cfg(loom)]
138                std::alloc::dealloc(b as *mut u8, Layout::for_value(&*b));
139            }
140
141            NonNull::new_unchecked(ptr)
142        }
143    }
144
145    fn from_sized(value: I::T) -> NonNull<Self>
146    where
147        I::T: Sized,
148    {
149        let b = Box::new(Self {
150            state: State::new(),
151            value,
152        });
153        NonNull::from(Box::leak(b))
154    }
155}
156
157pub struct Interned<I: Interner> {
158    inner: NonNull<RefCounted<I>>,
159}
160
161unsafe impl<I: Interner> Send for Interned<I> where I::T: Send + Sync + 'static {}
162unsafe impl<I: Interner> Sync for Interned<I> where I::T: Send + Sync + 'static {}
163
164impl<I: Interner> Interned<I> {
165    pub(crate) fn ref_count(&self) -> u32 {
166        self.lock().refs()
167    }
168
169    fn lock(&self) -> Guard<I> {
170        // this is safe since the existence of &self proves that the pointer is still valid
171        unsafe { self.inner.as_ref().state.lock() }
172    }
173
174    pub(crate) fn from_box(value: Box<I::T>) -> Self {
175        Self {
176            inner: RefCounted::from_box(value),
177        }
178    }
179
180    pub(crate) fn from_sized(value: I::T) -> Self
181    where
182        I::T: Sized,
183    {
184        Self {
185            inner: RefCounted::from_sized(value),
186        }
187    }
188
189    pub(crate) fn make_hot(&mut self, set: &Arc<I>) {
190        let mut state = self.lock();
191        *state.cleanup() = Some(Arc::downgrade(set));
192    }
193}
194
195// use the two upper bits as spin-wait conditions
196const MAX_REFCOUNT: u32 = u32::MAX - 2;
197
198impl<I: Interner> Clone for Interned<I> {
199    fn clone(&self) -> Self {
200        let refs = {
201            let mut state = self.lock();
202            *state.refs_mut() += 1;
203            state.refs()
204        };
205
206        if refs > MAX_REFCOUNT {
207            // the below misspelling is deliberate
208            panic!("either you are running on an 8086 or you are leaking Interned values at a phantastic rate");
209        }
210        let ret = Self { inner: self.inner };
211        #[cfg(feature = "println")]
212        println!("{:?} clone {:p}", current().id(), *self);
213        ret
214    }
215}
216
217impl<I: Interner> Drop for Interned<I> {
218    fn drop(&mut self) {
219        // printing `self` to mark this particular execution (points to the stack)
220        // printing `*self` to mark the interned value (as printed by `clone`)
221        #[cfg(feature = "println")]
222        println!("{:?} dropping {:p} {:p}", current().id(), self, *self);
223
224        // preconditions:
225        //  - this Interned may or may not be referenced by an interner (since the interner can be dropped)
226        //  - the `self` reference guarantees that the reference count is at least one
227        //  - whatever happens, we must decrement the reference count by one
228        //  - if the only remaining reference is the interner map, we need to try to remove it
229        //    (this races against an `intern` call for the same value and against dropping the interner)
230        //
231        // IMPORTANT NOTE: each Interned starts out with two references! (by virtue of creation and first clone)
232        //
233        // Also, THE REMOVAL POINTER NEEDS TO BE USED EXACTLY ONCE!
234
235        // take the lock — we MUST NOT hold this lock while calling the cleanup function!
236        // (A-B vs B-A deadlock would occur otherwise, since interning first takes the interner lock, then this one)
237        let mut state = self.lock();
238
239        #[cfg(feature = "println")]
240        println!(
241            "{:?} read {} {:p} {:p}",
242            current().id(),
243            state.refs(),
244            self,
245            *self
246        );
247
248        // decrement the count
249        *state.refs_mut() -= 1;
250
251        // two cases require action:
252        //  - count was two: perform the removal (unless already done)
253        //  - count was one: deallocate
254
255        if state.refs() > 1 {
256            return;
257        }
258
259        if state.refs() == 1 {
260            // the other reference could be the map or external (if the map was dropped and we didn’t get here yet)
261            // so this races against:
262            //  1. map being dropped
263            //  2. same value being interned again
264            //  3. other external reference being dropped
265            // In the dropping cases, the other thread saw read == 1.
266            if let Some(cleanup) = state.cleanup().take() {
267                #[cfg(feature = "println")]
268                println!("{:?} removing {:p} {:p}", current().id(), self, *self);
269                // At this point, the other remaining reference can either be the interner or an
270                // external one (if the interner was already dropped).
271                if let Some(strong) = cleanup.upgrade() {
272                    // Interner is still there, so the other reference is in there; we may race
273                    // against interning of the same value, which may already have taken the interner
274                    // lock, so we cannot just call the cleanup function.
275                    drop(state);
276                    loop {
277                        // in here another thread may have found the interned reference and started cloning it,
278                        // it might even have dropped it already (but without running cleanup, since we have that.
279                        // see the other `else` further down)
280                        let (removed, _value) = strong.remove(self);
281                        if removed {
282                            // nobody interfered and the value is now removed from the interner, we can safely drop it
283                            // which will re-enter this drop() function and decrement refs to zero
284                            break;
285                        } else {
286                            // someone interfered, so we need to take the lock again to put things in order
287                            let mut state = self.lock();
288                            // precondition: we hold the `cleanup` so there is still at least one reference in the
289                            // interner — remember that we hold a strong reference to that one!
290                            if state.refs() > 1 {
291                                // someone else will drop the refs back down to 1 again later, so hand the cleanup
292                                // function to them (this may happen far in the future!)
293                                *state.cleanup() = Some(cleanup);
294                                break;
295                            } else {
296                                // whoever interfered already dropped their reference again, so we need to retry;
297                                // it is important that we drop the lock before retrying, which would happen
298                                // automatically, but let’s write it down to make it dead obvious:
299                                drop(state);
300                            }
301                        }
302                    }
303                } else {
304                    // Interner is gone or on its way out, which means that no concurrent interning
305                    // is possible anymore; it also means that the other reference may well be from
306                    // the interner, still (it may be dropping right now). Our job here is done.
307                }
308                #[cfg(feature = "println")]
309                println!("{:?} removed {:p}", current().id(), self);
310            } else {
311                // someone else is currently taking care of correctly dropping this value from the interner, see above
312                #[cfg(feature = "println")]
313                println!("{:?} cleanup gone {:p}", current().id(), self);
314            }
315        } else if state.refs() == 0 {
316            #[cfg(feature = "println")]
317            println!("{:?} drop {:p} {:p}", current().id(), self, *self);
318
319            // drop the lock before freeing the memory, otherwise it would use-after-free
320            drop(state);
321
322            // since we created the pointer with Box::leak(), we can recreate the box to drop it
323            unsafe { Box::from_raw(self.inner.as_ptr()) };
324        }
325
326        #[cfg(feature = "println")]
327        println!("{:?} dropend {:p}", current().id(), self);
328    }
329}
330
331impl<I: Interner> PartialEq for Interned<I> {
332    fn eq(&self, other: &Self) -> bool {
333        std::ptr::eq(self.inner.as_ptr(), other.inner.as_ptr())
334    }
335}
336
337impl<I: Interner> Deref for Interned<I> {
338    type Target = I::T;
339
340    fn deref(&self) -> &Self::Target {
341        // safety: the presence of &self guarantees that the value has not yet been dropped
342        &unsafe { self.inner.as_ref() }.value
343    }
344}
345
346impl<I: Interner> Pointer for Interned<I> {
347    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
348        Pointer::fmt(&(&**self as *const I::T), f)
349    }
350}
351
352#[cfg(all(test, not(loom)))]
353mod tests {
354    use super::*;
355    use crate::OrdInterner;
356
357    #[test]
358    fn pointer() {
359        let interner = OrdInterner::new();
360        let i = interner.intern_sized(42);
361        let i2 = i.clone();
362        assert_eq!(format!("{:p}", i), format!("{:p}", i2));
363    }
364
365    #[test]
366    fn size() {
367        use std::mem::size_of;
368        const SIZE: usize = if size_of::<usize>() == 4 { 12 } else { 16 };
369        assert_eq!(size_of::<RefCounted<()>>(), SIZE);
370
371        let fake = RefCounted::<crate::hash::Hash<i32>> {
372            state: State::new(),
373            value: 42,
374        };
375
376        println!("base:  {:p}", &fake);
377        let base = &fake as *const _ as *const u8;
378        println!("state: {:p} (base + {})", &fake.state, unsafe {
379            (&fake.state as *const _ as *const u8).offset_from(base)
380        });
381        println!("mutex: {:p} (base + {})", &fake.state.mutex, unsafe {
382            (&fake.state.mutex as *const _ as *const u8).offset_from(base)
383        });
384        println!("refs:  {:p} (base + {})", &fake.state.refs, unsafe {
385            (&fake.state.refs as *const _ as *const u8).offset_from(base)
386        });
387        println!("clean: {:p} (base + {})", &fake.state.cleanup, unsafe {
388            (&fake.state.cleanup as *const _ as *const u8).offset_from(base)
389        });
390        println!("value: {:p} (base + {})", &fake.value, unsafe {
391            (&fake.value as *const _ as *const u8).offset_from(base)
392        });
393    }
394}