threadpool: add ranged Submit overload#34576
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please copy-paste ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
af11966 to
c985542
Compare
src/util/threadpool.h
Outdated
| * uncaught exceptions, as they would otherwise be silently discarded. | ||
| */ | ||
| template <class F> | ||
| [[nodiscard]] util::Expected<std::vector<std::future<std::invoke_result_t<F>>>, SubmitError> SubmitMany(std::vector<F>&& fns) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) |
There was a problem hiding this comment.
General question to think about (no need to answer here):
Is there any use-case for submitting callables with different return types? Because if there is, then we are forced to use a variadic-based approach.
There was a problem hiding this comment.
Can we add a new overload if the need arises?
cdecfa7 to
1593245
Compare
|
Thanks @furszy and @willcl-ark. Taken your suggestions and rebased. |
1593245 to
3295552
Compare
3295552 to
43c5c77
Compare
|
Rebased due to #34562. Addressed nit (#34576 (comment)). Reworded commit message. Only test changes since last push.
|
43c5c77 to
fdddacc
Compare
rkrux
left a comment
There was a problem hiding this comment.
Couple points in the test.
fdddacc to
2852489
Compare
|
Thanks for the review @rkrux. I took your suggestions. Test only changes style changes.
|
sedited
left a comment
There was a problem hiding this comment.
ACK 2852489
Can you fix the include order and some of the formatting issues as reported by clang-format-diff?
I also fuzzed this a bit with the following patch, but quickly stoppped finding new coverage:
Click to expand diff
diff --git a/src/test/fuzz/threadpool.cpp b/src/test/fuzz/threadpool.cpp
index a5b01db138..59e2f1b7a8 100644
--- a/src/test/fuzz/threadpool.cpp
+++ b/src/test/fuzz/threadpool.cpp
@@ -72 +72 @@ FUZZ_TARGET(threadpool, .init = setup_threadpool_test) EXCLUSIVE_LOCKS_REQUIRED(
- const uint32_t num_tasks = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, 1024);
+ uint32_t num_tasks = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, 1024);
@@ -85,0 +86,25 @@ FUZZ_TARGET(threadpool, .init = setup_threadpool_test) EXCLUSIVE_LOCKS_REQUIRED(
+ const bool submit_batch = fuzzed_data_provider.ConsumeBool();
+
+ if (submit_batch) {
+ const uint32_t batch_size = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+ i += batch_size;
+ if (will_throw) {
+ expected_fail_tasks += batch_size;
+ auto batch_futures = *Assert(g_pool.Submit(std::vector<ThrowTask>(batch_size)));
+ for (auto& batch_future : batch_futures) {
+ futures.emplace(std::move(batch_future));
+ }
+ } else {
+ std::vector<CounterTask> tasks;
+ tasks.reserve(batch_size);
+ for (uint32_t j = 0; j < batch_size; ++j) {
+ expected_task_counter++;
+ tasks.emplace_back(task_counter);
+ }
+ auto batch_futures = *Assert(g_pool.Submit(std::move(tasks)));
+ for (auto& batch_future : batch_futures) {
+ futures.emplace(std::move(batch_future));
+ }
+ }
+ continue;
+ }2852489 to
e62bfc2
Compare
|
Thanks for the review and for fuzzing @sedited. I pushed the formatting changes.
|
l0rinc
left a comment
There was a problem hiding this comment.
The current version is probably fine, but it seems to me we can make it more accurate by using range reference type instead of the value type - and iterating with auto&& so the overload handles more callable/range shapes correctly. And maybe handle empty with a lockless short-circuit.
src/util/threadpool.h
Outdated
| LOCK(m_mutex); | ||
| if (m_workers.empty()) return util::Unexpected{SubmitError::Inactive}; | ||
| if (m_interrupt) return util::Unexpected{SubmitError::Interrupted}; | ||
| for (auto& fn : fns) { |
There was a problem hiding this comment.
e62bfc2 threadpool: add ranged Submit overload:
What should the intended behavior be for an empty fns range?
It might be worth making that explicit and short-circuiting the locking, and maybe covering it with a small test.
There was a problem hiding this comment.
I initially did short-circuit it, but it was brought up that it is a behavior change from non-ranged Submit #34576 (comment). I think best to keep behavior identical, so we throw even if fns is empty.
Co-authored-by: l0rinc <pap.lorinc@gmail.com>
e62bfc2 to
79571b9
Compare
|
Thanks for your review @l0rinc. I have taken most of your suggestions.
|
|
ACK 79571b9 |
willcl-ark
left a comment
There was a problem hiding this comment.
ACK 79571b9
I like the template constraints via requires(), very neat :)
I did not test with e.g. 31132 picked, but the code and tests here look good to me.
|
ACK 79571b9 git range-diff e62bfc2...79571b9 |
The current
ThreadPool::Submitis not very efficient when we have a use case where we need to submit multiple tasks immediately. TheSubmitmethod must take the lock for each task, and notifies only a single worker thread. This will cause lock contention with the awakened worker thread trying to take the lock and the caller trying to submit the next task.Introduce a
Submitoverload, which takes the lock once and submits a range of tasks, then notifies all worker threads after the lock is released.This is needed for #31132 to be able to use
ThreadPool.