* Comet used.\r
*/\r
protected boolean comet = false;\r
-\r
+ \r
+ /**\r
+ * Closed flag, a Comet async thread can \r
+ * signal for this Nio processor to be closed and recycled instead\r
+ * of waiting for a timeout.\r
+ * Closed by HttpServletResponse.getWriter().close()\r
+ */\r
+ protected boolean cometClose = false;\r
\r
/**\r
* Content delimitator for the request (if false, the connection will\r
inputBuffer.recycle();\r
outputBuffer.recycle();\r
this.socket = null;\r
+ this.cometClose = false;\r
+ this.comet = false;\r
}\r
\r
\r
// transactions with the client\r
\r
comet = false;\r
+ cometClose = true;\r
+ SelectionKey key = socket.keyFor(endpoint.getPoller().getSelector());\r
+ if ( key != null ) {\r
+ NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();\r
+ if ( attach!=null && attach.getComet()) {\r
+ //we need to recycle\r
+ request.getAttributes().remove("org.apache.tomcat.comet.timeout");\r
+ attach.setError(true);\r
+ }\r
+ }\r
+\r
try {\r
outputBuffer.endRequest();\r
} catch (IOException e) {\r
\r
int total = 0;\r
private synchronized void addToBB(byte[] buf, int offset, int length) throws IOException {\r
- try {\r
- if (bbuf.capacity() <= (offset + length)) {\r
- flushBuffer();\r
- }\r
- bbuf.put(buf, offset, length);\r
- total += length;\r
- }catch ( Exception x ) {\r
- x.printStackTrace();\r
+ if (bbuf.capacity() <= (offset + length)) {\r
+ flushBuffer();\r
}\r
- //System.out.println("Total:"+total);\r
+ bbuf.put(buf, offset, length);\r
+ total += length;\r
}\r
\r
\r
public void setPollerThreadCount(int pollerThreadCount) { this.pollerThreadCount = pollerThreadCount; }\r
public int getPollerThreadCount() { return pollerThreadCount; }\r
\r
- protected long selectorTimeout = 5000;\r
+ protected long selectorTimeout = 1000;\r
public void setSelectorTimeout(long timeout){ this.selectorTimeout = timeout;}\r
public long getSelectorTimeout(){ return this.selectorTimeout; }\r
/**\r
addEvent(r);\r
}\r
\r
- public void events() {\r
+ public boolean events() {\r
+ boolean result = false;\r
synchronized (events) {\r
Runnable r = null;\r
+ result = (events.size() > 0);\r
while ( (events.size() > 0) && (r = events.removeFirst()) != null ) {\r
try {\r
r.run();\r
}\r
events.clear();\r
}\r
+ return result;\r
}\r
\r
public void register(final SocketChannel socket)\r
// Ignore\r
}\r
}\r
+ boolean hasEvents = false;\r
\r
- events();\r
+ hasEvents = (hasEvents | events());\r
// Time to terminate?\r
if (close) return;\r
\r
log.error("",x);\r
continue;\r
}\r
- \r
- \r
+\r
+ //either we timed out or we woke up, process events first\r
+ if ( keyCount == 0 ) hasEvents = (hasEvents | events());\r
\r
//if (keyCount == 0) continue;\r
\r
}\r
}//while\r
//process timeouts\r
- timeout();\r
- }\r
+ timeout(keyCount,hasEvents);\r
+ }//while\r
synchronized (this) {\r
this.notifyAll();\r
}\r
\r
}\r
- protected void timeout() {\r
+ protected void timeout(int keyCount, boolean hasEvents) {\r
long now = System.currentTimeMillis();\r
- if ( now < nextExpiration ) return;\r
+ //don't process timeouts too frequently, but if the selector simply timed out\r
+ //then we can check timeouts to avoid gaps\r
+ if ( (now < nextExpiration) && (keyCount>0 || hasEvents) ) return;\r
nextExpiration = now + (long)soTimeout;\r
//timeout\r
Set<SelectionKey> keys = selector.keys();\r