From 1a8fd62dd8e6c556adba46365b219f14ff732b01 Mon Sep 17 00:00:00 2001 From: wcrisman Date: Sat, 30 Aug 2014 17:28:58 -0700 Subject: [PATCH] Modified the web server to use a dedicated thread to handle socket management. It now will setup new sockets, and pass request and response handling for each socket needing it to a runnable run through the thread service. This may impact performance (not sure which direction) and will ensure that socket listening is never dropped in favor of message processing. --- .../com/foundation/web/server/WebServer.java | 138 +++++++++++------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/Foundation Web Core/src/com/foundation/web/server/WebServer.java b/Foundation Web Core/src/com/foundation/web/server/WebServer.java index 9b76471..21c85ff 100644 --- a/Foundation Web Core/src/com/foundation/web/server/WebServer.java +++ b/Foundation Web Core/src/com/foundation/web/server/WebServer.java @@ -1796,12 +1796,11 @@ public class WebServer { * @see java.lang.Runnable#run() */ public void run() { - boolean loop = true; +// boolean loop = true; //Looping only occurs when we are at the maximum allowed number of threads handling messages.// - while(!stop && loop) { + while(!stop /*&& loop*/) { SelectionKey key = null; - boolean isWrite = false; try { //Synchronize so that we ensure thread safe access to the activeThreadCount variable.// @@ -1834,12 +1833,9 @@ public class WebServer { selectedKeys.remove(); //Weed out invalid (cancelled) keys.// - if(key.isValid()) { - isWrite = key.isWritable(); - }//if// - else { + if(!key.isValid()) { key = null; - }//else// + }//if// if(!selectedKeys.hasNext()) { selectedKeys = null; @@ -1856,8 +1852,9 @@ public class WebServer { }//catch// if(key != null) { - ChannelContext context = (ChannelContext) key.attachment(); - SelectableChannel channel = key.channel(); + final boolean isWrite = key.isWritable(); + final ChannelContext context = (ChannelContext) key.attachment(); + final SelectableChannel channel = key.channel(); if(channel instanceof ServerSocketChannel) { try { @@ -1888,7 +1885,7 @@ public class WebServer { }//catch// }//if// else if(channel instanceof SocketChannel) { - boolean socketClosed = false; +// boolean socketClosed = false; //Toggle the write or read flag.// synchronized(key) { @@ -1903,9 +1900,52 @@ public class WebServer { //Not allowing either reads or writes to continue until all processing of this message is done.// // key.interestOps(0); }//synchronized// - - try { - if(((SocketChannel) channel).isOpen()) { + + if(((SocketChannel) channel).isOpen()) { + ThreadService.run(new Runnable() { + public void run() { + boolean socketClosed = false; + + try { + if(isWrite) { + //Prevent another thread from reading/writing on the same socket at the same time (safety). This would have to be removed if SPEEDY (or similar pipelining) were allowed, and AsynchronousSocketChannel/AsynchronousServerSocketChannel would have to be used (requiring jdk7).// + synchronized(((AbstractSocketContext) context).getLock()) { + //Process the pending write to the socket as much as is possible, then return.// + ((AbstractSocketContext) context).processResponses(); + }//synchronized// + }//if// + else { + //Prevent another thread from reading/writing on the same socket at the same time (safety). This would have to be removed if SPEEDY (or similar pipelining) were allowed, and AsynchronousSocketChannel/AsynchronousServerSocketChannel would have to be used (requiring jdk7).// + synchronized(((AbstractSocketContext) context).getLock()) { + //Process the incoming request and send the response (a partial response may be sent in which case the socket will be set to wait for a write opportunity and not a read opportunity).// + ((AbstractSocketContext) context).processRequest(); + }//synchronized// + }//else// + }//try// + catch(TlsFailureException e) { + //Allow the failure to be ignored. This occurs when the client fails to use TLS or fails to send the host name as part of the TLS handshake.// + try {((SocketChannel) channel).close();}catch(Throwable e2) {} //Release the socket so the message doesn't continue to be processed.// + }//catch// + catch(Throwable e) { + if(debug) Debug.log(e); + + //Force the socket to be closed (for sure).// + try {((SocketChannel) channel).close();} catch(Throwable e2) {} + //Debug.log(e); + socketClosed = true; + }//catch// + finally { + if(channel != null && !socketClosed && channel.isOpen() && context != null) { + selector.wakeup(); + }//if// + else if(channel != null && (!channel.isOpen() || socketClosed) && channel instanceof SocketChannel && context instanceof SocketContext) { + cleanupClientChannel((SocketContext) context, (SocketChannel) channel); + }//else if// + }//finally// + }//run()// + }); +/* + try { synchronized(this) { // if(++activeThreadCount != maxThreadCount) { //Start another thread to take this thread's place.// @@ -1937,42 +1977,42 @@ public class WebServer { ((AbstractSocketContext) context).processRequest(); }//synchronized// }//else// - }//if// - }//try// - catch(TlsFailureException e) { - //Allow the failure to be ignored. This occurs when the client fails to use TLS or fails to send the host name as part of the TLS handshake.// - try {((SocketChannel) channel).close();}catch(Throwable e2) {} //Release the socket so the message doesn't continue to be processed.// - }//catch// - catch(Throwable e) { - if(debug) Debug.log(e); - - //Force the socket to be closed (for sure).// - try {((SocketChannel) channel).close();}catch(Throwable e2) {} - //Debug.log(e); - socketClosed = true; - }//catch// - finally { - boolean requiresWakeup = false; - - if(channel != null && !socketClosed && channel.isOpen() && key != null && context != null) { - requiresWakeup = true; + }//try// + catch(TlsFailureException e) { + //Allow the failure to be ignored. This occurs when the client fails to use TLS or fails to send the host name as part of the TLS handshake.// + try {((SocketChannel) channel).close();}catch(Throwable e2) {} //Release the socket so the message doesn't continue to be processed.// + }//catch// + catch(Throwable e) { + if(debug) Debug.log(e); - }//if// - else if(channel != null && (!channel.isOpen() || socketClosed) && channel instanceof SocketChannel && context instanceof SocketContext) { - cleanupClientChannel((SocketContext) context, (SocketChannel) channel); - }//else if// - - //Loop if the last thread to wait for a message couldn't start another thread due to the max number of threads allowed.// - synchronized(this) { -// if(activeThreadCount-- != maxThreadCount) { - loop = false; - - if(requiresWakeup) { - selector.wakeup(); - }//if// -// }//if// - }//synchronized// - }//finally// + //Force the socket to be closed (for sure).// + try {((SocketChannel) channel).close();}catch(Throwable e2) {} + //Debug.log(e); + socketClosed = true; + }//catch// + finally { + boolean requiresWakeup = false; + + if(channel != null && !socketClosed && channel.isOpen() && key != null && context != null) { + requiresWakeup = true; + }//if// + else if(channel != null && (!channel.isOpen() || socketClosed) && channel instanceof SocketChannel && context instanceof SocketContext) { + cleanupClientChannel((SocketContext) context, (SocketChannel) channel); + }//else if// + + //Loop if the last thread to wait for a message couldn't start another thread due to the max number of threads allowed.// + synchronized(this) { + // if(activeThreadCount-- != maxThreadCount) { + loop = false; + + if(requiresWakeup) { + selector.wakeup(); + }//if// + // }//if// + }//synchronized// + }//finally// +*/ + }//if// }//else if// }//if// }//while//