Ensure fdopen uses greenio object under eventlet

The _fd_logger function works around a regular Unix pipe, with a python
file object wrapped around both pipe file descriptors.  This would be
stupidly simple and obvious, except eventlet.

We need to use the fdopen from io.open(), and not os.fdopen() (on py2;
on py3 they're the same), because the older python file objects have
broken behaviour regarding blocking reads.  Eventlet doesn't monkey
patch os.pipe, nor anything in io.* - so none of the existing eventlet
monkey_patching will "just work" for our case.

This change adds a custom `fdopen` function that explicitly uses
greenio.GreenPipe or io.open as appropriate - and uses this to always
return an eventlet-safe file object.

Change-Id: I4a6c0d4247aca17536316fb0ab163241ad545b20
This commit is contained in:
Angus Lees 2016-02-16 13:04:52 +11:00
parent a6e554bd49
commit 4fba8f505b
2 changed files with 18 additions and 2 deletions

View File

@ -61,6 +61,7 @@ if platform.system() == 'Linux':
import grp
import pwd
import eventlet
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import importutils
@ -177,11 +178,24 @@ class _ClientChannel(comm.ClientChannel):
raise ProtocolError(_('Unexpected response: %r') % result)
def fdopen(fd, *args, **kwargs):
# NOTE(gus): We can't just use os.fdopen() here and allow the
# regular (optional) monkey_patching to do its thing. Turns out
# that regular file objects (as returned by os.fdopen) on python2
# are broken in lots of ways regarding blocking behaviour. We
# *need* the newer io.* objects on py2 (doesn't matter on py3,
# since the old file code has been replaced with io.*)
if eventlet.patcher.is_monkey_patched('os'):
return eventlet.greenio.GreenPipe(fd, *args, **kwargs)
else:
return io.open(fd, *args, **kwargs)
def _fd_logger(level=logging.WARN):
"""Helper that returns a file object that is asynchronously logged"""
read_fd, write_fd = os.pipe()
read_end = io.open(read_fd, 'r', 1)
write_end = io.open(write_fd, 'w', 1)
read_end = fdopen(read_fd, 'r', 1)
write_end = fdopen(write_fd, 'w', 1)
def logger(f):
for line in f:

View File

@ -9,3 +9,5 @@ oslo.config>=3.4.0 # Apache-2.0
oslo.utils>=3.4.0 # Apache-2.0
enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
cffi # MIT
eventlet>=0.18.2 # MIT
greenlet>=0.3.2 # MIT