Commit 785b7b55 authored by Jason Madden's avatar Jason Madden

Avoid calling super during code-paths used by FileObjectThread.

The two-arg version of super depends on a module global, making it flaky during interpreter shutdown. Seen on Python 2: https://travis-ci.org/gevent/gevent/jobs/636366809#L501

Also some cleanups to subprocess handling during tests. Avoid a ref cycle.
parent f04f4835
...@@ -23,13 +23,15 @@ from gevent.lock import Semaphore, DummySemaphore ...@@ -23,13 +23,15 @@ from gevent.lock import Semaphore, DummySemaphore
class cancel_wait_ex(IOError): class cancel_wait_ex(IOError):
def __init__(self): def __init__(self):
super(cancel_wait_ex, self).__init__( IOError.__init__(
self,
EBADF, 'File descriptor was closed in another greenlet') EBADF, 'File descriptor was closed in another greenlet')
class FileObjectClosed(IOError): class FileObjectClosed(IOError):
def __init__(self): def __init__(self):
super(FileObjectClosed, self).__init__( IOError.__init__(
self,
EBADF, 'Bad file descriptor (FileObject was closed)') EBADF, 'Bad file descriptor (FileObject was closed)')
class UniversalNewlineBytesWrapper(io.TextIOWrapper): class UniversalNewlineBytesWrapper(io.TextIOWrapper):
...@@ -408,7 +410,7 @@ class FileObjectBlock(FileObjectBase): ...@@ -408,7 +410,7 @@ class FileObjectBlock(FileObjectBase):
def __init__(self, fobj, *args, **kwargs): def __init__(self, fobj, *args, **kwargs):
descriptor = OpenDescriptor(fobj, *args, **kwargs) descriptor = OpenDescriptor(fobj, *args, **kwargs)
super(FileObjectBlock, self).__init__(descriptor.open(), descriptor.closefd) FileObjectBase.__init__(self, descriptor.open(), descriptor.closefd)
def _do_close(self, fobj, closefd): def _do_close(self, fobj, closefd):
fobj.close() fobj.close()
...@@ -457,7 +459,7 @@ class FileObjectThread(FileObjectBase): ...@@ -457,7 +459,7 @@ class FileObjectThread(FileObjectBase):
raise TypeError('Expected a Semaphore or boolean, got %r' % type(self.lock)) raise TypeError('Expected a Semaphore or boolean, got %r' % type(self.lock))
self.__io_holder = [descriptor.open()] # signal for _wrap_method self.__io_holder = [descriptor.open()] # signal for _wrap_method
super(FileObjectThread, self).__init__(self.__io_holder[0], descriptor.closefd) FileObjectBase.__init__(self, self.__io_holder[0], descriptor.closefd)
def _do_close(self, fobj, closefd): def _do_close(self, fobj, closefd):
self.__io_holder[0] = None # for _wrap_method self.__io_holder[0] = None # for _wrap_method
...@@ -489,7 +491,7 @@ class FileObjectThread(FileObjectBase): ...@@ -489,7 +491,7 @@ class FileObjectThread(FileObjectBase):
reraise(*exc_info) reraise(*exc_info)
def _do_delegate_methods(self): def _do_delegate_methods(self):
super(FileObjectThread, self)._do_delegate_methods() FileObjectBase._do_delegate_methods(self)
# if not hasattr(self, 'read1') and 'r' in getattr(self._io, 'mode', ''): # if not hasattr(self, 'read1') and 'r' in getattr(self._io, 'mode', ''):
# self.read1 = self.read # self.read1 = self.read
self.__io_holder[0] = self._io self.__io_holder[0] = self._io
......
...@@ -10,6 +10,7 @@ import time ...@@ -10,6 +10,7 @@ import time
from . import six from . import six
from gevent._config import validate_bool from gevent._config import validate_bool
from gevent.monkey import get_original
# pylint: disable=broad-except,attribute-defined-outside-init # pylint: disable=broad-except,attribute-defined-outside-init
...@@ -22,6 +23,11 @@ QUIET = validate_bool(os.environ.get('GEVENTTEST_QUIET', '0')) ...@@ -22,6 +23,11 @@ QUIET = validate_bool(os.environ.get('GEVENTTEST_QUIET', '0'))
class Popen(subprocess.Popen): class Popen(subprocess.Popen):
"""
Depending on when we're imported and if the process has been monkey-patched,
this could use cooperative or native Popen.
"""
timer = None # a threading.Timer instance
def __enter__(self): def __enter__(self):
return self return self
...@@ -145,7 +151,7 @@ def killpg(pid): ...@@ -145,7 +151,7 @@ def killpg(pid):
def kill_processtree(pid): def kill_processtree(pid):
ignore_msg = 'ERROR: The process "%s" not found.' % pid ignore_msg = 'ERROR: The process "%s" not found.' % pid
err = subprocess.Popen('taskkill /F /PID %s /T' % pid, stderr=subprocess.PIPE).communicate()[1] err = Popen('taskkill /F /PID %s /T' % pid, stderr=subprocess.PIPE).communicate()[1]
if err and err.strip() not in [ignore_msg, '']: if err and err.strip() not in [ignore_msg, '']:
log('%r', err) log('%r', err)
...@@ -170,6 +176,7 @@ def _kill(popen): ...@@ -170,6 +176,7 @@ def _kill(popen):
def kill(popen): def kill(popen):
if popen.timer is not None: if popen.timer is not None:
popen.timer.cancel() popen.timer.cancel()
popen.timer = None
if popen.poll() is not None: if popen.poll() is not None:
return return
popen.was_killed = True popen.was_killed = True
...@@ -248,9 +255,9 @@ def start(command, quiet=False, **kwargs): ...@@ -248,9 +255,9 @@ def start(command, quiet=False, **kwargs):
popen.name = name popen.name = name
popen.setpgrp_enabled = preexec_fn is not None popen.setpgrp_enabled = preexec_fn is not None
popen.was_killed = False popen.was_killed = False
popen.timer = None
if timeout is not None: if timeout is not None:
t = threading.Timer(timeout, kill, args=(popen, )) t = get_original('threading', 'Timer')(timeout, kill, args=(popen, ))
popen.timer = t
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
popen.timer = t popen.timer = t
...@@ -393,6 +400,7 @@ def run(command, **kwargs): # pylint:disable=too-many-locals ...@@ -393,6 +400,7 @@ def run(command, **kwargs): # pylint:disable=too-many-locals
result = popen.poll() result = popen.poll()
finally: finally:
kill(popen) kill(popen)
assert popen.timer is None
failed = bool(result) failed = bool(result)
......
...@@ -127,6 +127,7 @@ class _WorkerGreenlet(RawGreenlet): ...@@ -127,6 +127,7 @@ class _WorkerGreenlet(RawGreenlet):
def run(self): def run(self):
# pylint:disable=too-many-branches # pylint:disable=too-many-branches
task = None
try: try:
while 1: # tiny bit faster than True on Py2 while 1: # tiny bit faster than True on Py2
self.__fixup_hub_before_block() self.__fixup_hub_before_block()
...@@ -141,7 +142,11 @@ class _WorkerGreenlet(RawGreenlet): ...@@ -141,7 +142,11 @@ class _WorkerGreenlet(RawGreenlet):
task = None task = None
self._task_queue.task_done() self._task_queue.task_done()
except Exception as e: # pylint:disable=broad-except except Exception as e: # pylint:disable=broad-except
print("Failed to run worker thread", e, file=self._stderr) print(
"Failed to run worker thread. Task=%r Exception=%s%r" % (
task, e, e
),
file=self._stderr)
finally: finally:
# Re-check for the hub in case the task created it but then # Re-check for the hub in case the task created it but then
# failed. # failed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment