| /* |
| * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.nio.ch; |
| |
| import java.nio.channels.*; |
| import java.net.InetSocketAddress; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.io.IOException; |
| import java.security.AccessControlContext; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import jdk.internal.misc.Unsafe; |
| |
| /** |
| * Windows implementation of AsynchronousServerSocketChannel using overlapped I/O. |
| */ |
| |
| class WindowsAsynchronousServerSocketChannelImpl |
| extends AsynchronousServerSocketChannelImpl implements Iocp.OverlappedChannel |
| { |
| private static final Unsafe unsafe = Unsafe.getUnsafe(); |
| |
| // 2 * (sizeof(SOCKET_ADDRESS) + 16) |
| private static final int DATA_BUFFER_SIZE = 88; |
| |
| private final long handle; |
| private final int completionKey; |
| private final Iocp 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. |
| private final PendingIoCache ioCache; |
| |
| // the data buffer to receive the local/remote socket address |
| private final long dataBuffer; |
| |
| // flag to indicate that an accept operation is outstanding |
| private AtomicBoolean accepting = new AtomicBoolean(); |
| |
| |
| WindowsAsynchronousServerSocketChannelImpl(Iocp iocp) throws IOException { |
| super(iocp); |
| |
| // associate socket with given completion port |
| long h = IOUtil.fdVal(fd); |
| int key; |
| try { |
| key = iocp.associate(this, h); |
| } catch (IOException x) { |
| closesocket0(h); // prevent leak |
| throw x; |
| } |
| |
| this.handle = h; |
| this.completionKey = key; |
| this.iocp = iocp; |
| this.ioCache = new PendingIoCache(); |
| this.dataBuffer = unsafe.allocateMemory(DATA_BUFFER_SIZE); |
| } |
| |
| @Override |
| public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) { |
| return ioCache.remove(overlapped); |
| } |
| |
| @Override |
| void implClose() throws IOException { |
| // close socket (which may cause outstanding accept to be aborted). |
| closesocket0(handle); |
| |
| // waits until the accept operations have completed |
| ioCache.close(); |
| |
| // finally disassociate from the completion port |
| iocp.disassociate(completionKey); |
| |
| // release other resources |
| unsafe.freeMemory(dataBuffer); |
| } |
| |
| @Override |
| public AsynchronousChannelGroupImpl group() { |
| return iocp; |
| } |
| |
| /** |
| * Task to initiate accept operation and to handle result. |
| */ |
| private class AcceptTask implements Runnable, Iocp.ResultHandler { |
| private final WindowsAsynchronousSocketChannelImpl channel; |
| private final AccessControlContext acc; |
| private final PendingFuture<AsynchronousSocketChannel,Object> result; |
| |
| AcceptTask(WindowsAsynchronousSocketChannelImpl channel, |
| AccessControlContext acc, |
| PendingFuture<AsynchronousSocketChannel,Object> result) |
| { |
| this.channel = channel; |
| this.acc = acc; |
| this.result = result; |
| } |
| |
| void enableAccept() { |
| accepting.set(false); |
| } |
| |
| void closeChildChannel() { |
| try { |
| channel.close(); |
| } catch (IOException ignore) { } |
| } |
| |
| // caller must have acquired read lock for the listener and child channel. |
| void finishAccept() throws IOException { |
| /** |
| * Set local/remote addresses. This is currently very inefficient |
| * in that it requires 2 calls to getsockname and 2 calls to getpeername. |
| * (should change this to use GetAcceptExSockaddrs) |
| */ |
| updateAcceptContext(handle, channel.handle()); |
| |
| InetSocketAddress local = Net.localAddress(channel.fd); |
| final InetSocketAddress remote = Net.remoteAddress(channel.fd); |
| channel.setConnected(local, remote); |
| |
| // permission check (in context of initiating thread) |
| if (acc != null) { |
| AccessController.doPrivileged(new PrivilegedAction<Void>() { |
| public Void run() { |
| SecurityManager sm = System.getSecurityManager(); |
| sm.checkAccept(remote.getAddress().getHostAddress(), |
| remote.getPort()); |
| return null; |
| } |
| }, acc); |
| } |
| } |
| |
| /** |
| * Initiates the accept operation. |
| */ |
| @Override |
| public void run() { |
| long overlapped = 0L; |
| |
| try { |
| // begin usage of listener socket |
| 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); |
| |
| int n = accept0(handle, channel.handle(), overlapped, dataBuffer); |
| if (n == IOStatus.UNAVAILABLE) { |
| return; |
| } |
| |
| // connection accepted immediately |
| finishAccept(); |
| |
| // allow another accept before the result is set |
| enableAccept(); |
| result.setResult(channel); |
| } |
| } finally { |
| // end usage on child socket |
| channel.end(); |
| } |
| } catch (Throwable x) { |
| // failed to initiate accept so release resources |
| if (overlapped != 0L) |
| ioCache.remove(overlapped); |
| closeChildChannel(); |
| if (x instanceof ClosedChannelException) |
| x = new AsynchronousCloseException(); |
| if (!(x instanceof IOException) && !(x instanceof SecurityException)) |
| x = new IOException(x); |
| enableAccept(); |
| result.setFailure(x); |
| } finally { |
| // end of usage of listener socket |
| 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 handler |
| Invoker.invokeIndirectly(result); |
| } |
| |
| /** |
| * Executed when the I/O has completed |
| */ |
| @Override |
| public void completed(int bytesTransferred, boolean canInvokeDirect) { |
| try { |
| // connection accept after group has shutdown |
| if (iocp.isShutdown()) { |
| throw new IOException(new ShutdownChannelGroupException()); |
| } |
| |
| // finish the accept |
| try { |
| begin(); |
| try { |
| channel.begin(); |
| finishAccept(); |
| } finally { |
| channel.end(); |
| } |
| } finally { |
| end(); |
| } |
| |
| // allow another accept before the result is set |
| enableAccept(); |
| result.setResult(channel); |
| } catch (Throwable x) { |
| enableAccept(); |
| closeChildChannel(); |
| if (x instanceof ClosedChannelException) |
| x = new AsynchronousCloseException(); |
| if (!(x instanceof IOException) && !(x instanceof SecurityException)) |
| x = new IOException(x); |
| result.setFailure(x); |
| } |
| |
| // if an async cancel has already cancelled the operation then |
| // close the new channel so as to free resources |
| if (result.isCancelled()) { |
| closeChildChannel(); |
| } |
| |
| // invoke handler (but not directly) |
| Invoker.invokeIndirectly(result); |
| } |
| |
| @Override |
| public void failed(int error, IOException x) { |
| enableAccept(); |
| closeChildChannel(); |
| |
| // release waiters |
| if (isOpen()) { |
| result.setFailure(x); |
| } else { |
| result.setFailure(new AsynchronousCloseException()); |
| } |
| Invoker.invokeIndirectly(result); |
| } |
| } |
| |
| @Override |
| Future<AsynchronousSocketChannel> implAccept(Object attachment, |
| final CompletionHandler<AsynchronousSocketChannel,Object> handler) |
| { |
| if (!isOpen()) { |
| Throwable exc = new ClosedChannelException(); |
| if (handler == null) |
| return CompletedFuture.withFailure(exc); |
| Invoker.invokeIndirectly(this, handler, attachment, null, exc); |
| return null; |
| } |
| if (isAcceptKilled()) |
| throw new RuntimeException("Accept not allowed due to cancellation"); |
| |
| // ensure channel is bound to local address |
| if (localAddress == null) |
| throw new NotYetBoundException(); |
| |
| // 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 { |
| begin(); |
| ch = new WindowsAsynchronousSocketChannelImpl(iocp, false); |
| } catch (IOException x) { |
| ioe = x; |
| } finally { |
| end(); |
| } |
| if (ioe != null) { |
| if (handler == null) |
| return CompletedFuture.withFailure(ioe); |
| Invoker.invokeIndirectly(this, handler, attachment, null, ioe); |
| return null; |
| } |
| |
| // 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 stack |
| AccessControlContext acc = (System.getSecurityManager() == null) ? |
| null : AccessController.getContext(); |
| |
| PendingFuture<AsynchronousSocketChannel,Object> result = |
| new PendingFuture<AsynchronousSocketChannel,Object>(this, handler, attachment); |
| AcceptTask task = new AcceptTask(ch, acc, result); |
| result.setContext(task); |
| |
| // check and set flag to prevent concurrent accepting |
| if (!accepting.compareAndSet(false, true)) |
| throw new AcceptPendingException(); |
| |
| // initiate I/O |
| if (Iocp.supportsThreadAgnosticIo()) { |
| task.run(); |
| } else { |
| Invoker.invokeOnThreadInThreadPool(this, task); |
| } |
| return result; |
| } |
| |
| // -- Native methods -- |
| |
| private static native void initIDs(); |
| |
| private static native int accept0(long listenSocket, long acceptSocket, |
| long overlapped, long dataBuffer) throws IOException; |
| |
| private static native void updateAcceptContext(long listenSocket, |
| long acceptSocket) throws IOException; |
| |
| private static native void closesocket0(long socket) throws IOException; |
| |
| static { |
| IOUtil.load(); |
| initIDs(); |
| } |
| } |