Skip to main content

vtable/
vrc.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4// cSpell: ignore pointee repr
5
6//! implementation of vtable::Vrc
7
8use super::*;
9use portable_atomic::{AtomicU32, Ordering};
10
11/// This trait is implemented by the [`#[vtable]`](macro@vtable) macro.
12///
13/// It is implemented if the macro has a "drop_in_place" function.
14///
15/// # Safety
16///
17/// The implementation of drop_in_place and dealloc must be correct
18pub unsafe trait VTableMetaDropInPlace: VTableMeta {
19    /// # Safety
20    /// The target ptr argument needs to be pointing to a an instance of the VTable
21    /// after the call to this function, the memory is still there but no longer contains
22    /// a valid object.
23    unsafe fn drop_in_place(vtable: &Self::VTable, ptr: *mut u8) -> vrc::Layout;
24    /// # Safety
25    /// The target ptr must have been allocated by the same allocator as the
26    /// one which the vtable will delegate to.
27    unsafe fn dealloc(vtable: &Self::VTable, ptr: *mut u8, layout: vrc::Layout);
28}
29
30/// This is a marker type to be used in [`VRc`] and [`VWeak`] to mean that the
31/// actual type is not known.
32// Note the use of PhantomData to make this type not Send, as a VRc<T, Dyn> cannot be send between thread
33pub struct Dyn(PhantomData<*mut ()>);
34
35/// Similar to [`core::alloc::Layout`], but `repr(C)`
36#[repr(C)]
37#[derive(Clone, Copy)]
38pub struct Layout {
39    /// The size in bytes
40    pub size: usize,
41    /// The minimum alignment in bytes
42    pub align: usize,
43}
44
45impl From<core::alloc::Layout> for Layout {
46    fn from(layout: core::alloc::Layout) -> Self {
47        Self { size: layout.size(), align: layout.align() }
48    }
49}
50
51impl core::convert::TryFrom<Layout> for core::alloc::Layout {
52    type Error = core::alloc::LayoutError;
53
54    fn try_from(value: Layout) -> Result<Self, Self::Error> {
55        Self::from_size_align(value.size, value.align)
56    }
57}
58
59#[repr(C)]
60struct VRcInner<'vt, VTable: VTableMeta, X> {
61    vtable: &'vt VTable::VTable,
62    /// The amount of VRc pointing to this object. When it reaches 0, the object will be dropped
63    strong_ref: AtomicU32,
64    /// The amount of VWeak +1. When it reaches 0, the memory will be deallocated.
65    /// The +1 is there such as all the VRc together hold a weak reference to the memory
66    weak_ref: AtomicU32,
67    /// offset to the data from the beginning of VRcInner. This is needed to cast a VRcInner<VT, X>
68    /// to VRcInner<VT, u8> as "dyn VRc" and then still be able to get the correct data pointer,
69    /// since the alignment of X may not be the same as u8.
70    data_offset: u16,
71    /// Actual data, or an instance of `Self::Layout` iff `strong_ref == 0`.
72    /// Can be seen as `union {data: X, layout: Layout}`  (but that's not stable)
73    data: X,
74}
75
76impl<VTable: VTableMeta, X> VRcInner<'_, VTable, X> {
77    unsafe fn data_ptr(s: *const Self) -> *const X {
78        unsafe { (s as *const u8).add(*core::ptr::addr_of!((*s).data_offset) as usize) as *const X }
79    }
80    fn as_ref(&self) -> &X {
81        let ptr = self as *const Self as *const u8;
82        unsafe { &*(ptr.add(self.data_offset as usize) as *const X) }
83    }
84}
85
86/// A reference counted pointer to an object matching the virtual table `T`
87///
88/// Similar to [`alloc::rc::Rc`] where the `VTable` type parameter is a VTable struct
89/// annotated with [`#[vtable]`](macro@vtable), and the `X` type parameter is the actual instance.
90/// When `X` is the [`Dyn`] type marker, this means that the X is not known and the only
91/// thing that can be done is to get a [`VRef<VTable>`] through the [`Self::borrow()`] function.
92///
93/// Other differences with the [`alloc::rc::Rc`] types are:
94/// - It does not allow to access mutable reference. (No `get_mut` or `make_mut`), meaning it is
95///   safe to get a Pin reference with `borrow_pin`.
96/// - It is safe to pass it across ffi boundaries.
97#[repr(transparent)]
98pub struct VRc<VTable: VTableMetaDropInPlace + 'static, X = Dyn> {
99    inner: NonNull<VRcInner<'static, VTable, X>>,
100}
101
102impl<VTable: VTableMetaDropInPlace + 'static, X> Drop for VRc<VTable, X> {
103    fn drop(&mut self) {
104        unsafe {
105            let inner = self.inner.as_ptr();
106            if (*inner).strong_ref.fetch_sub(1, Ordering::SeqCst) == 1 {
107                let data =
108                    (inner as *mut u8).add(*core::ptr::addr_of!((*inner).data_offset) as usize);
109                let vtable = core::ptr::addr_of!((*inner).vtable);
110                let mut layout = VTable::drop_in_place(*vtable, data);
111                layout = core::alloc::Layout::new::<VRcInner<VTable, ()>>()
112                    .extend(layout.try_into().unwrap())
113                    .unwrap()
114                    .0
115                    .pad_to_align()
116                    .into();
117                if (*core::ptr::addr_of!((*inner).weak_ref)).load(Ordering::SeqCst) > 1 {
118                    // at this point we are sure that no other thread can access the data
119                    // since we still hold a weak reference, so the other weak references
120                    // in other thread won't start destroying the object.
121                    *(VRcInner::data_ptr(self.inner.cast::<VRcInner<VTable, Layout>>().as_ptr())
122                        as *mut Layout) = layout;
123                }
124                if (*core::ptr::addr_of!((*inner).weak_ref)).fetch_sub(1, Ordering::SeqCst) == 1 {
125                    VTable::dealloc(*vtable, self.inner.cast().as_ptr(), layout);
126                }
127            }
128        }
129    }
130}
131
132impl<VTable: VTableMetaDropInPlace + 'static, X> core::fmt::Debug for VRc<VTable, X> {
133    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
134        f.debug_struct("VRc").field("inner", &self.inner).finish()
135    }
136}
137
138impl<VTable: VTableMetaDropInPlace, X: HasStaticVTable<VTable>> VRc<VTable, X> {
139    /// Create a new VRc from an instance of a type that can be associated with a VTable.
140    ///
141    /// Will move the instance on the heap.
142    ///
143    /// (the `HasStaticVTable` is implemented by the `“MyTrait”VTable_static!` macro generated by
144    /// the #[vtable] macro)
145    pub fn new(data: X) -> Self {
146        let layout = core::alloc::Layout::new::<VRcInner<VTable, X>>().pad_to_align();
147        // We must ensure the size is enough to hold a Layout when strong_count becomes 0
148        let layout_with_layout = core::alloc::Layout::new::<VRcInner<VTable, Layout>>();
149        let layout = core::alloc::Layout::from_size_align(
150            layout.size().max(layout_with_layout.size()),
151            layout.align().max(layout_with_layout.align()),
152        )
153        .unwrap();
154        let mem = unsafe { alloc::alloc::alloc(layout) as *mut VRcInner<VTable, X> };
155        let inner = NonNull::new(mem).unwrap();
156        assert!(!mem.is_null());
157
158        unsafe {
159            mem.write(VRcInner {
160                vtable: X::STATIC_VTABLE,
161                strong_ref: AtomicU32::new(1),
162                weak_ref: AtomicU32::new(1), // All the VRc together hold a weak_ref to the memory
163                data_offset: 0,
164                data,
165            });
166            (*mem).data_offset =
167                (&(*mem).data as *const _ as usize - mem as *const _ as usize) as u16;
168            VRc { inner }
169        }
170    }
171
172    /// Convert a VRc of a real instance to a VRc of a Dyn instance
173    pub fn into_dyn(this: Self) -> VRc<VTable, Dyn>
174    where
175        Self: 'static,
176    {
177        // Safety: they have the exact same representation: just a pointer to the same structure.
178        // no Drop will be called here, so no need to increment any ref count
179        unsafe { core::mem::transmute(this) }
180    }
181}
182
183impl<VTable: VTableMetaDropInPlace + 'static, X: HasStaticVTable<VTable> + 'static> VRc<VTable, X> {
184    /// This function allows safely holding a reference to a field inside the VRc. In order to accomplish
185    /// that, you need to provide a mapping function `map_fn` in which you need to provide and return a
186    /// pinned reference to the object you would like to map. The returned `VRcMapped` allows obtaining
187    /// that pinned reference again using [`VRcMapped::as_pin_ref`].
188    pub fn map<MappedType: ?Sized>(
189        this: Self,
190        map_fn: impl for<'r> FnOnce(Pin<&'r X>) -> Pin<&'r MappedType>,
191    ) -> VRcMapped<VTable, MappedType> {
192        VRcMapped {
193            parent_strong: Self::into_dyn(this.clone()),
194            object: map_fn(this.as_pin_ref()).get_ref(),
195        }
196    }
197}
198
199impl<VTable: VTableMetaDropInPlace + 'static> VRc<VTable, Dyn> {
200    /// This function allows safely holding a reference to a field inside the VRc. In order to accomplish
201    /// that, you need to provide a mapping function `map_fn` in which you need to provide and return a
202    /// pinned reference to the object you would like to map. The returned `VRcMapped` allows obtaining
203    /// that pinned reference again using [`VRcMapped::as_pin_ref`].
204    /// This works similar to [`VRc::map`] except that it works on a type-erased VRc.
205    pub fn map_dyn<MappedType: ?Sized>(
206        this: Self,
207        map_fn: impl for<'r> FnOnce(Pin<VRef<'r, VTable>>) -> Pin<&'r MappedType>,
208    ) -> VRcMapped<VTable, MappedType> {
209        VRcMapped { parent_strong: this.clone(), object: map_fn(Self::borrow_pin(&this)).get_ref() }
210    }
211}
212impl<VTable: VTableMetaDropInPlace, X> VRc<VTable, X> {
213    /// Create a Pinned reference to the inner.
214    ///
215    /// This is safe because we don't allow mutable reference to the inner
216    pub fn as_pin_ref(&self) -> Pin<&X> {
217        unsafe { Pin::new_unchecked(self) }
218    }
219
220    /// Gets a VRef pointing to this instance
221    pub fn borrow(this: &Self) -> VRef<'_, VTable> {
222        unsafe {
223            let inner = this.inner.cast::<VRcInner<VTable, u8>>();
224            VRef::from_raw(
225                NonNull::from(*::core::ptr::addr_of!((*inner.as_ptr()).vtable)),
226                NonNull::new_unchecked(VRcInner::data_ptr(inner.as_ptr()) as *mut u8),
227            )
228        }
229    }
230
231    /// Gets a `Pin<VRef>` pointing to this instance
232    ///
233    /// This is safe because there is no way to access a mutable reference to the pointee.
234    /// (There is no `get_mut` or `make_mut`),
235    pub fn borrow_pin(this: &Self) -> Pin<VRef<'_, VTable>> {
236        unsafe { Pin::new_unchecked(Self::borrow(this)) }
237    }
238
239    /// Construct a [`VWeak`] pointing to this instance.
240    pub fn downgrade(this: &Self) -> VWeak<VTable, X> {
241        let inner = unsafe { this.inner.as_ref() };
242        inner.weak_ref.fetch_add(1, Ordering::SeqCst);
243        VWeak { inner: Some(this.inner) }
244    }
245
246    /// Gets the number of strong (VRc) pointers to this allocation.
247    pub fn strong_count(this: &Self) -> usize {
248        unsafe { this.inner.as_ref().strong_ref.load(Ordering::SeqCst) as usize }
249    }
250
251    /// Returns true if the two VRc's point to the same allocation
252    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
253        this.inner == other.inner
254    }
255}
256
257impl<VTable: VTableMetaDropInPlace + 'static, X> Clone for VRc<VTable, X> {
258    fn clone(&self) -> Self {
259        let inner = unsafe { self.inner.as_ref() };
260        inner.strong_ref.fetch_add(1, Ordering::SeqCst);
261        Self { inner: self.inner }
262    }
263}
264
265impl<VTable: VTableMetaDropInPlace, X /*+ HasStaticVTable<VTable>*/> Deref for VRc<VTable, X> {
266    type Target = X;
267    fn deref(&self) -> &Self::Target {
268        let inner = unsafe { self.inner.as_ref() };
269        inner.as_ref()
270    }
271}
272
273// Safety: we use atomic reference count for the internal things
274unsafe impl<VTable: VTableMetaDropInPlace + Send + Sync + 'static, X: Send + Sync> Send
275    for VRc<VTable, X>
276{
277}
278unsafe impl<VTable: VTableMetaDropInPlace + Send + Sync + 'static, X: Send + Sync> Sync
279    for VRc<VTable, X>
280{
281}
282
283/// Weak pointer for the [`VRc`] where `VTable` is a VTable struct, and
284/// `X` is the type of the instance, or [`Dyn`] if it is not known
285///
286/// Similar to [`alloc::rc::Weak`].
287///
288/// Can be constructed with [`VRc::downgrade`] and use [`VWeak::upgrade`]
289/// to re-create the original VRc.
290#[repr(transparent)]
291pub struct VWeak<VTable: VTableMetaDropInPlace + 'static, X = Dyn> {
292    inner: Option<NonNull<VRcInner<'static, VTable, X>>>,
293}
294
295impl<VTable: VTableMetaDropInPlace + 'static, X> Default for VWeak<VTable, X> {
296    fn default() -> Self {
297        Self { inner: None }
298    }
299}
300
301impl<VTable: VTableMetaDropInPlace + 'static, X> Clone for VWeak<VTable, X> {
302    fn clone(&self) -> Self {
303        if let Some(inner) = self.inner {
304            let inner = unsafe { inner.as_ref() };
305            inner.weak_ref.fetch_add(1, Ordering::SeqCst);
306        }
307        VWeak { inner: self.inner }
308    }
309}
310
311impl<T: VTableMetaDropInPlace + 'static, X> Drop for VWeak<T, X> {
312    fn drop(&mut self) {
313        if let Some(i) = self.inner {
314            unsafe {
315                if (*core::ptr::addr_of!((*i.as_ptr()).weak_ref)).fetch_sub(1, Ordering::SeqCst)
316                    == 1
317                {
318                    // Safety: while allocating, we made sure that the size was big enough to
319                    // hold a VRcInner<T, Layout>.
320                    let vtable = &*core::ptr::addr_of!((*i.as_ptr()).vtable);
321                    let layout = *(VRcInner::data_ptr(i.cast::<VRcInner<T, Layout>>().as_ptr()));
322                    T::dealloc(vtable, i.cast().as_ptr(), layout);
323                }
324            }
325        }
326    }
327}
328
329impl<VTable: VTableMetaDropInPlace + 'static, X> VWeak<VTable, X> {
330    /// Returns a new `VRc` if some other instance still holds a strong reference to this item.
331    /// Otherwise, returns None.
332    pub fn upgrade(&self) -> Option<VRc<VTable, X>> {
333        if let Some(i) = self.inner {
334            let inner = unsafe { i.as_ref() };
335            if inner.strong_ref.load(Ordering::SeqCst) == 0 {
336                None
337            } else {
338                inner.strong_ref.fetch_add(1, Ordering::SeqCst);
339                Some(VRc { inner: i })
340            }
341        } else {
342            None
343        }
344    }
345
346    /// Returns true if the two VWeak instances point to the same allocation
347    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
348        this.inner == other.inner
349    }
350}
351
352impl<VTable: VTableMetaDropInPlace + 'static, X: HasStaticVTable<VTable> + 'static>
353    VWeak<VTable, X>
354{
355    /// Convert a VRc of a real instance to a VRc of a Dyn instance
356    pub fn into_dyn(self) -> VWeak<VTable, Dyn> {
357        // Safety: they have the exact same representation: just a pointer to the same structure.
358        // no Drop will be called here, so no need to increment any ref count
359        unsafe { core::mem::transmute(self) }
360    }
361}
362
363/// Safety: The data VRc manages is held by `VRcInner`, which maintains its address when the VRc
364/// is moved.
365unsafe impl<VTable: VTableMetaDropInPlace + 'static, X> stable_deref_trait::StableDeref
366    for VRc<VTable, X>
367{
368}
369
370/// Safety: The data VRc manages is held by `VRcInner`, and a clone of a VRc merely clones the pointer
371/// *to* the `VRcInner`.
372unsafe impl<VTable: VTableMetaDropInPlace + 'static, X> stable_deref_trait::CloneStableDeref
373    for VRc<VTable, X>
374{
375}
376
377/// VRcMapped allows bundling a VRc of a type along with a reference to an object that's
378/// reachable through the data the VRc owns and that satisfies the requirements of a Pin.
379/// VRCMapped is constructed using [`VRc::map`] and, like VRc, has a weak counterpart, [`VWeakMapped`].
380pub struct VRcMapped<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> {
381    parent_strong: VRc<VTable, Dyn>,
382    object: *const MappedType,
383}
384
385impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> Clone
386    for VRcMapped<VTable, MappedType>
387{
388    fn clone(&self) -> Self {
389        Self { parent_strong: self.parent_strong.clone(), object: self.object }
390    }
391}
392
393impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> VRcMapped<VTable, MappedType> {
394    /// Returns a new [`VWeakMapped`] that points to this instance and can be upgraded back to
395    /// a [`Self`] as long as a `VRc`/`VMapped` exists.
396    pub fn downgrade(this: &Self) -> VWeakMapped<VTable, MappedType> {
397        VWeakMapped { parent_weak: VRc::downgrade(&this.parent_strong), object: this.object }
398    }
399
400    /// Create a Pinned reference to the mapped type.
401    ///
402    /// This is safe because the map function returns a pinned reference.
403    pub fn as_pin_ref(&self) -> Pin<&MappedType> {
404        unsafe { Pin::new_unchecked(self) }
405    }
406
407    /// This function allows safely holding a reference to a field inside the `VRcMapped`. In order to accomplish
408    /// that, you need to provide a mapping function `map_fn` in which you need to provide and return a
409    /// pinned reference to the object you would like to map. The returned `VRcMapped` allows obtaining
410    /// that pinned reference again using [`VRcMapped::as_pin_ref`].
411    ///
412    /// See also [`VRc::map`]
413    pub fn map<ReMappedType: ?Sized>(
414        this: Self,
415        map_fn: impl for<'r> FnOnce(Pin<&'r MappedType>) -> Pin<&'r ReMappedType>,
416    ) -> VRcMapped<VTable, ReMappedType> {
417        VRcMapped {
418            parent_strong: this.parent_strong.clone(),
419            object: map_fn(this.as_pin_ref()).get_ref(),
420        }
421    }
422
423    /// Returns a strong reference to the object that the mapping originates
424    /// from.
425    pub fn origin(this: &Self) -> VRc<VTable> {
426        this.parent_strong.clone()
427    }
428}
429
430impl<VTable: VTableMetaDropInPlace + 'static, MappedType> VRcMapped<VTable, MappedType> {
431    /// A [`Dyn`] weak reference to the mapped object, keeping only a raw pointer to it.
432    /// Private: it is only ever paired with a matching function inside [`ErasedWeakFn`],
433    /// so the pointer is never handed to a function expecting a different type.
434    fn downgrade_dyn(this: &Self) -> VWeakMapped<VTable, Dyn> {
435        VWeakMapped {
436            parent_weak: VRc::downgrade(&this.parent_strong),
437            // Cast through raw pointers so the pointer keeps its provenance over all of
438            // `MappedType`, without forming an intermediate reference.
439            object: this.object as *const Dyn,
440        }
441    }
442
443    /// Bundle a weak reference to this object with `f`, so that `f` can later be run on the
444    /// object (while it is still alive) through the returned [`ErasedWeakFn`], without that
445    /// handle being generic over `MappedType`. Pass `Arg = ()` for a function that takes no
446    /// meaningful argument.
447    pub fn downgrade_erased_fn<Arg: ?Sized, Ret>(
448        this: &Self,
449        f: fn(core::pin::Pin<&MappedType>, &Arg) -> Ret,
450    ) -> ErasedWeakFn<VTable, Ret, Arg> {
451        ErasedWeakFn {
452            weak: Self::downgrade_dyn(this),
453            // SAFETY: `weak` yields a pointer to this `MappedType`, and `Pin<&MappedType>`
454            // (repr(transparent) over `&MappedType`, a thin pointer for the sized
455            // `MappedType`) is ABI-compatible with `*const ()` as a function parameter; the
456            // `&Arg` parameter is unchanged. So calling the transmuted pointer with that
457            // pointer is defined.
458            call: unsafe {
459                core::mem::transmute::<
460                    fn(core::pin::Pin<&MappedType>, &Arg) -> Ret,
461                    fn(*const (), &Arg) -> Ret,
462                >(f)
463            },
464        }
465    }
466}
467
468impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> Deref
469    for VRcMapped<VTable, MappedType>
470{
471    type Target = MappedType;
472    fn deref(&self) -> &Self::Target {
473        // Safety: self.object was mapped from self.parent_strong, which the VRc
474        // keeps alive *and* pinned.
475        unsafe { &*self.object }
476    }
477}
478
479/// VWeakMapped allows bundling a VWeak with a reference to an object that's reachable
480/// from the object a successfully upgraded VWeak points to. VWeakMapped's API consists
481/// only of the ability to create clones and to attempt upgrading back to a [`VRcMapped`].
482pub struct VWeakMapped<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> {
483    parent_weak: VWeak<VTable, Dyn>,
484    object: *const MappedType,
485}
486
487impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> VWeakMapped<VTable, MappedType> {
488    /// Returns a new `VRcMapped` if some other instance still holds a strong reference to the owned
489    /// object. Otherwise, returns None.
490    pub fn upgrade(&self) -> Option<VRcMapped<VTable, MappedType>> {
491        self.parent_weak
492            .upgrade()
493            .map(|parent| VRcMapped { parent_strong: parent, object: self.object })
494    }
495}
496
497impl<VTable: VTableMetaDropInPlace + 'static, MappedType: ?Sized> Clone
498    for VWeakMapped<VTable, MappedType>
499{
500    fn clone(&self) -> Self {
501        Self { parent_weak: self.parent_weak.clone(), object: self.object }
502    }
503}
504
505/// Runs a stored function on a mapped object as long as that object is still alive.
506///
507/// Create one from a [`VRcMapped`] and a function with [`VRcMapped::downgrade_erased_fn`],
508/// then call [`Self::upgrade_and_call`] whenever you want to run the function on the object,
509/// passing the argument it expects (`&()` if it takes none). Once every strong reference to
510/// the object is gone, `upgrade_and_call` returns `None` and the function is not run.
511///
512/// The type is only parameterized by the argument and return types, not by the type of the
513/// mapped object, so code that stores an `ErasedWeakFn` is compiled once no matter how many
514/// mapped types it is used with.
515pub struct ErasedWeakFn<VTable: VTableMetaDropInPlace + 'static, Ret, Arg: ?Sized = ()> {
516    weak: VWeakMapped<VTable, Dyn>,
517    call: fn(*const (), &Arg) -> Ret,
518}
519
520impl<VTable: VTableMetaDropInPlace + 'static, Ret, Arg: ?Sized> ErasedWeakFn<VTable, Ret, Arg> {
521    /// Run the stored function on the object with `arg`, or return `None` if the object has
522    /// been dropped.
523    pub fn upgrade_and_call(&self, arg: &Arg) -> Option<Ret> {
524        let strong = self.weak.upgrade()?;
525        // `strong` keeps the object alive and pinned for the duration of the call.
526        Some((self.call)(strong.object as *const (), arg))
527    }
528}
529
530impl<VTable: VTableMetaDropInPlace + 'static, MappedType> Default
531    for VWeakMapped<VTable, MappedType>
532{
533    fn default() -> Self {
534        Self { parent_weak: VWeak::default(), object: core::ptr::null() }
535    }
536}