Skip to main content

tvm_ffi/
object.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19use std::ops::{Deref, DerefMut};
20use std::sync::atomic::AtomicU64;
21
22use crate::derive::ObjectRef;
23use crate::type_traits::AnyCompatible;
24pub use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
25/// Object related ABI handling
26use tvm_ffi_sys::{
27    TVMFFIAny, TVMFFIGetCustomAllocator, TVMFFIGetTypeInfo, TVMFFIObject,
28    COMBINED_REF_COUNT_BOTH_ONE,
29};
30
31/// Object type is by default the TVMFFIObject
32#[repr(C)]
33pub struct Object {
34    /// example implementation of the object
35    header: TVMFFIObject,
36}
37
38/// Arc-like wrapper for Object that allows shared ownership
39///
40/// \tparam T The type of the object to be wrapped
41#[repr(C)]
42pub struct ObjectArc<T: ObjectCore> {
43    ptr: std::ptr::NonNull<T>,
44    _phantom: std::marker::PhantomData<T>,
45}
46
47unsafe impl<T: Send + Sync + ObjectCore> Send for ObjectArc<T> {}
48unsafe impl<T: Send + Sync + ObjectCore> Sync for ObjectArc<T> {}
49
50/// Traits that can be used to check if a type is an object
51///
52/// This trait is unsafe because it is used to access the object header
53/// and the object header is unsafe to access
54pub unsafe trait ObjectCore: Sized + 'static {
55    /// the type key of the object
56    const TYPE_KEY: &'static str;
57    /// Depth of this type in the object inheritance tree.
58    ///
59    /// The root [`Object`] has depth zero, and every registered subtype has
60    /// depth one greater than its parent. This value must be non-negative and
61    /// agree with the runtime type table entry for `Self`.
62    const TYPE_DEPTH: i32;
63    /// Whether every instance of this type has exactly `Self::type_index()`.
64    ///
65    /// A final type has no separately registered object-system subtype.
66    #[doc(hidden)]
67    const TYPE_FINAL: bool = false;
68    // return the type index of the object
69    fn type_index() -> i32;
70    /// Return the object header
71    /// This function is implemented as a static function so
72    ///
73    /// # Arguments
74    /// * `this` - The object to get the header
75    ///
76    /// # Returns
77    /// * `&mut TVMFFIObject` - The object header
78    /// \return The object header
79    unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject;
80}
81
82/// Traits for objects with extra items that follows the object
83///
84/// This extra trait can be helpful to implement array types and string types
85pub unsafe trait ObjectCoreWithExtraItems: ObjectCore {
86    /// type of extra items storage that follows the object
87    type ExtraItem;
88    /// Return the number of extra items
89    fn extra_items_count(this: &Self) -> usize;
90    /// Return the extra items data pointer
91    unsafe fn extra_items(this: &Self) -> &[Self::ExtraItem] {
92        let extra_items_ptr = (this as *const Self as *const u8).add(std::mem::size_of::<Self>());
93        std::slice::from_raw_parts(
94            extra_items_ptr as *const Self::ExtraItem,
95            Self::extra_items_count(this),
96        )
97    }
98    /// Return the extra items data pointer
99    unsafe fn extra_items_mut(this: &mut Self) -> &mut [Self::ExtraItem] {
100        let extra_items_ptr = (this as *mut Self as *mut u8).add(std::mem::size_of::<Self>());
101        std::slice::from_raw_parts_mut(
102            extra_items_ptr as *mut Self::ExtraItem,
103            Self::extra_items_count(this),
104        )
105    }
106}
107
108/// Traits to specify core operations of ObjectRef
109///
110/// used by the ffi Any system and not user facing
111///
112/// We mark as unsafe since it moves out the internal of the ObjectRef
113///
114/// # Safety
115///
116/// `data`, `into_data`, and `from_data` must preserve the same object
117/// allocation and form an ownership-preserving round trip. That allocation must
118/// start with a valid `TVMFFIObject` header whose registered object-range
119/// runtime type index correctly describes its layout and inheritance.
120///
121/// When `Self` also implements [`AnyCompatible`], `copy_to_any_view` must
122/// produce a non-owning view, while `move_to_any` must transfer ownership of
123/// the same object pointer and dynamic type index. `move_from_any_after_check`
124/// must be able to reclaim that owned representation exactly once, and a true
125/// `check_any_strict` result must guarantee that both after-check constructors
126/// are valid for it.
127pub unsafe trait ObjectRefCore: Sized + Clone {
128    type ContainerType: ObjectCore;
129    fn data(this: &Self) -> &ObjectArc<Self::ContainerType>;
130    fn into_data(this: Self) -> ObjectArc<Self::ContainerType>;
131    fn from_data(data: ObjectArc<Self::ContainerType>) -> Self;
132}
133
134/// Check whether a runtime type index refers to `Target` or one of its
135/// subtypes.
136///
137/// The subtype relation lives in the process-wide type table maintained by the
138/// tvm-ffi library: every registered type records its depth in the single
139/// inheritance tree together with the chain of its ancestors. The check is
140/// O(1) — if `target` really is an ancestor, it must appear in the candidate's
141/// ancestor array exactly at `target`'s depth.
142///
143/// This is a hidden support function for derive-generated object checks. Object
144/// indices in the registered range must refer to entries in the runtime type
145/// table.
146#[doc(hidden)]
147#[inline(always)]
148pub fn is_instance_of<Target: ObjectCore>(object_type_index: i32) -> bool {
149    let target_type_index = Target::type_index();
150    if object_type_index == target_type_index {
151        return true;
152    }
153    let object_begin = TypeIndex::kTVMFFIStaticObjectBegin as i32;
154    // Only object types participate in the type hierarchy.
155    if object_type_index < object_begin || target_type_index < object_begin {
156        return false;
157    }
158    // Parent indices are always smaller than their descendants.
159    if object_type_index < target_type_index {
160        return false;
161    }
162    unsafe {
163        let object_info = TVMFFIGetTypeInfo(object_type_index);
164        if object_info.is_null() {
165            return false;
166        }
167        let target_depth = Target::TYPE_DEPTH;
168        if (*object_info).type_depth <= target_depth {
169            return false;
170        }
171        let ancestor = *(*object_info).type_acenstors.add(target_depth as usize);
172        !ancestor.is_null() && (*ancestor).type_index == target_type_index
173    }
174}
175
176/// Runtime-checked casting between arbitrary `ObjectRef` types.
177///
178/// The cast uses the target's [`AnyCompatible::check_any_strict`] implementation,
179/// mirroring the semantics of `ObjectRef::as<T>` in C++. This supports both
180/// object hierarchies and parameterized object containers.
181///
182/// This trait is blanket-implemented for every [`ObjectRefCore`] type that is
183/// also [`AnyCompatible`].
184pub trait ObjectRefCast: ObjectRefCore + AnyCompatible {
185    /// Consume `self` and rewrap the underlying object as `B` without copying.
186    #[inline(always)]
187    fn try_cast<B>(self) -> crate::error::Result<B>
188    where
189        B: ObjectRefCore + AnyCompatible,
190    {
191        let mut any_data = TVMFFIAny::new();
192        unsafe {
193            // Keep ownership in `self` while the target check runs. This makes
194            // the failure and panic paths unwind normally instead of stranding
195            // an owned object inside a raw TVMFFIAny.
196            Self::copy_to_any_view(&self, &mut any_data);
197            debug_assert!(any_data.type_index >= TypeIndex::kTVMFFIStaticObjectBegin as i32);
198            // SAFETY: ObjectRefCore's contract requires its AnyCompatible
199            // representation to contain a valid object-range type index.
200            std::hint::assert_unchecked(
201                any_data.type_index >= TypeIndex::kTVMFFIStaticObjectBegin as i32,
202            );
203
204            if B::check_any_strict(&any_data) {
205                // Transfer ownership only after the borrowed representation has
206                // passed the target's complete hierarchy/container check.
207                Self::move_to_any(self, &mut any_data);
208                Ok(B::move_from_any_after_check(&mut any_data))
209            } else {
210                let msg = format!(
211                    "Cannot convert from type `{}` to `{}`",
212                    B::get_mismatch_type_info(&any_data),
213                    B::type_str()
214                );
215                Err(crate::error::Error::new(crate::error::TYPE_ERROR, &msg, ""))
216            }
217        }
218    }
219}
220
221impl<T: ObjectRefCore + AnyCompatible> ObjectRefCast for T {}
222
223/// Base class for ObjectRef
224///
225/// This class is used to store the data of the ObjectRef
226#[repr(C)]
227#[derive(ObjectRef, Clone)]
228pub struct ObjectRef {
229    data: ObjectArc<Object>,
230}
231
232/// Unsafe operations on object
233#[doc(hidden)]
234pub mod unsafe_ {
235    use tvm_ffi_sys::{
236        COMBINED_REF_COUNT_BOTH_ONE, COMBINED_REF_COUNT_MASK_U32, COMBINED_REF_COUNT_STRONG_ONE,
237        COMBINED_REF_COUNT_WEAK_ONE,
238    };
239
240    use std::ffi::c_void;
241    use std::sync::atomic::{fence, Ordering};
242    use tvm_ffi_sys::{TVMFFIObject, TVMFFIObjectAllocHeader};
243    use tvm_ffi_sys::TVMFFIObjectDeleterFlagBitMask::{
244        kTVMFFIObjectDeleterFlagBitMaskBoth, kTVMFFIObjectDeleterFlagBitMaskStrong,
245        kTVMFFIObjectDeleterFlagBitMaskWeak,
246    };
247
248    /// Increase the strong reference count of the object
249    ///
250    /// This function is same as TVMFFIObjectIncRef but implemented natively in Rust
251    ///
252    /// # Arguments
253    /// * `obj` - The object to increase the reference count
254    #[inline]
255    pub unsafe fn inc_ref(handle: *mut TVMFFIObject) {
256        let obj = &mut *handle;
257        obj.combined_ref_count.fetch_add(1, Ordering::Relaxed);
258    }
259
260    /// Decrease the strong reference count of the object
261    ///
262    /// This function is same as TVMFFIObjectDecRef but implemented natively in Rust
263    ///
264    /// # Arguments
265    /// * `obj` - The object to decrease the reference count
266    #[inline]
267    pub(crate) unsafe fn dec_ref(handle: *mut TVMFFIObject) {
268        let obj = &mut *handle;
269        let old_combined_count = obj
270            .combined_ref_count
271            .fetch_sub(COMBINED_REF_COUNT_STRONG_ONE, Ordering::Relaxed);
272        if old_combined_count == COMBINED_REF_COUNT_BOTH_ONE {
273            if let Some(deleter) = obj.deleter {
274                fence(Ordering::Acquire);
275                deleter(
276                    obj as *mut TVMFFIObject as *mut c_void,
277                    kTVMFFIObjectDeleterFlagBitMaskBoth as i32,
278                );
279            }
280        } else if (old_combined_count & COMBINED_REF_COUNT_MASK_U32)
281            == COMBINED_REF_COUNT_STRONG_ONE
282        {
283            // slow path, there is still a weak reference left
284            // need to run two phase decrement
285            fence(Ordering::Acquire);
286            if let Some(deleter) = obj.deleter {
287                deleter(
288                    obj as *mut TVMFFIObject as *mut c_void,
289                    kTVMFFIObjectDeleterFlagBitMaskStrong as i32,
290                );
291            }
292            let old_weak_count = obj
293                .combined_ref_count
294                .fetch_sub(COMBINED_REF_COUNT_WEAK_ONE, Ordering::Release);
295            if old_weak_count == COMBINED_REF_COUNT_WEAK_ONE {
296                fence(Ordering::Acquire);
297                if let Some(deleter) = obj.deleter {
298                    deleter(
299                        obj as *mut TVMFFIObject as *mut c_void,
300                        kTVMFFIObjectDeleterFlagBitMaskWeak as i32,
301                    );
302                }
303            }
304        }
305    }
306
307    #[inline]
308    pub(crate) unsafe fn strong_count(handle: *mut TVMFFIObject) -> usize {
309        let obj = &mut *handle;
310        (obj.combined_ref_count.load(Ordering::Relaxed) & COMBINED_REF_COUNT_MASK_U32) as usize
311    }
312
313    #[inline]
314    pub(crate) unsafe fn weak_count(handle: *mut TVMFFIObject) -> usize {
315        let obj = &mut *handle;
316        (obj.combined_ref_count.load(Ordering::Relaxed) >> 32) as usize
317    }
318
319    /// Generic object deleter for objects allocated through the registered
320    /// `TVMFFICustomAllocator`.
321    pub(crate) unsafe extern "C" fn object_deleter_for_new<T>(ptr: *mut c_void, flags: i32)
322    where
323        T: super::ObjectCore,
324    {
325        let obj = ptr as *mut T;
326        if flags & kTVMFFIObjectDeleterFlagBitMaskStrong as i32 != 0 {
327            std::ptr::drop_in_place(obj);
328        }
329        if flags & kTVMFFIObjectDeleterFlagBitMaskWeak as i32 != 0 {
330            let header_ptr = (ptr as *mut u8)
331                .sub(std::mem::size_of::<TVMFFIObjectAllocHeader>())
332                as *mut TVMFFIObjectAllocHeader;
333            let delete_space = (*header_ptr)
334                .delete_space
335                .expect("TVMFFIObjectAllocHeader::delete_space must be set by the allocator");
336            delete_space(ptr);
337        }
338    }
339}
340
341//---------------------
342// Object
343//---------------------
344
345impl Object {
346    pub fn new() -> Self {
347        Self {
348            header: TVMFFIObject::new(),
349        }
350    }
351}
352
353unsafe impl ObjectCore for Object {
354    const TYPE_KEY: &'static str = "ffi.Object";
355    const TYPE_DEPTH: i32 = 0;
356    #[inline]
357    fn type_index() -> i32 {
358        TypeIndex::kTVMFFIStaticObjectBegin as i32
359    }
360    #[inline]
361    unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject {
362        &mut this.header
363    }
364}
365
366//---------------------
367// ObjectArc
368//---------------------
369
370/// Allocate via the process-wide `TVMFFICustomAllocator`.
371unsafe fn allocate_via_registry(size: usize, alignment: usize, type_index: i32) -> *mut u8 {
372    let alloc = TVMFFIGetCustomAllocator();
373    let allocate_fn = (*alloc)
374        .allocate
375        .expect("TVMFFICustomAllocator::allocate must be set");
376    allocate_fn(size, alignment, type_index, (*alloc).context) as *mut u8
377}
378
379impl<T: ObjectCore> ObjectArc<T> {
380    pub fn new(data: T) -> Self {
381        unsafe {
382            let raw_data_ptr = allocate_via_registry(
383                std::mem::size_of::<T>(),
384                std::mem::align_of::<T>(),
385                T::type_index(),
386            );
387            let ptr = raw_data_ptr as *mut T;
388            std::ptr::write(ptr, data);
389            // now override the header directly
390            std::ptr::write(
391                ptr as *mut TVMFFIObject,
392                TVMFFIObject {
393                    combined_ref_count: AtomicU64::new(COMBINED_REF_COUNT_BOTH_ONE),
394                    type_index: T::type_index(),
395                    __padding: 0,
396                    deleter: Some(unsafe_::object_deleter_for_new::<T>),
397                },
398            );
399            // move into the object arc ptr
400            Self {
401                ptr: std::ptr::NonNull::new_unchecked(ptr as *mut T),
402                _phantom: std::marker::PhantomData,
403            }
404        }
405    }
406    pub fn new_with_extra_items<U>(data: T) -> Self
407    where
408        T: ObjectCoreWithExtraItems<ExtraItem = U>,
409    {
410        unsafe {
411            // ensure strict alignment requirements
412            // so we can have { T, U*extra_items } layout
413            assert_eq!(std::mem::align_of::<T>() % std::mem::align_of::<U>(), 0);
414            assert_eq!(std::mem::size_of::<T>() % std::mem::align_of::<U>(), 0);
415            let extra_items_count = T::extra_items_count(&data);
416            let total_size = std::mem::size_of::<T>() + extra_items_count * std::mem::size_of::<U>();
417            let raw_data_ptr =
418                allocate_via_registry(total_size, std::mem::align_of::<T>(), T::type_index());
419            let ptr = raw_data_ptr as *mut T;
420            std::ptr::write(ptr, data);
421            // now override the header directly
422            std::ptr::write(
423                ptr as *mut TVMFFIObject,
424                TVMFFIObject {
425                    combined_ref_count: AtomicU64::new(COMBINED_REF_COUNT_BOTH_ONE),
426                    type_index: T::type_index(),
427                    __padding: 0,
428                    deleter: Some(unsafe_::object_deleter_for_new::<T>),
429                },
430            );
431            // move into the object arc ptr
432            Self {
433                ptr: std::ptr::NonNull::new_unchecked(ptr as *mut T),
434                _phantom: std::marker::PhantomData,
435            }
436        }
437    }
438
439    /// Move a previously allocated object into the ObjectArc
440    ///
441    /// # Arguments
442    /// * `ptr` - The raw pointer to move into the ObjectArc
443    ///
444    /// # Returns
445    /// * `ObjectArc<T>` - The ObjectArc
446    /// \return The ObjectArc
447    #[inline]
448    pub unsafe fn from_raw(ptr: *const T) -> Self {
449        Self {
450            ptr: std::ptr::NonNull::new_unchecked(ptr as *mut T),
451            _phantom: std::marker::PhantomData,
452        }
453    }
454
455    /// Move the ObjectArc into a raw pointer
456    ///
457    /// # Arguments
458    /// * `this` - The ObjectArc to move into a raw pointer
459    ///
460    /// # Returns
461    /// * `*const T` - The raw pointer
462    #[inline]
463    pub unsafe fn into_raw(this: Self) -> *const T {
464        let droped_this = std::mem::ManuallyDrop::new(this);
465        droped_this.ptr.as_ptr() as *const T
466    }
467
468    /// Get the raw pointer from the ObjectArc
469    ///
470    /// Caller should view this as a non-owning reference
471    ///
472    /// # Arguments
473    /// * `this` - The ObjectArc to get the raw pointer
474    ///
475    /// # Returns
476    /// * `*const T` - The raw pointer
477    /// \return The raw pointer
478    #[inline]
479    pub unsafe fn as_raw(this: &Self) -> *const T {
480        this.ptr.as_ptr() as *const T
481    }
482
483    /// Get the raw mutable pointer from the ObjectArc
484    ///
485    /// Caller should view this as a non-owning reference
486    ///
487    /// # Arguments
488    /// * `this` - The ObjectArc to get the raw pointer
489    ///
490    /// # Returns
491    /// * `*mut T` - The raw pointer
492    #[inline]
493    pub unsafe fn as_raw_mut(this: &mut Self) -> *mut T {
494        this.ptr.as_mut()
495    }
496
497    /// Get the strong reference count of the ObjectArc
498    ///
499    /// # Arguments
500    /// * `this` - The ObjectArc to get the strong reference count
501    ///
502    /// # Returns
503    /// * `usize` - The strong reference count
504    #[inline]
505    pub fn strong_count(this: &Self) -> usize {
506        unsafe {
507            unsafe_::strong_count(this.ptr.as_ref() as *const T as *mut T as *mut TVMFFIObject)
508        }
509    }
510
511    /// Get the weak reference count of the ObjectArc
512    ///
513    /// # Arguments
514    /// * `this` - The ObjectArc to get the weak reference count
515    ///
516    /// # Returns
517    /// * `usize` - The weak reference count
518    #[inline]
519    pub fn weak_count(this: &Self) -> usize {
520        unsafe { unsafe_::weak_count(this.ptr.as_ref() as *const T as *mut T as *mut TVMFFIObject) }
521    }
522}
523
524// implement Deref for ObjectArc
525impl<T: ObjectCore> Deref for ObjectArc<T> {
526    type Target = T;
527    #[inline]
528    fn deref(&self) -> &Self::Target {
529        unsafe { self.ptr.as_ref() }
530    }
531}
532
533// implement DerefMut for ObjectArc
534impl<T: ObjectCore> DerefMut for ObjectArc<T> {
535    #[inline]
536    fn deref_mut(&mut self) -> &mut Self::Target {
537        unsafe { self.ptr.as_mut() }
538    }
539}
540
541// implement Drop for ObjectArc
542impl<T: ObjectCore> Drop for ObjectArc<T> {
543    fn drop(&mut self) {
544        unsafe { unsafe_::dec_ref(self.ptr.as_mut() as *mut T as *mut TVMFFIObject) }
545    }
546}
547
548// implement Clone for ObjectArc
549impl<T: ObjectCore> Clone for ObjectArc<T> {
550    #[inline]
551    fn clone(&self) -> Self {
552        unsafe { unsafe_::inc_ref(self.ptr.as_ref() as *const T as *mut T as *mut TVMFFIObject) }
553        Self {
554            ptr: self.ptr,
555            _phantom: std::marker::PhantomData,
556        }
557    }
558}