Coverage for cuda/core/_utils/_weak_handles.pyx: 75.00%
20 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 01:12 +0000
1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 1a
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._resource_handles cimport OpaqueHandle
29# Cython cannot spell ``weak_ptr[const void]`` inline (the ``const void``
30# template argument fails to parse), so the weak type and its one constructor
31# are provided by a small inline C++ shim local to this test-only module. This
32# keeps the production resource_handles translation units untouched.
33cdef extern from *:
34 """
35 #include <memory>
36 namespace cuda_core_test {
37 using OpaqueWeakHandle = std::weak_ptr<const void>;
38 static inline OpaqueWeakHandle make_weak(const std::shared_ptr<const void>& h) {
39 return OpaqueWeakHandle(h);
40 }
41 } // namespace cuda_core_test
42 """
43 cppclass OpaqueWeakHandle "cuda_core_test::OpaqueWeakHandle":
44 OpaqueWeakHandle()
45 bint expired()
46 long use_count()
47 OpaqueWeakHandle make_weak "cuda_core_test::make_weak" (const OpaqueHandle& h)
50cdef class WeakHandle:
51 """Non-owning weak handle for a resource's shared control block.
53 Truthy while some strong owner of the underlying resource handle remains,
54 falsy once the last strong reference is released. Obtain instances via
55 :func:`weak_handle` rather than constructing directly.
56 """
58 cdef OpaqueWeakHandle _w
60 def __bool__(self):
61 return not self._w.expired() 1a
63 def expired(self): 1a
64 """Return ``True`` once every strong owner of the handle is gone."""
65 return self._w.expired()
67 def use_count(self): 1a
68 """Number of strong owners currently sharing the handle."""
69 return self._w.use_count()
72cdef WeakHandle _weak_from_opaque(OpaqueHandle h): 1a
73 # Build the weak handle from a (temporary) strong handle. The strong copy
74 # lives only for the duration of this call, so it does not perturb the
75 # reference count the weak handle later reports.
76 cdef WeakHandle wh = WeakHandle.__new__(WeakHandle) 1a
77 wh._w = make_weak(h) 1a
78 return wh 1a
81cdef WeakHandle _weak_from_buffer(Buffer buf): 1a
82 cdef OpaqueHandle h = buf._h_ptr 1a
83 if not h: 1a
84 raise ValueError("Buffer has no active allocation")
85 return _weak_from_opaque(h) 1a
88def weak_handle(obj): 1a
89 """Return a :class:`WeakHandle` observing the resource behind ``obj``.
91 Currently supports :class:`~cuda.core.Buffer` (device allocation handle).
92 See the module docstring for how to add more types.
94 Raises
95 ------
96 ValueError
97 If ``obj`` is a :class:`~cuda.core.Buffer` with no active allocation.
98 TypeError
99 If ``obj`` is not a supported type.
100 """
101 if isinstance(obj, Buffer): 1a
102 return _weak_from_buffer(obj) 1a
103 raise TypeError(
104 f"weak_handle() does not support {type(obj).__name__!r}; "
105 "supported types: Buffer"
106 )