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