V
V
Vamp2015-07-24 01:34:12
Java
Vamp, 2015-07-24 01:34:12

How to nicely terminate NIO when a thread is interrupted?

Hello.
I'm making a server on non-blocking sockets. All sorts of different clients will connect to it and communicate with the server using the protocol. The protocol defines "connection start" and "connection end" packets. Normally, the client establishes a TCP connection with the server, sends a "connection start" packet, the server checks the validity of the login with the password from this packet and responds with "connection - ok". Next, the client asynchronously sends a bunch of messages in a large thread, which are processed by the server in the ThreadPoolExecutor.
At some point, one of the parties wants to end the connection and sends an "end of connection" packet. The other side replies "end of connection - ok" and after that the socket is closed.
Everything works fine until the administrator decides to restart the server. To do this, the jvm process sends a SIGTERM signal, which is processed quite straightforwardly - the interrupt status is set to all threads.
At the same time, I want to correctly terminate all client connections by processing the still running requests to the end, sending each client a "connection termination" packet, wait for each "connection termination - ok", close the socket and calmly terminate the stream.
The problem is that the socket is closed with a ClosedByInterruptException on the first read()/write() call made after setting the interrupt status. And this closes the possibilities for me to beautifully disconnect clients.
The question is how to make it so that the socket is not closed when an interrupt request arrives?
The code illustrating the situation can be seen at the link: pastebin.com/nVsVvWXq
Options that do not suit me:
1. Somehow notify the process that it needs to be terminated.
Here you have to poke flags-indicators of the completion of work everywhere. At the same time, I lose the ability to interrupt other blocking operations (such as Thread.sleep()), which can potentially take a long time to complete, and I get extra code. The administrator will also have to remember that this particular process is special and cannot be terminated like any other process in the system.
2. Do everything on traditional blocking sockets.
This is not good, because I have too many clients to allocate one stream to each. Actually, because of this, I am redoing the network part to NIO.
3. Before each call to read () / write () to check the interrupt status. Something like:

if ( Thread.interrupted() ) {
  // ...
  // завершающие действия
  // ...
  socketChannel.write(byeBye);
  // ...
  socketChannel.read(byeByeOk);
  socketChannel.close();
  Thread.currentThread().interrupt();
} else {
  socketChannel.read(inputBuffer);
}

But this does not solve the problem, since the interrupt status can be set exactly between calls to Thread.interrupted() and socketChannel.read().
Although it reduces the likelihood of its occurrence.
4. Convert to NIO.2.
I checked that when working with AsynchronousSocketChannel, the forced closing of the socket does not occur and the problem simply does not exist. But I'm limited to java 6.5
. Also, I don't want to consider options with modification of the protocol, clients, involvement of third-party libraries and software. That is, the question is fundamentally about NIO and the behavior of Channels when setting an interrupted status to a thread. It seems to me that the NIO architects could not have foreseen such a scenario. There must be a workaround, but I don't see it, or I'm just asking the wrong questions to Google.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vamp, 2015-08-09
@Vamp

The problem turned out to be an initially incorrect assumption.
All the books say that the jvm only terminates when all non-daemon threads have terminated. But nowhere is it specified under what conditions a thread receives interrupted status. It always seemed to me that when closing the application externally (Crtl + C, SIGTERM, restarting the system), the jvm will first set the interrupted status to all threads, wait for the last non-daemon thread to complete, and then close. And it is enough just to think over an adequate response to interrupting the flow.
As it turns out, the only way to set interrupted status is to call Thread.interrupt() from user code. And all other completion methods stupidly stop jvm threads without the ability to somehow respond to the situation. And the only way to track it down is to install a shutdown hook.
Basically, this decision quite suits me.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question