Title: [218055] trunk/Tools
Revision
218055
Author
[email protected]
Date
2017-06-10 12:51:13 -0700 (Sat, 10 Jun 2017)

Log Message

webkitpy: Reduce polling in ServerProcess
https://bugs.webkit.org/show_bug.cgi?id=173116

Reviewed by Ryosuke Niwa.

We should be smarter about polling. We do not need to poll every time a line is read from
stdout and stderr since lines are usually read from cached data. We should only poll
when extracting that cached data from stdout and stderr.

* Scripts/webkitpy/port/driver.py:
(Driver._read_block): Rely on output of the ServerProcess to detect a crash or a timeout
and on ServerProcess to poll the process if not data is available in stdout and stderr.
* Scripts/webkitpy/port/driver_unittest.py:
(DriverTest.test_read_block_crashed_process): Test that the Driver will handle a crashing
ServerProcess.
* Scripts/webkitpy/port/server_process.py:
(ServerProcess._read): Allow for data to be read from _output even if the process has
crashed. Only poll the process if data needs to be extracted from stdout or stderr.
* Scripts/webkitpy/port/server_process_mock.py:
(MockServerProcess): Add number_of_times_polled.
(MockServerProcess.poll): Increment number_of_times_polled.
(MockServerProcess.has_crashed): Poll before returning crash state.
(MockServerProcess.read_stdout_line): MockServerProcess should return None if it has crashed,
just like a ServerProcess would.
(MockServerProcess.read_stdout): Ditto.
* Scripts/webkitpy/port/server_process_unittest.py:
(TestServerProcess.test_basic): Use stdin.readline() instead of time.sleep() to prevent the
process from ending before stdout and stderr are read. This is the reason this test was flakey.
(TestServerProcess):
(TestServerProcess.test_process_crashing): Test that when a process crashes, data can be read until
the processes is polled.
(TestServerProcess.test_process_crashing_no_data): Test that when a process which has not output any
data to stdout and stderr crashes, ServerProcess._read(...) polls the process to detect the crash.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (218054 => 218055)


--- trunk/Tools/ChangeLog	2017-06-10 19:20:47 UTC (rev 218054)
+++ trunk/Tools/ChangeLog	2017-06-10 19:51:13 UTC (rev 218055)
@@ -1,3 +1,39 @@
+2017-06-10  Jonathan Bedard  <[email protected]>
+
+        webkitpy: Reduce polling in ServerProcess
+        https://bugs.webkit.org/show_bug.cgi?id=173116
+
+        Reviewed by Ryosuke Niwa.
+
+        We should be smarter about polling. We do not need to poll every time a line is read from
+        stdout and stderr since lines are usually read from cached data. We should only poll
+        when extracting that cached data from stdout and stderr.
+
+        * Scripts/webkitpy/port/driver.py:
+        (Driver._read_block): Rely on output of the ServerProcess to detect a crash or a timeout
+        and on ServerProcess to poll the process if not data is available in stdout and stderr.
+        * Scripts/webkitpy/port/driver_unittest.py:
+        (DriverTest.test_read_block_crashed_process): Test that the Driver will handle a crashing
+        ServerProcess.
+        * Scripts/webkitpy/port/server_process.py:
+        (ServerProcess._read): Allow for data to be read from _output even if the process has
+        crashed. Only poll the process if data needs to be extracted from stdout or stderr. 
+        * Scripts/webkitpy/port/server_process_mock.py:
+        (MockServerProcess): Add number_of_times_polled.
+        (MockServerProcess.poll): Increment number_of_times_polled.
+        (MockServerProcess.has_crashed): Poll before returning crash state.
+        (MockServerProcess.read_stdout_line): MockServerProcess should return None if it has crashed,
+        just like a ServerProcess would.
+        (MockServerProcess.read_stdout): Ditto.
+        * Scripts/webkitpy/port/server_process_unittest.py:
+        (TestServerProcess.test_basic): Use stdin.readline() instead of time.sleep() to prevent the
+        process from ending before stdout and stderr are read. This is the reason this test was flakey.
+        (TestServerProcess):
+        (TestServerProcess.test_process_crashing): Test that when a process crashes, data can be read until
+        the processes is polled.
+        (TestServerProcess.test_process_crashing_no_data): Test that when a process which has not output any
+        data to stdout and stderr crashes, ServerProcess._read(...) polls the process to detect the crash.
+
 2017-06-10  Andy Estes  <[email protected]>
 
         [QuickLook] PreviewLoader needs to check if its ResourceLoader has reached the terminal state before calling didReceiveResponse() and friends

Modified: trunk/Tools/Scripts/webkitpy/port/driver.py (218054 => 218055)


--- trunk/Tools/Scripts/webkitpy/port/driver.py	2017-06-10 19:20:47 UTC (rev 218054)
+++ trunk/Tools/Scripts/webkitpy/port/driver.py	2017-06-10 19:51:13 UTC (rev 218055)
@@ -552,7 +552,7 @@
         out_seen_eof = False
         asan_violation_detected = False
 
-        while not self.has_crashed():
+        while True:
             if out_seen_eof and (self.err_seen_eof or not wait_for_stderr_eof):
                 break
 
@@ -565,7 +565,8 @@
             else:
                 out_line, err_line = self._server_process.read_either_stdout_or_stderr_line(deadline)
 
-            if self._server_process.timed_out or self.has_crashed():
+            # ServerProcess returns None for time outs and crashes.
+            if out_line is None and err_line is None:
                 break
 
             if out_line:

Modified: trunk/Tools/Scripts/webkitpy/port/driver_unittest.py (218054 => 218055)


--- trunk/Tools/Scripts/webkitpy/port/driver_unittest.py	2017-06-10 19:20:47 UTC (rev 218054)
+++ trunk/Tools/Scripts/webkitpy/port/driver_unittest.py	2017-06-10 19:51:13 UTC (rev 218055)
@@ -142,8 +142,28 @@
         self.assertEqual(content_block.content_type, 'my_type')
         self.assertEqual(content_block.encoding, 'none')
         self.assertEqual(content_block.content_hash, 'foobar')
+        # We should only poll once for each line.
+        self.assertEqual(driver._server_process.number_of_times_polled, 4)
         driver._server_process = None
 
+    def test_read_block_crashed_process(self):
+        port = TestWebKitPort()
+        driver = Driver(port, 0, pixel_tests=False)
+        driver._server_process = MockServerProcess(
+            crashed=True,
+            lines=[
+                'ActualHash: foobar',
+                'Content-Type: my_type',
+                'Content-Transfer-Encoding: none',
+                '#EOF',
+            ])
+
+        content_block = driver._read_block(0, "")
+        self.assertEqual(content_block.content_type, None)
+        self.assertEqual(content_block.encoding, None)
+        self.assertEqual(content_block.content_hash, None)
+        driver._server_process = None
+
     def test_read_binary_block(self):
         port = TestWebKitPort()
         driver = Driver(port, 0, pixel_tests=True)

Modified: trunk/Tools/Scripts/webkitpy/port/server_process.py (218054 => 218055)


--- trunk/Tools/Scripts/webkitpy/port/server_process.py	2017-06-10 19:20:47 UTC (rev 218054)
+++ trunk/Tools/Scripts/webkitpy/port/server_process.py	2017-06-10 19:51:13 UTC (rev 218055)
@@ -316,7 +316,8 @@
     # It might be cleaner to pass in the file descriptor to poll instead.
     def _read(self, deadline, fetch_bytes_from_buffers_callback):
         while True:
-            if self.has_crashed():
+            # Polling does not need to occur before bytes are fetched from the buffer.
+            if self._crashed:
                 return None
 
             if time.time() > deadline:
@@ -327,6 +328,9 @@
             if bytes is not None:
                 return bytes
 
+            if self.has_crashed():
+                return None
+
             if self._use_win32_apis:
                 self._wait_for_data_and_update_buffers_using_win32_apis(deadline)
             else:

Modified: trunk/Tools/Scripts/webkitpy/port/server_process_mock.py (218054 => 218055)


--- trunk/Tools/Scripts/webkitpy/port/server_process_mock.py	2017-06-10 19:20:47 UTC (rev 218054)
+++ trunk/Tools/Scripts/webkitpy/port/server_process_mock.py	2017-06-10 19:51:13 UTC (rev 218055)
@@ -37,17 +37,26 @@
         self.env = env
         self.started = False
         self.stopped = False
+        self.number_of_times_polled = 0
 
+    def poll(self):
+        self.number_of_times_polled += 1
+
     def write(self, bytes):
         self.writes.append(bytes)
 
     def has_crashed(self):
+        self.poll()
         return self.crashed
 
     def read_stdout_line(self, deadline):
+        if self.has_crashed():
+            return None
         return self.lines.pop(0) + "\n"
 
     def read_stdout(self, deadline, size):
+        if self.has_crashed():
+            return None
         first_line = self.lines[0]
         if size > len(first_line):
             self.lines.pop(0)

Modified: trunk/Tools/Scripts/webkitpy/port/server_process_unittest.py (218054 => 218055)


--- trunk/Tools/Scripts/webkitpy/port/server_process_unittest.py	2017-06-10 19:20:47 UTC (rev 218054)
+++ trunk/Tools/Scripts/webkitpy/port/server_process_unittest.py	2017-06-10 19:51:13 UTC (rev 218055)
@@ -99,7 +99,7 @@
 
 class TestServerProcess(unittest.TestCase):
     def test_basic(self):
-        cmd = [sys.executable, '-c', 'import sys; import time; time.sleep(0.02); print "stdout"; sys.stdout.flush(); print >>sys.stderr, "stderr"']
+        cmd = [sys.executable, '-c', 'import sys; print "stdout"; sys.stdout.flush(); print >>sys.stderr, "stderr"; sys.stdin.readline();']
         host = SystemHost()
         factory = PortFactory(host)
         port = factory.get()
@@ -118,18 +118,62 @@
         line = proc.read_stdout_line(now - 1)
         self.assertEqual(line, None)
 
-        # FIXME: This part appears to be flaky. line should always be non-None.
-        # FIXME: https://bugs.webkit.org/show_bug.cgi?id=88280
         line = proc.read_stdout_line(now + 1.0)
-        if line:
-            self.assertEqual(line.strip(), "stdout")
+        self.assertEqual(line.strip(), "stdout")
 
         line = proc.read_stderr_line(now + 1.0)
-        if line:
-            self.assertEqual(line.strip(), "stderr")
+        self.assertEqual(line.strip(), "stderr")
 
+        proc.write('End\n')
         proc.stop(0)
 
+    def test_process_crashing(self):
+        cmd = [sys.executable, '-c', 'import sys; print "stdout 1"; print "stdout 2"; print "stdout 3"; sys.stdout.flush(); sys.stdin.readline(); sys.exit(1);']
+        host = SystemHost()
+        factory = PortFactory(host)
+        port = factory.get()
+        now = time.time()
+        proc = server_process.ServerProcess(port, 'python', cmd)
+        proc.write('')
+
+        line = proc.read_stdout_line(now + 1.0)
+        self.assertEqual(line.strip(), 'stdout 1')
+
+        proc.write('End\n')
+        time.sleep(0.1)  # Give process a moment to close.
+
+        line = proc.read_stdout_line(now + 1.0)
+        self.assertEqual(line.strip(), 'stdout 2')
+
+        self.assertEqual(True, proc.has_crashed())
+
+        line = proc.read_stdout_line(now + 1.0)
+        self.assertEqual(line, None)
+
+        proc.stop(0)
+
+    def test_process_crashing_no_data(self):
+        cmd = [sys.executable, '-c',
+               'import sys; sys.stdin.readline(); sys.exit(1);']
+        host = SystemHost()
+        factory = PortFactory(host)
+        port = factory.get()
+        now = time.time()
+        proc = server_process.ServerProcess(port, 'python', cmd)
+        proc.write('')
+
+        self.assertEqual(False, proc.has_crashed())
+
+        proc.write('End\n')
+        time.sleep(0.1)  # Give process a moment to close.
+
+        line = proc.read_stdout_line(now + 1.0)
+        self.assertEqual(line, None)
+
+        self.assertEqual(True, proc.has_crashed())
+
+        proc.stop(0)
+
     def test_cleanup(self):
         port_obj = TrivialMockPort()
         server_process = FakeServerProcess(port_obj=port_obj, name="test", cmd=["test"])
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to