4

Recently I had an issue with embedded-postgres library. I tried to manually pick a free port via

new ServerSocket(0).getLocalPort()

but I was hitting a race condition somewhere (my tests hanged on the second test suite startup).

Then I learned that the library has this capability (of using random port) itself and it didn't have the problem I had. I analyzed the source code and the only difference I found was that they do an additional check:

try (ServerSocket socket = new ServerSocket(0)) {
    while(!socket.isBound()) {
        Thread.sleep(50);
    }
    return socket.getLocalPort();
}

So after the port was randomized they wait until it is bound. Code I'm referring to.

I'd like to know why this code is there. My understanding of "binding" was that it's equal to "listening" on a given port, yet here it can't be the case, as this code runs before the server starts. In fact, the server will start (and bind itself) to this exact port. This makes me very confused.

Krever
  • 1,371
  • 1
  • 13
  • 32
  • That's actually a very good question and that code doesn't make any sense to me. – mutantkeyboard Feb 15 '21 at 15:08
  • Your new code still has a race condition... Because closing the socket releases the port, and then a new socket can use the same port. – user253751 Feb 15 '21 at 15:32
  • Are you sure the difference isn't that they are closing the socket, and you are not? – user253751 Feb 15 '21 at 15:32
  • Yes, my code was broken also due to not closing the socket, yet the issue disappeared when I attached the debugger anywhere before the part when the first connection is being made, so I don't know the exact cause. Maybe the related snippet was not connected, yet the question holds as the intent behind the snippet was not clear. – Krever Feb 15 '21 at 15:43

1 Answers1

2

The code in the library is a fix to this issue which gives the reason as

..it appears that the EmbeddedPostgres#detectPort method may cause the issue as it does not wait for the socket to be bound before querying the port, leading ServerSocket#getLocalPort to return -1 as documented.

So the call to ServerSocket.getLocalPort() returns -1 if the socket is not bound, and apparently this may happen some time after a call to new ServerSocket(0), though it's unclear under what circumstances. So the code simply waits to make sure it's bound.

However the documentation doesn't say anywhere that new ServerSocket(0) doesn't return a bound socket. The javadoc for ServerSocket(int port) says "Creates a server socket, bound to the specified port.".

However they did have an issue, that's the only fix, and I suppose it resolved the issue...so just very unclear documentation in the JDK?


As for binding vs. listening. Binding is the part where you assign the address and port, it still needs to actively start accepting connections.

Kayaman
  • 72,141
  • 5
  • 83
  • 121