Index: build/android/pylib/forwarder.py |
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py |
index f5cde9e29da230201fa3c4ec48408c8319f5cb3c..965823ee4dace1d0e3a33e64ecb95220771e9d24 100644 |
--- a/build/android/pylib/forwarder.py |
+++ b/build/android/pylib/forwarder.py |
@@ -6,6 +6,7 @@ import logging |
import os |
import re |
import sys |
+import threading |
import time |
import android_commands |
@@ -21,7 +22,7 @@ def _MakeBinaryPath(build_type, binary_name): |
class Forwarder(object): |
- """Class to manage port forwards from the device to the host.""" |
+ """Thread-safe class to manage port forwards from the device to the host.""" |
# Unix Abstract socket path: |
_DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' |
@@ -45,7 +46,9 @@ class Forwarder(object): |
assert build_type in ('Release', 'Debug') |
self._adb = adb |
self._host_to_device_port_map = dict() |
- self._device_process = None |
+ self._device_initialized = False |
+ self._host_adb_control_port = 0 |
+ self._lock = threading.Lock() |
self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') |
self._device_forwarder_path_on_host = os.path.join( |
cmd_helper.OutDirectory.get(), build_type, 'forwarder_dist') |
@@ -67,23 +70,58 @@ class Forwarder(object): |
Raises: |
Exception on failure to forward the port. |
""" |
+ with self._lock: |
+ self._InitDeviceLocked(tool) |
+ self._InitHostLocked() |
+ redirection_commands = [ |
+ '%d:%d:%d:%s' % (self._host_adb_control_port, device, host, |
+ host_name) for device, host in port_pairs] |
+ logging.info('Command format: <ADB port>:<Device port>' + |
+ '[:<Forward to port>:<Forward to address>]') |
+ logging.info('Forwarding using commands: %s', redirection_commands) |
+ |
+ for redirection_command in redirection_commands: |
+ try: |
+ (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
+ [self._host_forwarder_path, redirection_command]) |
+ except OSError as e: |
+ if e.errno == 2: |
+ raise Exception('Unable to start host forwarder. Make sure you have' |
+ ' built host_forwarder.') |
+ else: raise |
+ if exit_code != 0: |
+ raise Exception('%s exited with %d:\n%s' % ( |
+ self._host_forwarder_path, exit_code, '\n'.join(output))) |
+ tokens = output.split(':') |
+ if len(tokens) != 2: |
+ raise Exception('Unexpected host forwarder output "%s", ' + |
+ 'expected "device_port:host_port"' % output) |
+ device_port = int(tokens[0]) |
+ host_port = int(tokens[1]) |
+ self._host_to_device_port_map[host_port] = device_port |
+ logging.info('Forwarding device port: %d to host port: %d.', |
+ device_port, host_port) |
+ |
+ def _InitHostLocked(self): |
+ """Initializes the host forwarder process (only once).""" |
+ if self._host_adb_control_port: |
+ return |
self._host_adb_control_port = ports.AllocateTestServerPort() |
if not self._host_adb_control_port: |
raise Exception('Failed to allocate a TCP port in the host machine.') |
- self._adb.PushIfNeeded( |
- self._device_forwarder_path_on_host, Forwarder._DEVICE_FORWARDER_FOLDER) |
- redirection_commands = [ |
- '%d:%d:%d:%s' % (self._host_adb_control_port, device, host, |
- host_name) for device, host in port_pairs] |
- logging.info('Command format: <ADB port>:<Device port>' + |
- '[:<Forward to port>:<Forward to address>]') |
- logging.info('Forwarding using commands: %s', redirection_commands) |
if cmd_helper.RunCmd( |
['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward', |
'tcp:%s' % self._host_adb_control_port, |
'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0: |
raise Exception('Error while running adb forward.') |
+ def _InitDeviceLocked(self, tool): |
+ """Initializes the device forwarder process (only once).""" |
+ if self._device_initialized: |
+ return |
+ self._adb.PushIfNeeded( |
+ self._device_forwarder_path_on_host, |
+ Forwarder._DEVICE_FORWARDER_FOLDER) |
(exit_code, output) = self._adb.GetShellCommandStatusAndOutput( |
'%s %s %s %s' % (Forwarder._LD_LIBRARY_PATH, tool.GetUtilWrapper(), |
Forwarder._DEVICE_FORWARDER_PATH, |
@@ -91,40 +129,31 @@ class Forwarder(object): |
if exit_code != 0: |
raise Exception( |
'Failed to start device forwarder:\n%s' % '\n'.join(output)) |
+ self._device_initialized = True |
+ |
+ def UnmapDevicePort(self, device_port): |
+ """Unmaps a previously forwarded device port. |
- for redirection_command in redirection_commands: |
- try: |
- (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
- [self._host_forwarder_path, redirection_command]) |
- except OSError as e: |
- if e.errno == 2: |
- raise Exception('Unable to start host forwarder. Make sure you have ' |
- 'built host_forwarder.') |
- else: raise |
+ Args: |
+ device_port: A previously forwarded port (through Run()). |
+ """ |
+ with self._lock: |
+ # Please note the minus sign below. |
+ redirection_command = '%d:-%d' % ( |
+ self._host_adb_control_port, device_port) |
+ (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
+ [self._host_forwarder_path, redirection_command]) |
if exit_code != 0: |
raise Exception('%s exited with %d:\n%s' % ( |
self._host_forwarder_path, exit_code, '\n'.join(output))) |
- tokens = output.split(':') |
- if len(tokens) != 2: |
- raise Exception('Unexpected host forwarder output "%s", ' + |
- 'expected "device_port:host_port"' % output) |
- device_port = int(tokens[0]) |
- host_port = int(tokens[1]) |
- self._host_to_device_port_map[host_port] = device_port |
- logging.info('Forwarding device port: %d to host port: %d.', device_port, |
- host_port) |
- |
- def UnmapDevicePort(self, device_port): |
- # Please note the minus sign below. |
- redirection_command = '%d:-%d' % (self._host_adb_control_port, device_port) |
- (exit_code, output) = cmd_helper.GetCmdStatusAndOutput( |
- [self._host_forwarder_path, redirection_command]) |
- if exit_code != 0: |
- raise Exception('%s exited with %d:\n%s' % ( |
- self._host_forwarder_path, exit_code, '\n'.join(output))) |
@staticmethod |
def KillHost(build_type): |
+ """Kills the forwarder process running on the host. |
+ |
+ Args: |
+ build_type: 'Release' or 'Debug' |
+ """ |
logging.info('Killing host_forwarder.') |
host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder') |
assert os.path.exists(host_forwarder_path), 'Please build forwarder2' |
@@ -139,6 +168,13 @@ class Forwarder(object): |
@staticmethod |
def KillDevice(adb, tool): |
+ """Kills the forwarder process running on the device. |
+ |
+ Args: |
+ adb: Instance of AndroidCommands for talking to the device. |
+ tool: Wrapper tool (e.g. valgrind) that can be used to execute the device |
+ forwarder (see valgrind_tools.py). |
+ """ |
logging.info('Killing device_forwarder.') |
if not adb.FileExistsOnDevice(Forwarder._DEVICE_FORWARDER_PATH): |
return |
@@ -156,11 +192,12 @@ class Forwarder(object): |
raise Exception('Timed out while killing device_forwarder') |
def DevicePortForHostPort(self, host_port): |
- """Get the device port that corresponds to a given host port.""" |
- return self._host_to_device_port_map.get(host_port) |
+ """Returns the device port that corresponds to a given host port.""" |
+ with self._lock: |
+ return self._host_to_device_port_map.get(host_port) |
+ # Deprecated. |
def Close(self): |
- """Terminate the forwarder process.""" |
- if self._device_process: |
- self._device_process.close() |
- self._device_process = None |
+ """Terminates the forwarder process.""" |
+ # TODO(pliard): Remove references in client code. |
+ pass |