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):
try:
return func(*args)
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
# 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
......@@ -48,13 +48,16 @@ def posix_read(fd, n):
hub, event = None, None
while True:
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)
try:
return _read(fd, n)
except OSError, e:
if e.errno not in ignored_errors:
raise
if e.errno == EAGAIN and not blocking_fd:
raise
sys.exc_clear()
finally:
# Be sure to restore the fcntl flags before we switch into the hub.
......@@ -62,7 +65,7 @@ def posix_read(fd, n):
# (e.g. when using ttys/ptys). Those other file descriptors are
# impacted by our change of flags, so we should restore them
# 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)
if hub is None:
hub = get_hub()
......@@ -76,17 +79,20 @@ def posix_write(fd, buf):
hub, event = None, None
while True:
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)
try:
return _write(fd, buf)
except OSError, e:
if e.errno not in ignored_errors:
raise
if e.errno == EAGAIN and not blocking_fd:
raise
sys.exc_clear()
finally:
# See note in posix_read().
if not flags & os.O_NONBLOCK:
if blocking_fd:
_map_errors(fcntl.fcntl, fd, fcntl.F_SETFL, flags)
if hub is None:
hub = get_hub()
......
......@@ -8,6 +8,11 @@ try:
except ImportError:
fcntl = None
try:
import errno
except ImportError:
errno = None
class TestOS(TestCase):
......@@ -57,6 +62,59 @@ class TestOS(TestCase):
flags = fcntl.fcntl(w, fcntl.F_GETFL, 0)
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__':
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