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 /** 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 = accept(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, Net.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 = accept(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, Net.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 /** 336 * Accept a connection on a socket. 337 * 338 * @implNote Wrap native call to allow instrumentation. 339 */ 340 private int accept(FileDescriptor ssfd, FileDescriptor newfd, 341 InetSocketAddress[] isaa) 342 throws IOException 343 { 344 return accept0(ssfd, newfd, isaa); 345 } 346 347 // -- Native methods -- 348 349 private static native void initIDs(); 350 351 // Accepts a new connection, setting the given file descriptor to refer to 352 // the new socket and setting isaa[0] to the socket's remote address. 353 // Returns 1 on success, or IOStatus.UNAVAILABLE. 354 // 355 private native int accept0(FileDescriptor ssfd, FileDescriptor newfd, 356 InetSocketAddress[] isaa) 357 throws IOException; 358 359 static { 360 IOUtil.load(); 361 initIDs(); 362 } 363 }