Skip to content

nvexec: forward reference results from then, upon_error and upon_stopped - #2410

Open
alwaysprince05 wants to merge 1 commit into
NVIDIA:mainfrom
alwaysprince05:fix/nvexec-then-reference-958
Open

alwaysprince05 wants to merge 1 commit into
NVIDIA:mainfrom
alwaysprince05:fix/nvexec-then-reference-958

Conversation

@alwaysprince05

Copy link
Copy Markdown
Contributor

Fixes #958.

When the callable passed to nvexec::then, upon_error or upon_stopped returns a reference, the sender advertises set_value_t(T&) but the receiver was sent a decayed copy (std::move(*d_result)). So something like

ex::schedule(sch)
  | ex::then([p]() -> int& { return *p; })
  | ex::then([](int& r) { r = 7; });

failed to compile on a stream scheduler (common.cuh:776: no matching function for call to object of type 'stdexec::set_value_t'), while the same code works with inline_scheduler.

Changes

  • common.cuh: small helpers for how a result is kept in temp storage. A reference result is stored as a pointer to the referenced object, anything else as a decayed value like before. result_size_for uses the stored size.
  • then, upon_error, upon_stopped: the kernel stores the result through that helper, and the reference is passed on from the stored pointer.
  • A reference result waits for the stream (cudaStreamSynchronize) before it is passed on, because the address is only known after the kernel has run. Value results don't change. (Discussed on nvexec::then / upon_error / upon_stopped should handle callables that return by reference #958.)
  • then's set_value constraint is now std::invocable<Fun, Args...> instead of std::invocable<Fun, __decay_t<Args>...>, same as upon_error.

Tests

New cases in test/nvexec/then.cpp (int& and const int&), upon_error.cpp and upon_stopped.cpp (int&). Each one writes through or reads from the returned reference in the next step.

Tested on a T4 with clang 20 + CUDA 12.9, on top of main 697cd69:

  • without the header changes, all three test files fail to compile with the error above
  • with the fix, test.nvexec passes: 128 test cases / 779 assertions, 3 runs
  • compute-sanitizer on the new tests: memcheck 0 errors / 0 bytes leaked, racecheck 0 hazards
  • clang-format 21 clean

When the callable returns a reference, the kernel now stores a pointer
to the referenced object in temporary storage, and the reference is
passed on from that pointer. Before, the result was decay-copied, so
the receiver got a prvalue while the sender advertised
set_value_t(T&), and code like then(f -> int&) | then(g(int&)) did not
compile.

The address is only known after the kernel has run, so a reference
result waits for the stream (cudaStreamSynchronize) before it is
passed on. Value results are unchanged.

then's set_value constraint now checks the callable against the
arguments as they are passed, like upon_error already does.

Fixes NVIDIA#958
@copy-pr-bot

copy-pr-bot Bot commented Nov 5, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@ericniebler

Copy link
Copy Markdown
Collaborator

/ok to test b5fce10

@ericniebler

Copy link
Copy Markdown
Collaborator

unrelated: would you mind looking at the failure on #2277? my CUDA setup is borked at the moment. i don't know why that PR would cause the maxwell_gpu_s example program to crash.

@alwaysprince05

Copy link
Copy Markdown
Contributor Author

Thanks for the review!

Sure, I'll look at #2277. From the CI log, the crash is in the stream scheduler part of maxwell_gpu_s (it prints the GPU (cuda) line, then segfaults), and only in the nvc++ 26.9 / CUDA 13.3 Debug job; the same job passes on main. I'll try to reproduce it on a T4 and post what I find on #2277.

About the red checks here: all 14 failed before anything was compiled. Some got a 504 from github.com while rapids_cpm_init was downloading, the rest failed installing sccache (gh asked for GH_TOKEN). Other PRs that ran a few minutes later passed, so it looks like a temporary GitHub problem. Could you re-run the failed jobs when you get a chance?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nvexec::then / upon_error / upon_stopped should handle callables that return by reference

2 participants