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.net.InetSocketAddress;
  30 import java.util.concurrent.Future;
  31 import java.util.concurrent.atomic.AtomicBoolean;
  32 import java.io.IOException;
  33 import java.security.AccessControlContext;
  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
  36 import sun.misc.Unsafe;
  37 
  38 /**
  39  * Windows implementation of AsynchronousServerSocketChannel using overlapped I/O.
  40  */
  41 
  42 class WindowsAsynchronousServerSocketChannelImpl
  43     extends AsynchronousServerSocketChannelImpl implements Iocp.OverlappedChannel
  44 {
  45     private static final Unsafe unsafe = Unsafe.getUnsafe();
  46 
  47     // 2 * (sizeof(SOCKET_ADDRESS) + 16)
  48     private static final int DATA_BUFFER_SIZE = 88;
  49 
  50     private final long handle;
  51     private final int completionKey;
  52     private final Iocp iocp;
  53 
  54     // typically there will be zero, or one I/O operations pending. In rare
  55     // cases there may be more. These rare cases arise when a sequence of accept
  56     // operations complete immediately and handled by the initiating thread.
  57     // The corresponding OVERLAPPED cannot be reused/released until the completion
  58     // event has been posted.
  59     private final PendingIoCache ioCache;
  60 
  61     // the data buffer to receive the local/remote socket address
  62     private final long dataBuffer;
  63 
  64     // flag to indicate that an accept operation is outstanding
  65     private AtomicBoolean accepting = new AtomicBoolean();
  66 
  67 
  68     WindowsAsynchronousServerSocketChannelImpl(Iocp iocp) throws IOException {
  69         super(iocp);
  70 
  71         // associate socket with given completion port
  72         long h = IOUtil.fdVal(fd);
  73         int key;
  74         try {
  75             key = iocp.associate(this, h);
  76         } catch (IOException x) {
  77             closesocket0(h);   // prevent leak
  78             throw x;
  79         }
  80 
  81         this.handle = h;
  82         this.completionKey = key;
  83         this.iocp = iocp;
  84         this.ioCache = new PendingIoCache();
  85         this.dataBuffer = unsafe.allocateMemory(DATA_BUFFER_SIZE);
  86     }
  87 
  88     @Override
  89     public <V,A> PendingFuture<V,A> getByOverlapped(long overlapped) {
  90         return ioCache.remove(overlapped);
  91     }
  92 
  93     @Override
  94     void implClose() throws IOException {
  95         // close socket (which may cause outstanding accept to be aborted).
  96         closesocket0(handle);
  97 
  98         // waits until the accept operations have completed
  99         ioCache.close();
 100 
 101         // finally disassociate from the completion port
 102         iocp.disassociate(completionKey);
 103 
 104         // release other resources
 105         unsafe.freeMemory(dataBuffer);
 106     }
 107 
 108     @Override
 109     public AsynchronousChannelGroupImpl group() {
 110         return iocp;
 111     }
 112 
 113     /**
 114      * Task to initiate accept operation and to handle result.
 115      */
 116     private class AcceptTask implements Runnable, Iocp.ResultHandler {
 117         private final WindowsAsynchronousSocketChannelImpl channel;
 118         private final AccessControlContext acc;
 119         private final PendingFuture<AsynchronousSocketChannel,Object> result;
 120 
 121         AcceptTask(WindowsAsynchronousSocketChannelImpl channel,
 122                    AccessControlContext acc,
 123                    PendingFuture<AsynchronousSocketChannel,Object> result)
 124         {
 125             this.channel = channel;
 126             this.acc = acc;
 127             this.result = result;
 128         }
 129 
 130         void enableAccept() {
 131             accepting.set(false);
 132         }
 133 
 134         void closeChildChannel() {
 135             try {
 136                 channel.close();
 137             } catch (IOException ignore) { }
 138         }
 139 
 140         // caller must have acquired read lock for the listener and child channel.
 141         void finishAccept() throws IOException {
 142             /**
 143              * Set local/remote addresses. This is currently very inefficient
 144              * in that it requires 2 calls to getsockname and 2 calls to getpeername.
 145              * (should change this to use GetAcceptExSockaddrs)
 146              */
 147             updateAcceptContext(handle, channel.handle());
 148 
 149             InetSocketAddress local = Net.localAddress(channel.fd);
 150             final InetSocketAddress remote = Net.remoteAddress(channel.fd);
 151             channel.setConnected(local, remote);
 152 
 153             // permission check (in context of initiating thread)
 154             if (acc != null) {
 155                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
 156                     public Void run() {
 157                         SecurityManager sm = System.getSecurityManager();
 158                         sm.checkAccept(remote.getAddress().getHostAddress(),
 159                                        remote.getPort());
 160                         return null;
 161                     }
 162                 }, acc);
 163             }
 164         }
 165 
 166         /**
 167          * Initiates the accept operation.
 168          */
 169         @Override
 170         public void run() {
 171             long overlapped = 0L;
 172 
 173             try {
 174                 // begin usage of listener socket
 175                 begin();
 176                 try {
 177                     // begin usage of child socket (as it is registered with
 178                     // completion port and so may be closed in the event that
 179                     // the group is forcefully closed).
 180                     channel.begin();
 181 
 182                     synchronized (result) {
 183                         overlapped = ioCache.add(result);
 184 
 185                         int n = accept0(handle, channel.handle(), overlapped, dataBuffer);
 186                         if (n == IOStatus.UNAVAILABLE) {
 187                             return;
 188                         }
 189 
 190                         // connection accepted immediately
 191                         finishAccept();
 192 
 193                         // allow another accept before the result is set
 194                         enableAccept();
 195                         result.setResult(channel);
 196                     }
 197                 } finally {
 198                     // end usage on child socket
 199                     channel.end();
 200                 }
 201             } catch (Throwable x) {
 202                 // failed to initiate accept so release resources
 203                 if (overlapped != 0L)
 204                     ioCache.remove(overlapped);
 205                 closeChildChannel();
 206                 if (x instanceof ClosedChannelException)
 207                     x = new AsynchronousCloseException();
 208                 if (!(x instanceof IOException) && !(x instanceof SecurityException))
 209                     x = new IOException(x);
 210                 enableAccept();
 211                 result.setFailure(x);
 212             } finally {
 213                 // end of usage of listener socket
 214                 end();
 215             }
 216 
 217             // accept completed immediately but may not have executed on
 218             // initiating thread in which case the operation may have been
 219             // cancelled.
 220             if (result.isCancelled()) {
 221                 closeChildChannel();
 222             }
 223 
 224             // invoke completion handler
 225             Invoker.invokeIndirectly(result);
 226         }
 227 
 228         /**
 229          * Executed when the I/O has completed
 230          */
 231         @Override
 232         public void completed(int bytesTransferred, boolean canInvokeDirect) {
 233             try {
 234                 // connection accept after group has shutdown
 235                 if (iocp.isShutdown()) {
 236                     throw new IOException(new ShutdownChannelGroupException());
 237                 }
 238 
 239                 // finish the accept
 240                 try {
 241                     begin();
 242                     try {
 243                         channel.begin();
 244                         finishAccept();
 245                     } finally {
 246                         channel.end();
 247                     }
 248                 } finally {
 249                     end();
 250                 }
 251 
 252                 // allow another accept before the result is set
 253                 enableAccept();
 254                 result.setResult(channel);
 255             } catch (Throwable x) {
 256                 enableAccept();
 257                 closeChildChannel();
 258                 if (x instanceof ClosedChannelException)
 259                     x = new AsynchronousCloseException();
 260                 if (!(x instanceof IOException) && !(x instanceof SecurityException))
 261                     x = new IOException(x);
 262                 result.setFailure(x);
 263             }
 264 
 265             // if an async cancel has already cancelled the operation then
 266             // close the new channel so as to free resources
 267             if (result.isCancelled()) {
 268                 closeChildChannel();
 269             }
 270 
 271             // invoke handler (but not directly)
 272             Invoker.invokeIndirectly(result);
 273         }
 274 
 275         @Override
 276         public void failed(int error, IOException x) {
 277             enableAccept();
 278             closeChildChannel();
 279 
 280             // release waiters
 281             if (isOpen()) {
 282                 result.setFailure(x);
 283             } else {
 284                 result.setFailure(new AsynchronousCloseException());
 285             }
 286             Invoker.invokeIndirectly(result);
 287         }
 288     }
 289 
 290     @Override
 291     Future<AsynchronousSocketChannel> implAccept(Object attachment,
 292         final CompletionHandler<AsynchronousSocketChannel,Object> handler)
 293     {
 294         if (!isOpen()) {
 295             Throwable exc = new ClosedChannelException();
 296             if (handler == null)
 297                 return CompletedFuture.withFailure(exc);
 298             Invoker.invokeIndirectly(this, handler, attachment, null, exc);
 299             return null;
 300         }
 301         if (isAcceptKilled())
 302             throw new RuntimeException("Accept not allowed due to cancellation");
 303 
 304         // ensure channel is bound to local address
 305         if (localAddress == null)
 306             throw new NotYetBoundException();
 307 
 308         // create the socket that will be accepted. The creation of the socket
 309         // is enclosed by a begin/end for the listener socket to ensure that
 310         // we check that the listener is open and also to prevent the I/O
 311         // port from being closed as the new socket is registered.
 312         WindowsAsynchronousSocketChannelImpl ch = null;
 313         IOException ioe = null;
 314         try {
 315             begin();
 316             ch = new WindowsAsynchronousSocketChannelImpl(iocp, false);
 317         } catch (IOException x) {
 318             ioe = x;
 319         } finally {
 320             end();
 321         }
 322         if (ioe != null) {
 323             if (handler == null)
 324                 return CompletedFuture.withFailure(ioe);
 325             Invoker.invokeIndirectly(this, handler, attachment, null, ioe);
 326             return null;
 327         }
 328 
 329         // need calling context when there is security manager as
 330         // permission check may be done in a different thread without
 331         // any application call frames on the stack
 332         AccessControlContext acc = (System.getSecurityManager() == null) ?
 333             null : AccessController.getContext();
 334 
 335         PendingFuture<AsynchronousSocketChannel,Object> result =
 336             new PendingFuture<AsynchronousSocketChannel,Object>(this, handler, attachment);
 337         AcceptTask task = new AcceptTask(ch, acc, result);
 338         result.setContext(task);
 339 
 340         // check and set flag to prevent concurrent accepting
 341         if (!accepting.compareAndSet(false, true))
 342             throw new AcceptPendingException();
 343 
 344         // initiate I/O
 345         if (Iocp.supportsThreadAgnosticIo()) {
 346             task.run();
 347         } else {
 348             Invoker.invokeOnThreadInThreadPool(this, task);
 349         }
 350         return result;
 351     }
 352 
 353     // -- Native methods --
 354 
 355     private static native void initIDs();
 356 
 357     private static native int accept0(long listenSocket, long acceptSocket,
 358         long overlapped, long dataBuffer) throws IOException;
 359 
 360     private static native void updateAcceptContext(long listenSocket,
 361         long acceptSocket) throws IOException;
 362 
 363     private static native void closesocket0(long socket) throws IOException;
 364 
 365     static {
 366         IOUtil.load();
 367         initIDs();
 368     }
 369 }