init
This commit is contained in:
320
java/org/apache/tomcat/util/net/NioSelectorPool.java
Normal file
320
java/org/apache/tomcat/util/net/NioSelectorPool.java
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.tomcat.util.net;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* Thread safe non blocking selector pool
|
||||
* @version 1.0
|
||||
* @since 6.0
|
||||
*/
|
||||
|
||||
public class NioSelectorPool {
|
||||
|
||||
public NioSelectorPool() {
|
||||
}
|
||||
|
||||
private static final Log log = LogFactory.getLog(NioSelectorPool.class);
|
||||
|
||||
protected static final boolean SHARED =
|
||||
Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.net.NioSelectorShared", "true"));
|
||||
|
||||
protected NioBlockingSelector blockingSelector;
|
||||
|
||||
protected volatile Selector SHARED_SELECTOR;
|
||||
|
||||
protected int maxSelectors = 200;
|
||||
protected long sharedSelectorTimeout = 30000;
|
||||
protected int maxSpareSelectors = -1;
|
||||
protected boolean enabled = true;
|
||||
protected AtomicInteger active = new AtomicInteger(0);
|
||||
protected AtomicInteger spare = new AtomicInteger(0);
|
||||
protected ConcurrentLinkedQueue<Selector> selectors =
|
||||
new ConcurrentLinkedQueue<>();
|
||||
|
||||
protected Selector getSharedSelector() throws IOException {
|
||||
if (SHARED && SHARED_SELECTOR == null) {
|
||||
synchronized ( NioSelectorPool.class ) {
|
||||
if ( SHARED_SELECTOR == null ) {
|
||||
SHARED_SELECTOR = Selector.open();
|
||||
log.info("Using a shared selector for servlet write/read");
|
||||
}
|
||||
}
|
||||
}
|
||||
return SHARED_SELECTOR;
|
||||
}
|
||||
|
||||
public Selector get() throws IOException{
|
||||
if ( SHARED ) {
|
||||
return getSharedSelector();
|
||||
}
|
||||
if ( (!enabled) || active.incrementAndGet() >= maxSelectors ) {
|
||||
if ( enabled ) active.decrementAndGet();
|
||||
return null;
|
||||
}
|
||||
Selector s = null;
|
||||
try {
|
||||
s = selectors.size()>0?selectors.poll():null;
|
||||
if (s == null) {
|
||||
s = Selector.open();
|
||||
}
|
||||
else spare.decrementAndGet();
|
||||
|
||||
}catch (NoSuchElementException x ) {
|
||||
try {
|
||||
s = Selector.open();
|
||||
} catch (IOException iox) {
|
||||
}
|
||||
} finally {
|
||||
if ( s == null ) active.decrementAndGet();//we were unable to find a selector
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void put(Selector s) throws IOException {
|
||||
if ( SHARED ) return;
|
||||
if ( enabled ) active.decrementAndGet();
|
||||
if ( enabled && (maxSpareSelectors==-1 || spare.get() < Math.min(maxSpareSelectors,maxSelectors)) ) {
|
||||
spare.incrementAndGet();
|
||||
selectors.offer(s);
|
||||
}
|
||||
else s.close();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
enabled = false;
|
||||
Selector s;
|
||||
while ( (s = selectors.poll()) != null ) s.close();
|
||||
spare.set(0);
|
||||
active.set(0);
|
||||
if (blockingSelector!=null) {
|
||||
blockingSelector.close();
|
||||
}
|
||||
if ( SHARED && getSharedSelector()!=null ) {
|
||||
getSharedSelector().close();
|
||||
SHARED_SELECTOR = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void open() throws IOException {
|
||||
enabled = true;
|
||||
getSharedSelector();
|
||||
if (SHARED) {
|
||||
blockingSelector = new NioBlockingSelector();
|
||||
blockingSelector.open(getSharedSelector());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a write using the bytebuffer for data to be written and a
|
||||
* selector to block (if blocking is requested). If the
|
||||
* <code>selector</code> parameter is null, and blocking is requested then
|
||||
* it will perform a busy write that could take up a lot of CPU cycles.
|
||||
* @param buf The buffer containing the data, we will write as long as <code>(buf.hasRemaining()==true)</code>
|
||||
* @param socket The socket to write data to
|
||||
* @param selector The selector to use for blocking, if null then a busy write will be initiated
|
||||
* @param writeTimeout The timeout for this write operation in milliseconds, -1 means no timeout
|
||||
* @param block <code>true</code> to perform a blocking write
|
||||
* otherwise a non-blocking write will be performed
|
||||
* @return int - returns the number of bytes written
|
||||
* @throws EOFException if write returns -1
|
||||
* @throws SocketTimeoutException if the write times out
|
||||
* @throws IOException if an IO Exception occurs in the underlying socket logic
|
||||
*/
|
||||
public int write(ByteBuffer buf, NioChannel socket, Selector selector,
|
||||
long writeTimeout, boolean block) throws IOException {
|
||||
if ( SHARED && block ) {
|
||||
return blockingSelector.write(buf,socket,writeTimeout);
|
||||
}
|
||||
SelectionKey key = null;
|
||||
int written = 0;
|
||||
boolean timedout = false;
|
||||
int keycount = 1; //assume we can write
|
||||
long time = System.currentTimeMillis(); //start the timeout timer
|
||||
try {
|
||||
while ( (!timedout) && buf.hasRemaining() ) {
|
||||
int cnt = 0;
|
||||
if ( keycount > 0 ) { //only write if we were registered for a write
|
||||
cnt = socket.write(buf); //write the data
|
||||
if (cnt == -1) throw new EOFException();
|
||||
|
||||
written += cnt;
|
||||
if (cnt > 0) {
|
||||
time = System.currentTimeMillis(); //reset our timeout timer
|
||||
continue; //we successfully wrote, try again without a selector
|
||||
}
|
||||
if (cnt==0 && (!block)) break; //don't block
|
||||
}
|
||||
if ( selector != null ) {
|
||||
//register OP_WRITE to the selector
|
||||
if (key==null) key = socket.getIOChannel().register(selector, SelectionKey.OP_WRITE);
|
||||
else key.interestOps(SelectionKey.OP_WRITE);
|
||||
if (writeTimeout==0) {
|
||||
timedout = buf.hasRemaining();
|
||||
} else if (writeTimeout<0) {
|
||||
keycount = selector.select();
|
||||
} else {
|
||||
keycount = selector.select(writeTimeout);
|
||||
}
|
||||
}
|
||||
if (writeTimeout > 0 && (selector == null || keycount == 0) ) timedout = (System.currentTimeMillis()-time)>=writeTimeout;
|
||||
}//while
|
||||
if ( timedout ) throw new SocketTimeoutException();
|
||||
} finally {
|
||||
if (key != null) {
|
||||
key.cancel();
|
||||
if (selector != null) selector.selectNow();//removes the key from this selector
|
||||
}
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a blocking read using the bytebuffer for data to be read and a selector to block.
|
||||
* If the <code>selector</code> parameter is null, then it will perform a busy read that could
|
||||
* take up a lot of CPU cycles.
|
||||
* @param buf ByteBuffer - the buffer containing the data, we will read as until we have read at least one byte or we timed out
|
||||
* @param socket SocketChannel - the socket to write data to
|
||||
* @param selector Selector - the selector to use for blocking, if null then a busy read will be initiated
|
||||
* @param readTimeout long - the timeout for this read operation in milliseconds, -1 means no timeout
|
||||
* @return int - returns the number of bytes read
|
||||
* @throws EOFException if read returns -1
|
||||
* @throws SocketTimeoutException if the read times out
|
||||
* @throws IOException if an IO Exception occurs in the underlying socket logic
|
||||
*/
|
||||
public int read(ByteBuffer buf, NioChannel socket, Selector selector, long readTimeout) throws IOException {
|
||||
return read(buf,socket,selector,readTimeout,true);
|
||||
}
|
||||
/**
|
||||
* Performs a read using the bytebuffer for data to be read and a selector to register for events should
|
||||
* you have the block=true.
|
||||
* If the <code>selector</code> parameter is null, then it will perform a busy read that could
|
||||
* take up a lot of CPU cycles.
|
||||
* @param buf ByteBuffer - the buffer containing the data, we will read as until we have read at least one byte or we timed out
|
||||
* @param socket SocketChannel - the socket to write data to
|
||||
* @param selector Selector - the selector to use for blocking, if null then a busy read will be initiated
|
||||
* @param readTimeout long - the timeout for this read operation in milliseconds, -1 means no timeout
|
||||
* @param block - true if you want to block until data becomes available or timeout time has been reached
|
||||
* @return int - returns the number of bytes read
|
||||
* @throws EOFException if read returns -1
|
||||
* @throws SocketTimeoutException if the read times out
|
||||
* @throws IOException if an IO Exception occurs in the underlying socket logic
|
||||
*/
|
||||
public int read(ByteBuffer buf, NioChannel socket, Selector selector, long readTimeout, boolean block) throws IOException {
|
||||
if ( SHARED && block ) {
|
||||
return blockingSelector.read(buf,socket,readTimeout);
|
||||
}
|
||||
SelectionKey key = null;
|
||||
int read = 0;
|
||||
boolean timedout = false;
|
||||
int keycount = 1; //assume we can write
|
||||
long time = System.currentTimeMillis(); //start the timeout timer
|
||||
try {
|
||||
while ( (!timedout) ) {
|
||||
int cnt = 0;
|
||||
if ( keycount > 0 ) { //only read if we were registered for a read
|
||||
cnt = socket.read(buf);
|
||||
if (cnt == -1) {
|
||||
if (read == 0) {
|
||||
read = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
read += cnt;
|
||||
if (cnt > 0) continue; //read some more
|
||||
if (cnt==0 && (read>0 || (!block) ) ) break; //we are done reading
|
||||
}
|
||||
if ( selector != null ) {//perform a blocking read
|
||||
//register OP_WRITE to the selector
|
||||
if (key==null) key = socket.getIOChannel().register(selector, SelectionKey.OP_READ);
|
||||
else key.interestOps(SelectionKey.OP_READ);
|
||||
if (readTimeout==0) {
|
||||
timedout = (read==0);
|
||||
} else if (readTimeout<0) {
|
||||
keycount = selector.select();
|
||||
} else {
|
||||
keycount = selector.select(readTimeout);
|
||||
}
|
||||
}
|
||||
if (readTimeout > 0 && (selector == null || keycount == 0) ) timedout = (System.currentTimeMillis()-time)>=readTimeout;
|
||||
}//while
|
||||
if ( timedout ) throw new SocketTimeoutException();
|
||||
} finally {
|
||||
if (key != null) {
|
||||
key.cancel();
|
||||
if (selector != null) selector.selectNow();//removes the key from this selector
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public void setMaxSelectors(int maxSelectors) {
|
||||
this.maxSelectors = maxSelectors;
|
||||
}
|
||||
|
||||
public void setMaxSpareSelectors(int maxSpareSelectors) {
|
||||
this.maxSpareSelectors = maxSpareSelectors;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setSharedSelectorTimeout(long sharedSelectorTimeout) {
|
||||
this.sharedSelectorTimeout = sharedSelectorTimeout;
|
||||
}
|
||||
|
||||
public int getMaxSelectors() {
|
||||
return maxSelectors;
|
||||
}
|
||||
|
||||
public int getMaxSpareSelectors() {
|
||||
return maxSpareSelectors;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public long getSharedSelectorTimeout() {
|
||||
return sharedSelectorTimeout;
|
||||
}
|
||||
|
||||
public ConcurrentLinkedQueue<Selector> getSelectors() {
|
||||
return selectors;
|
||||
}
|
||||
|
||||
public AtomicInteger getSpare() {
|
||||
return spare;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user