|
26 | 26 | #include <exception> |
27 | 27 | #include <latch> |
28 | 28 | #include <mutex> |
| 29 | +#include <optional> |
29 | 30 | #include <ranges> |
30 | 31 | #include <stdexcept> |
31 | 32 | #include <thread> |
@@ -234,86 +235,106 @@ TEST_CASE("bulk on static_thread_pool executes on multiple threads, take 2", |
234 | 235 | REQUIRE(thread_ids.size() == num_of_threads); |
235 | 236 | } |
236 | 237 |
|
237 | | -TEST_CASE("static_thread_pool drains remote work after idle transitions", |
238 | | - "[types][static_thread_pool][stress]") |
| 238 | +namespace |
239 | 239 | { |
240 | | - constexpr std::size_t num_producers = 4; |
241 | | - constexpr std::size_t rounds = 10'000; |
242 | | - |
243 | | - std::latch ready{num_producers}; |
244 | | - std::atomic<bool> start{false}; |
245 | | - std::atomic<bool> stop{false}; |
246 | | - std::vector<std::atomic<std::size_t>> completed(num_producers); |
247 | | - std::vector<std::thread> producers; |
248 | | - producers.reserve(num_producers); |
249 | | - for (auto& count: completed) |
| 240 | + void run_remote_poll_stress(bool separate_schedulers) |
250 | 241 | { |
251 | | - count.store(0, std::memory_order_relaxed); |
252 | | - } |
253 | | - |
254 | | - exec::static_thread_pool pool{1}; |
255 | | - auto scheduler = pool.get_scheduler(); |
| 242 | + constexpr std::size_t num_producers = 4; |
| 243 | + constexpr std::size_t rounds = 10'000; |
| 244 | + |
| 245 | + std::latch ready{num_producers}; |
| 246 | + std::atomic<bool> start{false}; |
| 247 | + std::atomic<bool> stop{false}; |
| 248 | + std::vector<std::atomic<std::size_t>> completed(num_producers); |
| 249 | + std::vector<std::thread> producers; |
| 250 | + producers.reserve(num_producers); |
| 251 | + for (auto& count: completed) |
| 252 | + { |
| 253 | + count.store(0, std::memory_order_relaxed); |
| 254 | + } |
256 | 255 |
|
257 | | - for (std::size_t producer = 0; producer < num_producers; ++producer) |
258 | | - { |
259 | | - producers.emplace_back( |
260 | | - [&, producer] |
261 | | - { |
262 | | - ready.count_down(); |
263 | | - while (!start.load(std::memory_order_acquire)) |
264 | | - { |
265 | | - std::this_thread::yield(); |
266 | | - } |
| 256 | + exec::static_thread_pool pool{1}; |
| 257 | + using scheduler_t = decltype(pool.get_scheduler()); |
| 258 | + std::optional<scheduler_t> shared_scheduler; |
| 259 | + if (!separate_schedulers) |
| 260 | + { |
| 261 | + shared_scheduler.emplace(pool.get_scheduler()); |
| 262 | + } |
267 | 263 |
|
268 | | - auto* const producer_completed = &completed[producer]; |
269 | | - std::size_t expected = 0; |
270 | | - for (std::size_t round = 0; round < rounds && !stop.load(std::memory_order_relaxed); |
271 | | - ++round) |
| 264 | + for (std::size_t producer = 0; producer < num_producers; ++producer) |
| 265 | + { |
| 266 | + producers.emplace_back( |
| 267 | + [&, producer] |
272 | 268 | { |
273 | | - std::size_t const batch_size = (round % 4 == 0) ? 2 : 1; |
274 | | - expected += batch_size; |
275 | | - for (std::size_t i = 0; i < batch_size; ++i) |
| 269 | + auto scheduler = separate_schedulers ? pool.get_scheduler() : *shared_scheduler; |
| 270 | + ready.count_down(); |
| 271 | + while (!start.load(std::memory_order_acquire)) |
276 | 272 | { |
277 | | - exec::start_detached( |
278 | | - ex::schedule(scheduler) |
279 | | - | ex::then([producer_completed] |
280 | | - { producer_completed->fetch_add(1, std::memory_order_relaxed); })); |
| 273 | + std::this_thread::yield(); |
281 | 274 | } |
282 | 275 |
|
283 | | - while (!stop.load(std::memory_order_relaxed) |
284 | | - && producer_completed->load(std::memory_order_relaxed) < expected) |
| 276 | + auto* const producer_completed = &completed[producer]; |
| 277 | + std::size_t expected = 0; |
| 278 | + for (std::size_t round = 0; round < rounds && !stop.load(std::memory_order_relaxed); |
| 279 | + ++round) |
285 | 280 | { |
| 281 | + std::size_t const batch_size = (round % 4 == 0) ? 2 : 1; |
| 282 | + expected += batch_size; |
| 283 | + for (std::size_t i = 0; i < batch_size; ++i) |
| 284 | + { |
| 285 | + exec::start_detached( |
| 286 | + ex::schedule(scheduler) |
| 287 | + | ex::then([producer_completed] |
| 288 | + { producer_completed->fetch_add(1, std::memory_order_relaxed); })); |
| 289 | + } |
| 290 | + |
| 291 | + while (!stop.load(std::memory_order_relaxed) |
| 292 | + && producer_completed->load(std::memory_order_relaxed) < expected) |
| 293 | + { |
| 294 | + std::this_thread::yield(); |
| 295 | + } |
286 | 296 | std::this_thread::yield(); |
287 | 297 | } |
288 | | - std::this_thread::yield(); |
289 | | - } |
290 | | - }); |
291 | | - } |
| 298 | + }); |
| 299 | + } |
292 | 300 |
|
293 | | - ready.wait(); |
294 | | - start.store(true, std::memory_order_release); |
| 301 | + ready.wait(); |
| 302 | + start.store(true, std::memory_order_release); |
295 | 303 |
|
296 | | - auto const expected = num_producers * rounds + num_producers * ((rounds + 3) / 4); |
297 | | - auto const deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); |
298 | | - auto completed_total = [&] |
299 | | - { |
300 | | - std::size_t result = 0; |
301 | | - for (auto const & count: completed) |
| 304 | + auto const expected = num_producers * rounds + num_producers * ((rounds + 3) / 4); |
| 305 | + auto const deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); |
| 306 | + auto completed_total = [&] |
302 | 307 | { |
303 | | - result += count.load(std::memory_order_relaxed); |
| 308 | + std::size_t result = 0; |
| 309 | + for (auto const & count: completed) |
| 310 | + { |
| 311 | + result += count.load(std::memory_order_relaxed); |
| 312 | + } |
| 313 | + return result; |
| 314 | + }; |
| 315 | + |
| 316 | + while (completed_total() < expected && std::chrono::steady_clock::now() < deadline) |
| 317 | + { |
| 318 | + std::this_thread::yield(); |
| 319 | + } |
| 320 | + stop.store(true, std::memory_order_release); |
| 321 | + for (auto& producer: producers) |
| 322 | + { |
| 323 | + producer.join(); |
304 | 324 | } |
305 | | - return result; |
306 | | - }; |
307 | 325 |
|
308 | | - while (completed_total() < expected && std::chrono::steady_clock::now() < deadline) |
309 | | - { |
310 | | - std::this_thread::yield(); |
311 | | - } |
312 | | - stop.store(true, std::memory_order_release); |
313 | | - for (auto& producer: producers) |
314 | | - { |
315 | | - producer.join(); |
| 326 | + CHECK(completed_total() == expected); |
316 | 327 | } |
| 328 | +} // namespace |
| 329 | + |
| 330 | +TEST_CASE("static_thread_pool drains remote work from a shared scheduler", |
| 331 | + "[types][static_thread_pool][stress]") |
| 332 | +{ |
| 333 | + run_remote_poll_stress(false); |
| 334 | +} |
317 | 335 |
|
318 | | - CHECK(completed_total() == expected); |
| 336 | +TEST_CASE("static_thread_pool drains remote work from producer schedulers", |
| 337 | + "[types][static_thread_pool][stress]") |
| 338 | +{ |
| 339 | + run_remote_poll_stress(true); |
319 | 340 | } |
0 commit comments