/****/packagesun.nio.ch;importjava.io.IOException;importjava.lang.reflect.Field;importjava.lang.reflect.Method;importjava.net.InetAddress;importjava.net.InetSocketAddress;importjava.nio.channels.AcceptPendingException;importjava.nio.channels.AsynchronousCloseException;importjava.nio.channels.AsynchronousServerSocketChannel;importjava.nio.channels.AsynchronousSocketChannel;importjava.nio.channels.ClosedChannelException;importjava.nio.channels.CompletionHandler;importjava.nio.channels.NotYetBoundException;importjava.nio.channels.ShutdownChannelGroupException;importjava.security.AccessControlContext;importjava.security.AccessController;importjava.security.PrivilegedAction;importjava.util.Queue;importjava.util.concurrent.ConcurrentLinkedQueue;importjava.util.concurrent.Future;importjava.util.concurrent.atomic.AtomicBoolean;importjava.util.concurrent.atomic.AtomicInteger;importsun.misc.Unsafe;/*** This class enable multiple 'AcceptEx' post on the completion port, hence improve the concurrent connection number.
*@authorYvon
**/publicclassWindowsMultiAcceptSupport {
WindowsAsynchronousServerSocketChannelImpl schannel;privatestaticfinalUnsafe unsafe=Unsafe.getUnsafe();//2 * (sizeof(SOCKET_ADDRESS) + 16)privatestaticfinalintONE_DATA_BUFFER_SIZE=88;privatelonghandle;privateIocp iocp;//typically there will be zero, or one I/O operations pending. In rare//cases there may be more. These rare cases arise when a sequence of accept//operations complete immediately and handled by the initiating thread.//The corresponding OVERLAPPED cannot be reused/released until the completion//event has been posted.privatePendingIoCache ioCache;privateQueuedataBuffers;//the data buffer to receive the local/remote socket address//private final long dataBuffer;privateAtomicInteger pendingAccept;privateintmaxPending;
Method updateAcceptContextM;
Method acceptM;
WindowsMultiAcceptSupport() {//dummy for JNI code.}publicvoidclose()throwsIOException {
schannel.close();for(inti=0; i
}
}/****/publicWindowsMultiAcceptSupport(AsynchronousServerSocketChannel ch,intmaxPost) {if(maxPost<=0||maxPost>1024)thrownewIllegalStateException("maxPost can't less than 1 and greater than 1024");this.schannel=(WindowsAsynchronousServerSocketChannelImpl) ch;
maxPending=maxPost;
dataBuffers=newConcurrentLinkedQueue();for(inti=0; i
dataBuffers.add(unsafe.allocateMemory(ONE_DATA_BUFFER_SIZE));
}
pendingAccept=newAtomicInteger(0);try{
Field f=WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("handle");
f.setAccessible(true);
handle=f.getLong(schannel);
f=WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("iocp");
f.setAccessible(true);
iocp=(Iocp) f.get(schannel);
f=WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("ioCache");
f.setAccessible(true);
ioCache=(PendingIoCache) f.get(schannel);
f=WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("accepting");
f.setAccessible(true);
AtomicBoolean accepting=(AtomicBoolean) f.get(schannel);
accepting.set(true);//disable accepting by origin channel.}catch(Exception e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")publicfinalvoidaccept(A attachment,
CompletionHandlerhandler) {if(handler==null)thrownewNullPointerException("'handler' is null");
implAccept(attachment, (CompletionHandler) handler);
}/*** Task to initiate accept operation and to handle result.*/privateclassAcceptTaskimplementsRunnable, Iocp.ResultHandler {privatefinalWindowsAsynchronousSocketChannelImpl channel;privatefinalAccessControlContext acc;privatefinalPendingFutureresult;privatefinallongdataBuffer;
AcceptTask(WindowsAsynchronousSocketChannelImpl channel, AccessControlContext acc,longdataBuffer, PendingFutureresult) {this.channel=channel;this.acc=acc;this.result=result;this.dataBuffer=dataBuffer;
}voidenableAccept() {
pendingAccept.decrementAndGet();
dataBuffers.add(dataBuffer);
}voidcloseChildChannel() {try{
channel.close();
}catch(IOException ignore) {
}
}//caller must have acquired read lock for the listener and child channel.voidfinishAccept()throwsIOException {/*** JDK7 use 4 calls to getsockname to setup
* local& remote address, this is very inefficient.
*
* I change this to use GetAcceptExSockaddrs*/InetAddress[] socks=newInetAddress[2];int[] ports=newint[2];
updateAcceptContext(handle, channel.handle(), socks, ports, dataBuffer);
InetSocketAddress local=newInetSocketAddress(socks[0], ports[0]);finalInetSocketAddress remote=newInetSocketAddress(socks[1], ports[1]);
channel.setConnected(local, remote);//permission check (in context of initiating thread)if(acc!=null) {
AccessController.doPrivileged(newPrivilegedAction() {publicVoid run() {
SecurityManager sm=System.getSecurityManager();
sm.checkAccept(remote.getAddress().getHostAddress(), remote.getPort());returnnull;
}
}, acc);
}
}/*** Initiates the accept operation.*/@Overridepublicvoidrun() {longoverlapped=0L;try{//begin usage of listener socketschannel.begin();try{//begin usage of child socket (as it is registered with//completion port and so may be closed in the event that//the group is forcefully closed).channel.begin();synchronized(result) {
overlapped=ioCache.add(result);intn=accept0(handle, channel.handle(), overlapped, dataBuffer);//Be careful for the buffer addressif(n==IOStatus.UNAVAILABLE) {return;
}//connection accepted immediatelyfinishAccept();//allow another accept before the result is setenableAccept();
result.setResult(channel);
}
}finally{//end usage on child socketchannel.end();
}
}catch(Throwable x) {//failed to initiate accept so release resourcesif(overlapped!=0L)
ioCache.remove(overlapped);
closeChildChannel();if(xinstanceofClosedChannelException)
x=newAsynchronousCloseException();if(!(xinstanceofIOException)&&!(xinstanceofSecurityException))
x=newIOException(x);
enableAccept();
result.setFailure(x);
}finally{//end of usage of listener socketschannel.end();
}//accept completed immediately but may not have executed on//initiating thread in which case the operation may have been//cancelled.if(result.isCancelled()) {
closeChildChannel();
}//invoke completion handlerInvoker.invokeIndirectly(result);
}/*** Executed when the I/O has completed*/@Overridepublicvoidcompleted(intbytesTransferred,booleancanInvokeDirect) {try{//connection accept after group has shutdownif(iocp.isShutdown()) {thrownewIOException(newShutdownChannelGroupException());
}//finish the accepttry{
schannel.begin();try{
channel.begin();
finishAccept();
}finally{
channel.end();
}
}finally{
schannel.end();
}//allow another accept before the result is setenableAccept();
result.setResult(channel);
}catch(Throwable x) {
enableAccept();
closeChildChannel();if(xinstanceofClosedChannelException)
x=newAsynchronousCloseException();if(!(xinstanceofIOException)&&!(xinstanceofSecurityException))
x=newIOException(x);
result.setFailure(x);
}//if an async cancel has already cancelled the operation then//close the new channel so as to free resourcesif(result.isCancelled()) {
closeChildChannel();
}//invoke handler (but not directly)Invoker.invokeIndirectly(result);
}
@Overridepublicvoidfailed(interror, IOException x) {
enableAccept();
closeChildChannel();//release waitersif(schannel.isOpen()) {
result.setFailure(x);
}else{
result.setFailure(newAsynchronousCloseException());
}
Invoker.invokeIndirectly(result);
}
}
FutureimplAccept(Object attachment,finalCompletionHandlerhandler) {if(!schannel.isOpen()) {
Throwable exc=newClosedChannelException();if(handler==null)returnCompletedFuture.withFailure(exc);
Invoker.invokeIndirectly(schannel, handler, attachment,null, exc);returnnull;
}if(schannel.isAcceptKilled())thrownewRuntimeException("Accept not allowed due to cancellation");//ensure channel is bound to local addressif(schannel.localAddress==null)thrownewNotYetBoundException();//create the socket that will be accepted. The creation of the socket//is enclosed by a begin/end for the listener socket to ensure that//we check that the listener is open and also to prevent the I/O//port from being closed as the new socket is registered.WindowsAsynchronousSocketChannelImpl ch=null;
IOException ioe=null;try{
schannel.begin();
ch=newWindowsAsynchronousSocketChannelImpl(iocp,false);
}catch(IOException x) {
ioe=x;
}finally{
schannel.end();
}if(ioe!=null) {if(handler==null)returnCompletedFuture.withFailure(ioe);
Invoker.invokeIndirectly(this.schannel, handler, attachment,null, ioe);returnnull;
}//need calling context when there is security manager as//permission check may be done in a different thread without//any application call frames on the stackAccessControlContext acc=(System.getSecurityManager()==null)?null: AccessController.getContext();
PendingFutureresult=newPendingFuture(schannel, handler, attachment);//check and set flag to prevent concurrent acceptingif(pendingAccept.get()>=maxPending)thrownewAcceptPendingException();
pendingAccept.incrementAndGet();
AcceptTask task=newAcceptTask(ch, acc, dataBuffers.poll(), result);
result.setContext(task);//initiate I/Oif(Iocp.supportsThreadAgnosticIo()) {
task.run();
}else{
Invoker.invokeOnThreadInThreadPool(this.schannel, task);
}returnresult;
}reimplements for performancestaticnativevoidupdateAcceptContext(longlistenSocket,longacceptSocket,
InetAddress[] addresses,int[] ports,longdataBuffer)throwsIOException;staticnativeintaccept0(longhandle,longhandle2,longoverlapped,longdataBuffer);
}