From b0c28cc002fa50f9277c7a447404bc8073f3bd66 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Thu, 18 Jun 2026 19:06:07 +0200 Subject: [PATCH] Fix schedule_all chunking for small ranges --- include/exec/static_thread_pool.hpp | 11 +++++++---- test/exec/test_static_thread_pool.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index fd638904e..ae664af8c 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -1750,10 +1750,12 @@ namespace experimental::execution return; } - std::size_t nthreads = this->pool_.available_parallelism(); - bwos_params params = this->pool_.params(); - std::size_t local_size = params.blockSize * params.numBlocks; - std::size_t chunk_size = __umin({size / nthreads, local_size * nthreads}); + std::size_t nthreads = this->pool_.available_parallelism(); + STDEXEC_ASSERT(nthreads > 0); + bwos_params params = this->pool_.params(); + std::size_t local_size = params.blockSize * params.numBlocks; + std::size_t chunk_size = __umax( + {std::size_t{1}, __umin({size / nthreads, local_size * nthreads})}); auto& remote_queue = *this->pool_.get_remote_queue(); auto it = std::ranges::begin(this->range_); std::size_t i0 = 0; @@ -1782,6 +1784,7 @@ namespace experimental::execution std::unique_lock lock{this->start_mutex_}; this->pool_.bulk_enqueue(remote_queue, std::move(this->tasks_), this->tasks_size_); + this->tasks_size_ = 0; lock.unlock(); i0 += chunk_size; } diff --git a/test/exec/test_static_thread_pool.cpp b/test/exec/test_static_thread_pool.cpp index dc328a9d9..dde1f85f3 100644 --- a/test/exec/test_static_thread_pool.cpp +++ b/test/exec/test_static_thread_pool.cpp @@ -1,8 +1,10 @@ #include "catch2/catch_all.hpp" #include +#include #include #include +#include #include #include #include @@ -120,6 +122,30 @@ TEST_CASE("schedule_all on static_thread_pool sends errors from set_next", } #endif +TEST_CASE("schedule_all on static_thread_pool handles ranges smaller than available parallelism", + "[types][static_thread_pool]") +{ + constexpr size_t const num_of_threads = 5; + constexpr int const range_size = 3; + + exec::static_thread_pool pool{num_of_threads}; + REQUIRE(range_size < pool.available_parallelism()); + + std::atomic count{0}; + std::atomic sum{0}; + auto sender = exec::schedule_all(pool, std::views::iota(0, range_size)) + | exec::transform_each(ex::then( + [&](int x) noexcept + { + count.fetch_add(1, std::memory_order_relaxed); + sum.fetch_add(x, std::memory_order_relaxed); + })) + | exec::ignore_all_values(); + + CHECK(ex::sync_wait(std::move(sender))); + CHECK(count.load(std::memory_order_relaxed) == range_size); + CHECK(sum.load(std::memory_order_relaxed) == 3); +} TEST_CASE("bulk on static_thread_pool executes on multiple threads, take 2", "[types][static_thread_pool]") {