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