Skip to content

Socket timeout problem #676

@duncan-simey

Description

@duncan-simey

I've written a test application that dials a TCP echo service, and I'm using the Socket pattern.
It works well until I add a timeout to the socket. There a problem which causes read() to lockup.
The bug is in AsynchSocketImpl, probably caused by a problem in InputChannel.
Note: this is related to a Discourse entry

Here's my code

    public static void hitZitiService(@Nonnull ZitiContext zitiContext, @Nonnull String serviceName) throws Exception {
        try (Socket socket = zitiContext.connect(serviceName, 8111)) {
            socket.setSoTimeout(1000);
            InputStream zitiInputStream = socket.getInputStream();

            try {
                byte[] buff = new byte[1024];
                while (true){
                    try {
                        int rxLength = zitiInputStream.read(buff, 0, buff.length);
                        if (rxLength < 0) {
                            logger.info("=== [Received message length = "+rxLength+"]");
                        } else {
                            // Log the message
                            String message = new String(buff, 0, rxLength, StandardCharsets.UTF_8);
                            logger.info("=== " + StringUtils.toPrintable(message, 200));
                        }
                    } catch (SocketTimeoutException e) {
                        logger.debug("Socket timeout, trying again", e);
                    }
                }
            } catch (Exception e) {
                throw new Exception("Failed to receive message", e);
            }
        }
    }

The service sends a welcome message, which is read successfully by the first call to read().
The second call to read() throws a SocketTimeoutException, which is expected as there are no more messages from the service
Then the third call to read() is blocked forever, it should timeout.
It seems one of the mutex locks is not being released when the timeout exception is thrown.

Debugging shows that the second call to read() leaves AsynchSocketImpl:159 rf as [Not completed].

                    val rf = CompletableFuture<Int>()
                    val to = (getOption(SocketOptions.SO_TIMEOUT) as Number).toLong()
                    channel.read(input, 0, TimeUnit.MILLISECONDS, rf,
                        object : CompletionHandler<Int, CompletableFuture<Int>> {

There is quite a lot happening with mutex in that code, and I can't quite get my head it, but I think the cause is InputChannel:122 which only completes the handler if data is copied, and timeout exceptions don't copy any data.

        if (copied > 0) {
            inputSupport.mut.unlock()
            handler.completed(copied, att)
            return
        }

FYI - I regularly use socket timeouts to allow applications to shutdown in an orderly manner rather than hang waiting for input they are never going to receive.

Until this is fixed I'll modify the code to avoid using socket timeout, or use socket timeout to terminate execution (rather than check status).
Please could you have a look.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions