From a75341bf3a66f75edd68d1a8bc5efdb51c0c0740 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Tue, 14 Feb 2012 04:29:02 -0800 Subject: After python fork, don't close fds for PyPy 1.8. If we close all open file descriptors after a fork, with PyPy 1.8 it triggers "[Errno 9] Bad file descriptor" later in the subprocess. Apparently it is holding references to file descriptors and closing them after they've already been closed and re-opened for other purposes. As a workaround, we don't close the file descriptors, so that they won't be re-used and therefore we won't be vulnerable to this kind of interference. The obvious caveat of not closing the fds is that the subprocess can hold locks that belonged to the parent process, even after the parent process has released the locks. Hopefully this won't be a major problem though, since the subprocess has to exit at release the lock eventually, when the EbuildFetcher or _MergeProcess task is complete. --- pym/_emerge/EbuildFetcher.py | 6 +++++- pym/portage/dbapi/_MergeProcess.py | 7 ++++++- pym/portage/process.py | 20 +++++++++++--------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/pym/_emerge/EbuildFetcher.py b/pym/_emerge/EbuildFetcher.py index 61c7848ad..6ad434129 100644 --- a/pym/_emerge/EbuildFetcher.py +++ b/pym/_emerge/EbuildFetcher.py @@ -6,6 +6,7 @@ import traceback from _emerge.SpawnProcess import SpawnProcess import copy import io +import platform import signal import sys import portage @@ -166,7 +167,10 @@ class EbuildFetcher(SpawnProcess): portage.process.spawned_pids.append(pid) return [pid] - portage.process._setup_pipes(fd_pipes) + # TODO: Find out why PyPy 1.8 with close_fds=True triggers + # "[Errno 9] Bad file descriptor" in subprocesses. + close_fds = platform.python_implementation() != 'PyPy' + portage.process._setup_pipes(fd_pipes, close_fds=close_fds) # Use default signal handlers in order to avoid problems # killing subprocesses as reported in bug #353239. diff --git a/pym/portage/dbapi/_MergeProcess.py b/pym/portage/dbapi/_MergeProcess.py index 9bb67c9b6..cf5926529 100644 --- a/pym/portage/dbapi/_MergeProcess.py +++ b/pym/portage/dbapi/_MergeProcess.py @@ -2,6 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 import io +import platform import signal import traceback @@ -143,7 +144,11 @@ class MergeProcess(SpawnProcess): return [pid] os.close(elog_reader_fd) - portage.process._setup_pipes(fd_pipes) + + # TODO: Find out why PyPy 1.8 with close_fds=True triggers + # "[Errno 9] Bad file descriptor" in subprocesses. + close_fds = platform.python_implementation() != 'PyPy' + portage.process._setup_pipes(fd_pipes, close_fds=close_fds) # Use default signal handlers since the ones inherited # from the parent process are irrelevant here. diff --git a/pym/portage/process.py b/pym/portage/process.py index 47b0a2147..e7313abc3 100644 --- a/pym/portage/process.py +++ b/pym/portage/process.py @@ -386,7 +386,7 @@ def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask, # And switch to the new process. os.execve(binary, myargs, env) -def _setup_pipes(fd_pipes): +def _setup_pipes(fd_pipes, close_fds=True): """Setup pipes for a forked process.""" my_fds = {} # To protect from cases where direct assignment could @@ -397,14 +397,16 @@ def _setup_pipes(fd_pipes): # Then assign them to what they should be. for fd in my_fds: os.dup2(my_fds[fd], fd) - # Then close _all_ fds that haven't been explicitly - # requested to be kept open. - for fd in get_open_fds(): - if fd not in my_fds: - try: - os.close(fd) - except OSError: - pass + + if close_fds: + # Then close _all_ fds that haven't been explicitly + # requested to be kept open. + for fd in get_open_fds(): + if fd not in my_fds: + try: + os.close(fd) + except OSError: + pass def find_binary(binary): """ -- cgit v1.2.3-1-g7c22