Skip to content

feat(cuda.core): add host_launch for stream-ordered host functions - #3258

Merged
Andy-Jost merged 5 commits into
NVIDIA:mainfrom
Andy-Jost:ajost/host-launch
Oct 30, 2026
Merged

Andy-Jost merged 5 commits into
NVIDIA:mainfrom
Andy-Jost:ajost/host-launch

Conversation

@Andy-Jost

@Andy-Jost Andy-Jost commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

cuda.core had host callbacks for graphs but no way to enqueue one on a plain stream, so mixed host and device ordering needed cuda.bindings. This PR adds cuda.core.host_launch(stream, fn, *, user_data=None), the stream-side counterpart of GraphBuilder.callback. It enqueues a host function on a stream in stream order through cuLaunchHostFunc: a Python callable, which runs with the GIL, or a ctypes function pointer with the CUhostFn prototype, which cuda.core passes to the driver as is, so a pointer to compiled code runs without the GIL (a ctypes callback that wraps a Python function still acquires it through ctypes). The fn and user_data forms and their validation are the ones the graph callback methods already use, and a GraphBuilder argument forwards to its callback, so the same call works under stream capture.

cuda.core owns the callable and a copied user_data until the function has run. The trampoline hands the launch record to the existing deferred-cleanup queue after the user function returns, so the owners are released on the main thread, never on the driver's callback thread, where releasing a Python object would take the GIL and a finalizer could call CUDA. A plain Stream under active capture raises RuntimeError and points to GraphBuilder.callback, because the record's owners could not be attached to the captured node and the record must not be reused by graph replays. A Python exception inside the callback is reported through sys.unraisablehook and the stream continues, as on the graph path. The docstring states the driver's rules: the function must not call CUDA, and a thread that holds the GIL while blocked in a CUDA wait on the stream deadlocks with a Python callback.

Changes

  • cuda_core/cuda/core/_launcher.pyx: host_launch, exported from cuda.core.
  • cuda_core/cuda/core/_host_callback.pyx, .pxd: the shared callback helper, moved from cuda/core/graph/ unchanged; a core module must not import the graph package at module level. The three graph cimports follow.
  • cuda_core/cuda/core/_cpp/rt/host_launch.cpp: the launch record and trampoline behind one entry point, launch_host_function, declared in api.hpp, _rt.pxd, and _rt.pyx; cuLaunchHostFunc joins the driver table in driver_api.hpp.
  • cuda_core/docs/source/api.rst: host_launch next to launch; _cpp/rt/DESIGN.md and GRAPH_ATTACHMENTS.md name the new file and the queue's second producer.
  • Release-note entry for 1.3.0.
  • cuda_core/examples/host_launch.py: a host function ordered between two kernel launches, linked from docs/source/examples.rst.
  • Tests in cuda_core/tests/test_host_launch.py: ordering between prior and later stream work, release of the callable after it ran, copied bytes user_data, pass-through of an int user_data, the TypeError and ValueError cases, forwarding of a GraphBuilder with two graph launches, rejection of a capturing stream without invalidating the capture, unraisable reporting with the stream still usable, two streams keeping their own data, rejection of None, and the deprecated stream-protocol path.

Related Work

Closes #2058. The host copy function requested in #3256 builds on this.

🤖 Generated with Claude Code

Andy-Jost and others added 2 commits October 27, 2026 16:36
Add cuda.core.host_launch(stream, fn, *, user_data=None), the stream-side
counterpart of GraphBuilder.callback. It enqueues a Python callable or a
ctypes function pointer with the CUhostFn prototype on a stream through
cuLaunchHostFunc; a GraphBuilder argument forwards to its callback. The
callable and a copied user_data stay alive until the function has run and
are released afterwards on the main thread through the deferred-cleanup
queue, never on the driver thread. A plain stream under active capture is
rejected, because the owners could not be attached to the captured node.

The shared callback helper moves from cuda/core/graph/_host_callback to
cuda/core/_host_callback, since a core module must not import the graph
package at module level. The launch record and trampoline live in
_cpp/rt/host_launch.cpp, and cuLaunchHostFunc joins the driver table.

Written by Codex (gpt-6.1-sol); reviewed by Claude.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Andy-Jost Andy-Jost added this to the cuda.core 1.3.0 milestone Oct 27, 2026
@Andy-Jost Andy-Jost added P1 Medium priority - Should do feature New feature or request cuda.core Everything related to the cuda.core module labels Oct 27, 2026
@Andy-Jost Andy-Jost self-assigned this Oct 27, 2026
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 2c23d6f and 66c55f0.

Files selected for processing: 20
  • cuda_core/cuda/core/_cpp/rt/DESIGN.md
  • cuda_core/cuda/core/_cpp/rt/GRAPH_ATTACHMENTS.md
  • cuda_core/cuda/core/_cpp/rt/api.hpp
  • cuda_core/cuda/core/_cpp/rt/driver_api.hpp
  • cuda_core/cuda/core/_cpp/rt/host_launch.cpp
  • cuda_core/cuda/core/_host_callback.pxd
  • cuda_core/cuda/core/_host_callback.pyi
  • cuda_core/cuda/core/_host_callback.pyx
  • cuda_core/cuda/core/_launcher.pyi
  • cuda_core/cuda/core/_launcher.pyx
  • cuda_core/cuda/core/_rt.pxd
  • cuda_core/cuda/core/_rt.pyx
  • cuda_core/cuda/core/graph/_graph_builder.pyx
  • cuda_core/cuda/core/graph/_graph_node.pyx
  • cuda_core/cuda/core/graph/_subclasses.pyx
  • cuda_core/docs/source/api.rst
  • cuda_core/docs/source/examples.rst
  • cuda_core/docs/source/release/1.3.0-notes.rst
  • cuda_core/examples/host_launch.py
  • cuda_core/tests/test_host_launch.py
Files with no reviewable changes: 2
  • cuda_core/cuda/core/_host_callback.pyx
  • cuda_core/cuda/core/_host_callback.pxd
Files skipped from review as they are similar to previous changes: 5
  • cuda_core/docs/source/examples.rst
  • cuda_core/cuda/core/_cpp/rt/GRAPH_ATTACHMENTS.md
  • cuda_core/cuda/core/_host_callback.pyi
  • cuda_core/docs/source/api.rst
  • cuda_core/cuda/core/_cpp/rt/DESIGN.md

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

📝 Summary

Summary by CodeRabbit

  • New Features
    • Added host_launch to enqueue Python callbacks or compatible function pointers in stream order, with optional user data.
    • Callbacks can also be recorded through graph builders. Callback and user-data lifetimes are maintained until execution.
    • Python callback errors are reported as unraisable exceptions; compiled function pointers run without the GIL.
    • Added an example demonstrating a host callback between stream-ordered kernel launches.
  • Documentation
    • Added API reference and release-note coverage for host_launch.

The change adds host_launch for stream-ordered Python and ctypes callbacks. It adds a runtime launch path with callback and user-data lifetime handling, forwards GraphBuilder inputs to its callback API, and includes tests, an example, and API documentation.

Changes: Host launch API

Layer / File(s) Summary
Runtime host-function launch
cuda_core/cuda/core/_cpp/rt/api.hpp, cuda_core/cuda/core/_cpp/rt/driver_api.hpp, cuda_core/cuda/core/_cpp/rt/host_launch.cpp, cuda_core/cuda/core/_rt.pxd, cuda_core/cuda/core/_rt.pyx, cuda_core/cuda/core/_cpp/rt/DESIGN.md, cuda_core/cuda/core/_cpp/rt/GRAPH_ATTACHMENTS.md
The runtime adds cuLaunchHostFunc support and retains callback and user-data owners until invocation. The trampoline queues the record for cleanup after the callback returns.
Python API and graph integration
cuda_core/cuda/core/_launcher.pyx, cuda_core/cuda/core/_launcher.pyi, cuda_core/cuda/core/_host_callback.pxd, cuda_core/cuda/core/_host_callback.pyx, cuda_core/cuda/core/_host_callback.pyi, cuda_core/cuda/core/graph/_graph_builder.pyx, cuda_core/cuda/core/graph/_graph_node.pyx, cuda_core/cuda/core/graph/_subclasses.pyx
host_launch accepts streams and forwards GraphBuilder inputs to GraphBuilder.callback. It resolves callback inputs and rejects active stream capture. Graph modules now import callback helpers from cuda.core._host_callback.
Validation, example, and API documentation
cuda_core/tests/test_host_launch.py, cuda_core/examples/host_launch.py, cuda_core/docs/source/api.rst, cuda_core/docs/source/examples.rst, cuda_core/docs/source/release/1.3.0-notes.rst
Tests cover ordering, callback lifetime, input validation, graph behavior, exceptions, and streams. The example demonstrates a callback between two kernels. The documentation lists and describes host_launch.

Assessment against linked issues

Objective Addressed Explanation
Add a top-level API for scheduling Python callables or C function pointers in stream order [#2058] ✅
Add a corresponding HostNode type in cuda.core.graph._subclasses [#2058] ❌ The change forwards GraphBuilder inputs to GraphBuilder.callback, but does not add a HostNode type.
Document callback threading restrictions, add an example, and add API reference entries [#2058] ✅

Possibly related issues

Priority: Normal

Change: Feature

Merge Risk: Minimal · up to 66c55

The previously missing deadlock warning is present, and no actionable merge-blocking risk remains after normal checks.

- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@Andy-Jost Andy-Jost changed the title Merge remote-tracking branch 'origin/main' into ajost/host-launch feat(cuda.core): add host_launch for stream-ordered host functions Oct 27, 2026
State that a ctypes callback that wraps a Python function acquires the GIL
through ctypes, so the deadlock rule applies to it; complete the Raises
section; say the driver may reject CUDA calls from the callback rather than
that it does. Upcast the launch record to the cleanup base before the void*
parameter, as the graph producers do. Prove the user_data copy by changing
the caller's buffer while the stream is held, pin the unraisable type and
message, and link the example from the examples page.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


Review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 426ec5a and 2c23d6f.

Files selected for processing: 8
  • cuda_core/cuda/core/_cpp/rt/DESIGN.md
  • cuda_core/cuda/core/_cpp/rt/GRAPH_ATTACHMENTS.md
  • cuda_core/cuda/core/_cpp/rt/host_launch.cpp
  • cuda_core/cuda/core/_launcher.pyi
  • cuda_core/cuda/core/_launcher.pyx
  • cuda_core/docs/source/examples.rst
  • cuda_core/docs/source/release/1.3.0-notes.rst
  • cuda_core/tests/test_host_launch.py
Files skipped from review as they are similar to previous changes: 3
  • cuda_core/cuda/core/_cpp/rt/DESIGN.md
  • cuda_core/cuda/core/_cpp/rt/GRAPH_ATTACHMENTS.md
  • cuda_core/docs/source/release/1.3.0-notes.rst

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.

Comment thread cuda_core/cuda/core/_launcher.pyx Outdated
.. warning::

The function must not call CUDA, directly or through a library. The
driver does not permit such calls and may reject them with an error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Stability & Availability · Quick win

suggestion: State that CUDA calls from the callback can deadlock.

The driver does not guarantee an error for a CUDA call from a host callback. Such a call can also deadlock. State that consequence here and regenerate the matching stub documentation. (docs.nvidia.com)

Andy-Jost and others added 2 commits October 28, 2026 14:02
CodeRabbit's review of host_launch: the driver does not guarantee an error
for a CUDA call made from a host function; such a call may also deadlock.
State both outcomes in the warning.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
HANDLE_RETURN(cydriver.cuLaunchKernelEx(&drv_cfg, func_handle, args_ptr, NULL))


def host_launch(stream: Stream | GraphBuilder | IsStreamType, fn, *, user_data=None) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe a type annotation on fn? Definitely add one to user_data which is a lot more restricted than one might expect.

# ################################################################################

# /// script
# dependencies = ["cuda_bindings", "cuda_core"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Since this example doesn't import cuda_bindings directly, I would remove it as a direct dependency.

@Andy-Jost

Copy link
Copy Markdown
Contributor Author

Thanks @mdboom. I'll take both nits in a follow-up PR:

  • Type annotations for fn and user_data. The existing callback methods on GraphBuilder, GraphNode, and GraphDefinition take the same arguments, so I'll annotate all four together for consistency.
  • Remove cuda_bindings from the script dependencies of examples/host_launch.py.

Merging this one as is.

@Andy-Jost
Andy-Jost merged commit 230f0eb into NVIDIA:main Oct 30, 2026
115 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda.core Everything related to the cuda.core module feature New feature or request P1 Medium priority - Should do

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[cuda.core] Add support for host_launch (host callback nodes / host function launches)

2 participants