gpsd-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gpsd-dev] [PATCH 2/4] Makes fake.py's TCP and UDP port assignments para


From: Fred Wright
Subject: [gpsd-dev] [PATCH 2/4] Makes fake.py's TCP and UDP port assignments parallel-compatible.
Date: Sun, 28 Feb 2016 19:39:10 -0800

The current method for assigning ports uses a counter initialized to a
constant.  Although this works fine for multiple sessions managed by a
single instance of fake.py, it fails miserably when running multiple
parallel instances of fake.py.  The fix is to allow the OS to assign
the port numbers, since it's guaranteed to pick unused ports.

In the TCP case, this is simply a matter of specifying 0 as the port,
and then extracting the actual assigned port number with
getsockname().

In the UDP case, it's more complicated since the port number being
picked is actually for *gpsd's* end, which can't be done in a
straightforward manner.  The workaround, which was already being used
to pick the control-socket port for gpsd, is to bind a socket with a
reusable address, close it, and then assume that the port will remain
available until gpsd grabs it.  This change turns the existing code to
do that into a function, with the socket type now being specifiable.

TESTED:
Ran all daemon tests in both TCP and UDP modes, on three versions of
OSX as well as Linux, FreeBSD, and OpenBSD.  Used default WRITE_PAD
values except on OSX, where it was reduced to 1ms to save time.
---
 gps/fake.py | 37 ++++++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/gps/fake.py b/gps/fake.py
index 4da48e7..5ba980b 100644
--- a/gps/fake.py
+++ b/gps/fake.py
@@ -321,9 +321,9 @@ class FakePTY(FakeGPS):
         termios.tcdrain(self.fd)
 
 
-def cleansocket(host, port):
+def cleansocket(host, port, type=socket.SOCK_STREAM):
     "Get a socket that we can re-use cleanly after it's closed."
-    cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    cs = socket.socket(socket.AF_INET, type)
     # This magic prevents "Address already in use" errors after
     # we release the socket.
     cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -331,6 +331,19 @@ def cleansocket(host, port):
     return cs
 
 
+def freeport(type=socket.SOCK_STREAM):
+    """Get a free port number for the given connection type.
+
+    This lets the OS assign a unique port, and then assumes
+    that it will become available for reuse once the socket
+    is closed, and remain so long enough for the real use.
+    """
+    s = cleansocket("127.0.0.1", 0, type=type)
+    port = s.getsockname()[1]
+    s.close()
+    return port
+
+
 class FakeTCP(FakeGPS):
     "A TCP serverlet with a test log ready to be cycled to it."
 
@@ -339,9 +352,9 @@ class FakeTCP(FakeGPS):
                  progress=None):
         FakeGPS.__init__(self, testload, progress)
         self.host = host
-        self.port = int(port)
-        self.byname = "tcp://" + host + ":" + str(port)
-        self.dispatcher = cleansocket(self.host, self.port)
+        self.dispatcher = cleansocket(self.host, int(port))
+        self.port = self.dispatcher.getsockname()[1]  # Get actual assigned 
port
+        self.byname = "tcp://" + host + ":" + str(self.port)
         self.dispatcher.listen(5)
         self.readables = [self.dispatcher]
 
@@ -554,14 +567,10 @@ class TestSession:
         self.writers = 0
         self.runqueue = []
         self.index = 0
-        self.baseport = 49194  # In the IANA orivate port range
         if port:
             self.port = port
         else:
-            # Magic way to get a socket with an unused port number
-            s = cleansocket("localhost", 0)
-            self.port = s.getsockname()[1]
-            s.close()
+            self.port = freeport()
         self.progress = lambda x: None
         self.reporter = lambda x: None
         self.default_predicate = None
@@ -585,14 +594,12 @@ class TestSession:
             testload = TestLoad(logfile, predump=self.predump, slow=self.slow, 
oneshot=oneshot)
             if testload.sourcetype == "UDP" or self.udp:
                 newgps = FakeUDP(testload, ipaddr="127.0.0.1",
-                                 port=self.baseport,
+                                 port=freeport(socket.SOCK_DGRAM),
                                  progress=self.progress)
-                self.baseport += 1
             elif testload.sourcetype == "TCP" or self.tcp:
-                newgps = FakeTCP(testload, host="127.0.0.1",
-                                 port=self.baseport,
+                # Let OS assign the port
+                newgps = FakeTCP(testload, host="127.0.0.1", port=0,
                                  progress=self.progress)
-                self.baseport += 1
             else:
                 newgps = FakePTY(testload, speed=speed,
                                  progress=self.progress)
-- 
2.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]