| /* | |
| * Copyright (c) 2008, 2024, 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 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 PendingFuture<AsynchronousSocketChannel,Object> result; | |
| AcceptTask(WindowsAsynchronousSocketChannelImpl channel, | |
| PendingFuture<AsynchronousSocketChannel,Object> result) | |
| { | |
| this.channel = channel; | |
| 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); | |
| } | |
| /** | |
| * 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 = 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 = 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; | |
| } | |
| PendingFuture<AsynchronousSocketChannel,Object> result = | |
| new PendingFuture<AsynchronousSocketChannel,Object>(this, handler, attachment); | |
| AcceptTask task = new AcceptTask(ch, result); | |
| result.setContext(task); | |
| // check and set flag to prevent concurrent accepting | |
| if (!accepting.compareAndSet(false, true)) | |
| throw new AcceptPendingException(); | |
| // initiate I/O | |
| task.run(); | |
| 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(); | |
| } | |
| } |