Skip to content

fix(execution): correct sends_stopped in bulk and schedule_from completion signatures#7236

Open
arpittkhandelwal wants to merge 1 commit intoTheHPXProject:masterfrom
arpittkhandelwal:fix/sender-completion-sig-sends-stopped
Open

fix(execution): correct sends_stopped in bulk and schedule_from completion signatures#7236
arpittkhandelwal wants to merge 1 commit intoTheHPXProject:masterfrom
arpittkhandelwal:fix/sender-completion-sig-sends-stopped

Conversation

@arpittkhandelwal
Copy link
Copy Markdown
Contributor

Problem Summary

bulk_sender and schedule_from_sender incorrectly hard-coded:

static constexpr bool sends_stopped = false;

However, both internally propagate cancellation (set_stopped)
from upstream senders to downstream receivers.

This creates a mismatch between the static contract (completion signatures)
and runtime behavior, violating the P2300 std::execution specification
and leading to undefined behavior.

Why This Matters

Completion signatures define what signals a sender may emit.

If sends_stopped is declared false:

  • Downstream receivers may NOT implement set_stopped()
  • But at runtime, set_stopped() is still invoked
  • This leads to calls into receivers that are not prepared

=> Undefined behavior

Fix: bulk.hpp

bulk should transparently propagate stopping behavior
from its upstream sender.

BEFORE:
static constexpr bool sends_stopped = false;

AFTER:
static constexpr bool sends_stopped =
sends_stopped_of_v<Sender, Env>;

Fix: schedule_from.hpp

schedule_from can receive cancellation from:

  1. Predecessor sender
  2. Scheduler sender

So it must reflect both.

BEFORE:
static constexpr bool sends_stopped = false;

AFTER:
static constexpr bool sends_stopped =
sends_stopped_of_v<Sender, Env> ||
sends_stopped_of_v<scheduler_sender_type, Env>;

Not Changed

as_sender, keep_future:

These wrap HPX futures, which:

  • Do NOT support cancellation
  • Never call set_stopped()

So this remains correct:

static constexpr bool sends_stopped = false;

Tests Added

File:
libs/core/execution/tests/unit/algorithm_bulk.cpp

--- Static Tests ---

  1. Non-stopping upstream -> remains false
    static_assert(
    !check_sends_stopped<
    bulk(just(42), {})

    ()
    );

  2. Stopping upstream -> propagates true
    static_assert(
    check_sends_stopped<
    bulk(stopped_sender_with_value_type{}, {})

    ()
    );

  3. Chained bulk layers propagate correctly
    static_assert(
    check_sends_stopped<
    bulk(
    bulk(
    stopped_sender_with_value_type{},
    {}
    ),
    {}
    )

    ()
    );

--- Runtime Test ---

Ensure set_stopped is actually delivered

{
auto sender =
bulk(
stopped_sender_with_value_type{},
{}
);

auto op = connect(sender, expect_stopped_receiver{});
start(op);

}

Key Insight

Senders must NEVER lie in their completion signatures.

If set_stopped can happen at runtime,
sends_stopped MUST be true at compile time.

bulk and schedule_from are transparent propagators,
so their sends_stopped must reflect upstream behavior.

…etion signatures

bulk_sender and schedule_from_sender hard-coded sends_stopped = false in their
generate_completion_signatures struct, even though their internal receivers
unconditionally propagate set_stopped from the upstream sender to the downstream
receiver.

This breaks the Sender/Receiver contract (P2300): a downstream algorithm that
trusts sends_stopped=false may not install a stop handler, causing set_stopped
to arrive on an unprepared receiver — potential undefined behaviour.

Fix bulk_sender to inherit sends_stopped from the upstream:
  static constexpr bool sends_stopped = sends_stopped_of_v<Sender, Env>;

Fix schedule_from_sender to reflect that cancellation can arrive from either
the predecessor sender or the scheduler sender:
  static constexpr bool sends_stopped =
      sends_stopped_of_v<Sender, Env> ||
      sends_stopped_of_v<scheduler_sender_type, Env>;

Note: as_sender and keep_future correctly retain sends_stopped=false because
HPX futures have no cancellation mechanism — their receivers never call
set_stopped, so the existing value is accurate and is not changed.

Add tests to algorithm_bulk.cpp that verify:
- bulk over a non-stopping upstream still reports sends_stopped=false
- bulk over a stopped_sender_with_value_type now reports sends_stopped=true
- Chained bulk layers correctly propagate sends_stopped=true
- At runtime, set_stopped is delivered to the downstream receiver
@arpittkhandelwal arpittkhandelwal requested a review from hkaiser as a code owner May 1, 2026 05:18
Copilot AI review requested due to automatic review settings May 1, 2026 05:18
@codacy-production
Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes incorrect completion signature contracts in HPX std::execution-style algorithms by ensuring bulk and schedule_from correctly advertise sends_stopped when they can propagate set_stopped at runtime, aligning the static sender contract with actual behavior and avoiding undefined behavior.

Changes:

  • Update bulk_sender completion signatures to propagate sends_stopped from the upstream sender.
  • Update schedule_from_sender completion signatures to propagate sends_stopped from both the predecessor sender and the scheduler’s schedule() sender.
  • Add unit coverage in algorithm_bulk.cpp to validate bulk’s corrected sends_stopped behavior (static checks + runtime stopped delivery).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
libs/core/execution/include/hpx/execution/algorithms/bulk.hpp Fix bulk_sender completion signatures to reflect upstream sends_stopped.
libs/core/execution/include/hpx/execution/algorithms/schedule_from.hpp Fix schedule_from_sender completion signatures to reflect stopped propagation from predecessor and scheduler sender.
libs/core/execution/tests/unit/algorithm_bulk.cpp Add tests verifying bulk now correctly advertises and forwards set_stopped (non-stdexec path).

Comment on lines +64 to +66
static constexpr bool sends_stopped =
sends_stopped_of_v<Sender, Env> ||
sends_stopped_of_v<scheduler_sender_type, Env>;
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.

2 participants