Repository navigation
Conversation
Signed-off-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Parent-scoped registration and cleanup cuda_core/cuda/core/_cpp/rt/graph.cpp, cuda_core/cuda/core/_cpp/rt/internal.hpp, cuda_core/cuda/core/_cpp/rt/GRAPH_ATTACHMENTS.md, cuda_core/cuda/core/_cpp/rt/REGISTRY_DESIGN.md |
Child graph lookup and registration use a key containing the child graph resource and owner node in the immediate parent’s registry. Publication and retirement use that registry. The documentation describes the per-GraphBox registries. HandleRegistry::register_handles is removed. |
Cython registry tests and build cuda_core/tests/cython/test_graph_registry.pyx, cuda_core/tests/cython/build_tests.py, cuda_core/tests/cython/test_cython.py, cuda_core/tests/cython/test_get_cuda_native_handle.pyx |
New tests cover handle reuse and separation by parent and owner, plus preservation of replacement mappings after hierarchy deletion. The test build adds the Core source root and C++17 flags. The native-handle test removes its local C++17 flag. |
Possibly related issues
- NVIDIA/cuda-python#3156: The issue describes raw
CUgraphhandle reuse. This change scopes child metadata lookup and retirement to the parent and owner. - NVIDIA/cuda-python#2438: The change registers cloned child handles with their immediate parents, but it does not add the public cloning API.
Change: Bug fix
Merge Risk: 
Minimal · up to 9b5d1
-
No merge-blocking regression was established in the child-graph registry changes.
Comment @coderabbitai help to get the list of available commands.
|
|
Thanks, Ralf. This is an important catch, and the diagnosis in the description is right. The registry's I prefer a slightly different fix that scopes the lookup map rather than a change to the
A recycled address can then collide only inside one hierarchy. There, the documented external synchronization covers it, and every destroy and its retire happen inside one cuda.core call. Cross-hierarchy confusion, the case the CI failure hit, becomes impossible by construction rather than caught by a check. Your tests 1 and 3 carry over unchanged; I traced both. Test 2 models two live boxes at one address under one parent, which the synchronization contract excludes, so it would go. The Cython test scaffolding is valuable either way. If you prefer, I can make the change and update this PR. |
|
From the same codex session that produced this PR: codex GPT-6.1-Sol ultra: I agree with Andy and would prefer his design for #3294. He also posted a more detailed proposal on the PR, which I checked against the implementation. His key observation is correct: the global graph registry has only one reader, and that reader already knows the parent graph. Roots are registered but never looked up. Putting a child registry on each parent That makes the fix simpler:
I also agree about dropping test 2. It manufactures two active child generations at the same address under one parent. Supported usage requires mutations within a hierarchy to be externally synchronized; the implementation retires old metadata before publishing its replacement. That synthetic overlap should not dictate the design. Tests 1 and 3 still cover the cross-hierarchy problem and can remain unchanged. The main implementation detail to check carefully is cloning and replacement: each nested child must register with its immediate parent, and retirement must still invalidate existing wrappers correctly. I’d take Andy up on his offer to revise the PR, then validate the retained regressions, existing clone/replacement tests, and full CI. |
The process-wide graph registry mapped a raw CUgraph to its canonical GraphHandle. Every handle of a hierarchy aliases the hierarchy's single control block, so a lockable registry entry proved only that the hierarchy was alive, not that the child graph was. CUDA destroys a child graph inside cuGraphDestroyNode, cuGraphNodeSetParams, or capture invalidation, and the box is retired only after the driver call returns. In that window the map could hand one hierarchy's recycled address to another. The only reader, create_child_graph_handle, always knows the parent, and roots are never looked up. Each GraphBox now holds the registry of its direct child graphs, next to its node registry. A recycled address can only collide inside one hierarchy, where mutations are externally synchronized and every retire precedes the matching registration. The conditional unregister_handle overload, the bulk register_handles, the static registry, and the hierarchy deleter's unregister loop are gone. The same-parent registry test modeled two live generations at one address under one parent, which the synchronization contract excludes, and is removed. Written by Codex (gpt-6.1-sol); reviewed by Claude. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A child graph is identified within its parent by its CUgraph and the node that owns it. Keying the per-parent registry by that pair makes the owner check part of the lookup. A box whose graph CUDA destroyed before cuda.core retired it keeps a key distinct from a newer child that reuses its address under another owner, so retiring the stale box cannot erase the newer entry. The same-parent registry test is restored; it passes under the pair key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I pushed the rework to this branch: two commits on top of 46bab1f. 974a7ad scopes the registry as discussed. 9b5d126 keys the per-parent registry by That second commit is why test 2 is back, contrary to what we both said above. Under the pair key the scenario it models, two owners at one address under one parent, is exactly what the key protects, and the test passes unchanged. Tests 1 and 3 are unchanged. Both design notes are updated. Validated locally: build, import, full collection, the Cython test extension, pre-commit, and the |
Description
Prevent a newly created graph branch from adopting an older graph's metadata when CUDA reuses a native handle address.
A node's native destruction can finish before Core retires its child-graph metadata. During that window, an independent graph can receive the same raw
CUgraphaddress. The old registry lookup accepted that address without checking its parent or owner, and later cleanup could invalidate the new branch or erase its replacement registry entry. This is consistent with the Windows free-threaded CI failure, where a fresh conditional branch was already invalid before the test destroyed its owner; the exact address reuse was not captured in that run.The root cause is structural. The process-wide
graph_registrymapped a rawCUgraphto aweak_ptrthat aliases the hierarchy's single control block, so a lockable entry proved only that the hierarchy was alive, not that the child graph was. CUDA destroys child graphs insidecuGraphDestroyNode,cuGraphNodeSetParams, and capture invalidation, and Core retires the box only after the driver call returns.The fix removes the shared map instead of guarding it:
GraphBoxholds the registry of its direct child graphs, next to its existing node registry.create_child_graph_handle, the only reader, always has the parent handle, and roots are never looked up. A recycled address can no longer resolve to another hierarchy's metadata.(CUgraph, owner node), the pair that identifies a child graph. If CUDA frees a child before Core retires its box, a newer child at the recycled address has a different owner node and its own entry, so the late retire cannot erase it.graph_registry, the hierarchy deleter's unregister loop,register_handles, and the conditionalunregister_handleoverload are gone.HandleRegistryis unchanged; the key is a struct withoperator==and a hash functor, likeExportDataKeyinmemory.cpp.Three deterministic Cython regressions exercise the real metadata API with synthetic child/owner keys and real empty parent graphs. The synthetic keys never reach CUDA. The tests cover foreign parents, different owners within one hierarchy, stale child retirement, and stale hierarchy cleanup. The Cython test builder supplies Core's source include path and the C++17 flag on Linux and Windows. The registry and hierarchy design notes describe the resulting invariants.
History
The first version of this PR (46bab1f) kept the process-wide registry and added a parent-and-owner check on lookup plus a compare-and-erase
unregister_handle(key, expected). The design discussion in the comments led to the per-parent registry (974a7ad) and the pair key (9b5d126), which supersede that approach. The original description is in the edit history of this text.Validation
graphandcythontest directories on a GPU, 565 passed, 9 skipped, all four Cython cases passing.This is one shared Core implementation for CUDA 12 and CUDA 13. The opaque
CUgraphandCUgraphNodedeclarations in the CUDA 12 and CUDA 13cydriver.pxdfiles already match, so neither bindings root needs a source or generation change. Callers still synchronize mutations within a shared graph hierarchy.Suggested Review Order
ChildGraphKey, thechildrenregistry onGraphBox,create_child_graph_handle,register_child_handles, andretire_graph.HandleRegistryis back to its original interface.Checklist