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