Repository navigation
libutil: don't make ThreadPool work items wait for process() - #718
Open
czawistowski-nvidia wants to merge 1 commit into
Open
czawistowski-nvidia wants to merge 1 commit into
czawistowski-nvidia wants to merge 1 commit into
Conversation
`enqueue()` counted the thread that will call `process()` as a worker, so it only started a new thread once there were more pending items than workers plus one. Before `process()` is called, that thread isn't executing anything, so a single enqueued item wasn't executed until `process()` was called (or until more items were enqueued). This makes it impossible to wait for the completion of an item before calling `process()`. Only count the calling thread once `process()` has been called, and add a unit test. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 3 billable files and costs up to $0.75. Or wait 57 minutes for your next included review. View limit details
Comment |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
ThreadPool::enqueue()counts the thread that will later callprocess()as a worker, even before that thread has calledprocess(). It starts a new worker only whenpending > workers + 1, so beforeprocess():The header describes
process()as "Execute work items until the queue is empty", and nothing in the API says enqueued work is held back until then. A caller that enqueues work and waits for it some other way (a future, a condition variable, an in-flight counter) before callingprocess()hangs.As far as we can tell, no in-tree caller hits this today. Every one either calls
process()right after enqueueing, or keeps enqueueing so a second item soon starts a worker. We ran into it while working on GC deletion in background threads (a follow-up we plan to send), which waits for in-flight deletions before draining the pool. There,nix-store --gc --max-freed Xhung whenever the first dead path was already larger than X.Implementation
Count the calling thread only once
process()has started (state->draining, already set byprocess()under the same lock):process()has started: spawning is identical to before, so all existing fan-out is unchanged.workers.size() + 1 < maxThreadsstill reserves the caller's slot, andThreadPool(1)still runs everything on the caller insideprocess().process()are still rethrown byprocess().Context
New unit test
ThreadPool.executesWorkBeforeProcessenqueues one item into aThreadPool(2)and waits for it before callingprocess(). Onmainthe item never starts, so the test fails when its wait times out; with this change it passes.🤖 Claude