⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 220483 in webkit


Ignore:
Timestamp:
Aug 9, 2017, 2:58:18 PM (9 years ago)
Author:
Jonathan Bedard
Message:

Allow nested timeouts in webkitpy
https://bugs.webkit.org/show_bug.cgi?id=175390
<rdar://problem/33803003>

Reviewed by David Kilzer.

We need to be able to nest timeouts in webkitpy. In particular, we have a few cases where functions
which use timeouts also call the executive. For on-device testing, we need to have timeouts inside
the executive to detect and recover from any issues connecting with devices.

  • Scripts/webkitpy/benchmark_runner/utils.py:

(TimeoutError): Deleted.
(timeout): Deleted.

  • Scripts/webkitpy/common/timeout_context.py: Added.

(Timeout): A timeout context designed to be nested.
(Timeout.TimeoutData): The data required to construct an alarm for a given timeout.
(Timeout.TimeoutData.init):
(Timeout.default_handler): Timeout handler used if none is specified.
(Timeout.current): Access data about the most urgent timeout.
(Timeout.init): Construct a Timeout object with seconds and an optional handler.
(Timeout._bind_timeout_data_to_alarm): Given data about a timeout, initialize an alarm for that timeout.
(Timeout.enter): Un-bind all alarms. Add data for this timeout to the ordered list and bind the most
urgent timeout data.
(Timeout.exit): Un-bind all alarms. Remove data for this timeout from the ordered list and bind the
most urgent timeout data, if such data exists.

  • Scripts/webkitpy/common/timeout_context_unittest.py: Added.

(TimeoutContextTests):
(TimeoutContextTests.test_current_timeout): Test that accessing the nearest timeout works as expected.
(TimeoutContextTests.test_invalid_timeout): Test a timeout of 0.
(TimeoutContextTests.test_timeout_data): Confirm that timeouts are constructed correctly.
(TimeoutContextTests.test_nested_inner_precedence): Check that a more urgent inner timeout takes precedence
over a less urgent outer timeout.
(TimeoutContextTests.test_nested_outer_precedence): Check that a more urgent outer timeout takes precedence
over a less urgent inner timeout.
(TimeoutContextTests.test_no_timeout): Test a block of code without a timeout.
(TimeoutContextTests.test_basic_timeout): Test a block of code expected to timeout.
(TimeoutContextTests.test_exception_constructor_timeout): Test a timeout where the handler is an exception.
(TimeoutContextTests.test_nested_inner_timeout): Confirm that a more urgent inner timeout is triggered.
(TimeoutContextTests.test_nested_outer_timeout): Confirm that a more urgent outer timeout is triggered.

  • Scripts/webkitpy/port/simulator_process.py:

(SimulatorProcess._start): Use Timeout class.

  • Scripts/webkitpy/xcode/simulated_device.py:

(SimulatedDevice.launch_app._log_debug_error): Use Timeout class.
(SimulatedDevice.launch_app): Ditto.
(SimulatedDevice.launch_app._install_timeout): Deleted.

  • Scripts/webkitpy/xcode/simulator.py:

(Simulator.wait_until_device_is_booted):Use Timeout class.
(Simulator.wait_until_device_is_in_state): Ditto.

Location:
trunk/Tools
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r220473 r220483  
     12017-08-09  Jonathan Bedard  <jbedard@apple.com>
     2
     3        Allow nested timeouts in webkitpy
     4        https://bugs.webkit.org/show_bug.cgi?id=175390
     5        <rdar://problem/33803003>
     6
     7        Reviewed by David Kilzer.
     8
     9        We need to be able to nest timeouts in webkitpy. In particular, we have a few cases where functions
     10        which use timeouts also call the executive. For on-device testing, we need to have timeouts inside
     11        the executive to detect and recover from any issues connecting with devices.
     12
     13        * Scripts/webkitpy/benchmark_runner/utils.py:
     14        (TimeoutError): Deleted.
     15        (timeout): Deleted.
     16        * Scripts/webkitpy/common/timeout_context.py: Added.
     17        (Timeout): A timeout context designed to be nested.
     18        (Timeout.TimeoutData): The data required to construct an alarm for a given timeout.
     19        (Timeout.TimeoutData.__init__):
     20        (Timeout.default_handler): Timeout handler used if none is specified.
     21        (Timeout.current): Access data about the most urgent timeout.
     22        (Timeout.__init__): Construct a Timeout object with seconds and an optional handler.
     23        (Timeout._bind_timeout_data_to_alarm): Given data about a timeout, initialize an alarm for that timeout.
     24        (Timeout.__enter__): Un-bind all alarms. Add data for this timeout to the ordered list and bind the most
     25        urgent timeout data.
     26        (Timeout.__exit__): Un-bind all alarms. Remove data for this timeout from the ordered list and bind the
     27        most urgent timeout data, if such data exists.
     28        * Scripts/webkitpy/common/timeout_context_unittest.py: Added.
     29        (TimeoutContextTests):
     30        (TimeoutContextTests.test_current_timeout): Test that accessing the nearest timeout works as expected.
     31        (TimeoutContextTests.test_invalid_timeout): Test a timeout of 0.
     32        (TimeoutContextTests.test_timeout_data): Confirm that timeouts are constructed correctly.
     33        (TimeoutContextTests.test_nested_inner_precedence): Check that a more urgent inner timeout takes precedence
     34        over a less urgent outer timeout.
     35        (TimeoutContextTests.test_nested_outer_precedence): Check that a more urgent outer timeout takes precedence
     36        over a less urgent inner timeout.
     37        (TimeoutContextTests.test_no_timeout): Test a block of code without a timeout.
     38        (TimeoutContextTests.test_basic_timeout): Test a block of code expected to timeout.
     39        (TimeoutContextTests.test_exception_constructor_timeout): Test a timeout where the handler is an exception.
     40        (TimeoutContextTests.test_nested_inner_timeout): Confirm that a more urgent inner timeout is triggered.
     41        (TimeoutContextTests.test_nested_outer_timeout): Confirm that a more urgent outer timeout is triggered.
     42        * Scripts/webkitpy/port/simulator_process.py:
     43        (SimulatorProcess._start): Use Timeout class.
     44        * Scripts/webkitpy/xcode/simulated_device.py:
     45        (SimulatedDevice.launch_app._log_debug_error): Use Timeout class.
     46        (SimulatedDevice.launch_app): Ditto.
     47        (SimulatedDevice.launch_app._install_timeout): Deleted.
     48        * Scripts/webkitpy/xcode/simulator.py:
     49        (Simulator.wait_until_device_is_booted):Use Timeout class.
     50        (Simulator.wait_until_device_is_in_state): Ditto.
     51
    1522017-08-09  Wenson Hsieh  <wenson_hsieh@apple.com>
    253
  • trunk/Tools/Scripts/webkitpy/benchmark_runner/utils.py

    r219853 r220483  
    55import logging
    66import os
    7 import signal
    87import shutil
    9 import sys
    108
    119from webkitpy.common.memoized import memoized
     
    7876    defaults.synchronize()
    7977    return True
    80 
    81 
    82 # Borrow this code from
    83 # 'http://stackoverflow.com/questions/2281850/timeout-function-if-it-takes-too-long-to-finish'
    84 class TimeoutError(Exception):
    85     pass
    86 
    87 
    88 class timeout:
    89 
    90     def __init__(self, seconds=1, error_message='Timeout'):
    91         self.seconds = seconds
    92         self.error_message = error_message
    93 
    94     def handle_timeout(self, signum, frame):
    95         raise TimeoutError(self.error_message)
    96 
    97     def __enter__(self):
    98         signal.signal(signal.SIGALRM, self.handle_timeout)
    99         signal.alarm(self.seconds)
    100 
    101     def __exit__(self, type, value, traceback):
    102         signal.alarm(0)
  • trunk/Tools/Scripts/webkitpy/port/simulator_process.py

    r217856 r220483  
    2323
    2424import os
    25 import signal
    2625import time
    2726
     27from webkitpy.common.timeout_context import Timeout
    2828from webkitpy.port.server_process import ServerProcess
    29 
    3029
    3130class SimulatorProcess(ServerProcess):
     
    9695        self._pid = self._target_host.launch_app(self._bundle_id, self._cmd[1:], env=self._env)
    9796
    98         def handler(signum, frame):
    99             assert signum == signal.SIGALRM
    100             raise RuntimeError('Timed out waiting for pid {} to connect at port {}'.format(self._pid, self._target_host.listening_port()))
    101         signal.signal(signal.SIGALRM, handler)
    102         signal.alarm(6)  # In seconds
    103 
    104         stdin = None
    105         stdout = None
    106         stderr = None
    107         try:
    108             # This order matches the client side connections in Tools/TestRunnerShared/IOSLayoutTestCommunication.cpp setUpIOSLayoutTestCommunication()
    109             stdin = SimulatorProcess._accept_connection_create_file(self._target_host.listening_socket, 'w')
    110             stdout = SimulatorProcess._accept_connection_create_file(self._target_host.listening_socket, 'rb')
    111             stderr = SimulatorProcess._accept_connection_create_file(self._target_host.listening_socket, 'rb')
    112         except:
    113             # We set self._proc as _reset() and _kill() depend on it.
    114             self._proc = SimulatorProcess.Popen(self._pid, stdin, stdout, stderr, self._target_host)
    115             if self._proc.poll() is not None:
     97        with Timeout(6, RuntimeError('Timed out waiting for pid {} to connect at port {}'.format(self._pid, self._target_host.listening_port()))):
     98            stdin = None
     99            stdout = None
     100            stderr = None
     101            try:
     102                # This order matches the client side connections in Tools/TestRunnerShared/IOSLayoutTestCommunication.cpp setUpIOSLayoutTestCommunication()
     103                stdin = SimulatorProcess._accept_connection_create_file(self._target_host.listening_socket, 'w')
     104                stdout = SimulatorProcess._accept_connection_create_file(self._target_host.listening_socket, 'rb')
     105                stderr = SimulatorProcess._accept_connection_create_file(self._target_host.listening_socket, 'rb')
     106            except:
     107                # We set self._proc as _reset() and _kill() depend on it.
     108                self._proc = SimulatorProcess.Popen(self._pid, stdin, stdout, stderr, self._target_host)
     109                if self._proc.poll() is not None:
     110                    self._reset()
     111                    raise Exception('App {} with pid {} crashed before stdin could be attached'.format(os.path.basename(self._cmd[0]), self._pid))
     112                self._kill()
    116113                self._reset()
    117                 raise Exception('App {} with pid {} crashed before stdin could be attached'.format(os.path.basename(self._cmd[0]), self._pid))
    118             self._kill()
    119             self._reset()
    120             raise
    121         signal.alarm(0)  # Cancel alarm
     114                raise
    122115
    123116        self._proc = SimulatorProcess.Popen(self._pid, stdin, stdout, stderr, self._target_host)
  • trunk/Tools/Scripts/webkitpy/xcode/simulated_device.py

    r219534 r220483  
    2323import logging
    2424import re
    25 import signal
    2625import subprocess
    2726
     27from webkitpy.common.host import Host
    2828from webkitpy.common.system.executive import ScriptError
     29from webkitpy.common.timeout_context import Timeout
    2930from webkitpy.xcode.simulator import Simulator
    30 from webkitpy.common.host import Host
    3131
    3232_log = logging.getLogger(__name__)
     
    171171            _log.debug(error.message_with_output())
    172172
    173         def _install_timeout(signum, frame):
    174             assert signum == signal.SIGALRM
    175             raise RuntimeError('Timed out waiting for process to open {} on {}'.format(bundle_id, self.udid))
    176 
    177173        output = None
    178         signal.signal(signal.SIGALRM, _install_timeout)
    179         signal.alarm(timeout)  # In seconds
    180         while True:
    181             output = self._host.executive.run_command(
    182                 ['xcrun', 'simctl', 'launch', self.udid, bundle_id] + args,
    183                 env=environment_to_use,
    184                 error_handler=_log_debug_error,
    185             )
    186             match = re.match(r'(?P<bundle>[^:]+): (?P<pid>\d+)\n', output)
    187             # FIXME: We shouldn't need to check the PID <rdar://problem/31154075>.
    188             if match and self.executive.check_running_pid(int(match.group('pid'))):
    189                 break
    190             if match:
    191                 _log.debug('simctl launch reported pid {}, but this process is not running'.format(match.group('pid')))
    192             else:
    193                 _log.debug('simctl launch did not report a pid')
    194 
    195         signal.alarm(0)  # Cancel alarm
     174
     175        with Timeout(timeout, RuntimeError('Timed out waiting for process to open {} on {}'.format(bundle_id, self.udid))):
     176            while True:
     177                output = self._host.executive.run_command(
     178                    ['xcrun', 'simctl', 'launch', self.udid, bundle_id] + args,
     179                    env=environment_to_use,
     180                    error_handler=_log_debug_error,
     181                )
     182                match = re.match(r'(?P<bundle>[^:]+): (?P<pid>\d+)\n', output)
     183                # FIXME: We shouldn't need to check the PID <rdar://problem/31154075>.
     184                if match and self.executive.check_running_pid(int(match.group('pid'))):
     185                    break
     186                if match:
     187                    _log.debug('simctl launch reported pid {}, but this process is not running'.format(match.group('pid')))
     188                else:
     189                    _log.debug('simctl launch did not report a pid')
    196190
    197191        if match.group('bundle') != bundle_id:
  • trunk/Tools/Scripts/webkitpy/xcode/simulator.py

    r216899 r220483  
    2929import time
    3030
    31 from webkitpy.benchmark_runner.utils import timeout
     31from webkitpy.common.timeout_context import Timeout
    3232from webkitpy.common.host import Host
    3333
     
    243243    def wait_until_device_is_booted(udid, timeout_seconds=60 * 15):
    244244        Simulator.wait_until_device_is_in_state(udid, Simulator.DeviceState.BOOTED, timeout_seconds)
    245         with timeout(seconds=timeout_seconds):
     245        with Timeout(seconds=timeout_seconds):
    246246            while True:
    247247                try:
     
    261261    def wait_until_device_is_in_state(udid, wait_until_state, timeout_seconds=60 * 15):
    262262        _log.debug('waiting for device %s to enter state %s with timeout %s', udid, Simulator.device_state_description(wait_until_state), timeout_seconds)
    263         with timeout(seconds=timeout_seconds):
     263        with Timeout(seconds=timeout_seconds):
    264264            device_state = Simulator.device_state(udid)
    265265            while (device_state != wait_until_state):
Note: See TracChangeset for help on using the changeset viewer.