This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new 0cfb9a94 [FFI] Make Object::unique() require no weak references (#731)
0cfb9a94 is described below

commit 0cfb9a945b0a76be870bbda6d0bb41cf4cde11c7
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Sep 2 15:00:35 2026 -0400

    [FFI] Make Object::unique() require no weak references (#731)
    
    ## Summary
    
    - Make `Object::unique()` require one strong reference and no external
    weak references by comparing the packed count with
    `kCombinedRefCountBothOne`.
    - Delegate `ObjectPtr<T>::unique()` to the object-level implementation
    while leaving strong-only `use_count()` semantics unchanged.
    - Add focused coverage for uniqueness while a `WeakObjectPtr` is live.
    
    ## Rationale
    
    `unique()` gates copy-on-write and in-place mutation. A live weak
    pointer can still upgrade while the object has a strong owner, so
    strong-only uniqueness is insufficient for safely choosing in-place
    mutation. Comparing the combined counter also observes both halves in
    one atomic load.
    
    ## Compatibility
    
    Out-of-tree users that retain weak references may now copy instead of
    mutating in place. Current production call sites without weak references
    retain their existing behavior.
---
 include/tvm/ffi/object.h | 17 ++++++++++++++---
 tests/cpp/test_object.cc | 18 ++++++++++++++++++
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/include/tvm/ffi/object.h b/include/tvm/ffi/object.h
index 7a4007f7..e66d0dee 100644
--- a/include/tvm/ffi/object.h
+++ b/include/tvm/ffi/object.h
@@ -179,9 +179,20 @@ class Object {
   }
 
   /*!
-   * \return Whether the object.use_count() == 1.
+   * \return Whether the object has one strong reference and no external weak 
references.
+   * \note Checking both weak and strong count is needed to ensure correctness 
in decisions such as
+   *       copy-on-write in multi-threaded setting.
    */
-  bool unique() const { return use_count() == 1; }
+  bool unique() const {
+#ifdef _MSC_VER
+    return (reinterpret_cast<const volatile uint64_t*>(
+               &header_.combined_ref_count))[0] ==  // NOLINT(*)
+           kCombinedRefCountBothOne;
+#else
+    return __atomic_load_n(&(header_.combined_ref_count), __ATOMIC_RELAXED) ==
+           kCombinedRefCountBothOne;
+#endif
+  }
 
   /*!
    * \return The usage count of the cell.
@@ -496,7 +507,7 @@ class ObjectPtr {
   /*! \return The use count of the ptr, for debug purposes */
   int use_count() const { return data_ != nullptr ? data_->use_count() : 0; }
   /*! \return whether the reference is unique */
-  bool unique() const { return data_ != nullptr && data_->use_count() == 1; }
+  bool unique() const { return data_ != nullptr && data_->unique(); }
   /*! \return Whether two ObjectPtr do not equal each other */
   bool operator==(const ObjectPtr<T>& other) const { return data_ == 
other.data_; }
   /*! \return Whether two ObjectPtr equals each other */
diff --git a/tests/cpp/test_object.cc b/tests/cpp/test_object.cc
index 0c4b066a..a34efd98 100644
--- a/tests/cpp/test_object.cc
+++ b/tests/cpp/test_object.cc
@@ -353,6 +353,24 @@ TEST(Object, WeakObjectPtr) {
   EXPECT_TRUE(expired_lock == nullptr);
 }
 
+TEST(Object, UniqueWithWeakObjectPtr) {
+  ObjectPtr<TIntObj> strong_ptr = make_object<TIntObj>(42);
+
+  EXPECT_TRUE(strong_ptr.unique());
+  EXPECT_TRUE(strong_ptr->unique());
+  EXPECT_EQ(strong_ptr.use_count(), 1);
+
+  WeakObjectPtr<TIntObj> weak_ptr(strong_ptr);
+  EXPECT_FALSE(strong_ptr.unique());
+  EXPECT_FALSE(strong_ptr->unique());
+  EXPECT_EQ(strong_ptr.use_count(), 1);
+
+  weak_ptr.reset();
+  EXPECT_TRUE(strong_ptr.unique());
+  EXPECT_TRUE(strong_ptr->unique());
+  EXPECT_EQ(strong_ptr.use_count(), 1);
+}
+
 TEST(Object, WeakObjectPtrAssignment) {
   // Test copy construction
   ObjectPtr<TIntObj> new_strong = make_object<TIntObj>(100);

Reply via email to