[3.14] gh-115634: Fix ProcessPoolExecutor deadlock with max_tasks_per_child (GH-140900) (#152927)
gh-115634: Fix ProcessPoolExecutor deadlock with max_tasks_per_child (GH-140900)
The idle worker semaphore counts task completions, not idle workers, so
it can hold a stale token released by a worker that later exited upon
reaching its max_tasks_per_child limit. The worker replacement path
consumed such tokens and skipped spawning a replacement, deadlocking
the remaining queued tasks once no workers were left.
Replace dead workers based on len(self._processes) without consulting
the semaphore. The submit() path is unchanged, preserving on-demand
spawning and idle worker reuse.
Replace the documentation note added in GH-140897 with a versionchanged
entry now that the bug is fixed.
Based on a fix proposed by Tabrez Mohammed.
(cherry picked from commit
b706767d8fd7d21afc3f156fb9c173bc99855e0e)
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>