Commit 576a756d authored by Vitaly Kruglikov's avatar Vitaly Kruglikov Committed by Denis Bilenko

os: emulate non-blocking API in posix_read/write

parent fac300d1
...@@ -32,7 +32,7 @@ def _map_errors(func, *args): ...@@ -32,7 +32,7 @@ def _map_errors(func, *args):
try: try:
return func(*args) return func(*args)
except IOError, e: except IOError, e:
# IOError is structered like OSError in that it has two args: an error # IOError is structured like OSError in that it has two args: an error
# number and a error string. So we can just re-raise OSError passing it # number and a error string. So we can just re-raise OSError passing it
# the IOError args. If at some point we want to catch other errors and # the IOError args. If at some point we want to catch other errors and
# map those to OSError as well, we need to make sure that it follows # map those to OSError as well, we need to make sure that it follows
...@@ -48,13 +48,16 @@ def posix_read(fd, n): ...@@ -48,13 +48,16 @@ def posix_read(fd, n):
hub, event = None, None hub, event = None, None
while True: while True:
flags = _map_errors(fcntl.fcntl, fd, fcntl.F_GETFL, 0) flags = _map_errors(fcntl.fcntl, fd, fcntl.F_GETFL, 0)
if not flags & os.O_NONBLOCK: blocking_fd = not bool(flags & os.O_NONBLOCK)
if blocking_fd:
_map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags|os.O_NONBLOCK) _map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags|os.O_NONBLOCK)
try: try:
return _read(fd, n) return _read(fd, n)
except OSError, e: except OSError, e:
if e.errno not in ignored_errors: if e.errno not in ignored_errors:
raise raise
if e.errno == EAGAIN and not blocking_fd:
raise
sys.exc_clear() sys.exc_clear()
finally: finally:
# Be sure to restore the fcntl flags before we switch into the hub. # Be sure to restore the fcntl flags before we switch into the hub.
...@@ -62,7 +65,7 @@ def posix_read(fd, n): ...@@ -62,7 +65,7 @@ def posix_read(fd, n):
# (e.g. when using ttys/ptys). Those other file descriptors are # (e.g. when using ttys/ptys). Those other file descriptors are
# impacted by our change of flags, so we should restore them # impacted by our change of flags, so we should restore them
# before any other code can possibly run. # before any other code can possibly run.
if not flags & os.O_NONBLOCK: if blocking_fd:
_map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags) _map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags)
if hub is None: if hub is None:
hub = get_hub() hub = get_hub()
...@@ -76,17 +79,20 @@ def posix_write(fd, buf): ...@@ -76,17 +79,20 @@ def posix_write(fd, buf):
hub, event = None, None hub, event = None, None
while True: while True:
flags = _map_errors(fcntl.fcntl, fd, fcntl.F_GETFL, 0) flags = _map_errors(fcntl.fcntl, fd, fcntl.F_GETFL, 0)
if not flags & os.O_NONBLOCK: blocking_fd = not bool(flags & os.O_NONBLOCK)
if blocking_fd:
_map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags|os.O_NONBLOCK) _map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags|os.O_NONBLOCK)
try: try:
return _write(fd, buf) return _write(fd, buf)
except OSError, e: except OSError, e:
if e.errno not in ignored_errors: if e.errno not in ignored_errors:
raise raise
if e.errno == EAGAIN and not blocking_fd:
raise
sys.exc_clear() sys.exc_clear()
finally: finally:
# See note in posix_read(). # See note in posix_read().
if not flags & os.O_NONBLOCK: if blocking_fd:
_map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags) _map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags)
if hub is None: if hub is None:
hub = get_hub() hub = get_hub()
......
...@@ -8,6 +8,11 @@ try: ...@@ -8,6 +8,11 @@ try:
except ImportError: except ImportError:
fcntl = None fcntl = None
try:
import errno
except ImportError:
errno = None
class TestOS(TestCase): class TestOS(TestCase):
...@@ -57,6 +62,59 @@ class TestOS(TestCase): ...@@ -57,6 +62,59 @@ class TestOS(TestCase):
flags = fcntl.fcntl(w, fcntl.F_GETFL, 0) flags = fcntl.fcntl(w, fcntl.F_GETFL, 0)
assert not flags & os.O_NONBLOCK assert not flags & os.O_NONBLOCK
def test_o_nonblock_read(self):
if fcntl is None or errno is None:
return
r, w = os.pipe()
rflags = fcntl.fcntl(r, fcntl.F_GETFL, 0)
fcntl.fcntl(r, fcntl.F_SETFL, rflags|os.O_NONBLOCK)
gotEAGAIN = False
try:
os.read(r, 3)
except OSError, e:
if e.errno != errno.EAGAIN:
raise
gotEAGAIN = True
assert gotEAGAIN
os.write(w, "foo")
data = os.read(r, 3)
assert data == "foo"
gotEAGAIN = False
try:
os.read(r, 3)
except OSError, e:
if e.errno != errno.EAGAIN:
raise
gotEAGAIN = True
assert gotEAGAIN
def test_o_nonblock_write(self):
if fcntl is None or errno is None:
return
r, w = os.pipe()
wflags = fcntl.fcntl(r, fcntl.F_GETFL, 0)
fcntl.fcntl(w, fcntl.F_SETFL, wflags|os.O_NONBLOCK)
data = "d" * 1000000
# fill output buffer to force EAGAIN in next os.write() call
os.write(w, data)
gotEAGAIN = False
try:
os.write(w, data)
except OSError, e:
if e.errno != errno.EAGAIN:
raise
gotEAGAIN = True
assert gotEAGAIN
if __name__ == '__main__': if __name__ == '__main__':
main() main()
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