• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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