@@ -852,39 +852,54 @@ cdef class GraphBuilder:
852852 pointer (caller manages lifetime). If bytes- like, the data is
853853 copied and its lifetime is tied to the graph.
854854 """
855- GB_check_open(self)
856- cdef Stream stream = self._stream
857- cdef cydriver.CUstream c_stream = as_cu(stream._h_stream)
858- cdef cydriver.CUstreamCaptureStatus capture_status
855+ GB_callback(self, fn, user_data, False)
859856
860- with nogil:
861- _get_capture_info(c_stream, &capture_status, NULL)
862857
863- if capture_status != cydriver.CU_STREAM_CAPTURE_STATUS_ACTIVE:
864- raise RuntimeError("Cannot add callback when graph is not being built")
858+ cdef inline void GB_callback(
859+ GraphBuilder gb, object fn, object user_data,
860+ bint fail_tail_discovery_for_testing) except *:
861+ GB_check_open(gb)
862+ cdef Stream stream = gb._stream
863+ cdef cydriver.CUstream c_stream = as_cu(stream._h_stream)
864+ cdef cydriver.CUstreamCaptureStatus capture_status
865865
866- cdef cydriver.CUhostFn c_fn
867- cdef void* c_user_data = NULL
868- cdef OpaqueHandle fn_owner, data_owner
869- cdef GraphAttachmentsHandle attachments
870- _resolve_host_callback(fn, user_data, &c_fn, &c_user_data, &fn_owner, &data_owner)
871- attachments = _prepare_host_callback_owners(
872- self._h_graph, fn_owner, data_owner)
866+ with nogil:
867+ _get_capture_info(c_stream, &capture_status, NULL)
873868
874- with nogil :
875- HANDLE_RETURN(cydriver.cuLaunchHostFunc(c_stream, c_fn, c_user_data) )
869+ if capture_status != cydriver.CU_STREAM_CAPTURE_STATUS_ACTIVE :
870+ raise RuntimeError("Cannot add callback when graph is not being built" )
876871
877- # Capturing the host function added a node to the graph; it is now the
878- # stream's sole capture dependency. If tail discovery unexpectedly
879- # fails, keep the owners anonymously graph-retained: rolling them back
880- # would leave the already-captured node with dangling pointers.
881- cdef cydriver.CUgraphNode host_node
882- try:
883- host_node = _capture_tail_node(c_stream)
884- except:
885- graph_abandon_attachments(attachments)
886- raise
887- HANDLE_RETURN(graph_commit_attachments(attachments, host_node))
872+ cdef cydriver.CUhostFn c_fn
873+ cdef void* c_user_data = NULL
874+ cdef OpaqueHandle fn_owner, data_owner
875+ cdef GraphAttachmentsHandle attachments
876+ _resolve_host_callback(
877+ fn, user_data, &c_fn, &c_user_data, &fn_owner, &data_owner)
878+ attachments = _prepare_host_callback_owners(
879+ gb._h_graph, fn_owner, data_owner)
880+
881+ with nogil:
882+ HANDLE_RETURN(cydriver.cuLaunchHostFunc(c_stream, c_fn, c_user_data))
883+
884+ # Capturing the host function added a node to the graph; it is now the
885+ # stream's sole capture dependency. If tail discovery unexpectedly fails,
886+ # keep the owners anonymously graph-retained: rolling them back would
887+ # leave the already-captured node with dangling pointers.
888+ cdef cydriver.CUgraphNode host_node
889+ try:
890+ if fail_tail_discovery_for_testing:
891+ raise RuntimeError("forced capture tail discovery failure")
892+ host_node = _capture_tail_node(c_stream)
893+ except:
894+ graph_abandon_attachments(attachments)
895+ raise
896+ HANDLE_RETURN(graph_commit_attachments(attachments, host_node))
897+
898+
899+ def _capture_callback_with_tail_failure_for_testing(
900+ GraphBuilder gb, fn, *, user_data=None):
901+ """ Exercise anonymous attachment retention after capture commits a node."""
902+ GB_callback(gb, fn, user_data, True)
888903
889904
890905cdef inline int GB_check_open(GraphBuilder gb) except -1:
0 commit comments