Skip to content

Make function's declared stop-token query a plain requirement - #2382

Open
ispeters wants to merge 2 commits into
NVIDIA:mainfrom
ispeters:regularize-stop-token-query
Open

ispeters wants to merge 2 commits into
NVIDIA:mainfrom
ispeters:regularize-stop-token-query

Conversation

@ispeters

Copy link
Copy Markdown
Contributor

Depends on #2364.

exec::function shares its receiver type erasure with exec::any_sender, including any_sender's stop-token bridging. When the erased receiver's queries include inplace_stop_token(get_stop_token_t) noexcept, the concrete receiver is stored in a state that owns an inplace_stop_source and forwards the receiver's stop requests to it from a callback. That doesn't work for function:

  • the bridging state isn't movable, so it isn't a receiver, and the type-erased receiver can't be constructed from it; no function that declares the query can be connected at all, and
  • function's operation state never registers the forwarding callback, so even with the first problem fixed, stop requests never reach the implementation.

The type-erasure checks also accept, for that query, a receiver whose stop token is a never_stop_token, answering with a token that is never stopped, and accept never_stop_token(get_stop_token_t) noexcept whatever the receiver's stop token.

This diff stops function using the bridge. A declared stop-token query is now a requirement on the receiver's environment like any other declared query: the environment must answer it with a token that converts to the declared type, and the type-erased receiver answers it by converting the receiver's own token, so stop requests reach the implementation directly. A receiver whose stop token doesn't convert is rejected. Since get_stop_token answers never_stop_token for an environment without a stop token, such an environment satisfies a declared never_stop_token and not a declared inplace_stop_token.

any_sender keeps its bridging and its more permissive checks unchanged: the shared check is split into the requirement that function uses and any_sender's relaxation of it for the stop-token queries. Changing any_sender to match would break code that relies on the bridge.

ispeters and others added 2 commits October 28, 2026 19:27
A type-erased receiver answers a declared query `R(Q, Args...)` by
calling `Q()(get_env(rcvr), args...)` on the wrapped receiver's
environment, but type erasure checks something narrower: whether the
environment has a `query` member for `Q`. The two differ for a query
with a default answer. For example, `get_domain(env)` answers
`default_domain` for an environment with no `get_domain` member, so an
`exec::any_sender` or `exec::function` declaring
`default_domain(get_domain_t) noexcept` can answer the query in such an
environment, but can't be connected there.

This diff checks the call instead: the environment satisfies a declared
query when calling the query object on it is well-formed, converts to
the declared result type and, if the declaration is `noexcept`, isn't
potentially throwing. For the standard queries, the two checks agree
wherever the environment has its own `query` member, so every
environment accepted before is still accepted; what's new is that a
query's default answer now counts. Only `get_domain` and `get_stop_token`
have one, and `any_sender`'s existing special cases for `get_stop_token`
still decide those declarations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`exec::function` shares its receiver type erasure with `exec::any_sender`,
including `any_sender`'s stop-token bridging. When the erased receiver's
queries include `inplace_stop_token(get_stop_token_t) noexcept`, the
concrete receiver is stored in a state that owns an `inplace_stop_source`
and forwards the receiver's stop requests to it from a callback. That
doesn't work for `function`:

- the bridging state isn't movable, so it isn't a receiver, and the
  type-erased receiver can't be constructed from it; no `function` that
  declares the query can be connected at all, and
- `function`'s operation state never registers the forwarding callback,
  so even with the first problem fixed, stop requests never reach the
  implementation.

The type-erasure checks also accept, for that query, a receiver whose
stop token is a `never_stop_token`, answering with a token that is never
stopped, and accept `never_stop_token(get_stop_token_t) noexcept`
whatever the receiver's stop token.

This diff stops `function` using the bridge. A declared stop-token query
is now a requirement on the receiver's environment like any other
declared query: the environment must answer it with a token that
converts to the declared type, and the type-erased receiver answers it
by converting the receiver's own token, so stop requests reach the
implementation directly. A receiver whose stop token doesn't convert is
rejected. Since `get_stop_token` answers `never_stop_token` for an
environment without a stop token, such an environment satisfies a
declared `never_stop_token` and not a declared `inplace_stop_token`.

`any_sender` keeps its bridging and its more permissive checks
unchanged: the shared check is split into the requirement that
`function` uses and `any_sender`'s relaxation of it for the stop-token
queries. Changing `any_sender` to match would break code that relies on
the bridge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 29, 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.

@ispeters

Copy link
Copy Markdown
Contributor Author

@SamGaaWaa, once this merges, you should be able to use fumction instead of task with stop tokens passed through if that suits you.

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.

1 participant