@@ -284,15 +284,25 @@ cdef class GraphNode:
284284 """
285285 return GN_free(self, <cydriver.CUdeviceptr>dptr)
286286
287- def memset(self, dst: Buffer | int, value, size_t width, size_t height=1, size_t pitch=0) -> MemsetNode:
287+ def memset(
288+ self,
289+ dst: Buffer | int,
290+ value,
291+ size_t width,
292+ *,
293+ size_t height=1,
294+ size_t pitch=0,
295+ dst_owner=None,
296+ ) -> MemsetNode:
288297 """ Add a memset node depending on this node.
289298
290299 Parameters
291300 ----------
292301 dst : Buffer or int
293- Destination. If a :class :`Buffer` is given it is retained for the
294- graph' s lifetime; if a raw device pointer (int) is given it is used
295- as - is and the caller must keep the underlying memory alive.
302+ Destination. A :class :`Buffer` is retained for the graph' s lifetime.
303+ A raw pointer (``int ``) is used as - is ; the caller must keep the
304+ underlying memory alive, or supply ``dst_owner`` to have the graph
305+ retain it.
296306 value : int or buffer - protocol object
297307 Fill value. int for 1 - byte fill (range [0 , 256 )),
298308 or buffer - protocol object of 1 , 2 , or 4 bytes.
@@ -302,20 +312,37 @@ cdef class GraphNode:
302312 Number of rows (default 1 ).
303313 pitch : int , optional
304314 Pitch of destination in bytes (default 0 , unused if height is 1 ).
315+ dst_owner : object , optional
316+ Object retained for the graph' s lifetime when ``dst`` is a raw
317+ pointer. Must not be passed when ``dst`` is a :class :`Buffer`, which
318+ is retained automatically.
305319
306320 Returns
307321 -------
308322 MemsetNode
309323 A new MemsetNode representing the memset operation.
324+
325+ Raises
326+ ------
327+ ValueError
328+ If ``dst_owner`` is given together with a :class :`Buffer` ``dst``.
310329 """
311330 cdef cydriver.CUdeviceptr c_dst
312331 cdef unsigned int val
313332 cdef unsigned int elem_size
314- dst_owner = _resolve_ptr (dst, &c_dst)
333+ dst_keepalive = _resolve_memcpy_operand (dst, dst_owner, "dst" , &c_dst)
315334 val, elem_size = _parse_fill_value(value)
316- return GN_memset(self, c_dst, dst_owner, val, elem_size, width, height, pitch)
317-
318- def memcpy(self, dst: Buffer | int, src: Buffer | int, size_t size) -> MemcpyNode:
335+ return GN_memset(self, c_dst, dst_keepalive, val, elem_size, width, height, pitch)
336+
337+ def memcpy(
338+ self,
339+ dst: Buffer | int,
340+ src: Buffer | int,
341+ size_t size,
342+ *,
343+ dst_owner=None,
344+ src_owner=None,
345+ ) -> MemcpyNode:
319346 """ Add a memcpy node depending on this node.
320347
321348 Copies ``size`` bytes from ``src`` to ``dst``. Memory types are
@@ -325,24 +352,40 @@ cdef class GraphNode:
325352 Parameters
326353 ----------
327354 dst : Buffer or int
328- Destination (device or pinned host). If a :class :`Buffer` is given
329- it is retained for the graph' s lifetime; a raw pointer (int) is used
330- as - is and the caller must keep the underlying memory alive.
355+ Destination (device or pinned host). A :class :`Buffer` is retained
356+ for the graph' s lifetime. A raw pointer (``int``) is used as-is; the
357+ caller must keep the underlying memory alive, or supply ``dst_owner``
358+ to have the graph retain it.
331359 src : Buffer or int
332- Source (device or pinned host). Retained like ``dst`` when a Buffer.
360+ Source (device or pinned host). Same retention rules as ``dst``;
361+ use ``src_owner`` for a raw pointer.
333362 size : int
334363 Number of bytes to copy.
364+ dst_owner : object , optional
365+ Object retained for the graph' s lifetime when ``dst`` is a raw
366+ pointer. Must not be passed when ``dst`` is a :class :`Buffer`, which
367+ is retained automatically.
368+ src_owner : object , optional
369+ Object retained for the graph' s lifetime when ``src`` is a raw
370+ pointer. Must not be passed when ``src`` is a :class :`Buffer`, which
371+ is retained automatically.
335372
336373 Returns
337374 -------
338375 MemcpyNode
339376 A new MemcpyNode representing the copy operation.
377+
378+ Raises
379+ ------
380+ ValueError
381+ If ``dst_owner`` or ``src_owner`` is given together with a
382+ :class :`Buffer` ``dst`` or ``src`` respectively.
340383 """
341384 cdef cydriver.CUdeviceptr c_dst
342385 cdef cydriver.CUdeviceptr c_src
343- dst_owner = _resolve_ptr (dst, &c_dst)
344- src_owner = _resolve_ptr (src, &c_src)
345- return GN_memcpy(self, c_dst, dst_owner , c_src, src_owner , size)
386+ dst_keepalive = _resolve_memcpy_operand (dst, dst_owner, "dst" , &c_dst)
387+ src_keepalive = _resolve_memcpy_operand (src, src_owner, "src" , &c_src)
388+ return GN_memcpy(self, c_dst, dst_keepalive , c_src, src_keepalive , size)
346389
347390 def embed(self, child: GraphDefinition) -> ChildGraphNode:
348391 """ Add a child graph node depending on this node.
@@ -789,20 +832,28 @@ cdef inline FreeNode GN_free(GraphNode self, cydriver.CUdeviceptr c_dptr):
789832 return _registered(FreeNode._create_with_params(create_graph_node_handle(new_node, h_graph), c_dptr))
790833
791834
792- cdef inline object _resolve_ptr(object value, cydriver.CUdeviceptr* out_ptr):
793- """ Resolve a memcpy/ memset operand into a device pointer.
835+ cdef inline object _resolve_memcpy_operand(
836+ object operand, object owner, str side, cydriver.CUdeviceptr* out_ptr):
837+ """ Resolve a memcpy/ memset operand to a device pointer and an owner.
838+
839+ ``operand`` is a :class :`Buffer` or a raw integer address; its device
840+ pointer is written to ``out_ptr``. Returns the object to retain on the
841+ graph' s slot table for the graph' s lifetime: the Buffer itself, or
842+ ``owner`` (possibly ``None ``) for a raw pointer. ``side`` is ``" dst" `` or
843+ ``" src" `` and is used only to compose the error message.
794844
795- ``value`` is a :class :`Buffer` or a raw integer address. For a Buffer the
796- device pointer is read from the buffer and the buffer is returned so the
797- caller can retain it on the node' s slot table for the graph' s lifetime. For
798- a raw integer no owner is returned and the caller is responsible for keeping
799- the underlying memory alive.
845+ Raises ValueError if ``operand`` is a Buffer and ``owner`` is not None ,
846+ since a Buffer is already retained automatically.
800847 """
801- if isinstance(value, Buffer):
802- out_ptr[0] = as_cu((<Buffer>value)._h_ptr)
803- return value
804- out_ptr[0] = <cydriver.CUdeviceptr><uintptr_t>value
805- return None
848+ if isinstance(operand, Buffer):
849+ if owner is not None:
850+ raise ValueError(
851+ f"{side}_owner cannot be used when {side} is a Buffer"
852+ )
853+ out_ptr[0] = as_cu((<Buffer>operand)._h_ptr)
854+ return operand
855+ out_ptr[0] = <cydriver.CUdeviceptr><uintptr_t>operand
856+ return owner
806857
807858
808859cdef inline MemsetNode GN_memset(
@@ -837,7 +888,7 @@ cdef inline MemsetNode GN_memset(
837888 &new_node, as_cu(h_graph), deps, num_deps,
838889 &memset_params, ctx))
839890
840- # Retain a Buffer destination for the graph's lifetime (slot 0).
891+ # Retain the destination owner for the graph's lifetime (slot 0).
841892 if dst_owner is not None:
842893 HANDLE_RETURN(graph_set_slot(h_graph, new_node, 0, make_opaque_py(dst_owner)))
843894
@@ -902,7 +953,7 @@ cdef inline MemcpyNode GN_memcpy(
902953 HANDLE_RETURN(cydriver.cuGraphAddMemcpyNode(
903954 &new_node, as_cu(h_graph), deps, num_deps, ¶ms, ctx))
904955
905- # Retain Buffer operands for the graph's lifetime (dst -> slot 0, src -> slot 1).
956+ # Retain operand owners for the graph's lifetime (dst -> slot 0, src -> slot 1).
906957 if dst_owner is not None:
907958 HANDLE_RETURN(graph_set_slot(h_graph, new_node, 0, make_opaque_py(dst_owner)))
908959 if src_owner is not None:
0 commit comments