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 cfff30b  [CYTHON] Fix potential flaky issue in dispatcher (#63)
cfff30b is described below

commit cfff30bd59e401e426ed6f3a3de5f7280ce5aed0
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Sep 26 21:35:49 2025 -0400

    [CYTHON] Fix potential flaky issue in dispatcher (#63)
    
    The C++ dispatcher dispatches the argument passing by TYPE(obj) pointer
    which
    is non-owning. This means that there is the following edge case:
    - type A is registered through dispatcher
    - type A gets garbage collected (because it is a local type)
    - type B is created and uses the same memory address as type A
    
    Then when we pass in type B, it will mistakenly use the dispatch
    function for type A
    
    To prevent this, we keep alive the types that are registered through
    dispatcher
    by appending them to _DISPATCH_TYPE_KEEP_ALIVE
    
    NOTE that the total number of types that are registered through
    dispatcher is expected
    to be limited in practice so we can afford to keep them alive
---
 python/tvm_ffi/cython/function.pxi | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/python/tvm_ffi/cython/function.pxi 
b/python/tvm_ffi/cython/function.pxi
index ff79cff..095e3d6 100644
--- a/python/tvm_ffi/cython/function.pxi
+++ b/python/tvm_ffi/cython/function.pxi
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 import ctypes
+import threading
 import os
 from numbers import Real, Integral
 
@@ -501,6 +502,10 @@ cdef int TVMFFIPyArgSetterDTypeFromNumpy_(
     out.v_dtype = NUMPY_DTYPE_TO_DTYPE[py_obj]
     return 0
 
+
+cdef _DISPATCH_TYPE_KEEP_ALIVE = set()
+cdef _DISPATCH_TYPE_KEEP_ALIVE_LOCK = threading.Lock()
+
 cdef int TVMFFIPyArgSetterFactory_(PyObject* value, TVMFFIPyArgSetter* out) 
except -1:
     """
     Factory function that creates an argument setter for a given Python 
argument type.
@@ -510,6 +515,24 @@ cdef int TVMFFIPyArgSetterFactory_(PyObject* value, 
TVMFFIPyArgSetter* out) exce
     # priortize native types over external types
     cdef object arg = <object>value
     cdef long long temp_ptr
+
+    # The C++ dispatcher dispatches the argument passing by TYPE(obj) pointer 
which
+    # is non-owning. This means that there is the following edge case:
+    # - type A is registered through dispatcher
+    # - type A gets garbage collected (because it is a local type)
+    # - type B is created and uses the same memory address as type A
+    #
+    # Then when we pass in type B, it will mistakenly use the dispatch 
function for type A
+    #
+    # To prevent this, we keep alive the types that are registered through 
dispatcher
+    # by adding them to _DISPATCH_TYPE_KEEP_ALIVE
+    #
+    # NOTE that the total number of types that are registered through 
dispatcher is expected
+    # to be limited in practice so we can afford to keep them alive
+    # Lock is used to ensure thread-safety for future thread-free python case
+    with _DISPATCH_TYPE_KEEP_ALIVE_LOCK:
+        _DISPATCH_TYPE_KEEP_ALIVE.add(type(arg))
+
     if arg is None:
         out.func = TVMFFIPyArgSetterNone_
         return 0

Reply via email to