package water;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import water.util.Log;
/**
* The Thread that looks for TCP Cloud requests.
*
* This thread just spins on reading TCP requests from other Nodes.
* @author <a href="mailto:cliffc@h2o.ai"></a>
* @version 1.0
*/
public class TCPReceiverThread extends Thread {
public static ServerSocketChannel SOCK;
public TCPReceiverThread() { super("TCP-Accept"); }
// The Run Method.
// Started by main() on a single thread, this code manages reading TCP requests
@SuppressWarnings("resource")
public void run() {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
ServerSocketChannel errsock = null;
boolean saw_error = false;
while( true ) {
try {
// Cleanup from any prior socket failures. Rare unless we're really sick.
if( errsock != null ) { // One time attempt a socket close
final ServerSocketChannel tmp2 = errsock; errsock = null;
tmp2.close(); // Could throw, but errsock cleared for next pass
}
if( saw_error ) Thread.sleep(100); // prevent deny-of-service endless socket-creates
saw_error = false;
// ---
// More common-case setup of a ServerSocket
if( SOCK == null ) {
SOCK = ServerSocketChannel.open();
SOCK.socket().setReceiveBufferSize(AutoBuffer.BBSIZE);
SOCK.socket().bind(H2O.SELF._key);
}
// Block for TCP connection and setup to read from it.
SocketChannel sock = SOCK.accept();
// Pass off the TCP connection to a separate reader thread
new TCPReaderThread(sock,new AutoBuffer(sock)).start();
} catch( java.nio.channels.AsynchronousCloseException ex ) {
break; // Socket closed for shutdown
} catch( Exception e ) {
// On any error from anybody, close all sockets & re-open
Log.err("Retrying after IO error on TCP port "+H2O.H2O_PORT +": ",e);
saw_error = true;
errsock = SOCK ; SOCK = null; // Signal error recovery on the next loop
}
}
}
// A private thread for reading from this open socket.
public static class TCPReaderThread extends Thread {
public SocketChannel _sock;
public AutoBuffer _ab;
public TCPReaderThread(SocketChannel sock, AutoBuffer ab) {
super("TCP-"+ab._h2o+"-"+(ab._h2o._tcp_readers++));
_sock = sock;
_ab = ab;
setPriority(MAX_PRIORITY-1);
}
public void run() {
while( true ) { // Loop, reading fresh TCP requests until the sender closes
try {
// Record the last time we heard from any given Node
_ab._h2o._last_heard_from = System.currentTimeMillis();
TimeLine.record_recv(_ab, true,0);
// Hand off the TCP connection to the proper handler
int ctrl = _ab.getCtrl();
int x = ctrl;
if( ctrl < 0 || ctrl >= UDP.udp.UDPS.length ) x = 0;
switch( UDP.udp.UDPS[x] ) {
case exec: RPC.remote_exec (_ab); break;
case ack: RPC.tcp_ack (_ab); break;
case timeline: TimeLine.tcp_call(_ab); break;
default: throw new RuntimeException("Unknown TCP Type: " + ctrl+" "+_ab._h2o);
}
} catch( java.nio.channels.AsynchronousCloseException ex ) {
break; // Socket closed for shutdown
} catch( Throwable e ) {
// On any error from anybody, close everything
System.err.println("IO error");
e.printStackTrace();
Log.err("IO error on TCP port "+H2O.H2O_PORT +": ",e);
break;
}
// Reuse open sockets for the next task
try {
if( !_sock.isOpen() ) break;
_ab = new AutoBuffer(_sock);
} catch( Exception e ) {
// Exceptions here are *normal*, this is an idle TCP connection and
// either the OS can time it out, or the cloud might shutdown. We
// don't care what happens to this socket.
break; // Ignore all errors; silently die if socket is closed
}
}
}
}
}