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.nio.ByteBuffer;
30 import java.net.*;
31 import java.util.concurrent.*;
32 import java.io.IOException;
33 import java.io.FileDescriptor;
34 import java.security.AccessController;
35 import sun.net.NetHooks;
36 import sun.security.action.GetPropertyAction;
37 
38 import dalvik.annotation.optimization.ReachabilitySensitive;
39 import dalvik.system.CloseGuard;
40 
41 /**
42  * Unix implementation of AsynchronousSocketChannel
43  */
44 
45 class UnixAsynchronousSocketChannelImpl
46     extends AsynchronousSocketChannelImpl implements Port.PollableChannel
47 {
48     private final static NativeDispatcher nd = new SocketDispatcher();
49     private static enum OpType { CONNECT, READ, WRITE };
50 
51     private static final boolean disableSynchronousRead;
52     static {
53         String propValue = AccessController.doPrivileged(
54             new GetPropertyAction("sun.nio.ch.disableSynchronousRead", "false"));
55         disableSynchronousRead = (propValue.length() == 0) ?
56             true : Boolean.valueOf(propValue);
57     }
58 
59     private final Port port;
60     private final int fdVal;
61 
62     // used to ensure that the context for I/O operations that complete
63     // ascynrhonously is visible to the pooled threads handling I/O events.
64     private final Object updateLock = new Object();
65 
66     // pending connect (updateLock)
67     private boolean connectPending;
68     private CompletionHandler<Void,Object> connectHandler;
69     private Object connectAttachment;
70     private PendingFuture<Void,Object> connectFuture;
71 
72     // pending remote address (stateLock)
73     private SocketAddress pendingRemote;
74 
75     // pending read (updateLock)
76     private boolean readPending;
77     private boolean isScatteringRead;
78     private ByteBuffer readBuffer;
79     private ByteBuffer[] readBuffers;
80     private CompletionHandler<Number,Object> readHandler;
81     private Object readAttachment;
82     private PendingFuture<Number,Object> readFuture;
83     private Future<?> readTimer;
84 
85     // pending write (updateLock)
86     private boolean writePending;
87     private boolean isGatheringWrite;
88     private ByteBuffer writeBuffer;
89     private ByteBuffer[] writeBuffers;
90     private CompletionHandler<Number,Object> writeHandler;
91     private Object writeAttachment;
92     private PendingFuture<Number,Object> writeFuture;
93     private Future<?> writeTimer;
94 
95     // Android-added: CloseGuard support.
96     @ReachabilitySensitive
97     private final CloseGuard guard = CloseGuard.get();
98 
UnixAsynchronousSocketChannelImpl(Port port)99     UnixAsynchronousSocketChannelImpl(Port port)
100         throws IOException
101     {
102         super(port);
103 
104         // set non-blocking
105         try {
106             IOUtil.configureBlocking(fd, false);
107         } catch (IOException x) {
108             nd.close(fd);
109             throw x;
110         }
111 
112         this.port = port;
113         this.fdVal = IOUtil.fdVal(fd);
114 
115         // add mapping from file descriptor to this channel
116         port.register(fdVal, this);
117         // Android-added: CloseGuard support.
118         guard.open("close");
119     }
120 
121     // Constructor for sockets created by UnixAsynchronousServerSocketChannelImpl
UnixAsynchronousSocketChannelImpl(Port port, FileDescriptor fd, InetSocketAddress remote)122     UnixAsynchronousSocketChannelImpl(Port port,
123                                       FileDescriptor fd,
124                                       InetSocketAddress remote)
125         throws IOException
126     {
127         super(port, fd, remote);
128 
129         this.fdVal = IOUtil.fdVal(fd);
130         IOUtil.configureBlocking(fd, false);
131 
132         try {
133             port.register(fdVal, this);
134         } catch (ShutdownChannelGroupException x) {
135             // ShutdownChannelGroupException thrown if we attempt to register a
136             // new channel after the group is shutdown
137             throw new IOException(x);
138         }
139 
140         this.port = port;
141         // Android-added: CloseGuard support.
142         guard.open("close");
143     }
144 
145     @Override
group()146     public AsynchronousChannelGroupImpl group() {
147         return port;
148     }
149 
150     // register events for outstanding I/O operations, caller already owns updateLock
updateEvents()151     private void updateEvents() {
152         assert Thread.holdsLock(updateLock);
153         int events = 0;
154         if (readPending)
155             events |= Net.POLLIN;
156         if (connectPending || writePending)
157             events |= Net.POLLOUT;
158         if (events != 0)
159             port.startPoll(fdVal, events);
160     }
161 
162     // register events for outstanding I/O operations
lockAndUpdateEvents()163     private void lockAndUpdateEvents() {
164         synchronized (updateLock) {
165             updateEvents();
166         }
167     }
168 
169     // invoke to finish read and/or write operations
finish(boolean mayInvokeDirect, boolean readable, boolean writable)170     private void finish(boolean mayInvokeDirect,
171                         boolean readable,
172                         boolean writable)
173     {
174         boolean finishRead = false;
175         boolean finishWrite = false;
176         boolean finishConnect = false;
177 
178         // map event to pending result
179         synchronized (updateLock) {
180             if (readable && this.readPending) {
181                 this.readPending = false;
182                 finishRead = true;
183             }
184             if (writable) {
185                 if (this.writePending) {
186                     this.writePending = false;
187                     finishWrite = true;
188                 } else if (this.connectPending) {
189                     this.connectPending = false;
190                     finishConnect = true;
191                 }
192             }
193         }
194 
195         // complete the I/O operation. Special case for when channel is
196         // ready for both reading and writing. In that case, submit task to
197         // complete write if write operation has a completion handler.
198         if (finishRead) {
199             if (finishWrite)
200                 finishWrite(false);
201             finishRead(mayInvokeDirect);
202             return;
203         }
204         if (finishWrite) {
205             finishWrite(mayInvokeDirect);
206         }
207         if (finishConnect) {
208             finishConnect(mayInvokeDirect);
209         }
210     }
211 
212     /**
213      * Invoked by event handler thread when file descriptor is polled
214      */
215     @Override
onEvent(int events, boolean mayInvokeDirect)216     public void onEvent(int events, boolean mayInvokeDirect) {
217         boolean readable = (events & Net.POLLIN) > 0;
218         boolean writable = (events & Net.POLLOUT) > 0;
219         if ((events & (Net.POLLERR | Net.POLLHUP)) > 0) {
220             readable = true;
221             writable = true;
222         }
223         finish(mayInvokeDirect, readable, writable);
224     }
225 
226     @Override
implClose()227     void implClose() throws IOException {
228         // Android-added: CloseGuard support.
229         guard.close();
230         // remove the mapping
231         port.unregister(fdVal);
232 
233         // close file descriptor
234         nd.close(fd);
235 
236         // All outstanding I/O operations are required to fail
237         finish(false, true, true);
238     }
239 
240     // Android-added: CloseGuard support.
finalize()241     protected void finalize() throws Throwable {
242         try {
243             if (guard != null) {
244                 guard.warnIfOpen();
245             }
246             close();
247         } finally {
248             super.finalize();
249         }
250     }
251 
252     @Override
onCancel(PendingFuture<?,?> task)253     public void onCancel(PendingFuture<?,?> task) {
254         if (task.getContext() == OpType.CONNECT)
255             killConnect();
256         if (task.getContext() == OpType.READ)
257             killReading();
258         if (task.getContext() == OpType.WRITE)
259             killWriting();
260     }
261 
262     // -- connect --
263 
setConnected()264     private void setConnected() throws IOException {
265         synchronized (stateLock) {
266             state = ST_CONNECTED;
267             localAddress = Net.localAddress(fd);
268             remoteAddress = (InetSocketAddress)pendingRemote;
269         }
270     }
271 
finishConnect(boolean mayInvokeDirect)272     private void finishConnect(boolean mayInvokeDirect) {
273         Throwable e = null;
274         try {
275             begin();
276             checkConnect(fdVal);
277             setConnected();
278         } catch (Throwable x) {
279             if (x instanceof ClosedChannelException)
280                 x = new AsynchronousCloseException();
281             e = x;
282         } finally {
283             end();
284         }
285         if (e != null) {
286             // close channel if connection cannot be established
287             try {
288                 close();
289             } catch (Throwable suppressed) {
290                 e.addSuppressed(suppressed);
291             }
292         }
293 
294         // invoke handler and set result
295         CompletionHandler<Void,Object> handler = connectHandler;
296         Object att = connectAttachment;
297         PendingFuture<Void,Object> future = connectFuture;
298         if (handler == null) {
299             future.setResult(null, e);
300         } else {
301             if (mayInvokeDirect) {
302                 Invoker.invokeUnchecked(handler, att, null, e);
303             } else {
304                 Invoker.invokeIndirectly(this, handler, att, null, e);
305             }
306         }
307     }
308 
309     @Override
310     @SuppressWarnings("unchecked")
implConnect(SocketAddress remote, A attachment, CompletionHandler<Void,? super A> handler)311     <A> Future<Void> implConnect(SocketAddress remote,
312                                  A attachment,
313                                  CompletionHandler<Void,? super A> handler)
314     {
315         if (!isOpen()) {
316             Throwable e = new ClosedChannelException();
317             if (handler == null) {
318                 return CompletedFuture.withFailure(e);
319             } else {
320                 Invoker.invoke(this, handler, attachment, null, e);
321                 return null;
322             }
323         }
324 
325         InetSocketAddress isa = Net.checkAddress(remote);
326 
327         // permission check
328         SecurityManager sm = System.getSecurityManager();
329         if (sm != null)
330             sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
331 
332         // check and set state
333         boolean notifyBeforeTcpConnect;
334         synchronized (stateLock) {
335             if (state == ST_CONNECTED)
336                 throw new AlreadyConnectedException();
337             if (state == ST_PENDING)
338                 throw new ConnectionPendingException();
339             state = ST_PENDING;
340             pendingRemote = remote;
341             notifyBeforeTcpConnect = (localAddress == null);
342         }
343 
344         Throwable e = null;
345         try {
346             begin();
347             // notify hook if unbound
348             if (notifyBeforeTcpConnect)
349                 NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort());
350             int n = Net.connect(fd, isa.getAddress(), isa.getPort());
351             if (n == IOStatus.UNAVAILABLE) {
352                 // connection could not be established immediately
353                 PendingFuture<Void,A> result = null;
354                 synchronized (updateLock) {
355                     if (handler == null) {
356                         result = new PendingFuture<Void,A>(this, OpType.CONNECT);
357                         this.connectFuture = (PendingFuture<Void,Object>)result;
358                     } else {
359                         this.connectHandler = (CompletionHandler<Void,Object>)handler;
360                         this.connectAttachment = attachment;
361                     }
362                     this.connectPending = true;
363                     updateEvents();
364                 }
365                 return result;
366             }
367             setConnected();
368         } catch (Throwable x) {
369             if (x instanceof ClosedChannelException)
370                 x = new AsynchronousCloseException();
371             e = x;
372         } finally {
373             end();
374         }
375 
376         // close channel if connect fails
377         if (e != null) {
378             try {
379                 close();
380             } catch (Throwable suppressed) {
381                 e.addSuppressed(suppressed);
382             }
383         }
384         if (handler == null) {
385             return CompletedFuture.withResult(null, e);
386         } else {
387             Invoker.invoke(this, handler, attachment, null, e);
388             return null;
389         }
390     }
391 
392     // -- read --
393 
finishRead(boolean mayInvokeDirect)394     private void finishRead(boolean mayInvokeDirect) {
395         int n = -1;
396         Throwable exc = null;
397 
398         // copy fields as we can't access them after reading is re-enabled.
399         boolean scattering = isScatteringRead;
400         CompletionHandler<Number,Object> handler = readHandler;
401         Object att = readAttachment;
402         PendingFuture<Number,Object> future = readFuture;
403         Future<?> timeout = readTimer;
404 
405         try {
406             begin();
407 
408             if (scattering) {
409                 n = (int)IOUtil.read(fd, readBuffers, nd);
410             } else {
411                 n = IOUtil.read(fd, readBuffer, -1, nd);
412             }
413             if (n == IOStatus.UNAVAILABLE) {
414                 // spurious wakeup, is this possible?
415                 synchronized (updateLock) {
416                     readPending = true;
417                 }
418                 return;
419             }
420 
421             // allow objects to be GC'ed.
422             this.readBuffer = null;
423             this.readBuffers = null;
424             this.readAttachment = null;
425 
426             // allow another read to be initiated
427             enableReading();
428 
429         } catch (Throwable x) {
430             enableReading();
431             if (x instanceof ClosedChannelException)
432                 x = new AsynchronousCloseException();
433             exc = x;
434         } finally {
435             // restart poll in case of concurrent write
436             if (!(exc instanceof AsynchronousCloseException))
437                 lockAndUpdateEvents();
438             end();
439         }
440 
441         // cancel the associated timer
442         if (timeout != null)
443             timeout.cancel(false);
444 
445         // create result
446         Number result = (exc != null) ? null : (scattering) ?
447             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
448 
449         // invoke handler or set result
450         if (handler == null) {
451             future.setResult(result, exc);
452         } else {
453             if (mayInvokeDirect) {
454                 Invoker.invokeUnchecked(handler, att, result, exc);
455             } else {
456                 Invoker.invokeIndirectly(this, handler, att, result, exc);
457             }
458         }
459     }
460 
461     private Runnable readTimeoutTask = new Runnable() {
462         public void run() {
463             CompletionHandler<Number,Object> handler = null;
464             Object att = null;
465             PendingFuture<Number,Object> future = null;
466 
467             synchronized (updateLock) {
468                 if (!readPending)
469                     return;
470                 readPending = false;
471                 handler = readHandler;
472                 att = readAttachment;
473                 future = readFuture;
474             }
475 
476             // kill further reading before releasing waiters
477             enableReading(true);
478 
479             // invoke handler or set result
480             Exception exc = new InterruptedByTimeoutException();
481             if (handler == null) {
482                 future.setFailure(exc);
483             } else {
484                 AsynchronousChannel ch = UnixAsynchronousSocketChannelImpl.this;
485                 Invoker.invokeIndirectly(ch, handler, att, null, exc);
486             }
487         }
488     };
489 
490     /**
491      * Initiates a read or scattering read operation
492      */
493     @Override
494     @SuppressWarnings("unchecked")
implRead(boolean isScatteringRead, ByteBuffer dst, ByteBuffer[] dsts, long timeout, TimeUnit unit, A attachment, CompletionHandler<V,? super A> handler)495     <V extends Number,A> Future<V> implRead(boolean isScatteringRead,
496                                             ByteBuffer dst,
497                                             ByteBuffer[] dsts,
498                                             long timeout,
499                                             TimeUnit unit,
500                                             A attachment,
501                                             CompletionHandler<V,? super A> handler)
502     {
503         // A synchronous read is not attempted if disallowed by system property
504         // or, we are using a fixed thread pool and the completion handler may
505         // not be invoked directly (because the thread is not a pooled thread or
506         // there are too many handlers on the stack).
507         Invoker.GroupAndInvokeCount myGroupAndInvokeCount = null;
508         boolean invokeDirect = false;
509         boolean attemptRead = false;
510         if (!disableSynchronousRead) {
511             if (handler == null) {
512                 attemptRead = true;
513             } else {
514                 myGroupAndInvokeCount = Invoker.getGroupAndInvokeCount();
515                 invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port);
516                 // okay to attempt read with user thread pool
517                 attemptRead = invokeDirect || !port.isFixedThreadPool();
518             }
519         }
520 
521         int n = IOStatus.UNAVAILABLE;
522         Throwable exc = null;
523         boolean pending = false;
524 
525         try {
526             begin();
527 
528             if (attemptRead) {
529                 if (isScatteringRead) {
530                     n = (int)IOUtil.read(fd, dsts, nd);
531                 } else {
532                     n = IOUtil.read(fd, dst, -1, nd);
533                 }
534             }
535 
536             if (n == IOStatus.UNAVAILABLE) {
537                 PendingFuture<V,A> result = null;
538                 synchronized (updateLock) {
539                     this.isScatteringRead = isScatteringRead;
540                     this.readBuffer = dst;
541                     this.readBuffers = dsts;
542                     if (handler == null) {
543                         this.readHandler = null;
544                         result = new PendingFuture<V,A>(this, OpType.READ);
545                         this.readFuture = (PendingFuture<Number,Object>)result;
546                         this.readAttachment = null;
547                     } else {
548                         this.readHandler = (CompletionHandler<Number,Object>)handler;
549                         this.readAttachment = attachment;
550                         this.readFuture = null;
551                     }
552                     if (timeout > 0L) {
553                         this.readTimer = port.schedule(readTimeoutTask, timeout, unit);
554                     }
555                     this.readPending = true;
556                     updateEvents();
557                 }
558                 pending = true;
559                 return result;
560             }
561         } catch (Throwable x) {
562             if (x instanceof ClosedChannelException)
563                 x = new AsynchronousCloseException();
564             exc = x;
565         } finally {
566             if (!pending)
567                 enableReading();
568             end();
569         }
570 
571         Number result = (exc != null) ? null : (isScatteringRead) ?
572             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
573 
574         // read completed immediately
575         if (handler != null) {
576             if (invokeDirect) {
577                 Invoker.invokeDirect(myGroupAndInvokeCount, handler, attachment, (V)result, exc);
578             } else {
579                 Invoker.invokeIndirectly(this, handler, attachment, (V)result, exc);
580             }
581             return null;
582         } else {
583             return CompletedFuture.withResult((V)result, exc);
584         }
585     }
586 
587     // -- write --
588 
finishWrite(boolean mayInvokeDirect)589     private void finishWrite(boolean mayInvokeDirect) {
590         int n = -1;
591         Throwable exc = null;
592 
593         // copy fields as we can't access them after reading is re-enabled.
594         boolean gathering = this.isGatheringWrite;
595         CompletionHandler<Number,Object> handler = this.writeHandler;
596         Object att = this.writeAttachment;
597         PendingFuture<Number,Object> future = this.writeFuture;
598         Future<?> timer = this.writeTimer;
599 
600         try {
601             begin();
602 
603             if (gathering) {
604                 n = (int)IOUtil.write(fd, writeBuffers, nd);
605             } else {
606                 n = IOUtil.write(fd, writeBuffer, -1, nd);
607             }
608             if (n == IOStatus.UNAVAILABLE) {
609                 // spurious wakeup, is this possible?
610                 synchronized (updateLock) {
611                     writePending = true;
612                 }
613                 return;
614             }
615 
616             // allow objects to be GC'ed.
617             this.writeBuffer = null;
618             this.writeBuffers = null;
619             this.writeAttachment = null;
620 
621             // allow another write to be initiated
622             enableWriting();
623 
624         } catch (Throwable x) {
625             enableWriting();
626             if (x instanceof ClosedChannelException)
627                 x = new AsynchronousCloseException();
628             exc = x;
629         } finally {
630             // restart poll in case of concurrent write
631             if (!(exc instanceof AsynchronousCloseException))
632                 lockAndUpdateEvents();
633             end();
634         }
635 
636         // cancel the associated timer
637         if (timer != null)
638             timer.cancel(false);
639 
640         // create result
641         Number result = (exc != null) ? null : (gathering) ?
642             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
643 
644         // invoke handler or set result
645         if (handler == null) {
646             future.setResult(result, exc);
647         } else {
648             if (mayInvokeDirect) {
649                 Invoker.invokeUnchecked(handler, att, result, exc);
650             } else {
651                 Invoker.invokeIndirectly(this, handler, att, result, exc);
652             }
653         }
654     }
655 
656     private Runnable writeTimeoutTask = new Runnable() {
657         public void run() {
658             CompletionHandler<Number,Object> handler = null;
659             Object att = null;
660             PendingFuture<Number,Object> future = null;
661 
662             synchronized (updateLock) {
663                 if (!writePending)
664                     return;
665                 writePending = false;
666                 handler = writeHandler;
667                 att = writeAttachment;
668                 future = writeFuture;
669             }
670 
671             // kill further writing before releasing waiters
672             enableWriting(true);
673 
674             // invoke handler or set result
675             Exception exc = new InterruptedByTimeoutException();
676             if (handler != null) {
677                 Invoker.invokeIndirectly(UnixAsynchronousSocketChannelImpl.this,
678                     handler, att, null, exc);
679             } else {
680                 future.setFailure(exc);
681             }
682         }
683     };
684 
685     /**
686      * Initiates a read or scattering read operation
687      */
688     @Override
689     @SuppressWarnings("unchecked")
implWrite(boolean isGatheringWrite, ByteBuffer src, ByteBuffer[] srcs, long timeout, TimeUnit unit, A attachment, CompletionHandler<V,? super A> handler)690     <V extends Number,A> Future<V> implWrite(boolean isGatheringWrite,
691                                              ByteBuffer src,
692                                              ByteBuffer[] srcs,
693                                              long timeout,
694                                              TimeUnit unit,
695                                              A attachment,
696                                              CompletionHandler<V,? super A> handler)
697     {
698         Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
699             Invoker.getGroupAndInvokeCount();
700         boolean invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port);
701         boolean attemptWrite = (handler == null) || invokeDirect ||
702             !port.isFixedThreadPool();  // okay to attempt write with user thread pool
703 
704         int n = IOStatus.UNAVAILABLE;
705         Throwable exc = null;
706         boolean pending = false;
707 
708         try {
709             begin();
710 
711             if (attemptWrite) {
712                 if (isGatheringWrite) {
713                     n = (int)IOUtil.write(fd, srcs, nd);
714                 } else {
715                     n = IOUtil.write(fd, src, -1, nd);
716                 }
717             }
718 
719             if (n == IOStatus.UNAVAILABLE) {
720                 PendingFuture<V,A> result = null;
721                 synchronized (updateLock) {
722                     this.isGatheringWrite = isGatheringWrite;
723                     this.writeBuffer = src;
724                     this.writeBuffers = srcs;
725                     if (handler == null) {
726                         this.writeHandler = null;
727                         result = new PendingFuture<V,A>(this, OpType.WRITE);
728                         this.writeFuture = (PendingFuture<Number,Object>)result;
729                         this.writeAttachment = null;
730                     } else {
731                         this.writeHandler = (CompletionHandler<Number,Object>)handler;
732                         this.writeAttachment = attachment;
733                         this.writeFuture = null;
734                     }
735                     if (timeout > 0L) {
736                         this.writeTimer = port.schedule(writeTimeoutTask, timeout, unit);
737                     }
738                     this.writePending = true;
739                     updateEvents();
740                 }
741                 pending = true;
742                 return result;
743             }
744         } catch (Throwable x) {
745             if (x instanceof ClosedChannelException)
746                 x = new AsynchronousCloseException();
747             exc = x;
748         } finally {
749             if (!pending)
750                 enableWriting();
751             end();
752         }
753 
754         Number result = (exc != null) ? null : (isGatheringWrite) ?
755             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
756 
757         // write completed immediately
758         if (handler != null) {
759             if (invokeDirect) {
760                 Invoker.invokeDirect(myGroupAndInvokeCount, handler, attachment, (V)result, exc);
761             } else {
762                 Invoker.invokeIndirectly(this, handler, attachment, (V)result, exc);
763             }
764             return null;
765         } else {
766             return CompletedFuture.withResult((V)result, exc);
767         }
768     }
769 
770     // -- Native methods --
771 
checkConnect(int fdVal)772     private static native void checkConnect(int fdVal) throws IOException;
773 
774     // Android-removed: Code to load native libraries, doesn't make sense on Android.
775     /*
776     static {
777         IOUtil.load();
778     }
779     */
780 }
781