-
Notifications
You must be signed in to change notification settings - Fork 208
Open
Description
Description
When using any_sequence_of to type-erase a sequence sender, the async_scope::request_stop() mechanism fails to stop the sequence generation, while concrete-typed sequence senders work correctly.
code
#include <iostream>
#include <chrono>
#include <ranges>
#include <atomic>
#include <stdexec/execution.hpp>
#include <exec/async_scope.hpp>
#include <exec/sequence/iterate.hpp>
#include <exec/sequence/transform_each.hpp>
#include <exec/sequence/ignore_all_values.hpp>
#include <exec/sequence/any_sequence_of.hpp>
#include <exec/timed_thread_scheduler.hpp>
using namespace std::chrono_literals;
namespace ex = stdexec;
template<class... Sigs>
using any_sequence_of = typename exec::any_sequence_receiver_ref<
ex::completion_signatures<Sigs...>>::template any_sender<>;
using string_sequence = any_sequence_of<
ex::set_value_t(std::string),
ex::set_error_t(std::exception_ptr),
ex::set_stopped_t()>;
auto generate_concrete_sequence(ex::scheduler auto sched)
{
return exec::iterate(std::views::iota(0))
| exec::transform_each(ex::let_value([sched](int i) {
return exec::schedule_after(sched, 100ms)
| ex::then([i]() {
return std::to_string(i);
});
}));
}
string_sequence generate_erased_sequence(ex::scheduler auto sched)
{
return generate_concrete_sequence(sched);
}
int main()
{
exec::timed_thread_context timer_ctx;
auto time_sched = timer_ctx.get_scheduler();
exec::async_scope scope;
std::atomic<int> erased_counter{0};
std::atomic<int> concrete_counter{0};
auto process_a = generate_concrete_sequence(time_sched)
| exec::transform_each(ex::then([&](const std::string& str) {
concrete_counter.fetch_add(1);
std::cout << "A: " << str << std::endl;
}))
| exec::ignore_all_values();
auto process_b = generate_erased_sequence(time_sched)
| exec::transform_each(ex::then([&](const std::string& str) {
erased_counter.fetch_add(1);
std::cout << "B: " << str << std::endl;
}))
| exec::ignore_all_values();
auto timeout = exec::schedule_after(time_sched, 1s)
| ex::then([&]() {
std::cout << "\n=== Timeout - requesting stop ===" << std::endl;
scope.request_stop();
});
scope.spawn(std::move(process_a));
scope.spawn(std::move(process_b));
scope.spawn(std::move(timeout));
ex::sync_wait(scope.on_empty());
std::cout << "\n=== Results ===" << std::endl;
std::cout << "Concrete sequence counter: " << concrete_counter.load() << std::endl;
std::cout << "Erased sequence counter: " << erased_counter.load() << std::endl;
std::cout << "\nExpected: Both should stop at ~10 iterations (1s / 100ms)" << std::endl;
std::cout << "Actual: Concrete stops correctly, Erased continues indefinitely" << std::endl;
return 0;
}Expected:
- After 1 second, scope.request_stop() is called
- Both sequences should stop generating new elements
Actual:
- Concrete-typed sequence stops correctly
- Type-erased sequence continues indefinitely
Metadata
Metadata
Assignees
Labels
No labels