Skip to content

ProcessPoolExecutor raises exception or hangs the process when shutdown(wait=False) and worker throws exception #132969

Closed

Description

@ogbiggles

Bug report

Bug description:

Below code demonstrates two problems that occur when
(a) worker process throws an exception, and
(b) shutdown(wait=False) is used

  1. When max_workers < number of submitted tasks, raises exception TypeError: object of type 'NoneType' has no len()
  2. When max_workers > number of submitted tasks, raises exception TypeError: object of type 'NoneType' has no len() AND then program hangs

There is no problem when shutdown(wait=True) is used or if the worker processes exit cleanly.

import concurrent.futures
import multiprocessing as mp
import time


def task(n: int) -> int:
    if n == 2:
        raise Exception("Not gonna do it")
    else:
        time.sleep(n)
        return n


def main() -> None:
    # max_workers=2 results in raised exception and program exits
    # max_workers=4 results in raised exception and program hangs
    #
    executor = concurrent.futures.ProcessPoolExecutor(
        max_workers=2, mp_context=mp.get_context("forkserver"), max_tasks_per_child=1
    )
    f1 = executor.submit(task, 1)
    f2 = executor.submit(task, 2)
    f3 = executor.submit(task, 3)
    result = 0
    try:
        result += f1.result()
        result += f2.result()
        result += f3.result()
        print(f"Result = {result}")
    except Exception as e:
        print(f"Exception while getting result : {e}")

    executor.shutdown(wait=False)


if __name__ == "__main__":
    main()

CPython versions tested on:

3.12

Operating systems tested on:

Linux, macOS

Linked PRs

Activity

  1. YvesDup commented on Apr 30, 2025

    @YvesDup
    Contributor

    After deeping in code, it seems to me that the bug is the run method of _ExecutorManagerThread.
    When shutdown with wait set to False is called, this method always tries to ajust the process count.

    if process_exited:
    with self.shutdown_lock:
    executor._adjust_process_count()

    These lines should be replace as below:

             if process_exited:
                  if not self.is_shutting_down():
                      with self.shutdown_lock:
                          executor._adjust_process_count()
    

    This modification fixes the 2 cases

  2. ogbiggles commented on Apr 30, 2025

    @ogbiggles
    ContributorAuthor

    Shutdown wait=False resets the ProcessPoolExecutor's state, hence returning from _adjust_process_counts() when the state has been reset fixes both issues. I submitted a PR #133220 with that fix and a couple of unittests to validate the fix.

    p.s. my PR is on the 3.12 branch, and this is my first time contributing to cpython, so am not sure what steps (if any) I need to follow for porting the fix to 3.13 and main.

  3. ogbiggles commented on May 1, 2025

    @ogbiggles
    ContributorAuthor

    p.s. my PR is on the 3.12 branch, and this is my first time contributing to cpython, so am not sure what steps (if any) I need to follow for porting the fix to 3.13 and main.

    Resubmitted a new pull request on main since the 3.12 branch is closed for bug fixes.

  4. hugovk commented on May 5, 2025

    @hugovk
    Member

    Deferred re: #133222 (comment).

  5. 4 remaining items

  6. added 2 commits that reference this issue on Jun 10, 2025
  7. added 2 commits that reference this issue on Jun 13, 2025
  8. encukou commented on Jun 16, 2025

    @encukou
    Member

    Backported. Thank you @ogbiggles & and all reviewers!

  9. added a commit that references this issue on Jun 19, 2025
  10. added a commit that references this issue on Jul 12, 2025
  11. added a commit that references this issue on Jul 13, 2025
  12. added a commit that references this issue on Jul 13, 2025
  13. added 2 commits that reference this issue on Aug 4, 2025
  14. added 2 commits that reference this issue on Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13only security fixes3.14bugs and security fixes3.15bugs and security fixesdeferred-blockerstdlibStandard Library Python modules in the Lib/ directorytopic-multiprocessingtype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions