Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subprocess.Popen does not release process handles if process cannot be started #47460

Closed
gjb1002 mannequin opened this issue Jun 26, 2008 · 21 comments
Closed

subprocess.Popen does not release process handles if process cannot be started #47460

gjb1002 mannequin opened this issue Jun 26, 2008 · 21 comments
Assignees
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@gjb1002
Copy link
Mannequin

gjb1002 mannequin commented Jun 26, 2008

BPO 3210
Nosy @terryjreedy, @gpshead, @tjguk, @markmentovai, @briancurtin
Files
  • sp-3.py: Reduced example of subprocess handle error
  • adhok.patch
  • 3210.py
  • 3210.r83741.patch
  • 3210.release31-maint.patch
  • 3210.release27-maint.patch
  • issue3210_py3k.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 = 'https://github.com/tjguk'
    closed_at = <Date 2010-08-08.16:15:14.797>
    created_at = <Date 2008-06-26.15:22:42.028>
    labels = ['type-bug', 'library']
    title = 'subprocess.Popen does not release process handles if process cannot be started'
    updated_at = <Date 2010-08-08.16:15:14.796>
    user = 'https://bugs.python.org/gjb1002'

    bugs.python.org fields:

    activity = <Date 2010-08-08.16:15:14.796>
    actor = 'tim.golden'
    assignee = 'tim.golden'
    closed = True
    closed_date = <Date 2010-08-08.16:15:14.797>
    closer = 'tim.golden'
    components = ['Library (Lib)']
    creation = <Date 2008-06-26.15:22:42.028>
    creator = 'gjb1002'
    dependencies = []
    files = ['10744', '11068', '12287', '18398', '18399', '18400', '18401']
    hgrepos = []
    issue_num = 3210
    keywords = ['patch']
    message_count = 21.0
    messages = ['68787', '68796', '68817', '70679', '70826', '77340', '91989', '93787', '93788', '112711', '112968', '112984', '112985', '112986', '112987', '112988', '113102', '113106', '113107', '113249', '113277']
    nosy_count = 8.0
    nosy_names = ['terry.reedy', 'gregory.p.smith', 'gjb1002', 'ocean-city', 'tim.golden', 'twhitema', 'markmentovai', 'brian.curtin']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue3210'
    versions = ['Python 3.1', 'Python 2.7', 'Python 3.2']

    @gjb1002
    Copy link
    Mannequin Author

    gjb1002 mannequin commented Jun 26, 2008

    Run the following code on Windows:

    import subprocess, os
    
    file = open("filename", "w")
    try:
        proc = subprocess.Popen("nosuchprogram", stdout=file)
    except OSError:
        file.close()
        os.remove("filename")

    This produces the following exception:

    Traceback (most recent call last):
      File "C:\processown.py", line 10, in <module>
        os.remove("filename")
    WindowsError: [Error 32] The process cannot access the file because it
    is being used by another process: 'filename'

    When the CreateProcess call fails the subprocess module should release
    the handles it provides. Unfortunately it seems to raise WindowsError
    before doing this.

    See also
    http://groups.google.com/group/comp.lang.python/browse_thread/thread/6157691ea3324779/6274e9f8bc8a71ee?hl=en#6274e9f8bc8a71ee

    As Tim Golden points out, this can be worked around by doing
    os.close(file.fileno()) at the end instead of file.close()

    @gjb1002 gjb1002 mannequin added extension-modules C modules in the Modules dir type-bug An unexpected behavior, bug, or error labels Jun 26, 2008
    @tjguk
    Copy link
    Member

    tjguk commented Jun 26, 2008

    The attached file sp-3.py simulates what I think is happening within the
    subprocess module. Note that the OS handle is duplicated to allow
    inheritance and then left unclosed on failure. If it is explicitly
    closed, the file can be removed.

    There is no hope of closing the file handle since it was local to the
    __init__ method which failed but was not closed before exit and is now
    inaccessible.

    On the surface, the obvious fix would be a try/except block around the
    CreateProcess call (or its caller) which would then release whatever
    handles were needed. I'll try to get the time to put a patch together,
    but it would be useful to have confirmation of my theory.

    @gjb1002
    Copy link
    Mannequin Author

    gjb1002 mannequin commented Jun 27, 2008

    A note on workarounds, the garbage collector seems to release the
    handles when the function exits, so removing the file in a caller works
    for me. However Tim's proposed fix with os.close didn't do so.

    @gpshead gpshead added stdlib Python modules in the Lib dir and removed extension-modules C modules in the Modules dir labels Jul 7, 2008
    @gpshead gpshead self-assigned this Jul 7, 2008
    @gpshead
    Copy link
    Member

    gpshead commented Aug 4, 2008

    I tried closing -all- of the handles listed here (the std ones used as
    input to _get_handles and the pipe read/write ones returned) on an
    OSError during CreateProcess but the problem still occurs for me using
    the test code in msg68787.

        (p2cread, p2cwrite,
         c2pread, c2pwrite,
         errread, errwrite) = self._get_handles(stdin, stdout, stderr)
    

    someone with more windows willingness/knowledge/fu should take this one on.

    @gpshead gpshead removed their assignment Aug 4, 2008
    @ocean-city
    Copy link
    Mannequin

    ocean-city mannequin commented Aug 7, 2008

    Hello. I had lecture about exception & frames on bpo-3515.

    When Lib/subprocess.py (814)'s CreateProcess() raises exception,
    p2cread will be freed by refcount GC, but it never happen before
    os.remove() because sys.exc_traceback holds reference to frame
    which has p2cread.

    import subprocess, os
    
    file = open("filename", "w")
    try:
        proc = subprocess.Popen("nosuchprogram", stdout=file)
    except OSError:
        pass

    try:
    raise RuntimeError() # hack to clear previous exc_traceback
    except:
    pass

    file.close()
    os.remove("filename") # runs fine

    So, I think subprocess module's practice which relys on refcount GC
    is not good. (p2cread is PC/_subprocess.c 's sp_handle_object, so
    automatically freed by refcount GC) I don't think attached "adhok.patch"
    is enough, but seems to fix this issue at least.

    @markmentovai
    Copy link
    Mannequin

    markmentovai mannequin commented Dec 8, 2008

    This is not limited to Windows. I experience this bug on Mac OS X and
    Linux as well.

    http://mail.python.org/pipermail/python-list/2008-August/505753.html

    Attachment 3210.py is a reduced testcase. It attempts to execute nocmd,
    which should not exist. The expected behavior is for OSError to be
    thrown each time, with ENOENT or EACCES set in the errno field,
    depending on the environment. Due to this bug, Python will hit the file
    descriptor limit at some point instead.

    For quick reproduction, set a low but reasonable limit on the number of
    maximum open files:

    mark@anodizer bash$ ulimit -n 256
    mark@anodizer bash$ python 3210.py
    250
    Traceback (most recent call last):
      File "3210.py", line 11, in <module>
        raise e
    OSError: [Errno 24] Too many open files

    @twhitema
    Copy link
    Mannequin

    twhitema mannequin commented Aug 26, 2009

    Is this a duplicate of this already fixed issue: bpo-5179 ?

    @ocean-city
    Copy link
    Mannequin

    ocean-city mannequin commented Oct 9, 2009

    No, this is not duplicate of bpo-5179. That issue described handle was
    leaked when exception occurred. But this issue is not *leak*. See
    following code.

    import subprocess, os, sys
    file = open("filename", "w")
    try:
        proc = subprocess.Popen("nosuchprogram", stdout=file)
    except OSError:
        file.close()
        sys.exc_clear() # here we go....
        os.remove("filename") # now we can delete file!

    subprocess is implemented using sp_handle_type in PC/_subprocess.c
    (windows). This object is in exception stack frame(?), so handle lives
    until another exception occurs or explicitly sys.exc_clear() is called.

    @ocean-city
    Copy link
    Mannequin

    ocean-city mannequin commented Oct 9, 2009

    Probably we can fix this issue by calling Close() of sp_handle_type
    somewhere in Lib/subprocess.py, but I have no patch now.

    @terryjreedy
    Copy link
    Member

    Same problem in 3.1.2

    @tjguk
    Copy link
    Member

    tjguk commented Aug 5, 2010

    Patch attached with code & test which fixes this within _subprocess.c at least for Windows

    @tjguk tjguk self-assigned this Aug 5, 2010
    @tjguk
    Copy link
    Member

    tjguk commented Aug 5, 2010

    Patch added for 31 branch

    @tjguk
    Copy link
    Member

    tjguk commented Aug 5, 2010

    Patch added for 27 branch

    @briancurtin
    Copy link
    Member

    Tim,
    I updated your test to use some of the newer and preferred unittest features and made a change to do the common stuff in loops. The _subprocess.c changes look fine to me. See attached issue3210_py3k.diff.

    @tjguk
    Copy link
    Member

    tjguk commented Aug 5, 2010

    Brian, I'm not sure that the test as rewritten will exercise the error.

    The key is that the traceback object will prevent the handles
    from being finalised until it is itself finalised. After your
    change I expect the handles to release anyway since the traceback
    is already finalised.

    In other words, I'm not testing that an exception is raised (which
    is what the with assertRaises would suggest); I'm testing that
    *within the lifetime of the exception* it's still possible to
    remove the files which were passed as stdin/out/err where it wasn't
    previously.

    @briancurtin
    Copy link
    Member

    Ah ok. I got hooked onto new unittest stuff and overdid it. Whoops.

    In that case, I guess just the last lines converting your "assert_" to "assertFalse" would be my only suggestion.

    @tjguk
    Copy link
    Member

    tjguk commented Aug 6, 2010

    Committed in r83759 r83760 r83761

    @tjguk tjguk closed this as completed Aug 6, 2010
    @ocean-city
    Copy link
    Mannequin

    ocean-city mannequin commented Aug 6, 2010

    Sorry for posting to closed entry, but I think handle should be closed in Lib/subprocess.py not in PC/_subprocess.c. I noticed following code showed strange error.

    import subprocess
    
    for _ in xrange(2):
        stdout = open("stdout.txt", "w")
        try:
            p = subprocess.Popen(["unknown"], stdout=stdout)
        except WindowsError:
            pass

    // error

    close failed in file object destructor:
    IOError: [Errno 9] Bad file descriptor

    @tjguk
    Copy link
    Member

    tjguk commented Aug 6, 2010

    Blast. Thanks; I'll have to rework those patches then.

    @tjguk tjguk reopened this Aug 6, 2010
    @tjguk
    Copy link
    Member

    tjguk commented Aug 8, 2010

    OK, the issue identified by Hirokazu Yamamoto in msg113106 only actually affects the 2.x series, because of the awkwardly multiple-level interaction between file handles. The io rewrite in 3.x seems not to suffer the same way. Nonetheless, the proposed adhok.patch (slightly updated to match the current codebase) seems to cover all the cases, and is rather more elegant. So I'll revert the C module changes and apply that instead to all branches.

    @tjguk
    Copy link
    Member

    tjguk commented Aug 8, 2010

    Committed in r83815, r83816, r83817

    @tjguk tjguk closed this as completed Aug 8, 2010
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants