Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: |
|
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
b56716f2ac6b6dc0e769d25513f73e66 |
User & Date: | max 2011-06-28 11:32:15 |
Context
2011-06-28
| ||
14:42 | replace socket-14.3 with a test that is more useful and less likely to randomly fail depending on th... check-in: 2eb58b2d67 user: max tags: trunk | |
11:32 |
| |
2011-06-24
| ||
17:50 | merge mark check-in: a5f1869eae user: dgp tags: trunk | |
Changes
Changes to ChangeLog.
1 2 3 4 5 6 7 | 2011-06-22 Andreas Kupries <[email protected]> * library/platform/pkgIndex.tcl: Updated to platform 1.0.10. Added * library/platform/platform.tcl: handling of the DEB_HOST_MULTIARCH * unix/Makefile.in: location change for libc. * win/Makefile.in: | > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 2011-06-28 Reinhard Max <[email protected]> * unix/tclUnixSock.c (CreateClientSocket): Fix and simplify posting of the writable fileevent at the end of an asynchronous connection attempt. Improve comments for some of the trickery around [socket -async]. [Bug 3325339] * tests/socket.test: Adjust tests to the async code changes. Add more tests for corner cases of async sockets. 2011-06-22 Andreas Kupries <[email protected]> * library/platform/pkgIndex.tcl: Updated to platform 1.0.10. Added * library/platform/platform.tcl: handling of the DEB_HOST_MULTIARCH * unix/Makefile.in: location change for libc. * win/Makefile.in: |
︙ | ︙ |
Changes to tests/socket.test.
︙ | ︙ | |||
1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | global x puts $s bye close $s lappend x ok } set server [socket -server accept -myaddr 127.0.0.1 0] set port [lindex [fconfigure $server -sockname] 2] } -body { set client [socket -async localhost $port] fileevent $client writable { | > | | | | < > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 | global x puts $s bye close $s lappend x ok } set server [socket -server accept -myaddr 127.0.0.1 0] set port [lindex [fconfigure $server -sockname] 2] set x "" } -body { set client [socket -async localhost $port] fileevent $client writable { lappend x [fconfigure $client -error] } set after [after 1000 {lappend x timeout}] while {[llength $x] < 2 && "timeout" ni $x} { vwait x } lsort $x; # we only want to see both events, the order doesn't matter } -cleanup { after cancel $after close $server close $client unset x } -result {{} ok} test socket-14.2 {[socket -async] fileevent connection refused} \ -constraints [list socket supported_any] \ -body { set client [socket -async localhost [randport]] fileevent $client writable {set x [fconfigure $client -error]} set after [after 1000 {set x timeout}] vwait x if {$x eq "timeout"} { append x ": [fconfigure $client -error]" } set x } -cleanup { after cancel $after close $client unset x } -result "connection refused" test socket-14.3 {[socket -async] fileevent host unreachable} \ -constraints [list socket supported_any] \ -body { # address from rfc5737 set client [socket -async 192.0.2.42 [randport]] fileevent $client writable {set x [fconfigure $client -error]} set after [after 5000 {set x timeout}] vwait x if {$x eq "timeout"} { append x ": [fconfigure $client -error]" } set x } -cleanup { after cancel $after close $client unset x } -result "host is unreachable" test socket-14.4 {[socket -async] and both, readdable and writable fileevents} \ -constraints [list socket supported_any] \ -setup { proc accept {s a p} { puts $s bye close $s } set server [socket -server accept -myaddr 127.0.0.1 0] set port [lindex [fconfigure $server -sockname] 2] set x "" } -body { set client [socket -async localhost $port] fileevent $client writable { lappend x [fconfigure $client -error] fileevent $client writable {} } fileevent $client readable {lappend x [gets $client]} set after [after 1000 {lappend x timeout}] while {[llength $x] < 2 && "timeout" ni $x} { vwait x } lsort $x } -cleanup { after cancel $after close $client close $server } -result {{} bye} test socket-14.5 {[socket -async] which fails before any connect() can be made} \ -constraints [list socket supported_any] \ -body { # addresses from rfc5737 socket -async -myaddr 192.0.2.42 198.51.100.42 [randport] } \ -returnCodes 1 \ -result {couldn't open socket: cannot assign requested address} ::tcltest::cleanupTests flush stdout return # Local Variables: # mode: tcl # fill-column: 78 # End: |
Changes to unix/tclUnixSock.c.
︙ | ︙ | |||
854 855 856 857 858 859 860 861 862 863 864 865 866 867 | { TcpState *statePtr = (TcpState *) instanceData; *handlePtr = INT2PTR(statePtr->fds.fd); return TCL_OK; } static void TcpAsyncCallback( ClientData clientData, /* The socket state. */ int mask) /* Events of interest; an OR-ed combination of * TCL_READABLE, TCL_WRITABLE and * TCL_EXCEPTION. */ { | > > > > > > > > > > > | 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 | { TcpState *statePtr = (TcpState *) instanceData; *handlePtr = INT2PTR(statePtr->fds.fd); return TCL_OK; } /* *---------------------------------------------------------------------- * * TcpAsyncCallback -- * * Called by the event handler that CreateClientSocket sets up * internally for [socket -async] to get notified when the * asyncronous connection attempt has succeeded or failed. * *---------------------------------------------------------------------- */ static void TcpAsyncCallback( ClientData clientData, /* The socket state. */ int mask) /* Events of interest; an OR-ed combination of * TCL_READABLE, TCL_WRITABLE and * TCL_EXCEPTION. */ { |
︙ | ︙ | |||
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 | * TCL_OK, if the socket was successfully connected or an asynchronous * connection is in progress. If an error occurs, TCL_ERROR is returned * and an error message is left in interp. * * Side effects: * Opens a socket. * *---------------------------------------------------------------------- */ static int CreateClientSocket( Tcl_Interp *interp, /* For error reporting; can be NULL. */ TcpState *state) { socklen_t optlen; | > > > > > > > > > > > > | | | | | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | * TCL_OK, if the socket was successfully connected or an asynchronous * connection is in progress. If an error occurs, TCL_ERROR is returned * and an error message is left in interp. * * Side effects: * Opens a socket. * * Remarks: * A single host name may resolve to more than one IP address, e.g. for * an IPv4/IPv6 dual stack host. For handling asyncronously connecting * sockets in the background for such hosts, this function can act as a * coroutine. On the first call, it sets up the control variables for the * two nested loops over the local and remote addresses. Once the first * connection attempt is in progress, it sets up itself as a writable * event handler for that socket, and returns. When the callback occurs, * control is transferred to the "reenter" label, right after the initial * return and the loops resume as if they had never been interrupted. * For syncronously connecting sockets, the loops work the usual way. * *---------------------------------------------------------------------- */ static int CreateClientSocket( Tcl_Interp *interp, /* For error reporting; can be NULL. */ TcpState *state) { socklen_t optlen; int async_callback = (state->addr != NULL); int status; int async = state->flags & TCP_ASYNC_CONNECT; if (async_callback) { goto reenter; } for (state->addr = state->addrlist; state->addr != NULL; state->addr = state->addr->ai_next) { status = -1; for (state->myaddr = state->myaddrlist; state->myaddr != NULL; state->myaddr = state->myaddr->ai_next) { |
︙ | ︙ | |||
972 973 974 975 976 977 978 | state->addr->ai_addrlen); if (status < 0 && errno == EINPROGRESS) { Tcl_CreateFileHandler(state->fds.fd, TCL_WRITABLE | TCL_EXCEPTION, TcpAsyncCallback, state); return TCL_OK; | | | | > | | > > > | | > > | < > > > > | > | > > > > > | | | | | < | 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 | state->addr->ai_addrlen); if (status < 0 && errno == EINPROGRESS) { Tcl_CreateFileHandler(state->fds.fd, TCL_WRITABLE | TCL_EXCEPTION, TcpAsyncCallback, state); return TCL_OK; reenter: Tcl_DeleteFileHandler(state->fds.fd); /* * Read the error state from the socket to see if the async * connection has succeeded or failed. As this clears the * error condition, we cache the status in the socket state * struct for later retrieval by [fconfigure -error]. */ optlen = sizeof(int); getsockopt(state->fds.fd, SOL_SOCKET, SO_ERROR, (char *)&status, &optlen); state->status = status; } if (status == 0) { goto out; } } } out: if (async_callback) { /* * An asynchonous connection has finally succeeded or failed. */ CLEAR_BITS(state->flags, TCP_ASYNC_CONNECT); TcpWatchProc(state, state->filehandlers); TclUnixSetBlockingMode(state->fds.fd, TCL_MODE_BLOCKING); /* * We need to forward the writable event that brought us here, bcasue * upon reading of getsockopt(SO_ERROR), at least some OSes clear the * writable state from the socket, and so a subsequent select() on * behalf of a script level [fileevent] would not fire. It doesn't * hurt that this is also called in the successful case and will save * the event mechanism one roundtrip through select(). */ Tcl_NotifyChannel(state->channel, TCL_WRITABLE); } else if (status != 0) { /* * Failure for either a synchronous connection, or an async one that * failed before it could enter background mode, e.g. because an * invalid -myaddr was given. */ if (interp != NULL) { Tcl_AppendResult(interp, "couldn't open socket: ", Tcl_PosixError(interp), NULL); } return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * |
︙ | ︙ |