Skip to content

subprocess wait() hangs when stdin is closed #56995

Closed

Description

@idank
mannequin
BPO 12786
Nosy @pitrou, @vstinner
Files
  • subprocess_cloexec.diff
  • subprocess_cloexec-1.diff
  • 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 = <Date 2011-08-25.20:17:25.956>
    created_at = <Date 2011-08-19.16:31:01.984>
    labels = ['type-bug', 'library']
    title = 'subprocess wait() hangs when stdin is closed'
    updated_at = <Date 2011-08-25.20:17:25.955>
    user = 'https://bugs.python.org/idank'

    bugs.python.org fields:

    activity = <Date 2011-08-25.20:17:25.955>
    actor = 'neologix'
    assignee = 'none'
    closed = True
    closed_date = <Date 2011-08-25.20:17:25.956>
    closer = 'neologix'
    components = ['Library (Lib)']
    creation = <Date 2011-08-19.16:31:01.984>
    creator = 'idank'
    dependencies = []
    files = ['22951', '23032']
    hgrepos = []
    issue_num = 12786
    keywords = ['patch', 'needs review']
    message_count = 11.0
    messages = ['142475', '142483', '142485', '142501', '142559', '142914', '142917', '142937', '142965', '142987', '142990']
    nosy_count = 8.0
    nosy_names = ['pitrou', 'vstinner', 'kiilerix', 'neologix', 'rosslagerwall', 'Ben.Wolfson', 'python-dev', 'idank']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue12786'
    versions = ['Python 2.7']

    Activity

    1. idank commented on Aug 19, 2011

      idankmannequin
      MannequinAuthor

      The following program hangs on Debian, Python 2.6.6:

      import subprocess
      
      proc1 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      proc2 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      
      proc1.stdin.close()
      proc1.wait()

      Changing the last two lines to:

      proc2.stdin.close()
      proc2.wait()

      Doesn't hang. The guys at #python-dev confirmed the same happens on 2.7 but not on 3.x.

    2. added
      performancePerformance or resource usage
      stdlibStandard Library Python modules in the Lib/ directory
      on Aug 19, 2011
    3. added
      type-bugAn unexpected behavior, bug, or error
      and removed
      performancePerformance or resource usage
      on Aug 19, 2011
    4. BenWolfson commented on Aug 19, 2011

      BenWolfsonmannequin
      Mannequin

      "The guys at #python-dev confirmed the same happens on 2.7 but not on 3.x."

      Really? This is on gentoo, not debian, admittedly:

      coelacanth ~ 11:12:36 $ python3
      Python 3.1.3 (r313:86834, May  1 2011, 09:41:48) 
      [GCC 4.4.4] on linux2
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import subprocess
      >>> proc1 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      >>> proc2 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      >>> proc2.stdin.close()
      >>> proc2.wait()
      0
      >>> 
      
      
      
      coelacanth ~ 11:12:13 $ python3.1
      Python 3.1.3 (r313:86834, May  1 2011, 09:41:48) 
      [GCC 4.4.4] on linux2
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import subprocess
      >>> proc1 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      >>> proc2 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      >>> proc1.stdin.close()
      >>> proc1.wait()

      [hangs]

    5. neologix commented on Aug 19, 2011

      neologixmannequin
      Mannequin

      Hello Idan,

      The following program hangs on Debian

      Debian is a good choice :-)

      Concerning your example, it hangs is because the file descriptor used
      to communicate with proc1 is inherited when proc2 is created (FDs are
      inherited upon fork by default): thus, when you call
      proc1.stdin.close(), cat doesn't receive EOF on read(0), and remains
      stuck.
      If you close proc2's stdin and wait it first, then you don't have this problem:

      • because it's been created after proc1, its stdin FD has not been inherited
      • when you call proc2.stdin.close(), cat receives EOF from read(0), and exits

      There are two reasons while it doesn't hang on Python 3.x:

      1. it uses close_fds=True by default
      2. it sets the pipe FDs CLOEXEC, so that they're closed when cat is executed

      Try with
      """
      proc1 = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
      proc2 = subprocess.Popen(['cat'], stdin=subprocess.PIPE, close_fds=True)

      proc1.stdin.close()
      proc1.wait()
      """

      And you'll be fine.

      We can't make the change 1) in 2.7, because that's a behavior change.
      We could however set the FDs used to communicate with the children CLOEXEC.
      I'll work on a patch for 2.7 (but it won't be thread-safe, because
      pipe2 is not exposed and there's no _posixsubprocess.c like in 3.x).

    6. neologix commented on Aug 19, 2011

      neologixmannequin
      Mannequin

      Here's a patch + test for 2.7.

      Really? This is on gentoo, not debian, admittedly:

      That's because the change of close_fds to True by default and the CLOEXEC flag were added in 3.2.
      Since 3.1 is in security-fix mode only, this patch is only relevant to 2.7.

    7. idank commented on Aug 20, 2011

      idankmannequin
      MannequinAuthor

      Thanks for getting on top of this so quickly Charles. Setting close_fds=True worked like a charm.

    8. pitrou commented on Aug 24, 2011

      @pitrou
      Member

      You might want to add a comment in the patch that the cloexec flag is removed from the child's pipes by calling dup2() before exec() :) I was a bit baffled at first when reading the patch.

    9. neologix commented on Aug 24, 2011

      neologixmannequin
      Mannequin

      With comment.
      I'll add a similar comment to default.

    10. vstinner commented on Aug 24, 2011

      @vstinner
      Member

      subprocess_cloexec-1.diff: I'm too tired too review the test. The subprocess.py part looks correct, except the pipe2 name. Python 3 uses "_create_pipe = _posixsubprocess.cloexec_pipe". Pick one of those.

    11. rosslagerwall commented on Aug 25, 2011

      rosslagerwallmannequin
      Mannequin

      Patch looks good after Victor's comment.

    12. python-dev commented on Aug 25, 2011

      python-devmannequin
      Mannequin

      New changeset 498b03a55297 by Charles-François Natali in branch '2.7':
      Issue bpo-12786: Set communication pipes used by subprocess.Popen CLOEXEC to avoid
      http://hg.python.org/cpython/rev/498b03a55297

    13. neologix commented on Aug 25, 2011

      neologixmannequin
      Mannequin

      Patch committed.
      Idan, thanks for the report.

    14. transferred this issue fromon Apr 10, 2022
    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

      stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions