99from typing import Optional , Tuple , TypeVar , Union
1010
1111from cuda .core .experimental ._dlpack import DLDeviceType , make_py_capsule
12- from cuda .core .experimental ._stream import default_stream
12+ from cuda .core .experimental ._stream import Stream , default_stream
1313from cuda .core .experimental ._utils .cuda_utils import driver , handle_return
1414
15- PyCapsule = TypeVar ("PyCapsule" )
16-
1715
1816# TODO: define a memory property mixin class and make Buffer and
1917# MemoryResource both inherit from it
2018
19+
20+ PyCapsule = TypeVar ("PyCapsule" )
21+ """Represent the capsule type."""
22+
2123DevicePointerT = Union [driver .CUdeviceptr , int , None ]
22- """A type union of `Cudeviceptr `, `int` and `None` for hinting Buffer.handle."""
24+ """A type union of :obj:`~driver.CUdeviceptr `, `int` and `None` for hinting :attr:` Buffer.handle` ."""
2325
2426
2527class Buffer :
@@ -29,19 +31,7 @@ class Buffer:
2931 different memory resources are to give access to their memory
3032 allocations.
3133
32- Support for data interchange mechanisms are provided by
33- establishing both the DLPack and the Python-level buffer
34- protocols.
35-
36- Parameters
37- ----------
38- ptr : Any
39- Allocated buffer handle object
40- size : Any
41- Memory size of the buffer
42- mr : :obj:`~_memory.MemoryResource`, optional
43- Memory resource associated with the buffer
44-
34+ Support for data interchange mechanisms are provided by DLPack.
4535 """
4636
4737 class _MembersNeededForFinalize :
@@ -64,22 +54,26 @@ def close(self, stream=None):
6454 # TODO: handle ownership? (_mr could be None)
6555 __slots__ = ("__weakref__" , "_mnff" )
6656
67- def __init__ (self , ptr , size , mr : MemoryResource = None ):
57+ def __new__ (self , * args , ** kwargs ):
58+ raise RuntimeError ("Buffer objects cannot be instantiated directly. Please use MemoryResource APIs." )
59+
60+ @classmethod
61+ def _init (cls , ptr : DevicePointerT , size : int , mr : Optional [MemoryResource ] = None ):
62+ self = super ().__new__ (cls )
6863 self ._mnff = Buffer ._MembersNeededForFinalize (self , ptr , size , mr )
64+ return self
6965
70- def close (self , stream = None ):
66+ def close (self , stream : Stream = None ):
7167 """Deallocate this buffer asynchronously on the given stream.
7268
7369 This buffer is released back to their memory resource
7470 asynchronously on the given stream.
7571
7672 Parameters
7773 ----------
78- stream : Any, optional
79- The stream object with a __cuda_stream__ protocol to
80- use for asynchronous deallocation. Defaults to using
81- the default stream.
82-
74+ stream : Stream, optional
75+ The stream object to use for asynchronous deallocation. If not set,
76+ the current default is to the default stream.
8377 """
8478 self ._mnff .close (stream )
8579
@@ -95,7 +89,7 @@ def handle(self) -> DevicePointerT:
9589 return self ._mnff .ptr
9690
9791 @property
98- def size (self ):
92+ def size (self ) -> int :
9993 """Return the memory size of this buffer."""
10094 return self ._mnff .size
10195
@@ -125,7 +119,7 @@ def device_id(self) -> int:
125119 return self ._mnff .mr .device_id
126120 raise NotImplementedError ("WIP: Currently this property only supports buffers with associated MemoryResource" )
127121
128- def copy_to (self , dst : Buffer = None , * , stream ) -> Buffer :
122+ def copy_to (self , dst : Buffer = None , * , stream : Stream ) -> Buffer :
129123 """Copy from this buffer to the dst buffer asynchronously on the given stream.
130124
131125 Copies the data from this buffer to the provided dst buffer.
@@ -136,7 +130,7 @@ def copy_to(self, dst: Buffer = None, *, stream) -> Buffer:
136130 ----------
137131 dst : :obj:`~_memory.Buffer`
138132 Source buffer to copy data from
139- stream : Any
133+ stream : Stream
140134 Keyword argument specifying the stream for the
141135 asynchronous copy
142136
@@ -154,14 +148,14 @@ def copy_to(self, dst: Buffer = None, *, stream) -> Buffer:
154148 handle_return (driver .cuMemcpyAsync (dst ._mnff .ptr , self ._mnff .ptr , self ._mnff .size , stream .handle ))
155149 return dst
156150
157- def copy_from (self , src : Buffer , * , stream ):
151+ def copy_from (self , src : Buffer , * , stream : Stream ):
158152 """Copy from the src buffer to this buffer asynchronously on the given stream.
159153
160154 Parameters
161155 ----------
162156 src : :obj:`~_memory.Buffer`
163157 Source buffer to copy data from
164- stream : Any
158+ stream : Stream
165159 Keyword argument specifying the stream for the
166160 asynchronous copy
167161
@@ -219,55 +213,117 @@ def __release_buffer__(self, buffer: memoryview, /):
219213 # Supporting method paired with __buffer__.
220214 raise NotImplementedError ("WIP: Buffer.__release_buffer__ hasn't been implemented yet." )
221215
216+ @staticmethod
217+ def from_handle (ptr : DevicePointerT , size : int , mr : Optional [MemoryResource ] = None ) -> Buffer :
218+ """Create a new :class:`Buffer` object from a pointer.
219+
220+ Parameters
221+ ----------
222+ ptr : :obj:`~_memory.DevicePointerT`
223+ Allocated buffer handle object
224+ size : int
225+ Memory size of the buffer
226+ mr : :obj:`~_memory.MemoryResource`, optional
227+ Memory resource associated with the buffer
228+ """
229+ return Buffer ._init (ptr , size , mr = mr )
230+
222231
223232class MemoryResource (abc .ABC ):
233+ """Abstract base class for memory resources that manage allocation and deallocation of buffers.
234+
235+ Subclasses must implement methods for allocating and deallocation, as well as properties
236+ associated with this memory resource from which all allocated buffers will inherit. (Since
237+ all :class:`Buffer` instances allocated and returned by the :meth:`allocate` method would
238+ hold a reference to self, the buffer properties are retrieved simply by looking up the underlying
239+ memory resource's respective property.)
240+ """
241+
224242 __slots__ = ("_handle" ,)
225243
226244 @abc .abstractmethod
227- def __init__ (self , * args , ** kwargs ): ...
245+ def __init__ (self , * args , ** kwargs ):
246+ """Initialize the memory resource.
247+
248+ Subclasses may use additional arguments to configure the resource.
249+ """
250+ ...
228251
229252 @abc .abstractmethod
230- def allocate (self , size , stream = None ) -> Buffer : ...
253+ def allocate (self , size : int , stream : Stream = None ) -> Buffer :
254+ """Allocate a buffer of the requested size.
255+
256+ Parameters
257+ ----------
258+ size : int
259+ The size of the buffer to allocate, in bytes.
260+ stream : object, optional
261+ The stream on which to perform the allocation asynchronously.
262+ If None, allocation is synchronous.
263+
264+ Returns
265+ -------
266+ Buffer
267+ The allocated buffer object, which can be used for device or host operations
268+ depending on the resource's properties.
269+ """
270+ ...
231271
232272 @abc .abstractmethod
233- def deallocate (self , ptr , size , stream = None ): ...
273+ def deallocate (self , ptr : DevicePointerT , size : int , stream : Stream = None ):
274+ """Deallocate a buffer previously allocated by this resource.
275+
276+ Parameters
277+ ----------
278+ ptr : object
279+ The pointer or handle to the buffer to deallocate.
280+ size : int
281+ The size of the buffer to deallocate, in bytes.
282+ stream : object, optional
283+ The stream on which to perform the deallocation asynchronously.
284+ If None, deallocation is synchronous.
285+ """
286+ ...
234287
235288 @property
236289 @abc .abstractmethod
237290 def is_device_accessible (self ) -> bool :
238- # Check if the buffers allocated from this MR can be accessed from
239- # GPUs.
291+ """bool: True if buffers allocated by this resource can be accessed on the device."""
240292 ...
241293
242294 @property
243295 @abc .abstractmethod
244296 def is_host_accessible (self ) -> bool :
245- # Check if the buffers allocated from this MR can be accessed from
246- # CPUs.
297+ """bool: True if buffers allocated by this resource can be accessed on the host."""
247298 ...
248299
249300 @property
250301 @abc .abstractmethod
251302 def device_id (self ) -> int :
252- # Return the device ID if this MR is for single devices. Raise an
253- # exception if it is not.
303+ """int: The device ordinal for which this memory resource is responsible.
304+
305+ Raises
306+ ------
307+ RuntimeError
308+ If the resource is not bound to a specific device.
309+ """
254310 ...
255311
256312
257313class _DefaultAsyncMempool (MemoryResource ):
258314 __slots__ = ("_dev_id" ,)
259315
260- def __init__ (self , dev_id ):
316+ def __init__ (self , dev_id : int ):
261317 self ._handle = handle_return (driver .cuDeviceGetMemPool (dev_id ))
262318 self ._dev_id = dev_id
263319
264- def allocate (self , size , stream = None ) -> Buffer :
320+ def allocate (self , size : int , stream : Stream = None ) -> Buffer :
265321 if stream is None :
266322 stream = default_stream ()
267323 ptr = handle_return (driver .cuMemAllocFromPoolAsync (size , self ._handle , stream .handle ))
268- return Buffer (ptr , size , self )
324+ return Buffer . _init (ptr , size , self )
269325
270- def deallocate (self , ptr , size , stream = None ):
326+ def deallocate (self , ptr : DevicePointerT , size : int , stream : Stream = None ):
271327 if stream is None :
272328 stream = default_stream ()
273329 handle_return (driver .cuMemFreeAsync (ptr , stream .handle ))
@@ -290,11 +346,11 @@ def __init__(self):
290346 # TODO: support flags from cuMemHostAlloc?
291347 self ._handle = None
292348
293- def allocate (self , size , stream = None ) -> Buffer :
349+ def allocate (self , size : int , stream : Stream = None ) -> Buffer :
294350 ptr = handle_return (driver .cuMemAllocHost (size ))
295- return Buffer (ptr , size , self )
351+ return Buffer . _init (ptr , size , self )
296352
297- def deallocate (self , ptr , size , stream = None ):
353+ def deallocate (self , ptr : DevicePointerT , size : int , stream : Stream = None ):
298354 handle_return (driver .cuMemFreeHost (ptr ))
299355
300356 @property
@@ -319,7 +375,7 @@ def __init__(self, dev_id):
319375
320376 def allocate (self , size , stream = None ) -> Buffer :
321377 ptr = handle_return (driver .cuMemAlloc (size ))
322- return Buffer (ptr , size , self )
378+ return Buffer . _init (ptr , size , self )
323379
324380 def deallocate (self , ptr , size , stream = None ):
325381 if stream is None :
0 commit comments