Bug 1293982 - TcpTransport.close() has to force a shutdown of the socket. draft
authorHenrik Skupin <mail@hskupin.info>
Mon, 15 Aug 2016 13:55:43 +0200
changeset 400711 73e6051ba8cca2e35b1b15ef84a02b1b5473dceb
parent 399408 0502bd9e025edde29777ba1de4280f9b52af4663
child 528297 fced95eb5de16aec8ecc6e25ac47b540a392ec51
push id26249
push userbmo:hskupin@gmail.com
push dateMon, 15 Aug 2016 14:35:56 +0000
bugs1293982
milestone51.0a1
Bug 1293982 - TcpTransport.close() has to force a shutdown of the socket. When the Marionette client closes the socket connection it currently only calls close() on it. This will actually decrease the reference counter, and keeps the OS socket around until it gets garbage collected by the system. This can cause port in use failures for socket connections created shortly after eg. restarts of the appication. By using shutdown() the client indicates that the socket has to be closed immediately. MozReview-Commit-ID: 3jUgaWnujLc
testing/marionette/client/marionette_driver/transport.py
--- a/testing/marionette/client/marionette_driver/transport.py
+++ b/testing/marionette/client/marionette_driver/transport.py
@@ -127,19 +127,17 @@ class TcpTransport(object):
         self.addr = addr
         self.port = port
         self.socket_timeout = socket_timeout
 
         self.protocol = 1
         self.application_type = None
         self.last_id = 0
         self.expected_response = None
-
-        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        self.sock.settimeout(self.socket_timeout)
+        self.sock = None
 
     def _unmarshal(self, packet):
         msg = None
 
         # protocol 3 and above
         if self.protocol >= 3:
             typ = int(packet[1])
             if typ == Command.TYPE:
@@ -205,16 +203,19 @@ class TcpTransport(object):
 
     def connect(self):
         """Connect to the server and process the hello message we expect
         to receive in response.
 
         Returns a tuple of the protocol level and the application type.
         """
         try:
+            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.sock.settimeout(self.socket_timeout)
+
             self.sock.connect((self.addr, self.port))
         except:
             # Unset self.sock so that the next attempt to send will cause
             # another connection attempt.
             self.sock = None
             raise
 
         with SocketTimeout(self.sock, 2.0):
@@ -271,34 +272,36 @@ class TcpTransport(object):
         self.last_id = self.last_id + 1
         cmd = Command(self.last_id, name, params)
         self.send(cmd)
         return self.receive()
 
     def close(self):
         """Close the socket."""
         if self.sock:
+            self.sock.shutdown(socket.SHUT_RDWR)
             self.sock.close()
+            self.sock = None
 
     def __del__(self):
         self.close()
-        self.sock = None
 
 
 def wait_for_port(host, port, timeout=60):
     """Wait for the specified host/port to become available."""
     starttime = datetime.datetime.now()
     poll_interval = 0.1
     while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
         sock = None
         try:
             sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             sock.settimeout(0.5)
             sock.connect((host, port))
             data = sock.recv(16)
+            sock.shutdown(socket.SHUT_RDWR)
             sock.close()
             if ":" in data:
                 return True
         except socket.error:
             pass
         finally:
             if sock is not None:
                 sock.close()