[bcl] Grab free ports randomly in NetworkHelpers (#5312)
authorAlexander Köplinger <alex.koeplinger@outlook.com>
Fri, 4 Aug 2017 18:22:50 +0000 (20:22 +0200)
committerGitHub <noreply@github.com>
Fri, 4 Aug 2017 18:22:50 +0000 (20:22 +0200)
* [bcl] Grab free ports randomly in NetworkHelpers

We're frequently seeing "address already in use" errors on Jenkins.

The theory is that when we're running tests and grab the next free
port via our custom NetworkHelpers we're getting a port which will
also be returned to a simultaneously running test (e.g. another chroot)
because we're closing the TcpListener and thus releasing the port until
we start using it in actual test code. By that time the other test
might've already opened the port, causing our test to fail.

Instead we now try to use a random port in the range 10000-60000
and try if it's available. This doesn't completely fix the inherent
race but should hopefully make it way less likely.

mcs/class/test-helpers/NetworkHelpers.cs

index fcb63961d23eaa7a64e09ba4c7bc835ea05214ac..7ebc1fc9c1512b54e5bf808c6ebde1908f659fac 100644 (file)
@@ -6,17 +6,25 @@ namespace MonoTests.Helpers {
 
        public static class NetworkHelpers
        {
+               static Random rndPort = new Random ();
+
                public static int FindFreePort ()
                {
-                       TcpListener l = new TcpListener(IPAddress.Loopback, 0);
-                       l.Start();
-                       int port = ((IPEndPoint)l.LocalEndpoint).Port;
-                       l.Stop();
-                       return port;
+                       return LocalEphemeralEndPoint ().Port;
                }
+
                public static IPEndPoint LocalEphemeralEndPoint ()
                {
-                       return new IPEndPoint (IPAddress.Loopback, FindFreePort());
+                       while (true) {
+                               var ep = new IPEndPoint (IPAddress.Loopback, rndPort.Next (10000, 60000));
+                               var socket = new Socket (ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+
+                               try {
+                                       socket.Bind (ep);
+                                       socket.Close ();
+                                       return ep;
+                               } catch (SocketException) { }
+                       }
                }
        }
 }