Home | History | Annotate | Download | only in ch
      1 /*
      2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * 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 import dalvik.annotation.optimization.ReachabilitySensitive;
     39 import dalvik.system.CloseGuard;
     40 
     41 /**
     42  * Unix implementation of AsynchronousServerSocketChannel
     43  */
     44 
     45 class UnixAsynchronousServerSocketChannelImpl
     46     extends AsynchronousServerSocketChannelImpl
     47     implements Port.PollableChannel
     48 {
     49     private final static NativeDispatcher nd = new SocketDispatcher();
     50 
     51     private final Port port;
     52     private final int fdVal;
     53 
     54     // flag to indicate an accept is outstanding
     55     private final AtomicBoolean accepting = new AtomicBoolean();
     56     private void enableAccept() {
     57         accepting.set(false);
     58     }
     59 
     60     // used to ensure that the context for an asynchronous accept is visible
     61     // the pooled thread that handles the I/O event
     62     private final Object updateLock = new Object();
     63 
     64     // pending accept
     65     private boolean acceptPending;
     66     private CompletionHandler<AsynchronousSocketChannel,Object> acceptHandler;
     67     private Object acceptAttachment;
     68     private PendingFuture<AsynchronousSocketChannel,Object> acceptFuture;
     69 
     70     // context for permission check when security manager set
     71     private AccessControlContext acceptAcc;
     72 
     73     // Android-added: CloseGuard support.
     74     @ReachabilitySensitive
     75     private final CloseGuard guard = CloseGuard.get();
     76 
     77 
     78     UnixAsynchronousServerSocketChannelImpl(Port port)
     79         throws IOException
     80     {
     81         super(port);
     82 
     83         try {
     84             IOUtil.configureBlocking(fd, false);
     85         } catch (IOException x) {
     86             nd.close(fd);  // prevent leak
     87             throw x;
     88         }
     89         this.port = port;
     90         this.fdVal = IOUtil.fdVal(fd);
     91 
     92         // add mapping from file descriptor to this channel
     93         port.register(fdVal, this);
     94         // Android-added: CloseGuard support.
     95         guard.open("close");
     96     }
     97 
     98     @Override
     99     void implClose() throws IOException {
    100         // Android-added: CloseGuard support.
    101         guard.close();
    102         // remove the mapping
    103         port.unregister(fdVal);
    104 
    105         // close file descriptor
    106         nd.close(fd);
    107 
    108         // if there is a pending accept then complete it
    109         CompletionHandler<AsynchronousSocketChannel,Object> handler;
    110         Object att;
    111         PendingFuture<AsynchronousSocketChannel,Object> future;
    112         synchronized (updateLock) {
    113             if (!acceptPending)
    114                 return;  // no pending accept
    115             acceptPending = false;
    116             handler = acceptHandler;
    117             att = acceptAttachment;
    118             future = acceptFuture;
    119         }
    120 
    121         // discard the stack trace as otherwise it may appear that implClose
    122         // has thrown the exception.
    123         AsynchronousCloseException x = new AsynchronousCloseException();
    124         x.setStackTrace(new StackTraceElement[0]);
    125         if (handler == null) {
    126             future.setFailure(x);
    127         } else {
    128             // invoke by submitting task rather than directly
    129             Invoker.invokeIndirectly(this, handler, att, null, x);
    130         }
    131     }
    132 
    133     // Android-added: CloseGuard support.
    134     protected void finalize() throws Throwable {
    135         try {
    136             if (guard != null) {
    137                 guard.warnIfOpen();
    138             }
    139             close();
    140         } finally {
    141             super.finalize();
    142         }
    143     }
    144 
    145     @Override
    146     public AsynchronousChannelGroupImpl group() {
    147         return port;
    148     }
    149 
    150     /**
    151      * Invoked by event handling thread when listener socket is polled
    152      */
    153     @Override
    154     public void onEvent(int events, boolean mayInvokeDirect) {
    155         synchronized (updateLock) {
    156             if (!acceptPending)
    157                 return;  // may have been grabbed by asynchronous close
    158             acceptPending = false;
    159         }
    160 
    161         // attempt to accept connection
    162         FileDescriptor newfd = new FileDescriptor();
    163         InetSocketAddress[] isaa = new InetSocketAddress[1];
    164         Throwable exc = null;
    165         try {
    166             begin();
    167             int n = accept(this.fd, newfd, isaa);
    168 
    169             // spurious wakeup, is this possible?
    170             if (n == IOStatus.UNAVAILABLE) {
    171                 synchronized (updateLock) {
    172                     acceptPending = true;
    173                 }
    174                 port.startPoll(fdVal, Net.POLLIN);
    175                 return;
    176             }
    177 
    178         } catch (Throwable x) {
    179             if (x instanceof ClosedChannelException)
    180                 x = new AsynchronousCloseException();
    181             exc = x;
    182         } finally {
    183             end();
    184         }
    185 
    186         // Connection accepted so finish it when not holding locks.
    187         AsynchronousSocketChannel child = null;
    188         if (exc == null) {
    189             try {
    190                 child = finishAccept(newfd, isaa[0], acceptAcc);
    191             } catch (Throwable x) {
    192                 if (!(x instanceof IOException) && !(x instanceof SecurityException))
    193                     x = new IOException(x);
    194                 exc = x;
    195             }
    196         }
    197 
    198         // copy field befores accept is re-renabled
    199         CompletionHandler<AsynchronousSocketChannel,Object> handler = acceptHandler;
    200         Object att = acceptAttachment;
    201         PendingFuture<AsynchronousSocketChannel,Object> future = acceptFuture;
    202 
    203         // re-enable accepting and invoke handler
    204         enableAccept();
    205 
    206         if (handler == null) {
    207             future.setResult(child, exc);
    208             // if an async cancel has already cancelled the operation then
    209             // close the new channel so as to free resources
    210             if (child != null && future.isCancelled()) {
    211                 try {
    212                     child.close();
    213                 } catch (IOException ignore) { }
    214             }
    215         } else {
    216             Invoker.invoke(this, handler, att, child, exc);
    217         }
    218     }
    219 
    220     /**
    221      * Completes the accept by creating the AsynchronousSocketChannel for
    222      * the given file descriptor and remote address. If this method completes
    223      * with an IOException or SecurityException then the channel/file descriptor
    224      * will be closed.
    225      */
    226     private AsynchronousSocketChannel finishAccept(FileDescriptor newfd,
    227                                                    final InetSocketAddress remote,
    228                                                    AccessControlContext acc)
    229         throws IOException, SecurityException
    230     {
    231         AsynchronousSocketChannel ch = null;
    232         try {
    233             ch = new UnixAsynchronousSocketChannelImpl(port, newfd, remote);
    234         } catch (IOException x) {
    235             nd.close(newfd);
    236             throw x;
    237         }
    238 
    239         // permission check must always be in initiator's context
    240         try {
    241             if (acc != null) {
    242                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
    243                     public Void run() {
    244                         SecurityManager sm = System.getSecurityManager();
    245                         if (sm != null) {
    246                             sm.checkAccept(remote.getAddress().getHostAddress(),
    247                                            remote.getPort());
    248                         }
    249                         return null;
    250                     }
    251                 }, acc);
    252             } else {
    253                 SecurityManager sm = System.getSecurityManager();
    254                 if (sm != null) {
    255                     sm.checkAccept(remote.getAddress().getHostAddress(),
    256                                    remote.getPort());
    257                 }
    258             }
    259         } catch (SecurityException x) {
    260             try {
    261                 ch.close();
    262             } catch (Throwable suppressed) {
    263                 x.addSuppressed(suppressed);
    264             }
    265             throw x;
    266         }
    267         return ch;
    268     }
    269 
    270     @Override
    271     Future<AsynchronousSocketChannel> implAccept(Object att,
    272         CompletionHandler<AsynchronousSocketChannel,Object> handler)
    273     {
    274         // complete immediately if channel is closed
    275         if (!isOpen()) {
    276             Throwable e = new ClosedChannelException();
    277             if (handler == null) {
    278                 return CompletedFuture.withFailure(e);
    279             } else {
    280                 Invoker.invoke(this, handler, att, null, e);
    281                 return null;
    282             }
    283         }
    284         if (localAddress == null)
    285             throw new NotYetBoundException();
    286 
    287         // cancel was invoked with pending accept so connection may have been
    288         // dropped.
    289         if (isAcceptKilled())
    290             throw new RuntimeException("Accept not allowed due cancellation");
    291 
    292         // check and set flag to prevent concurrent accepting
    293         if (!accepting.compareAndSet(false, true))
    294             throw new AcceptPendingException();
    295 
    296         // attempt accept
    297         FileDescriptor newfd = new FileDescriptor();
    298         InetSocketAddress[] isaa = new InetSocketAddress[1];
    299         Throwable exc = null;
    300         try {
    301             begin();
    302 
    303             int n = accept(this.fd, newfd, isaa);
    304             if (n == IOStatus.UNAVAILABLE) {
    305 
    306                 // need calling context when there is security manager as
    307                 // permission check may be done in a different thread without
    308                 // any application call frames on the stack
    309                 PendingFuture<AsynchronousSocketChannel,Object> result = null;
    310                 synchronized (updateLock) {
    311                     if (handler == null) {
    312                         this.acceptHandler = null;
    313                         result = new PendingFuture<AsynchronousSocketChannel,Object>(this);
    314                         this.acceptFuture = result;
    315                     } else {
    316                         this.acceptHandler = handler;
    317                         this.acceptAttachment = att;
    318                     }
    319                     this.acceptAcc = (System.getSecurityManager() == null) ?
    320                         null : AccessController.getContext();
    321                     this.acceptPending = true;
    322                 }
    323 
    324                 // register for connections
    325                 port.startPoll(fdVal, Net.POLLIN);
    326                 return result;
    327             }
    328         } catch (Throwable x) {
    329             // accept failed
    330             if (x instanceof ClosedChannelException)
    331                 x = new AsynchronousCloseException();
    332             exc = x;
    333         } finally {
    334             end();
    335         }
    336 
    337         AsynchronousSocketChannel child = null;
    338         if (exc == null) {
    339             // connection accepted immediately
    340             try {
    341                 child = finishAccept(newfd, isaa[0], null);
    342             } catch (Throwable x) {
    343                 exc = x;
    344             }
    345         }
    346 
    347         // re-enable accepting before invoking handler
    348         enableAccept();
    349 
    350         if (handler == null) {
    351             return CompletedFuture.withResult(child, exc);
    352         } else {
    353             Invoker.invokeIndirectly(this, handler, att, child, exc);
    354             return null;
    355         }
    356     }
    357 
    358     /**
    359      * Accept a connection on a socket.
    360      *
    361      * @implNote Wrap native call to allow instrumentation.
    362      */
    363     private int accept(FileDescriptor ssfd, FileDescriptor newfd,
    364                        InetSocketAddress[] isaa)
    365         throws IOException
    366     {
    367         return accept0(ssfd, newfd, isaa);
    368     }
    369 
    370     // -- Native methods --
    371 
    372     private static native void initIDs();
    373 
    374     // Accepts a new connection, setting the given file descriptor to refer to
    375     // the new socket and setting isaa[0] to the socket's remote address.
    376     // Returns 1 on success, or IOStatus.UNAVAILABLE.
    377     //
    378     private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
    379                                InetSocketAddress[] isaa)
    380         throws IOException;
    381 
    382     static {
    383         // Android-removed: Code to load native libraries, doesn't make sense on Android.
    384         // IOUtil.load();
    385         initIDs();
    386     }
    387 }
    388