master: make write_all() handle O_NONBLOCK and deadlines.

wip-fakessh-exit-status
David Wilson 7 years ago
parent 2ee7309378
commit 1411790f56

@ -157,14 +157,25 @@ def tty_create_child(*args):
return pid, master_fd return pid, master_fd
def write_all(fd, s): def write_all(fd, s, deadline=None):
timeout = None
written = 0 written = 0
while written < len(s): while written < len(s):
rc = os.write(fd, buffer(s, written)) if deadline is not None:
if not rc: timeout = max(0, deadline - time.time())
raise IOError('short write') if timeout == 0:
written += rc raise mitogen.core.TimeoutError('write timed out')
return written
_, wfds, _ = select.select([], [fd], [], timeout)
if not wfds:
continue
n, disconnected = mitogen.core.io_op(os.write, fd, buffer(s, written))
if disconnected:
raise mitogen.core.StreamError('EOF on stream during write')
written += n
def iter_read(fd, deadline=None): def iter_read(fd, deadline=None):

@ -0,0 +1,7 @@
#!/bin/bash
# I consume 65535 bytes every 10ms, for testing mitogen.core.write_all()
while :; do
read -n 65535
sleep 0.01
done

@ -12,7 +12,9 @@ class IterReadTest(unittest.TestCase):
def make_proc(self): def make_proc(self):
args = [testlib.data_path('iter_read_generator.sh')] args = [testlib.data_path('iter_read_generator.sh')]
return subprocess.Popen(args, stdout=subprocess.PIPE) proc = subprocess.Popen(args, stdout=subprocess.PIPE)
mitogen.core.set_nonblock(proc.stdout.fileno())
return proc
def test_no_deadline(self): def test_no_deadline(self):
proc = self.make_proc() proc = self.make_proc()
@ -54,3 +56,43 @@ class IterReadTest(unittest.TestCase):
assert 3 < len(got) < 5 assert 3 < len(got) < 5
finally: finally:
proc.terminate() proc.terminate()
class WriteAllTest(unittest.TestCase):
func = staticmethod(mitogen.master.write_all)
def make_proc(self):
args = [testlib.data_path('write_all_consumer.sh')]
proc = subprocess.Popen(args, stdin=subprocess.PIPE)
mitogen.core.set_nonblock(proc.stdin.fileno())
return proc
ten_ms_chunk = ('x' * 65535)
def test_no_deadline(self):
proc = self.make_proc()
try:
self.func(proc.stdin.fileno(), self.ten_ms_chunk)
finally:
proc.terminate()
def test_deadline_exceeded_before_call(self):
proc = self.make_proc()
try:
self.assertRaises(mitogen.core.TimeoutError, (
lambda: self.func(proc.stdin.fileno(), self.ten_ms_chunk, 0)
))
finally:
proc.terminate()
def test_deadline_exceeded_during_call(self):
proc = self.make_proc()
try:
deadline = time.time() + 0.1 # 100ms deadline
self.assertRaises(mitogen.core.TimeoutError, (
lambda: self.func(proc.stdin.fileno(),
self.ten_ms_chunk * 100, # 1s of data
deadline)
))
finally:
proc.terminate()

Loading…
Cancel
Save