Commit bc406517 authored by Kirill Smelkov's avatar Kirill Smelkov Committed by Jason Madden

Fix FileObjectThread.readinto to be cooperative

.readinto was forgotten to be wrapped by FileObjectBase, and without the
wrapping FileObjectThread.readinto was calling into e.g.  RawIO.readinto
directly - instead of via threadpool - leading to deadlocks if e.g. that
readinto would need to read data from a pipe produced by another greenlet.

The fix is simple: add `readinto` into the list of wrapped methods in
FileObjectBase. Without the fix added `test_readinto` hangs for
TestFileObjectThread.

NOTE FileObjectPosix.readinto was working ok even without wrapping
because it implements cooperation by another way - via explicitly
`make_nonblocking` provided file descriptors.

/cc @jamadden
parent 7484e95d
......@@ -469,6 +469,7 @@ class FileObjectBase(object):
'readline',
'readlines',
'read1',
'readinto',
# Write.
# Note that we do not extend WriteallMixin,
......
......@@ -299,8 +299,24 @@ class TestFileObjectBlock(CleanupMixin,
with io.open(path) as nf:
check(nf)
@skipUnlessWorksWithRegularFiles
def test_readinto_serial(self):
fileno, path = self._mkstemp('.gevent_test_readinto')
os.write(fileno, b'hello world')
os.close(fileno)
with self._makeOne(path, 'rb') as f:
buf = bytearray(32)
mbuf = memoryview(buf)
def assertReadInto(n, dataok):
n_ = f.readinto(mbuf[:n])
self.assertEqual(n_, len(dataok))
self.assertEqual(buf[:n_], dataok)
assertReadInto(2, b'he')
assertReadInto(1, b'l')
assertReadInto(32, b'lo world')
assertReadInto(32, b'')
class ConcurrentFileObjectMixin(object):
......@@ -371,6 +387,28 @@ class ConcurrentFileObjectMixin(object):
finally:
g.kill()
def test_readinto(self):
# verify that .readinto() is cooperative.
# if .readinto() is not cooperative spawned greenlet will not be able
# to run and call to .readinto() will block forever.
r, w = self._pipe()
rf = self._makeOne(r, 'rb')
wf = self._makeOne(w, 'wb')
g = gevent.spawn(Writer, wf, [b'hello'])
try:
buf1 = bytearray(32)
buf2 = bytearray(32)
n1 = rf.readinto(buf1)
n2 = rf.readinto(buf2)
self.assertEqual(n1, 5)
self.assertEqual(buf1[:n1], b'hello')
self.assertEqual(n2, 0)
finally:
g.kill()
class TestFileObjectThread(ConcurrentFileObjectMixin, # pylint:disable=too-many-ancestors
TestFileObjectBlock):
......
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