586 } |
586 } |
587 } |
587 } |
588 |
588 |
589 /** |
589 /** |
590 * Marks the beginning of a connect operation that might block. |
590 * Marks the beginning of a connect operation that might block. |
591 * |
591 * @param blocking true if configured blocking |
|
592 * @param isa the remote address |
592 * @throws ClosedChannelException if the channel is closed |
593 * @throws ClosedChannelException if the channel is closed |
593 * @throws AlreadyConnectedException if already connected |
594 * @throws AlreadyConnectedException if already connected |
594 * @throws ConnectionPendingException is a connection is pending |
595 * @throws ConnectionPendingException is a connection is pending |
595 */ |
596 * @throws IOException if the pre-connect hook fails |
596 private void beginConnect(boolean blocking) throws ClosedChannelException { |
597 */ |
|
598 private void beginConnect(boolean blocking, InetSocketAddress isa) |
|
599 throws IOException |
|
600 { |
597 if (blocking) { |
601 if (blocking) { |
598 // set hook for Thread.interrupt |
602 // set hook for Thread.interrupt |
599 begin(); |
603 begin(); |
600 } |
604 } |
601 synchronized (stateLock) { |
605 synchronized (stateLock) { |
602 ensureOpen(); |
606 ensureOpen(); |
603 if (state == ST_CONNECTED) |
607 if (state == ST_CONNECTED) |
604 throw new AlreadyConnectedException(); |
608 throw new AlreadyConnectedException(); |
605 if (state == ST_CONNECTIONPENDING) |
609 if (state == ST_CONNECTIONPENDING) |
606 throw new ConnectionPendingException(); |
610 throw new ConnectionPendingException(); |
|
611 assert state == ST_UNCONNECTED; |
|
612 state = ST_CONNECTIONPENDING; |
|
613 |
|
614 if (localAddress == null) |
|
615 NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort()); |
|
616 remoteAddress = isa; |
|
617 |
607 if (blocking) |
618 if (blocking) |
608 readerThread = NativeThread.current(); |
619 readerThread = NativeThread.current(); |
609 } |
620 } |
610 } |
621 } |
611 |
622 |
612 /** |
623 /** |
613 * Marks the end of a connect operation that may have blocked. |
624 * Marks the end of a connect operation that may have blocked. |
614 * |
625 * |
615 * @throws AsynchronousCloseException if the channel was closed due to this |
626 * @throws AsynchronousCloseException if the channel was closed due to this |
616 * thread being interrupted on a blocking connect operation. |
627 * thread being interrupted on a blocking connect operation. |
|
628 * @throws IOException if completed and unable to obtain the local address |
617 */ |
629 */ |
618 private void endConnect(boolean blocking, boolean completed) |
630 private void endConnect(boolean blocking, boolean completed) |
619 throws AsynchronousCloseException |
631 throws IOException |
620 { |
632 { |
621 endRead(blocking, completed); |
633 endRead(blocking, completed); |
|
634 |
|
635 if (completed) { |
|
636 synchronized (stateLock) { |
|
637 if (state == ST_CONNECTIONPENDING) { |
|
638 localAddress = Net.localAddress(fd); |
|
639 state = ST_CONNECTED; |
|
640 } |
|
641 } |
|
642 } |
622 } |
643 } |
623 |
644 |
624 @Override |
645 @Override |
625 public boolean connect(SocketAddress sa) throws IOException { |
646 public boolean connect(SocketAddress sa) throws IOException { |
626 InetSocketAddress isa = Net.checkAddress(sa); |
647 InetSocketAddress isa = Net.checkAddress(sa); |
627 SecurityManager sm = System.getSecurityManager(); |
648 SecurityManager sm = System.getSecurityManager(); |
628 if (sm != null) |
649 if (sm != null) |
629 sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); |
650 sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); |
630 |
651 |
631 readLock.lock(); |
652 InetAddress ia = isa.getAddress(); |
|
653 if (ia.isAnyLocalAddress()) |
|
654 ia = InetAddress.getLocalHost(); |
|
655 |
632 try { |
656 try { |
633 writeLock.lock(); |
657 readLock.lock(); |
634 try { |
658 try { |
635 // notify before-connect hook |
659 writeLock.lock(); |
636 synchronized (stateLock) { |
660 try { |
637 if (state == ST_UNCONNECTED && localAddress == null) { |
661 int n = 0; |
638 NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort()); |
662 boolean blocking = isBlocking(); |
|
663 try { |
|
664 beginConnect(blocking, isa); |
|
665 do { |
|
666 n = Net.connect(fd, ia, isa.getPort()); |
|
667 } while (n == IOStatus.INTERRUPTED && isOpen()); |
|
668 } finally { |
|
669 endConnect(blocking, (n > 0)); |
639 } |
670 } |
640 } |
671 assert IOStatus.check(n); |
641 |
672 return n > 0; |
642 InetAddress ia = isa.getAddress(); |
673 } finally { |
643 if (ia.isAnyLocalAddress()) |
674 writeLock.unlock(); |
644 ia = InetAddress.getLocalHost(); |
675 } |
645 |
676 } finally { |
646 int n = 0; |
677 readLock.unlock(); |
647 boolean blocking = isBlocking(); |
678 } |
648 try { |
679 } catch (IOException ioe) { |
649 try { |
680 // connect failed, close the channel |
650 beginConnect(blocking); |
681 close(); |
651 if (blocking) { |
682 throw ioe; |
652 do { |
|
653 n = Net.connect(fd, ia, isa.getPort()); |
|
654 } while (n == IOStatus.INTERRUPTED && isOpen()); |
|
655 } else { |
|
656 n = Net.connect(fd, ia, isa.getPort()); |
|
657 } |
|
658 } finally { |
|
659 endConnect(blocking, n > 0); |
|
660 } |
|
661 } catch (IOException x) { |
|
662 // connect failed, close socket |
|
663 close(); |
|
664 throw x; |
|
665 } |
|
666 |
|
667 // connection may be established |
|
668 synchronized (stateLock) { |
|
669 if (!isOpen()) |
|
670 throw new AsynchronousCloseException(); |
|
671 remoteAddress = isa; |
|
672 if (n > 0) { |
|
673 // connected established |
|
674 localAddress = Net.localAddress(fd); |
|
675 state = ST_CONNECTED; |
|
676 return true; |
|
677 } else { |
|
678 // connection pending |
|
679 assert !blocking; |
|
680 state = ST_CONNECTIONPENDING; |
|
681 return false; |
|
682 } |
|
683 } |
|
684 } finally { |
|
685 writeLock.unlock(); |
|
686 } |
|
687 } finally { |
|
688 readLock.unlock(); |
|
689 } |
683 } |
690 } |
684 } |
691 |
685 |
692 /** |
686 /** |
693 * Marks the beginning of a finishConnect operation that might block. |
687 * Marks the beginning of a finishConnect operation that might block. |
712 /** |
706 /** |
713 * Marks the end of a finishConnect operation that may have blocked. |
707 * Marks the end of a finishConnect operation that may have blocked. |
714 * |
708 * |
715 * @throws AsynchronousCloseException if the channel was closed due to this |
709 * @throws AsynchronousCloseException if the channel was closed due to this |
716 * thread being interrupted on a blocking connect operation. |
710 * thread being interrupted on a blocking connect operation. |
|
711 * @throws IOException if completed and unable to obtain the local address |
717 */ |
712 */ |
718 private void endFinishConnect(boolean blocking, boolean completed) |
713 private void endFinishConnect(boolean blocking, boolean completed) |
719 throws AsynchronousCloseException |
714 throws IOException |
720 { |
715 { |
721 endRead(blocking, completed); |
716 endRead(blocking, completed); |
|
717 |
|
718 if (completed) { |
|
719 synchronized (stateLock) { |
|
720 if (state == ST_CONNECTIONPENDING) { |
|
721 localAddress = Net.localAddress(fd); |
|
722 state = ST_CONNECTED; |
|
723 } |
|
724 } |
|
725 } |
722 } |
726 } |
723 |
727 |
724 @Override |
728 @Override |
725 public boolean finishConnect() throws IOException { |
729 public boolean finishConnect() throws IOException { |
726 readLock.lock(); |
|
727 try { |
730 try { |
728 writeLock.lock(); |
731 readLock.lock(); |
729 try { |
732 try { |
730 // already connected? |
733 writeLock.lock(); |
731 synchronized (stateLock) { |
734 try { |
732 if (state == ST_CONNECTED) |
735 // no-op if already connected |
|
736 if (isConnected()) |
733 return true; |
737 return true; |
734 } |
738 |
735 |
739 boolean blocking = isBlocking(); |
736 int n = 0; |
740 boolean connected = false; |
737 boolean blocking = isBlocking(); |
|
738 try { |
|
739 try { |
741 try { |
740 beginFinishConnect(blocking); |
742 beginFinishConnect(blocking); |
|
743 int n = 0; |
741 if (blocking) { |
744 if (blocking) { |
742 do { |
745 do { |
743 n = checkConnect(fd, true); |
746 n = checkConnect(fd, true); |
744 } while (n == 0 || (n == IOStatus.INTERRUPTED) && isOpen()); |
747 } while ((n == 0 || n == IOStatus.INTERRUPTED) && isOpen()); |
745 } else { |
748 } else { |
746 n = checkConnect(fd, false); |
749 n = checkConnect(fd, false); |
747 } |
750 } |
|
751 connected = (n > 0); |
748 } finally { |
752 } finally { |
749 endFinishConnect(blocking, n > 0); |
753 endFinishConnect(blocking, connected); |
750 } |
754 } |
751 } catch (IOException x) { |
755 assert (blocking && connected) ^ !blocking; |
752 close(); |
756 return connected; |
753 throw x; |
757 } finally { |
754 } |
758 writeLock.unlock(); |
755 |
759 } |
756 // post finishConnect, connection may be established |
760 } finally { |
757 synchronized (stateLock) { |
761 readLock.unlock(); |
758 if (!isOpen()) |
762 } |
759 throw new AsynchronousCloseException(); |
763 } catch (IOException ioe) { |
760 if (n > 0) { |
764 // connect failed, close the channel |
761 // connection established |
765 close(); |
762 localAddress = Net.localAddress(fd); |
766 throw ioe; |
763 state = ST_CONNECTED; |
|
764 return true; |
|
765 } else { |
|
766 // connection still pending |
|
767 assert !blocking; |
|
768 return false; |
|
769 } |
|
770 } |
|
771 } finally { |
|
772 writeLock.unlock(); |
|
773 } |
|
774 } finally { |
|
775 readLock.unlock(); |
|
776 } |
767 } |
777 } |
768 } |
778 |
769 |
779 /** |
770 /** |
780 * Invoked by implCloseChannel to close the channel. |
771 * Invoked by implCloseChannel to close the channel. |