提交 80ebe9c3 authored 作者: Arnaud Bergeron's avatar Arnaud Bergeron

Fix usage of Popen.wait(). This is liable to deadlock if used with…

Fix usage of Popen.wait(). This is liable to deadlock if used with subprocess.PIPE and it was used like that all over the place. I've created new wrapper that will hopefully avoid this broken usage.
上级 19c0dbcb
...@@ -117,19 +117,10 @@ AddConfigVar('mode', ...@@ -117,19 +117,10 @@ AddConfigVar('mode',
enum = EnumStr("g++", "") enum = EnumStr("g++", "")
# Test whether or not g++ is present: disable C code if it is not. # Test whether or not g++ is present: disable C code if it is not.
# Using the dummy file descriptor below is a workaround for a crash experienced
# in an unusual Python 2.4.4 Windows environment with the default stdin=None.
dummy_stdin = open(os.devnull)
try: try:
try: rc = call_subprocess_Popen(['g++', '-v'])
rc = call_subprocess_Popen(['g++', '-v'], stdout=subprocess.PIPE, except OSError:
stderr=subprocess.PIPE, rc = 1
stdin=dummy_stdin).wait()
except OSError:
rc = 1
finally:
dummy_stdin.close()
del dummy_stdin
if rc == 0: if rc == 0:
# Keep the default linker the same as the one for the mode FAST_RUN # Keep the default linker the same as the one for the mode FAST_RUN
AddConfigVar('linker', AddConfigVar('linker',
......
...@@ -29,7 +29,8 @@ from theano.compat.six import b, BytesIO, StringIO ...@@ -29,7 +29,8 @@ from theano.compat.six import b, BytesIO, StringIO
from theano.gof.utils import flatten from theano.gof.utils import flatten
from theano.configparser import config from theano.configparser import config
from theano.gof.cc import hash_from_code from theano.gof.cc import hash_from_code
from theano.misc.windows import call_subprocess_Popen from theano.misc.windows import (subprocess_Popen, call_subprocess_Popen,
output_subprocess_Popen)
# we will abuse the lockfile mechanism when reading and writing the registry # we will abuse the lockfile mechanism when reading and writing the registry
from theano.gof import compilelock from theano.gof import compilelock
...@@ -1516,11 +1517,8 @@ def gcc_llvm(): ...@@ -1516,11 +1517,8 @@ def gcc_llvm():
pass pass
p = None p = None
try: try:
p = call_subprocess_Popen(['g++', '--version'], p_out = output_subprocess_Popen(['g++', '--version'])
stdout=subprocess.PIPE, output = p_out[0] + p_out[1]
stderr=subprocess.PIPE)
p.wait()
output = p.stdout.read() + p.stderr.read()
except OSError: except OSError:
# Typically means g++ cannot be found. # Typically means g++ cannot be found.
# So it is not an llvm compiler. # So it is not an llvm compiler.
...@@ -1573,11 +1571,11 @@ class GCC_compiler(object): ...@@ -1573,11 +1571,11 @@ class GCC_compiler(object):
GCC_compiler.march_flags = [] GCC_compiler.march_flags = []
def get_lines(cmd, parse=True): def get_lines(cmd, parse=True):
p = call_subprocess_Popen(cmd, p = subprocess_Popen(cmd,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
shell=True) shell=True)
# For mingw64 with GCC >= 4.7, passing os.devnull # For mingw64 with GCC >= 4.7, passing os.devnull
# as stdin (which is the default) results in the process # as stdin (which is the default) results in the process
# waiting forever without returning. For that reason, # waiting forever without returning. For that reason,
...@@ -1815,21 +1813,15 @@ class GCC_compiler(object): ...@@ -1815,21 +1813,15 @@ class GCC_compiler(object):
os.write(fd, src_code) os.write(fd, src_code)
os.close(fd) os.close(fd)
fd = None fd = None
proc = call_subprocess_Popen( p_ret = call_subprocess_Popen(
['g++', path, '-o', exe_path] + flags, ['g++', path, '-o', exe_path] + flags)
stdout=subprocess.PIPE, if p_ret != 0:
stderr=subprocess.PIPE)
proc.wait()
if proc.returncode != 0:
compilation_ok = False compilation_ok = False
elif try_run: elif try_run:
# Try to execute the program # Try to execute the program
try: try:
proc = call_subprocess_Popen([exe_path], p_ret = call_subprocess_Popen([exe_path])
stdout=subprocess.PIPE, run_ok = (p_ret == 0)
stderr=subprocess.PIPE)
proc.wait()
run_ok = (proc.returncode == 0)
finally: finally:
os.remove(exe_path) os.remove(exe_path)
finally: finally:
...@@ -1962,14 +1954,14 @@ class GCC_compiler(object): ...@@ -1962,14 +1954,14 @@ class GCC_compiler(object):
print >> sys.stderr, ' '.join(cmd) print >> sys.stderr, ' '.join(cmd)
try: try:
p = call_subprocess_Popen(cmd, stderr=subprocess.PIPE) p_out = output_subprocess_Popen(cmd)
compile_stderr = decode(p.communicate()[1]) compile_stderr = decode(p_out[1])
except Exception: except Exception:
# An exception can occur e.g. if `g++` is not found. # An exception can occur e.g. if `g++` is not found.
print_command_line_error() print_command_line_error()
raise raise
status = p.returncode status = p_out[2]
if status: if status:
print '===============================' print '==============================='
......
...@@ -16,27 +16,17 @@ import numpy ...@@ -16,27 +16,17 @@ import numpy
import theano import theano
from theano.configparser import config, AddConfigVar, ConfigParam, StrParam from theano.configparser import config, AddConfigVar, ConfigParam, StrParam
from theano.gof.utils import flatten from theano.gof.utils import flatten
from theano.misc.windows import call_subprocess_Popen from theano.misc.windows import output_subprocess_Popen
_logger = logging.getLogger("theano.gof.compiledir") _logger = logging.getLogger("theano.gof.compiledir")
# Using the dummy file descriptors below is a workaround for a crash
# experienced in an unusual Python 2.4.4 Windows environment with the default
# None values.
dummy_err = open(os.devnull, 'w')
p = None
try: try:
p = call_subprocess_Popen(['g++', '-dumpversion'], p_out = output_subprocess_Popen(['g++', '-dumpversion'])
stdout=subprocess.PIPE, gcc_version_str = p_out[0].strip().decode()
stderr=dummy_err.fileno())
p.wait()
gcc_version_str = p.stdout.readline().strip().decode()
except OSError: except OSError:
# Typically means gcc cannot be found. # Typically means gcc cannot be found.
gcc_version_str = 'GCC_NOT_FOUND' gcc_version_str = 'GCC_NOT_FOUND'
del p
del dummy_err
def local_bitwidth(): def local_bitwidth():
......
...@@ -2,7 +2,7 @@ import os ...@@ -2,7 +2,7 @@ import os
import subprocess import subprocess
def call_subprocess_Popen(command, **params): def subprocess_Popen(command, **params):
""" """
Utility function to work around windows behavior that open windows Utility function to work around windows behavior that open windows
""" """
...@@ -36,3 +36,30 @@ def call_subprocess_Popen(command, **params): ...@@ -36,3 +36,30 @@ def call_subprocess_Popen(command, **params):
if stdin is not None: if stdin is not None:
del stdin del stdin
return proc return proc
def call_subprocess_Popen(command, **params):
if 'stdout' in params or 'stderr' in params:
raise TypeError("don't use stderr or stdout with call_subprocess_Popen")
null = open(os.devnull, 'wb')
# stdin to devnull is a workaround for a crash in a weird Windows
# environement where sys.stdin was None
params.setdefault('stdin', null)
params['stdout'] = null
params['stderr'] = null
p = subprocess_Popen(command, **params)
p.wait()
return p.returncode
def output_subprocess_Popen(command, **params):
if 'stdout' in params or 'stderr' in params:
raise TypeError("don't use stderr or stdout with output_subprocess_Popen")
# stdin to devnull is a workaround for a crash in a weird Windows
# environement where sys.stdin was None
if not hasattr(params, 'stdin'):
null = open(os.devnull, 'wb')
params['stdin'] = null
params['stdout'] = subprocess.PIPE
params['stderr'] = subprocess.PIPE
p = subprocess_Popen(command, **params)
out = p.communicate()
return out + (p.returncode,)
...@@ -16,7 +16,7 @@ from theano.gof.cmodule import (std_libs, std_lib_dirs, ...@@ -16,7 +16,7 @@ from theano.gof.cmodule import (std_libs, std_lib_dirs,
std_include_dirs, dlimport, std_include_dirs, dlimport,
get_lib_extension) get_lib_extension)
from theano.gof.python25 import any from theano.gof.python25 import any
from theano.misc.windows import call_subprocess_Popen from theano.misc.windows import output_subprocess_Popen
_logger = logging.getLogger("theano.sandbox.cuda.nvcc_compiler") _logger = logging.getLogger("theano.sandbox.cuda.nvcc_compiler")
_logger.setLevel(logging.WARN) _logger.setLevel(logging.WARN)
...@@ -98,12 +98,8 @@ nvcc_version = None ...@@ -98,12 +98,8 @@ nvcc_version = None
def is_nvcc_available(): def is_nvcc_available():
"""Return True iff the nvcc compiler is found.""" """Return True iff the nvcc compiler is found."""
def set_version(): def set_version():
p = call_subprocess_Popen([nvcc_path, '--version'], p_out = output_subprocess_Popen([nvcc_path, '--version'])
stdout=subprocess.PIPE, ver_line = decode(p_out[0].split('\n')[-1])
stderr=subprocess.PIPE)
p.wait()
ver_line = decode(p.stdout.readlines()[-1])
build, version = ver_line.split(',')[1].strip().split() build, version = ver_line.split(',')[1].strip().split()
assert build == 'release' assert build == 'release'
......
...@@ -62,7 +62,7 @@ import sys ...@@ -62,7 +62,7 @@ import sys
import time import time
import theano import theano
from theano.misc.windows import call_subprocess_Popen from theano.misc.windows import output_subprocess_Popen
def main(stdout=None, stderr=None, argv=None, theano_nose=None, def main(stdout=None, stderr=None, argv=None, theano_nose=None,
...@@ -271,19 +271,17 @@ def run(stdout, stderr, argv, theano_nose, batch_size, time_profile, ...@@ -271,19 +271,17 @@ def run(stdout, stderr, argv, theano_nose, batch_size, time_profile,
time.ctime(), test_id, data["ids"][test_id])) time.ctime(), test_id, data["ids"][test_id]))
f_rawlog.flush() f_rawlog.flush()
proc = call_subprocess_Popen( p_out = output_subprocess_Popen(
([python, theano_nose, '-v', '--with-id'] ([python, theano_nose, '-v', '--with-id']
+ [str(test_id)] + argv + + [str(test_id)] + argv +
['--disabdocstring']), ['--disabdocstring']))
# the previous option calls a custom Nosetests plugin # the previous option calls a custom Nosetests plugin
# precluding automatic sustitution of doc. string for # precluding automatic sustitution of doc. string for
# test name in display # test name in display
# (see class 'DisabDocString' in file theano-nose) # (see class 'DisabDocString' in file theano-nose)
stderr=subprocess.PIPE,
stdout=dummy_out.fileno())
# recovering and processing data from pipe # recovering and processing data from pipe
err = proc.stderr.read() err = p_out[1]
# print the raw log # print the raw log
f_rawlog.write(err) f_rawlog.write(err)
f_rawlog.flush() f_rawlog.flush()
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论