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(); enableAccept()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 UnixAsynchronousServerSocketChannelImpl(Port port)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 implClose()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. finalize()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 group()146 public AsynchronousChannelGroupImpl group() { 147 return port; 148 } 149 150 /** 151 * Invoked by event handling thread when listener socket is polled 152 */ 153 @Override onEvent(int events, boolean mayInvokeDirect)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 */ finishAccept(FileDescriptor newfd, final InetSocketAddress remote, AccessControlContext acc)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 implAccept(Object att, CompletionHandler<AsynchronousSocketChannel,Object> handler)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 */ accept(FileDescriptor ssfd, FileDescriptor newfd, InetSocketAddress[] isaa)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 initIDs()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 // accept0(FileDescriptor ssfd, FileDescriptor newfd, InetSocketAddress[] isaa)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(); initIDs()385 initIDs(); 386 } 387 } 388