Coverage for cuda/core/_utils/_weak_handles.pyx: 77.78%
27 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-03 02:41 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-03 02:41 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
4"""Test-only weak handles for resource-handle lifetime checks.
6This module is **not** part of the public ``cuda.core`` API. It is built into
7the package (like other private ``_utils`` modules) purely so the test suite can
8observe, deterministically, when the strong references that keep a CUDA resource
9alive have all been released -- without relying on driver- or hardware-specific
10side effects (for example, whether freed device memory happens to remain
11readable).
13Every resource handle is owned by a C++ ``std::shared_ptr``. A **weak handle**
14is a non-owning ``std::weak_ptr`` observer of that control block: truthy while
15some strong owner remains, falsy once the last one is gone. Use :func:`weak_handle`
16to obtain a weak handle from a supported front-end object.
18To support another type, add a ``cdef _weak_from_<type>`` that reads its ``cdef``
19handle field (see ``*.pxd``), assigns to :ctype:`OpaqueHandle`, and extend the
20``isinstance`` chain in :func:`weak_handle`. Types whose slots hold arbitrary
21Python owners via ``make_opaque_py`` are not covered here -- use
22:class:`weakref.ref` on a weak-referenceable owner object in tests instead.
23"""
25from cuda.core._memory._buffer cimport Buffer
26from cuda.core.graph._graph_definition cimport GraphDefinition
27from cuda.core._resource_handles cimport OpaqueHandle
30# Cython cannot spell ``weak_ptr[const void]`` inline (the ``const void``
31# template argument fails to parse), so the weak type and its one constructor
32# are provided by a small inline C++ shim local to this test-only module. This
33# keeps the production resource_handles translation units untouched.
34cdef extern from *:
35 """
36 #include <memory>
37 namespace cuda_core_test {
38 using OpaqueWeakHandle = std::weak_ptr<const void>;
39 static inline OpaqueWeakHandle make_weak(const std::shared_ptr<const void>& h) {
40 return OpaqueWeakHandle(h);
41 }
42 } // namespace cuda_core_test
43 """
44 cppclass OpaqueWeakHandle "cuda_core_test::OpaqueWeakHandle":
45 OpaqueWeakHandle()
46 bint expired()
47 long use_count()
48 OpaqueWeakHandle make_weak "cuda_core_test::make_weak" (const OpaqueHandle& h)
51cdef class WeakHandle:
52 """Non-owning weak handle for a resource's shared control block.
54 Truthy while some strong owner of the underlying resource handle remains,
55 falsy once the last strong reference is released. Obtain instances via
56 :func:`weak_handle` rather than constructing directly.
57 """
59 cdef OpaqueWeakHandle _w
61 def __bool__(self):
62 return not self._w.expired() 1cdaebfg
64 def expired(self):
65 """Return ``True`` once every strong owner of the handle is gone."""
66 return self._w.expired()
68 def use_count(self):
69 """Number of strong owners currently sharing the handle."""
70 return self._w.use_count()
73cdef WeakHandle _weak_from_opaque(OpaqueHandle h):
74 # Build the weak handle from a (temporary) strong handle. The strong copy
75 # lives only for the duration of this call, so it does not perturb the
76 # reference count the weak handle later reports.
77 cdef WeakHandle wh = WeakHandle.__new__(WeakHandle) 1cdaebfg
78 wh._w = make_weak(h) 1cdaebfg
79 return wh 1cdaebfg
82cdef WeakHandle _weak_from_buffer(Buffer buf):
83 cdef OpaqueHandle h = buf._h_ptr 1cdefg
84 if not h: 1cdefg
85 raise ValueError("Buffer has no active allocation")
86 return _weak_from_opaque(h) 1cdefg
89cdef WeakHandle _weak_from_graph_definition(GraphDefinition graph):
90 cdef OpaqueHandle h = graph._h_graph 1ab
91 if not h: 1ab
92 raise ValueError("GraphDefinition has no active graph")
93 return _weak_from_opaque(h) 1ab
96def weak_handle(obj):
97 """Return a :class:`WeakHandle` observing the resource behind ``obj``.
99 Currently supports :class:`~cuda.core.Buffer` (allocation handle) and
100 :class:`~cuda.core.graph.GraphDefinition` (graph hierarchy handle). See
101 the module docstring for how to add more types.
103 Raises
104 ------
105 ValueError
106 If ``obj`` is a :class:`~cuda.core.Buffer` with no active allocation.
107 TypeError
108 If ``obj`` is not a supported type.
109 """
110 if isinstance(obj, Buffer): 1cdaebfg
111 return _weak_from_buffer(obj) 1cdefg
112 if isinstance(obj, GraphDefinition): 1ab
113 return _weak_from_graph_definition(obj) 1ab
114 raise TypeError(
115 f"weak_handle() does not support {type(obj).__name__!r}; "
116 "supported types: Buffer, GraphDefinition"
117 )