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

1# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 

2# SPDX-License-Identifier: Apache-2.0 

3  

4"""Test-only weak handles for resource-handle lifetime checks. 

5  

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). 

12  

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. 

17  

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""" 

24  

25from cuda.core._memory._buffer cimport Buffer 

26from cuda.core.graph._graph_definition cimport GraphDefinition 

27from cuda.core._resource_handles cimport OpaqueHandle 

28  

29  

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) 

49  

50  

51cdef class WeakHandle: 

52 """Non-owning weak handle for a resource's shared control block. 

53  

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 """ 

58  

59 cdef OpaqueWeakHandle _w 

60  

61 def __bool__(self): 

62 return not self._w.expired() 1cdaebfg

63  

64 def expired(self): 

65 """Return ``True`` once every strong owner of the handle is gone.""" 

66 return self._w.expired() 

67  

68 def use_count(self): 

69 """Number of strong owners currently sharing the handle.""" 

70 return self._w.use_count() 

71  

72  

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

80  

81  

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

87  

88  

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

94  

95  

96def weak_handle(obj): 

97 """Return a :class:`WeakHandle` observing the resource behind ``obj``. 

98  

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. 

102  

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 )