Path: blob/master/src/java.base/unix/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java
41137 views
/*1* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.nio.channels.*;28import java.util.concurrent.*;29import java.io.IOException;30import java.io.FileDescriptor;31import java.net.InetSocketAddress;32import java.util.concurrent.atomic.AtomicBoolean;33import java.security.AccessControlContext;34import java.security.AccessController;35import java.security.PrivilegedAction;3637/**38* Unix implementation of AsynchronousServerSocketChannel39*/4041class UnixAsynchronousServerSocketChannelImpl42extends AsynchronousServerSocketChannelImpl43implements Port.PollableChannel44{45private static final NativeDispatcher nd = new SocketDispatcher();4647private final Port port;48private final int fdVal;4950// flag to indicate an accept is outstanding51private final AtomicBoolean accepting = new AtomicBoolean();52private void enableAccept() {53accepting.set(false);54}5556// used to ensure that the context for an asynchronous accept is visible57// the pooled thread that handles the I/O event58private final Object updateLock = new Object();5960// pending accept61private boolean acceptPending;62private CompletionHandler<AsynchronousSocketChannel,Object> acceptHandler;63private Object acceptAttachment;64private PendingFuture<AsynchronousSocketChannel,Object> acceptFuture;6566// context for permission check when security manager set67@SuppressWarnings("removal")68private AccessControlContext acceptAcc;697071UnixAsynchronousServerSocketChannelImpl(Port port)72throws IOException73{74super(port);7576try {77IOUtil.configureBlocking(fd, false);78} catch (IOException x) {79nd.close(fd); // prevent leak80throw x;81}82this.port = port;83this.fdVal = IOUtil.fdVal(fd);8485// add mapping from file descriptor to this channel86port.register(fdVal, this);87}8889@Override90void implClose() throws IOException {91// remove the mapping92port.unregister(fdVal);9394// close file descriptor95nd.close(fd);9697// if there is a pending accept then complete it98CompletionHandler<AsynchronousSocketChannel,Object> handler;99Object att;100PendingFuture<AsynchronousSocketChannel,Object> future;101synchronized (updateLock) {102if (!acceptPending)103return; // no pending accept104acceptPending = false;105handler = acceptHandler;106att = acceptAttachment;107future = acceptFuture;108}109110// discard the stack trace as otherwise it may appear that implClose111// has thrown the exception.112AsynchronousCloseException x = new AsynchronousCloseException();113x.setStackTrace(new StackTraceElement[0]);114if (handler == null) {115future.setFailure(x);116} else {117// invoke by submitting task rather than directly118Invoker.invokeIndirectly(this, handler, att, null, x);119}120}121122@Override123public AsynchronousChannelGroupImpl group() {124return port;125}126127/**128* Invoked by event handling thread when listener socket is polled129*/130@Override131public void onEvent(int events, boolean mayInvokeDirect) {132synchronized (updateLock) {133if (!acceptPending)134return; // may have been grabbed by asynchronous close135acceptPending = false;136}137138// attempt to accept connection139FileDescriptor newfd = new FileDescriptor();140InetSocketAddress[] isaa = new InetSocketAddress[1];141Throwable exc = null;142try {143begin();144int n = Net.accept(this.fd, newfd, isaa);145146// spurious wakeup, is this possible?147if (n == IOStatus.UNAVAILABLE) {148synchronized (updateLock) {149acceptPending = true;150}151port.startPoll(fdVal, Net.POLLIN);152return;153}154155} catch (Throwable x) {156if (x instanceof ClosedChannelException)157x = new AsynchronousCloseException();158exc = x;159} finally {160end();161}162163// Connection accepted so finish it when not holding locks.164AsynchronousSocketChannel child = null;165if (exc == null) {166try {167child = finishAccept(newfd, isaa[0], acceptAcc);168} catch (Throwable x) {169if (!(x instanceof IOException) && !(x instanceof SecurityException))170x = new IOException(x);171exc = x;172}173}174175// copy field befores accept is re-renabled176CompletionHandler<AsynchronousSocketChannel,Object> handler = acceptHandler;177Object att = acceptAttachment;178PendingFuture<AsynchronousSocketChannel,Object> future = acceptFuture;179180// re-enable accepting and invoke handler181enableAccept();182183if (handler == null) {184future.setResult(child, exc);185// if an async cancel has already cancelled the operation then186// close the new channel so as to free resources187if (child != null && future.isCancelled()) {188try {189child.close();190} catch (IOException ignore) { }191}192} else {193Invoker.invoke(this, handler, att, child, exc);194}195}196197/**198* Completes the accept by creating the AsynchronousSocketChannel for199* the given file descriptor and remote address. If this method completes200* with an IOException or SecurityException then the channel/file descriptor201* will be closed.202*/203@SuppressWarnings("removal")204private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,205final InetSocketAddress remote,206AccessControlContext acc)207throws IOException, SecurityException208{209AsynchronousSocketChannel ch = null;210try {211ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);212} catch (IOException x) {213nd.close(newfd);214throw x;215}216217// permission check must always be in initiator's context218try {219if (acc != null) {220AccessController.doPrivileged(new PrivilegedAction<>() {221public Void run() {222SecurityManager sm = System.getSecurityManager();223if (sm != null) {224sm.checkAccept(remote.getAddress().getHostAddress(),225remote.getPort());226}227return null;228}229}, acc);230} else {231SecurityManager sm = System.getSecurityManager();232if (sm != null) {233sm.checkAccept(remote.getAddress().getHostAddress(),234remote.getPort());235}236}237} catch (SecurityException x) {238try {239ch.close();240} catch (Throwable suppressed) {241x.addSuppressed(suppressed);242}243throw x;244}245return ch;246}247248@SuppressWarnings("removal")249@Override250Future<AsynchronousSocketChannel> implAccept(Object att,251CompletionHandler<AsynchronousSocketChannel,Object> handler)252{253// complete immediately if channel is closed254if (!isOpen()) {255Throwable e = new ClosedChannelException();256if (handler == null) {257return CompletedFuture.withFailure(e);258} else {259Invoker.invoke(this, handler, att, null, e);260return null;261}262}263if (localAddress == null)264throw new NotYetBoundException();265266// cancel was invoked with pending accept so connection may have been267// dropped.268if (isAcceptKilled())269throw new RuntimeException("Accept not allowed due cancellation");270271// check and set flag to prevent concurrent accepting272if (!accepting.compareAndSet(false, true))273throw new AcceptPendingException();274275// attempt accept276FileDescriptor newfd = new FileDescriptor();277InetSocketAddress[] isaa = new InetSocketAddress[1];278Throwable exc = null;279try {280begin();281282int n = Net.accept(this.fd, newfd, isaa);283if (n == IOStatus.UNAVAILABLE) {284285// need calling context when there is security manager as286// permission check may be done in a different thread without287// any application call frames on the stack288PendingFuture<AsynchronousSocketChannel,Object> result = null;289synchronized (updateLock) {290if (handler == null) {291this.acceptHandler = null;292result = new PendingFuture<>(this);293this.acceptFuture = result;294} else {295this.acceptHandler = handler;296this.acceptAttachment = att;297}298this.acceptAcc = (System.getSecurityManager() == null) ?299null : AccessController.getContext();300this.acceptPending = true;301}302303// register for connections304port.startPoll(fdVal, Net.POLLIN);305return result;306}307} catch (Throwable x) {308// accept failed309if (x instanceof ClosedChannelException)310x = new AsynchronousCloseException();311exc = x;312} finally {313end();314}315316AsynchronousSocketChannel child = null;317if (exc == null) {318// connection accepted immediately319try {320child = finishAccept(newfd, isaa[0], null);321} catch (Throwable x) {322exc = x;323}324}325326// re-enable accepting before invoking handler327enableAccept();328329if (handler == null) {330return CompletedFuture.withResult(child, exc);331} else {332Invoker.invokeIndirectly(this, handler, att, child, exc);333return null;334}335}336}337338339