Tcl Source Code

Check-in [9b3b3adf47]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:
  • Give clearer names to some of the state flags and sync them with Windows where it makes sense.
  • Rework WaitForConnect once more to always report ENOTCONN on I/O operations on failed async sockets.
  • Fix synchronous connections to a server that only listens on IPv6 (or whatever comes later in the list returned by getaddrinfo(), socket-15.*)
  • Fix spurious writable event on async sockets (socket-14.15).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | bug-13d3af3ad5
Files: files | file ages | folders
SHA1: 9b3b3adf47823f5980b57a1df6432be39c5858c3
User & Date: max 2014-04-08 18:00:35
Original Comment:
  • Give clearer names to some of the state flags and sync them with Windos where it makes sense.
  • Rework WaitForConnect once more to always report ENOTCONN on I/O operations on failed async sockets.
  • Fix synchronous connections to a server that only listens on IPv6 (or whatever comes later in the list returned by getaddrinfo(), socket-15.*)
  • Fix spurious writable event on async sockets (socket-14.15).
References
2014-05-30
13:43 New ticket [734138ded8] close does not report error which was saved by a failing background flush (socket-14.11.1). artifact: 282c1a24d4 user: oehhar
Context
2014-05-20
14:46
Fix and improve socket -async [13d3af3ad5] check-in: fdd8917252 user: oehhar tags: trunk
2014-04-08
18:00
  • Give clearer names to some of the state flags and sync them with Windows where it makes sense. ...
Closed-Leaf check-in: 9b3b3adf47 user: max tags: bug-13d3af3ad5
15:15
Beautify check for async connect reentry check-in: 2b3521353b user: oehhar tags: bug-13d3af3ad5
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to tests/socket.test.

2221
2222
2223
2224
2225
2226
2227


































2228
2229
2230
2231
2232
2233
2234
    vwait x
    set x
} -cleanup {
    catch {close $s}
    after cancel $a1
} -result readable




































::tcltest::cleanupTests
flush stdout
return

# Local Variables:
# mode: tcl







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
    vwait x
    set x
} -cleanup {
    catch {close $s}
    after cancel $a1
} -result readable

test socket-14.15 {blocking read on async socket should not trigger event handlers} \
    -constraints socket -body {
        set s [socket -async localhost [randport]]
        set x ok
        fileevent $s writable {set x fail}
        catch {read $s}
        set x
    } -result ok

set num 0
foreach servip {127.0.0.1 ::1 localhost} {
    foreach cliip {127.0.0.1 ::1 localhost} {
        if {$servip eq $cliip || "localhost" in [list $servip $cliip]} {
            set result {-result "sock*" -match glob}
        } else {
            set result {
                -result {couldn't open socket: connection refused}
                -returnCodes 1
            }
        }
        test socket-15.1.$num "Connect to $servip from $cliip" \
            -constraints {socket supported_inet supported_inet6} -setup {
                set server [socket -server accept -myaddr $servip 0]
                proc accept {s h p} { close $s }
                set port [lindex [fconfigure $server -sockname] 2]
            } -body {
                set s [socket $cliip $port]
            } -cleanup {
                close $server
                catch {close $s}
            } {*}$result
        incr num
    }
}

::tcltest::cleanupTests
flush stdout
return

# Local Variables:
# mode: tcl

Changes to unix/tclUnixSock.c.

78
79
80
81
82
83
84
85
86





87
88
89
90
91
92
93
};

/*
 * These bits may be ORed together into the "flags" field of a TcpState
 * structure.
 */

#define TCP_ASYNC_SOCKET	(1<<0)	/* Asynchronous socket. */
#define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */






/*
 * The following defines the maximum length of the listen queue. This is the
 * number of outstanding yet-to-be-serviced requests for a connection on a
 * server socket, more than this number of outstanding requests and the
 * connection request will fail.
 */







|

>
>
>
>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
};

/*
 * These bits may be ORed together into the "flags" field of a TcpState
 * structure.
 */

#define TCP_NONBLOCKING		(1<<0)	/* Socket with non-blocking I/O */
#define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */
#define TCP_ASYNC_PENDING	(1<<4)	/* TcpConnect was called to
					 * process an async connect. This
					 * flag indicates that reentry is
					 * still pending */
#define TCP_ASYNC_FAILED	(1<<5)	/* An async connect finally failed */

/*
 * The following defines the maximum length of the listen queue. This is the
 * number of outstanding yet-to-be-serviced requests for a connection on a
 * server socket, more than this number of outstanding requests and the
 * connection request will fail.
 */
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
			    Tcl_Interp *interp, const char *optionName,
			    Tcl_DString *dsPtr);
static int		TcpInputProc(ClientData instanceData, char *buf,
			    int toRead, int *errorCode);
static int		TcpOutputProc(ClientData instanceData,
			    const char *buf, int toWrite, int *errorCode);
static void		TcpWatchProc(ClientData instanceData, int mask);
static int		WaitForConnect(TcpState *statePtr, int noblock);

/*
 * This structure describes the channel type structure for TCP socket
 * based IO:
 */

static const Tcl_ChannelType tcpChannelType = {







|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
			    Tcl_Interp *interp, const char *optionName,
			    Tcl_DString *dsPtr);
static int		TcpInputProc(ClientData instanceData, char *buf,
			    int toRead, int *errorCode);
static int		TcpOutputProc(ClientData instanceData,
			    const char *buf, int toWrite, int *errorCode);
static void		TcpWatchProc(ClientData instanceData, int mask);
static int		WaitForConnect(TcpState *statePtr, int *errorCodePtr);

/*
 * This structure describes the channel type structure for TCP socket
 * based IO:
 */

static const Tcl_ChannelType tcpChannelType = {
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

387




388
389







390
391
392
393
394




395
396
397
398
399
400
401
402


403



404




405


406









407
408
409

410
411
412
413
414


415
416
417
418
419
420
421
422

423

424
425

426
427


428
429
430
431
432
433
434
    int mode)			/* The mode to set. Can be one of
				 * TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    TcpState *statePtr = instanceData;

    if (mode == TCL_MODE_BLOCKING) {
	CLEAR_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
    } else {
	SET_BITS(statePtr->flags, TCP_ASYNC_SOCKET);
    }
    if (statePtr->flags & TCP_ASYNC_CONNECT) {
        statePtr->cachedBlocking = mode;
        return 0;
    }
    if (TclUnixSetBlockingMode(statePtr->fds.fd, mode) < 0) {
	return errno;
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * WaitForConnect --
 *
 *	Wait for a connection on an asynchronously opened socket to be

 *	completed.  In nonblocking mode, just test if the connection




 *	has completed without blocking. The noblock parameter allows to
 *	enforce nonblocking behaviour even on sockets in blocking mode.







 *
 * Results:
 * 	0 if the connection has completed, -1 if still in progress
 * 	or there is an error.
 *




 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    TcpState *statePtr,		/* State of the socket. */
    int noblock)		/* Don't wait, even for sockets in blocking mode */
{


    /*



     * If an asynchronous connect is in progress, attempt to wait for it to




     * complete before reading.


     */










    if (statePtr->flags & TCP_ASYNC_CONNECT) {
	if (noblock || (statePtr->flags & TCP_ASYNC_SOCKET)) {

            if (TclUnixWaitForFile(statePtr->fds.fd,
                                    TCL_WRITABLE | TCL_EXCEPTION, 0) != 0) {
                TcpConnect(NULL, statePtr);
            }
	} else {


            while (statePtr->flags & TCP_ASYNC_CONNECT) {
                if (TclUnixWaitForFile(statePtr->fds.fd,
                                        TCL_WRITABLE | TCL_EXCEPTION, -1) != 0) {
                    TcpConnect(NULL, statePtr);
                }
            }
        }
    }

    if (statePtr->connectError != 0) {

        return -1;
    } else {

        return 0;
    }


}

/*
 *----------------------------------------------------------------------
 *
 * TcpInputProc --
 *







|

|
















|
>
|
>
>
>
>
|
<
>
>
>
>
>
>
>





>
>
>
>






|

>
>

>
>
>
|
>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
|
<
<
>
|
|
|
|
<
>
>
|
<
<
<
|
<
<
<
>
|
>
|
|
>
|
|
>
>







365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447


448
449
450
451
452

453
454
455



456



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    int mode)			/* The mode to set. Can be one of
				 * TCL_MODE_BLOCKING or
				 * TCL_MODE_NONBLOCKING. */
{
    TcpState *statePtr = instanceData;

    if (mode == TCL_MODE_BLOCKING) {
	CLEAR_BITS(statePtr->flags, TCP_NONBLOCKING);
    } else {
	SET_BITS(statePtr->flags, TCP_NONBLOCKING);
    }
    if (statePtr->flags & TCP_ASYNC_CONNECT) {
        statePtr->cachedBlocking = mode;
        return 0;
    }
    if (TclUnixSetBlockingMode(statePtr->fds.fd, mode) < 0) {
	return errno;
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * WaitForConnect --
 *
 *	Check the state of an async connect process. If a connection
 *	attempt terminated, process it, which may finalize it or may
 *	start the next attempt. If a connect error occures, it is saved
 *	in statePtr->connectError to be reported by 'fconfigure -error'.
 *
 *	There are two modes of operation, defined by errorCodePtr:
 *	 *  non-NULL: Called by explicite read/write command. block if
 *	    socket is blocking.

 *	    May return two error codes:
 *	     *	EWOULDBLOCK: if connect is still in progress
 *	     *	ENOTCONN: if connect failed. This would be the error
 *		message of a rect or sendto syscall so this is
 *		emulated here.
 *	 *  NULL: Called by a backround operation. Do not block and
 *	    don't return any error code.
 *
 * Results:
 * 	0 if the connection has completed, -1 if still in progress
 * 	or there is an error.
 *
 * Side effects:
 *	Processes socket events off the system queue.
 *	May process asynchroneous connect.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    TcpState *statePtr,		/* State of the socket. */
    int *errorCodePtr)
{
    int timeout;

    /*
     * Check if an async connect failed already and error reporting is demanded,
     * return the error ENOTCONN
     */

    if (errorCodePtr != NULL && (statePtr->flags & TCP_ASYNC_FAILED)) {
	*errorCodePtr = ENOTCONN;
	return -1;
    }

    /*
     * Check if an async connect is running. If not return ok
     */
     
    if (!(statePtr->flags & TCP_ASYNC_PENDING)) {
	return 0;
    }
    
    if (errorCodePtr == NULL || (statePtr->flags & TCP_NONBLOCKING)) {
        timeout = 0;
    } else {
        timeout = -1;
    }


    do {
        if (TclUnixWaitForFile(statePtr->fds.fd,
                                TCL_WRITABLE | TCL_EXCEPTION, timeout) != 0) {
            TcpConnect(NULL, statePtr);
        }

        /* Do this only once in the nonblocking case and repeat it until the
         * socket is final when blocking */
    } while (timeout == -1 && statePtr->flags & TCP_ASYNC_CONNECT);







    if (errorCodePtr != NULL) {
        if (statePtr->flags & TCP_ASYNC_PENDING) {
            *errorCodePtr = EAGAIN;
            return -1;
        } else if (statePtr->connectError != 0) {
            *errorCodePtr = ENOTCONN;
            return -1;
        }
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TcpInputProc --
 *
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
				 * buffer? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = instanceData;
    int bytesRead;

    *errorCodePtr = 0;
    if (WaitForConnect(statePtr, 0) != 0) {
        *errorCodePtr = statePtr->connectError;
        statePtr->connectError = 0;
	return -1;
    }
    bytesRead = recv(statePtr->fds.fd, buf, (size_t) bufSize, 0);
    if (bytesRead > -1) {
	return bytesRead;
    }
    if (errno == ECONNRESET) {







|
<
<







497
498
499
500
501
502
503
504


505
506
507
508
509
510
511
				 * buffer? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = instanceData;
    int bytesRead;

    *errorCodePtr = 0;
    if (WaitForConnect(statePtr, errorCodePtr) != 0) {


	return -1;
    }
    bytesRead = recv(statePtr->fds.fd, buf, (size_t) bufSize, 0);
    if (bytesRead > -1) {
	return bytesRead;
    }
    if (errno == ECONNRESET) {
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
    int toWrite,		/* How many bytes to write? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = instanceData;
    int written;

    *errorCodePtr = 0;
    if (WaitForConnect(statePtr, 0) != 0) {
        *errorCodePtr = statePtr->connectError;
        statePtr->connectError = 0;
	return -1;
    }
    written = send(statePtr->fds.fd, buf, (size_t) toWrite, 0);

    if (written > -1) {
	return written;
    }







|
<
<







547
548
549
550
551
552
553
554


555
556
557
558
559
560
561
    int toWrite,		/* How many bytes to write? */
    int *errorCodePtr)		/* Where to store error code. */
{
    TcpState *statePtr = instanceData;
    int written;

    *errorCodePtr = 0;
    if (WaitForConnect(statePtr, errorCodePtr) != 0) {


	return -1;
    }
    written = send(statePtr->fds.fd, buf, (size_t) toWrite, 0);

    if (written > -1) {
	return written;
    }
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
				 * values. */
    Tcl_DString *dsPtr)		/* Where to store the computed value;
				 * initialized by caller. */
{
    TcpState *statePtr = instanceData;
    size_t len = 0;

    WaitForConnect(statePtr, 1);

    if (optionName != NULL) {
	len = strlen(optionName);
    }

    if ((len > 1) && (optionName[1] == 'e') &&
	    (strncmp(optionName, "-error", len) == 0)) {







|







775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
				 * values. */
    Tcl_DString *dsPtr)		/* Where to store the computed value;
				 * initialized by caller. */
{
    TcpState *statePtr = instanceData;
    size_t len = 0;

    WaitForConnect(statePtr, NULL);

    if (optionName != NULL) {
	len = strlen(optionName);
    }

    if ((len > 1) && (optionName[1] == 'e') &&
	    (strncmp(optionName, "-error", len) == 0)) {
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
         * Make sure we don't mess with server sockets since they will never
         * be readable or writable at the Tcl level. This keeps Tcl scripts
         * from interfering with the -accept behavior (bug #3394732).
         */
    	return;
    }
     
    if (statePtr->flags & TCP_ASYNC_CONNECT) {
        /* Async sockets use a FileHandler internally while connecting, so we
         * need to cache this request until the connection has succeeded. */
        statePtr->filehandlers = mask;
    } else if (mask) {
        Tcl_CreateFileHandler(statePtr->fds.fd, mask,
                (Tcl_FileProc *) Tcl_NotifyChannel, statePtr->channel);
    } else {







|







919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
         * Make sure we don't mess with server sockets since they will never
         * be readable or writable at the Tcl level. This keeps Tcl scripts
         * from interfering with the -accept behavior (bug #3394732).
         */
    	return;
    }
     
    if (statePtr->flags & TCP_ASYNC_PENDING) {
        /* Async sockets use a FileHandler internally while connecting, so we
         * need to cache this request until the connection has succeeded. */
        statePtr->filehandlers = mask;
    } else if (mask) {
        Tcl_CreateFileHandler(statePtr->fds.fd, mask,
                (Tcl_FileProc *) Tcl_NotifyChannel, statePtr->channel);
    } else {
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000

1001
1002
1003
1004
1005
1006
1007
1008
1009
1010

static int
TcpConnect(
    Tcl_Interp *interp,		/* For error reporting; can be NULL. */
    TcpState *statePtr)
{
    socklen_t optlen;
    int async_callback = (statePtr->addr != NULL);
    int ret = -1, error = 0;
    int async = statePtr->flags & TCP_ASYNC_CONNECT;

    if (async_callback) {
        goto reenter;
    }

    for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL;
            statePtr->addr = statePtr->addr->ai_next) {

        for (statePtr->myaddr = statePtr->myaddrlist; statePtr->myaddr != NULL;
                statePtr->myaddr = statePtr->myaddr->ai_next) {
            int reuseaddr;
            
	    /*
	     * No need to try combinations of local and remote addresses of
	     * different families.
	     */

	    if (statePtr->myaddr->ai_family != statePtr->addr->ai_family) {







|
|








>


|







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

static int
TcpConnect(
    Tcl_Interp *interp,		/* For error reporting; can be NULL. */
    TcpState *statePtr)
{
    socklen_t optlen;
    int async_callback = statePtr->flags & TCP_ASYNC_PENDING;
    int ret = -1, error;
    int async = statePtr->flags & TCP_ASYNC_CONNECT;

    if (async_callback) {
        goto reenter;
    }

    for (statePtr->addr = statePtr->addrlist; statePtr->addr != NULL;
            statePtr->addr = statePtr->addr->ai_next) {

        for (statePtr->myaddr = statePtr->myaddrlist; statePtr->myaddr != NULL;
                statePtr->myaddr = statePtr->myaddr->ai_next) {
            int reuseaddr = 1;
            
	    /*
	     * No need to try combinations of local and remote addresses of
	     * different families.
	     */

	    if (statePtr->myaddr->ai_family != statePtr->addr->ai_family) {
1043
1044
1045
1046
1047
1048
1049


1050

1051
1052
1053
1054
1055
1056
1057
	    if (async) {
                ret = TclUnixSetBlockingMode(statePtr->fds.fd,TCL_MODE_NONBLOCKING);
                if (ret < 0) {
                    continue;
                }
            }



            reuseaddr = 1;

            (void) setsockopt(statePtr->fds.fd, SOL_SOCKET, SO_REUSEADDR,
                    (char *) &reuseaddr, sizeof(reuseaddr));
            ret = bind(statePtr->fds.fd, statePtr->myaddr->ai_addr,
                    statePtr->myaddr->ai_addrlen);
            if (ret < 0) {
                error = errno;
                continue;







>
>
|
>







1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
	    if (async) {
                ret = TclUnixSetBlockingMode(statePtr->fds.fd,TCL_MODE_NONBLOCKING);
                if (ret < 0) {
                    continue;
                }
            }

            /* Gotta reset the error variable here, before we use it for the
             * first time in this iteration. */
            error = 0;
            
            (void) setsockopt(statePtr->fds.fd, SOL_SOCKET, SO_REUSEADDR,
                    (char *) &reuseaddr, sizeof(reuseaddr));
            ret = bind(statePtr->fds.fd, statePtr->myaddr->ai_addr,
                    statePtr->myaddr->ai_addrlen);
            if (ret < 0) {
                error = errno;
                continue;
1066
1067
1068
1069
1070
1071
1072
1073

1074
1075
1076

1077
1078
1079
1080
1081
1082
1083
	    
	    ret = connect(statePtr->fds.fd, statePtr->addr->ai_addr,
                        statePtr->addr->ai_addrlen);
            if (ret < 0) error = errno;
	    if (ret < 0 && errno == EINPROGRESS) {
                Tcl_CreateFileHandler(statePtr->fds.fd,
                        TCL_WRITABLE|TCL_EXCEPTION, TcpAsyncCallback, statePtr);
                statePtr->connectError = errno = EWOULDBLOCK;

                return TCL_OK;

            reenter:

                Tcl_DeleteFileHandler(statePtr->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].







|
>



>







1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
	    
	    ret = connect(statePtr->fds.fd, statePtr->addr->ai_addr,
                        statePtr->addr->ai_addrlen);
            if (ret < 0) error = errno;
	    if (ret < 0 && errno == EINPROGRESS) {
                Tcl_CreateFileHandler(statePtr->fds.fd,
                        TCL_WRITABLE|TCL_EXCEPTION, TcpAsyncCallback, statePtr);
                errno = EWOULDBLOCK;
                SET_BITS(statePtr->flags, TCP_ASYNC_PENDING);
                return TCL_OK;

            reenter:
                CLEAR_BITS(statePtr->flags, TCP_ASYNC_PENDING);
                Tcl_DeleteFileHandler(statePtr->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].
1101
1102
1103
1104
1105
1106
1107




1108
1109
1110
1111
1112
1113
1114
1115
1116
1117



1118

1119
1120
1121
1122
1123
1124
1125
    if (async_callback) {
        /*
         * An asynchonous connection has finally succeeded or failed.
         */

        TcpWatchProc(statePtr, statePtr->filehandlers);
        TclUnixSetBlockingMode(statePtr->fds.fd, statePtr->cachedBlocking);





        /*
         * 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(statePtr->channel, TCL_WRITABLE);

    }
    if (error != 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.
         */







>
>
>
>










>
>
>

>







1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
    if (async_callback) {
        /*
         * An asynchonous connection has finally succeeded or failed.
         */

        TcpWatchProc(statePtr, statePtr->filehandlers);
        TclUnixSetBlockingMode(statePtr->fds.fd, statePtr->cachedBlocking);

        if (error != 0) {
            SET_BITS(statePtr->flags, TCP_ASYNC_FAILED);
        }

        /*
         * 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().
         */

        /* Note: disabling this for now as it causes spurious event triggering
         * under Linux (see test socket-14.15). */
#if 0
        Tcl_NotifyChannel(statePtr->channel, TCL_WRITABLE);
#endif
    }
    if (error != 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.
         */