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