Summary
Audit of all public from_handle / from_* methods in cuda.core reveals several inconsistencies in how lifetime management is exposed to users. These should be resolved before the 1.0 stable release.
Inventory
| Class |
Method |
Ownership Model |
Stream |
from_handle(handle) |
Always non-owning (borrowed) |
Kernel |
from_handle(handle, mod=) |
Non-owning ref; optional _keepalive holds ObjectCode |
Buffer |
from_handle(ptr, size, mr=, owner=) |
3 modes: non-owning / mr-deallocates / owner-ref |
Event |
from_ipc_descriptor(...) |
Owns imported event |
Buffer |
from_ipc_descriptor(...) |
Owns imported buffer |
GraphicsResource |
from_gl_buffer(...) / from_gl_image(...) |
Owns GL registration |
DeviceMemoryResource |
from_allocation_handle(...) / from_registry(uuid) |
Owns imported pool / retrieves existing |
PinnedMemoryResource |
from_allocation_handle(...) / from_registry(uuid) |
Owns imported pool / retrieves existing |
ObjectCode |
from_cubin/ptx/ltoir/fatbin/object/library() |
Owns module data |
StridedMemoryView |
from_dlpack/cai/array_interface/buffer/any_interface() |
Always non-owning (view) |
Inconsistencies
1. Stream.from_handle cannot opt into keeping an owner alive
Buffer.from_handle accepts owner= to hold a Python reference and prevent GC of the foreign allocator.
Kernel.from_handle accepts mod= to hold a reference and prevent GC of the parent ObjectCode/library.
Stream.from_handle has no equivalent parameter. It creates a borrowed stream with no way for the user to tie lifetime to an owner object.
The internal machinery already supports this — create_stream_handle_with_owner() in C++ accepts a Python owner and does Py_INCREF/DECREF. But from_handle() only passes the ephemeral _stream_holder wrapper as the owner, not the user's actual owner object.
Suggestion: Add an owner parameter to Stream.from_handle, consistent with Buffer.from_handle.
2. Event has no public from_handle()
Stream, Kernel, and Buffer all expose public from_handle() for wrapping foreign CUDA handles.
Event only has from_ipc_descriptor(). There is an internal _from_handle() (cdef), but no public way to wrap a foreign CUevent.
Suggestion: Add Event.from_handle(handle) for symmetry, with the same borrowed/non-owning semantics.
3. Kernel.from_handle docstring doesn't clearly document ownership
Stream.from_handle docstring explicitly says: "Stream lifetime is not managed, foreign object must remain alive while this stream is active."
Buffer.from_handle docstring explicitly documents all three ownership modes and when the pointer is/isn't freed.
Kernel.from_handle docstring mentions mod "provides library lifetime for foreign kernels" but doesn't clearly state that the kernel handle itself is non-owning and won't be destroyed on close/GC.
Suggestion: Improve the Kernel.from_handle docstring to match the clarity of the other two.
Additional observation
All from_handle() methods use @staticmethod, while from_ipc_descriptor() / from_gl_*() / from_allocation_handle() use @classmethod. This may be intentional (the latter group uses cls for subclassing support), but worth confirming as a deliberate convention and documenting if so.
-- Leo's bot
Summary
Audit of all public
from_handle/from_*methods incuda.corereveals several inconsistencies in how lifetime management is exposed to users. These should be resolved before the 1.0 stable release.Inventory
Streamfrom_handle(handle)Kernelfrom_handle(handle, mod=)_keepaliveholdsObjectCodeBufferfrom_handle(ptr, size, mr=, owner=)Eventfrom_ipc_descriptor(...)Bufferfrom_ipc_descriptor(...)GraphicsResourcefrom_gl_buffer(...)/from_gl_image(...)DeviceMemoryResourcefrom_allocation_handle(...)/from_registry(uuid)PinnedMemoryResourcefrom_allocation_handle(...)/from_registry(uuid)ObjectCodefrom_cubin/ptx/ltoir/fatbin/object/library()StridedMemoryViewfrom_dlpack/cai/array_interface/buffer/any_interface()Inconsistencies
1.
Stream.from_handlecannot opt into keeping an owner aliveBuffer.from_handleacceptsowner=to hold a Python reference and prevent GC of the foreign allocator.Kernel.from_handleacceptsmod=to hold a reference and prevent GC of the parentObjectCode/library.Stream.from_handlehas no equivalent parameter. It creates a borrowed stream with no way for the user to tie lifetime to an owner object.The internal machinery already supports this —
create_stream_handle_with_owner()in C++ accepts a Python owner and doesPy_INCREF/DECREF. Butfrom_handle()only passes the ephemeral_stream_holderwrapper as the owner, not the user's actual owner object.Suggestion: Add an
ownerparameter toStream.from_handle, consistent withBuffer.from_handle.2.
Eventhas no publicfrom_handle()Stream,Kernel, andBufferall expose publicfrom_handle()for wrapping foreign CUDA handles.Eventonly hasfrom_ipc_descriptor(). There is an internal_from_handle()(cdef), but no public way to wrap a foreignCUevent.Suggestion: Add
Event.from_handle(handle)for symmetry, with the same borrowed/non-owning semantics.3.
Kernel.from_handledocstring doesn't clearly document ownershipStream.from_handledocstring explicitly says: "Stream lifetime is not managed, foreign object must remain alive while this stream is active."Buffer.from_handledocstring explicitly documents all three ownership modes and when the pointer is/isn't freed.Kernel.from_handledocstring mentionsmod"provides library lifetime for foreign kernels" but doesn't clearly state that the kernel handle itself is non-owning and won't be destroyed on close/GC.Suggestion: Improve the
Kernel.from_handledocstring to match the clarity of the other two.Additional observation
All
from_handle()methods use@staticmethod, whilefrom_ipc_descriptor()/from_gl_*()/from_allocation_handle()use@classmethod. This may be intentional (the latter group usesclsfor subclassing support), but worth confirming as a deliberate convention and documenting if so.-- Leo's bot