jdk/src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java
changeset 2057 3acf8e5e2ca0
child 3632 399359a027de
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.nio.ch;
       
    27 
       
    28 import java.nio.channels.*;
       
    29 import java.util.concurrent.*;
       
    30 import java.io.IOException;
       
    31 import java.io.FileDescriptor;
       
    32 import java.net.InetSocketAddress;
       
    33 import java.util.concurrent.atomic.AtomicBoolean;
       
    34 import java.security.AccessControlContext;
       
    35 import java.security.AccessController;
       
    36 import java.security.PrivilegedAction;
       
    37 
       
    38 /**
       
    39  * Unix implementation of AsynchronousServerSocketChannel
       
    40  */
       
    41 
       
    42 class UnixAsynchronousServerSocketChannelImpl
       
    43     extends AsynchronousServerSocketChannelImpl
       
    44     implements Port.PollableChannel
       
    45 {
       
    46     private final static NativeDispatcher nd = new SocketDispatcher();
       
    47 
       
    48     private final Port port;
       
    49     private final int fdVal;
       
    50 
       
    51     // flag to indicate an accept is outstanding
       
    52     private final AtomicBoolean accepting = new AtomicBoolean();
       
    53     private void enableAccept() {
       
    54         accepting.set(false);
       
    55     }
       
    56 
       
    57     // used to ensure that the context for an asynchronous accept is visible
       
    58     // the pooled thread that handles the I/O event
       
    59     private final Object updateLock = new Object();
       
    60 
       
    61     // pending accept
       
    62     private PendingFuture<AsynchronousSocketChannel,Object> pendingAccept;
       
    63 
       
    64     // context for permission check when security manager set
       
    65     private AccessControlContext acc;
       
    66 
       
    67 
       
    68     UnixAsynchronousServerSocketChannelImpl(Port port)
       
    69         throws IOException
       
    70     {
       
    71         super(port);
       
    72 
       
    73         try {
       
    74             IOUtil.configureBlocking(fd, false);
       
    75         } catch (IOException x) {
       
    76             nd.close(fd);  // prevent leak
       
    77             throw x;
       
    78         }
       
    79         this.port = port;
       
    80         this.fdVal = IOUtil.fdVal(fd);
       
    81 
       
    82         // add mapping from file descriptor to this channel
       
    83         port.register(fdVal, this);
       
    84     }
       
    85 
       
    86     // returns and clears the result of a pending accept
       
    87     private PendingFuture<AsynchronousSocketChannel,Object> grabPendingAccept() {
       
    88         synchronized (updateLock) {
       
    89             PendingFuture<AsynchronousSocketChannel,Object> result = pendingAccept;
       
    90             pendingAccept = null;
       
    91             return result;
       
    92         }
       
    93     }
       
    94 
       
    95     @Override
       
    96     void implClose() throws IOException {
       
    97         // remove the mapping
       
    98         port.unregister(fdVal);
       
    99 
       
   100         // close file descriptor
       
   101         nd.close(fd);
       
   102 
       
   103         // if there is a pending accept then complete it
       
   104         final PendingFuture<AsynchronousSocketChannel,Object> result =
       
   105             grabPendingAccept();
       
   106         if (result != null) {
       
   107             // discard the stack trace as otherwise it may appear that implClose
       
   108             // has thrown the exception.
       
   109             AsynchronousCloseException x = new AsynchronousCloseException();
       
   110             x.setStackTrace(new StackTraceElement[0]);
       
   111             result.setFailure(x);
       
   112 
       
   113             // invoke by submitting task rather than directly
       
   114             Invoker.invokeIndirectly(result.handler(), result);
       
   115         }
       
   116     }
       
   117 
       
   118     @Override
       
   119     public AsynchronousChannelGroupImpl group() {
       
   120         return port;
       
   121     }
       
   122 
       
   123     /**
       
   124      * Invoked by event handling thread when listener socket is polled
       
   125      */
       
   126     @Override
       
   127     public void onEvent(int events) {
       
   128         PendingFuture<AsynchronousSocketChannel,Object> result = grabPendingAccept();
       
   129         if (result == null)
       
   130             return; // may have been grabbed by asynchronous close
       
   131 
       
   132         // attempt to accept connection
       
   133         FileDescriptor newfd = new FileDescriptor();
       
   134         InetSocketAddress[] isaa = new InetSocketAddress[1];
       
   135         boolean accepted = false;
       
   136         try {
       
   137             begin();
       
   138             int n = accept0(this.fd, newfd, isaa);
       
   139 
       
   140             // spurious wakeup, is this possible?
       
   141             if (n == IOStatus.UNAVAILABLE) {
       
   142                 synchronized (updateLock) {
       
   143                     this.pendingAccept = result;
       
   144                 }
       
   145                 port.startPoll(fdVal, Port.POLLIN);
       
   146                 return;
       
   147             }
       
   148 
       
   149             // connection accepted
       
   150             accepted = true;
       
   151 
       
   152         } catch (Throwable x) {
       
   153             if (x instanceof ClosedChannelException)
       
   154                 x = new AsynchronousCloseException();
       
   155             enableAccept();
       
   156             result.setFailure(x);
       
   157         } finally {
       
   158             end();
       
   159         }
       
   160 
       
   161         // Connection accepted so finish it when not holding locks.
       
   162         AsynchronousSocketChannel child = null;
       
   163         if (accepted) {
       
   164             try {
       
   165                 child = finishAccept(newfd, isaa[0], acc);
       
   166                 enableAccept();
       
   167                 result.setResult(child);
       
   168             } catch (Throwable x) {
       
   169                 enableAccept();
       
   170                 if (!(x instanceof IOException) && !(x instanceof SecurityException))
       
   171                     x = new IOException(x);
       
   172                 result.setFailure(x);
       
   173             }
       
   174         }
       
   175 
       
   176         // if an async cancel has already cancelled the operation then
       
   177         // close the new channel so as to free resources
       
   178         if (child != null && result.isCancelled()) {
       
   179             try {
       
   180                 child.close();
       
   181             } catch (IOException ignore) { }
       
   182         }
       
   183 
       
   184         // invoke the handler
       
   185         Invoker.invoke(result.handler(), result);
       
   186     }
       
   187 
       
   188     /**
       
   189      * Completes the accept by creating the AsynchronousSocketChannel for
       
   190      * the given file descriptor and remote address. If this method completes
       
   191      * with an IOException or SecurityException then the channel/file descriptor
       
   192      * will be closed.
       
   193      */
       
   194     private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,
       
   195                                                    final InetSocketAddress remote,
       
   196                                                    AccessControlContext acc)
       
   197         throws IOException, SecurityException
       
   198     {
       
   199         AsynchronousSocketChannel ch = null;
       
   200         try {
       
   201             ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);
       
   202         } catch (IOException x) {
       
   203             nd.close(newfd);
       
   204             throw x;
       
   205         }
       
   206 
       
   207         // permission check must always be in initiator's context
       
   208         try {
       
   209             if (acc != null) {
       
   210                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
       
   211                     public Void run() {
       
   212                         SecurityManager sm = System.getSecurityManager();
       
   213                         if (sm != null) {
       
   214                             sm.checkAccept(remote.getAddress().getHostAddress(),
       
   215                                            remote.getPort());
       
   216                         }
       
   217                         return null;
       
   218                     }
       
   219                 }, acc);
       
   220             } else {
       
   221                 SecurityManager sm = System.getSecurityManager();
       
   222                 if (sm != null) {
       
   223                     sm.checkAccept(remote.getAddress().getHostAddress(),
       
   224                                    remote.getPort());
       
   225                 }
       
   226             }
       
   227         } catch (SecurityException x) {
       
   228             try {
       
   229                 ch.close();
       
   230             } catch (IOException ignore) { }
       
   231             throw x;
       
   232         }
       
   233         return ch;
       
   234     }
       
   235 
       
   236     @Override
       
   237     @SuppressWarnings("unchecked")
       
   238     public <A> Future<AsynchronousSocketChannel> accept(A attachment,
       
   239         final CompletionHandler<AsynchronousSocketChannel,? super A> handler)
       
   240     {
       
   241         // complete immediately if channel is closed
       
   242         if (!isOpen()) {
       
   243             CompletedFuture<AsynchronousSocketChannel,A> result = CompletedFuture
       
   244                 .withFailure(this, new ClosedChannelException(), attachment);
       
   245             Invoker.invokeIndirectly(handler, result);
       
   246             return result;
       
   247         }
       
   248         if (localAddress == null)
       
   249             throw new NotYetBoundException();
       
   250 
       
   251         // cancel was invoked with pending accept so connection may have been
       
   252         // dropped.
       
   253         if (isAcceptKilled())
       
   254             throw new RuntimeException("Accept not allowed due cancellation");
       
   255 
       
   256         // check and set flag to prevent concurrent accepting
       
   257         if (!accepting.compareAndSet(false, true))
       
   258             throw new AcceptPendingException();
       
   259 
       
   260         // attempt accept
       
   261         AbstractFuture<AsynchronousSocketChannel,A> result = null;
       
   262         FileDescriptor newfd = new FileDescriptor();
       
   263         InetSocketAddress[] isaa = new InetSocketAddress[1];
       
   264         try {
       
   265             begin();
       
   266 
       
   267             int n = accept0(this.fd, newfd, isaa);
       
   268             if (n == IOStatus.UNAVAILABLE) {
       
   269                 // no connection to accept
       
   270                 result = new PendingFuture<AsynchronousSocketChannel,A>(this, handler, attachment);
       
   271 
       
   272                 // need calling context when there is security manager as
       
   273                 // permission check may be done in a different thread without
       
   274                 // any application call frames on the stack
       
   275                 synchronized (this) {
       
   276                     this.acc = (System.getSecurityManager() == null) ?
       
   277                         null : AccessController.getContext();
       
   278                     this.pendingAccept =
       
   279                         (PendingFuture<AsynchronousSocketChannel,Object>)result;
       
   280                 }
       
   281 
       
   282                 // register for connections
       
   283                 port.startPoll(fdVal, Port.POLLIN);
       
   284                 return result;
       
   285             }
       
   286         } catch (Throwable x) {
       
   287             // accept failed
       
   288             if (x instanceof ClosedChannelException)
       
   289                 x = new AsynchronousCloseException();
       
   290             result = CompletedFuture.withFailure(this, x, attachment);
       
   291         } finally {
       
   292             end();
       
   293         }
       
   294 
       
   295         // connection accepted immediately
       
   296         if (result == null) {
       
   297             try {
       
   298                 AsynchronousSocketChannel ch = finishAccept(newfd, isaa[0], null);
       
   299                 result = CompletedFuture.withResult(this, ch, attachment);
       
   300             } catch (Throwable x) {
       
   301                 result = CompletedFuture.withFailure(this, x, attachment);
       
   302             }
       
   303         }
       
   304 
       
   305         // re-enable accepting and invoke handler
       
   306         enableAccept();
       
   307         Invoker.invokeIndirectly(handler, result);
       
   308         return result;
       
   309     }
       
   310 
       
   311     // -- Native methods --
       
   312 
       
   313     private static native void initIDs();
       
   314 
       
   315     // Accepts a new connection, setting the given file descriptor to refer to
       
   316     // the new socket and setting isaa[0] to the socket's remote address.
       
   317     // Returns 1 on success, or IOStatus.UNAVAILABLE.
       
   318     //
       
   319     private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
       
   320                                InetSocketAddress[] isaa)
       
   321         throws IOException;
       
   322 
       
   323     static {
       
   324         Util.load();
       
   325         initIDs();
       
   326     }
       
   327 }