Visitar URL original
multiprocessing.Pool shouldn't hang forever if a worker process dies unexpectedly · Issue #66587 · python/cpython · GitHub
Skip to content

multiprocessing.Pool shouldn't hang forever if a worker process dies unexpectedly #66587

Open

Description

@danoreilly
BPO 22393
Nosy @pitrou, @applio, @oesteban, @rkm, @kormang, @shnizzedy
PRs
  • bpo-22393: Fix multiprocessing.Pool hangs if a worker process dies unexpectedly #10441
  • gh-66587: Fix deadlock from pool worker death without communication #16103
  • Files
  • multiproc_broken_pool.diff: Abort running task and close down a pool if a worker is unexpectedly terminated.
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2014-09-11.22:33:06.739>
    labels = ['3.8', 'type-feature', 'library']
    title = "multiprocessing.Pool shouldn't hang forever if a worker process dies unexpectedly"
    updated_at = <Date 2021-10-18.03:11:08.813>
    user = 'https://bugs.python.org/danoreilly'

    bugs.python.org fields:

    activity = <Date 2021-10-18.03:11:08.813>
    actor = 'myles.steinhauser'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2014-09-11.22:33:06.739>
    creator = 'dan.oreilly'
    dependencies = []
    files = ['36603']
    hgrepos = []
    issue_num = 22393
    keywords = ['patch']
    message_count = 11.0
    messages = ['226805', '294968', '315684', '315687', '329381', '329383', '329770', '333895', '351754', '390775', '390780']
    nosy_count = 14.0
    nosy_names = ['pitrou', 'jnoller', 'cvrebert', 'sbt', 'dan.oreilly', 'davin', 'brianboonstra', 'cjmarkie', 'Francis Bolduc', 'oesteban', 'rkm', 'kormang', 'shnizzedy', 'myles.steinhauser']
    pr_nums = ['10441', '16103']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue22393'
    versions = ['Python 3.8']

    Activity

    1. danoreilly commented on Sep 11, 2014

      danoreillymannequin
      MannequinAuthor

      This is essentially a dupe of bpo-9205, but it was suggested I open a new issue, since that one ended up being used to fix this same problem in concurrent.futures, and was subsequently closed.

      Right now, should a worker process in a Pool unexpectedly get terminated while a blocking Pool method is running (e.g. apply, map), the method will hang forever. This isn't a normal occurrence, but it does occasionally happen (either because someone sends a SIGTERM, or because of a bug in the interpreter or a C-extension). It would be preferable for multiprocessing to follow the lead of concurrent.futures.ProcessPoolExecutor when this happens, and abort all running tasks and close down the Pool.

      Attached is a patch that implements this behavior. Should a process in a Pool unexpectedly exit (meaning, *not* because of hitting the maxtasksperchild limit), the Pool will be closed/terminated and all cached/running tasks will raise a BrokenProcessPool exception. These changes also prevent the Pool from going into a bad state if the "initializer" function raises an exception (previously, the pool would end up infinitely starting new processes, which would immediately die because of the exception).

      One concern with the patch: The way timings are altered with these changes, the Pool seems to be particularly susceptible to bpo-6721 in certain cases. If processes in the Pool are being restarted due to maxtasksperchild just as the worker is being closed or joined, there is a chance the worker will be forked while some of the debug logging inside of Pool is running (and holding locks on either sys.stdout or sys.stderr). When this happens, the worker deadlocks on startup, which will hang the whole program. I believe the current implementation is susceptible to this as well, but I could reproduce it much more consistently with this patch. I think its rare enough in practice that it shouldn't prevent the patch from being accepted, but thought I should point it out.

      (I do think bpo-6721 should be addressed, or at the very least internal I/O locks should always reset after forking.)

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-featureA feature request or enhancement
      on Sep 11, 2014
    3. FrancisBolduc commented on Jun 1, 2017

      FrancisBolducmannequin
      Mannequin

      This problem also happens simply by calling sys.exit from one of the child processes.

      The following script exhibits the problem:

      import multiprocessing
      import sys
      def test(value):
          if value:
              sys.exit(123)
      if __name__ == '__main__':
          pool = multiprocessing.Pool(4)
          cases = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
          pool.map(test, cases)
    4. oesteban commented on Apr 24, 2018

      oestebanmannequin
      Mannequin

      We use multiprocessing to parallelize many tasks that run either python code or call subprocess.run that are memory hungry.

      At times the OOM Killer kicks in. When one of the workers is killed, the queue never returns an error for the task being run by the worker.

      Are there any plans to merge the patch proposed in this issue?

    5. pitrou commented on Apr 24, 2018

      @pitrou
      Member

      Oscar, the patch posted here needs updating for the latest git master.

      If you want to avoid this issue, you can also use concurrent.futures where the issue is fixed.

    6. oesteban commented on Nov 6, 2018

      oestebanmannequin
      Mannequin

      Hi Antoine,

      I may take a stab at it. Before I start, should I branch from master or from 3.7.1 (as 3.7 is still accepting bugfixes).

      Best,
      Oscar

    7. pitrou commented on Nov 6, 2018

      @pitrou
      Member

      You should start from master. Bugfixes can backported afterwards if appropriate. Thanks!

    8. oesteban commented on Nov 12, 2018

      oestebanmannequin
      Mannequin

      I tried to reuse as much as I could from the patch, but it didn't solve the issue at first.

      I have changed the responsibility of identifying and prescribing a solution when a worker got killed. In the proposed patch, the thread handling results (i.e. tasks queued by one worker as done) was responsible. In the PR, the responsibility is reassigned to the thread handling workers (since, basically, one or more workers suddenly die).

      The patch defined a new BROKEN state that was assigned to the results handler thread. I transferred this behavior to the worker handler thread. But, I'm guessing that the BROKEN state should be assigned to the Pool object instead, to be fully semantic. Although that would require passing the reference to the object around and complicate unnecessarily the implementation. Happy to reconsider though.

      I added three tests, one that was present with the patch, a variation of it adding some wait before killing the worker, and the one that Francis Bolduc posted here (https://bugs.python.org/issue22393#msg294968).

      Please let me know whether any conversation about this bug should take place in GitHub, with the PR instead of here.

      Thanks a lot for the guidance, Antoine.

    9. cjmarkie commented on Jan 17, 2019

      cjmarkiemannequin
      Mannequin

      Just a bump to note that the PR (10441) is ready for another round of review.

    10. vstinner commented on Sep 10, 2019

      @vstinner
      Member

      I just marked bpo-38084 as duplicate of this issue. I manually merged the nosy lists.

    11. kormang commented on Apr 11, 2021

      kormangmannequin
      Mannequin

      I've created bpo-43805. I think it would be better to have universal solution. And not specific ones, like in bpo-9205.

      Haven't checked the PRs, though.

    12. kormang commented on Apr 11, 2021

      kormangmannequin
      Mannequin

      Somewhat related bpo-43806 with asyncio.StreamReader

    13. transferred this issue fromon Apr 10, 2022
    14. 8 remaining items

    15. dobos commented on Jul 6, 2023

      @dobos

      Here's my solution: dobos@281291a

    16. added a commit that references this issue on May 3, 2024
    17. self-assigned this
      on May 20, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-multiprocessingtype-featureA feature request or enhancement

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions