Tcl Source Code

Check-in [711c272f36]
Login

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

Overview
Comment:Async connect terminates: fire fileevent by setting readyEvent, propage commit fail message to [fconfigure -error]
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | bug-13d3af3ad5
Files: files | file ages | folders
SHA1: 711c272f369d86d95493230c1999184a11e5a225
User & Date: oehhar 2014-03-14 09:12:13
Context
2014-03-14
14:26
  • More test improvements for async sockets.
  • Advance async connections whenever the channel i...
check-in: ece135ac28 user: max tags: bug-13d3af3ad5
10:59
Remove writable shortcut and errorneous workaround to get [connect -async] fail error to [fconfigure... Closed-Leaf check-in: aa6c8b17e1 user: oehhar tags: bug-13d3af3ad5-fork
09:12
Async connect terminates: fire fileevent by setting readyEvent, propage commit fail message to [fcon... check-in: 711c272f36 user: oehhar tags: bug-13d3af3ad5
2014-03-11
17:34
  • Hide transient errors of the internal iterations of [socket -async] from the script level. * ...
check-in: 5fafcb7857 user: max tags: bug-13d3af3ad5
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to win/tclWinSock.c.

166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
    ClientData acceptProcData;	/* The data for the accept proc. */
    struct addrinfo *addrlist;	/* Addresses to connect to. */
    struct addrinfo *addr;	/* Iterator over addrlist. */
    struct addrinfo *myaddrlist;/* Local address. */
    struct addrinfo *myaddr;	/* Iterator over myaddrlist. */
    int status;                 /* Cache status of async socket. */
    int cachedBlocking;         /* Cache blocking mode of async socket. */
    int lastError;		/* Error code from last message. */

    struct SocketInfo *nextPtr;	/* The next socket on the per-thread socket
				 * list. */
};

/*
 * The following structure is what is added to the Tcl event queue when a
 * socket event occurs.







|
>







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    ClientData acceptProcData;	/* The data for the accept proc. */
    struct addrinfo *addrlist;	/* Addresses to connect to. */
    struct addrinfo *addr;	/* Iterator over addrlist. */
    struct addrinfo *myaddrlist;/* Local address. */
    struct addrinfo *myaddr;	/* Iterator over myaddrlist. */
    int status;                 /* Cache status of async socket. */
    int cachedBlocking;         /* Cache blocking mode of async socket. */
    int lastError;		/* Error code from notifier thread. */
    int connectError;		/* Error code from failed async connect. */
    struct SocketInfo *nextPtr;	/* The next socket on the per-thread socket
				 * list. */
};

/*
 * The following structure is what is added to the Tcl event queue when a
 * socket event occurs.
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
static void		InitSockets(void);
static SocketInfo *	NewSocketInfo(SOCKET socket);
static void		SocketExitHandler(ClientData clientData);
static LRESULT CALLBACK	SocketProc(HWND hwnd, UINT message, WPARAM wParam,
			    LPARAM lParam);
static int		SocketsEnabled(void);
static void		TcpAccept(TcpFdList *fds, SOCKET newSocket, address addr);
static int		WaitForConnect(SocketInfo *infoPtr, int *errorCodePtr);

static int		WaitForSocketEvent(SocketInfo *infoPtr, int events,
			    int *errorCodePtr);
static int		FindFDInList(SocketInfo *infoPtr, SOCKET socket);
static DWORD WINAPI	SocketThread(LPVOID arg);
static void		TcpThreadActionProc(ClientData instanceData,
			    int action);








|
>







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
static void		InitSockets(void);
static SocketInfo *	NewSocketInfo(SOCKET socket);
static void		SocketExitHandler(ClientData clientData);
static LRESULT CALLBACK	SocketProc(HWND hwnd, UINT message, WPARAM wParam,
			    LPARAM lParam);
static int		SocketsEnabled(void);
static void		TcpAccept(TcpFdList *fds, SOCKET newSocket, address addr);
static int		WaitForConnect(SocketInfo *infoPtr, int *errorCodePtr,
			    int terminate_connect);
static int		WaitForSocketEvent(SocketInfo *infoPtr, int events,
			    int *errorCodePtr);
static int		FindFDInList(SocketInfo *infoPtr, SOCKET socket);
static DWORD WINAPI	SocketThread(LPVOID arg);
static void		TcpThreadActionProc(ClientData instanceData,
			    int action);

774
775
776
777
778
779
780

781

782
783



784
785
786
787
788
789
790
    infoPtr->flags &= ~SOCKET_PENDING;

    /* Continue async connect if pending and ready */
    if ( infoPtr->readyEvents & FD_CONNECT ) {
	infoPtr->readyEvents &= ~(FD_CONNECT);
	DEBUG("FD_CONNECT");
        if ( infoPtr->flags & SOCKET_REENTER_PENDING ) {

	    SetEvent(tsdPtr->socketListLock);

	    CreateClientSocket(NULL, infoPtr);
	    return 1;



	}
    }

    /*
     * Handle connection requests directly.
     */
    if (infoPtr->readyEvents & FD_ACCEPT) {







>

>
|
<
>
>
>







776
777
778
779
780
781
782
783
784
785
786

787
788
789
790
791
792
793
794
795
796
    infoPtr->flags &= ~SOCKET_PENDING;

    /* Continue async connect if pending and ready */
    if ( infoPtr->readyEvents & FD_CONNECT ) {
	infoPtr->readyEvents &= ~(FD_CONNECT);
	DEBUG("FD_CONNECT");
        if ( infoPtr->flags & SOCKET_REENTER_PENDING ) {
	    /* free list lock */
	    SetEvent(tsdPtr->socketListLock);
	    /* Do one connect step */
	    if (TCL_OK != CreateClientSocket(NULL, infoPtr) ) {

		/* On final fail save error for fconfigure -error */
		infoPtr->connectError = Tcl_GetErrno();
	    }
	}
    }

    /*
     * Handle connection requests directly.
     */
    if (infoPtr->readyEvents & FD_ACCEPT) {
1389
1390
1391
1392
1393
1394
1395



1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412




1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427









1428




1429















1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440


1441

1442
1443
1444
1445
1446
1447


1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459

1460
1461
1462
1463
1464
1465
1466
	    if (Tcl_GetErrno() == 0) {
		goto out;
	    }
	}
    }

out:



    DEBUG("connected or finally failed");
    /* Clear async flag (not really necessary, not used any more) */
    infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
    if ( Tcl_GetErrno() != 0 ) {
	DEBUG("ERRNO");
	if (interp != NULL) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
				"couldn't open socket: %s", Tcl_PosixError(interp)));
	}
	/*
	 * In the final error case inform fileevent that we failed
	 */
	if (async_callback) {
	    Tcl_NotifyChannel(infoPtr->channel, TCL_WRITABLE);
	}
	return TCL_ERROR;
    }




    /*
     * Set up the select mask for read/write events.
     */
    DEBUG("selectEvents = FD_READ | FD_WRITE | FD_CLOSE");    
    infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
    
    /*
     * Register for interest in events in the select mask. Note that this
     * automatically places the socket into non-blocking mode.
     */
    
    tsdPtr = TclThreadDataKeyGet(&dataKey);
    ioctlsocket(infoPtr->sockets->fd, (long) FIONBIO, &flag);
    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
		(LPARAM) infoPtr);









    if (async_callback) {




	Tcl_NotifyChannel(infoPtr->channel, TCL_WRITABLE);















    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * WaitForConnect --
 *
 *	Process an asyncroneous connect by gets/puts commands.
 *	For blocking calls, terminate connect synchroneously.


 *	For non blocking calls, do one asynchroneous step if possible.

 *	This routine should only be called if flag SOCKET_REENTER_PENDING
 *	is set.
 *
 * Results:
 *	Returns 1 on success or 0 on failure, with an error code in
 *	errorCodePtr.


 *
 * Side effects:
 *	Processes socket events off the system queue.
 *	May process asynchroneous connect.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    SocketInfo *infoPtr,	/* Information about this socket. */
    int *errorCodePtr)		/* Where to store errors? */

{
    int result;
    int oldMode;
    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);

    /*
     * Be sure to disable event servicing so we are truly modal.







>
>
>



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









|
|
>
>
|
>




|

>
>











|
>







1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407





1408
1409
1410
1411


1412


1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427


1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
	    if (Tcl_GetErrno() == 0) {
		goto out;
	    }
	}
    }

out:
    /*
     * Socket connected or connection failed
     */
    DEBUG("connected or finally failed");
    /* Clear async flag (not really necessary, not used any more) */
    infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);






    /*
     * Final connect failure
     */





    if ( Tcl_GetErrno() == 0 ) {
	/*
	 * Succesfully connected
	 */
	/*
	 * Set up the select mask for read/write events.
	 */
	DEBUG("selectEvents = FD_READ | FD_WRITE | FD_CLOSE");    
	infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
        
	/*
	 * Register for interest in events in the select mask. Note that this
	 * automatically places the socket into non-blocking mode.
	 */
        


	SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
		    (LPARAM) infoPtr);
    } else {
	/*
	 * Connect failed
	 */
	DEBUG("ERRNO");

	/*
	 * For async connect schedule a writable event to report the fail.
	 */
	if (async_callback) {
	    /*
	     * Set up the select mask for read/write events.
	     */
	    DEBUG("selectEvents = FD_WRITE for fail writable");    
	    infoPtr->selectEvents = FD_WRITE;
	    /* get infoPtr lock */
	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
	    /* Clear eventual connect flag */
	    infoPtr->readyEvents |= FD_WRITE;
	    /* Free list lock */
	    SetEvent(tsdPtr->socketListLock);
	}
	/*
	 * Error message on syncroneous connect
	 */
	if (interp != NULL) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "couldn't open socket: %s", Tcl_PosixError(interp)));
	}
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * WaitForConnect --
 *
 *	Process an asyncroneous connect by other commands (gets... ).
 *	Do one connect step if pending as if the event loop would run.
 *
 *	Blocking commands may call in with terminate_connect to terminate
 *	the syncroneous connect syncroneously.
 *
 *	This routine should only be called if flag SOCKET_REENTER_PENDING
 *	is set.
 *
 * Results:
 *	Returns 1 on success or 0 on failure, with a possix error code in
 *	errorCodePtr.
 *	If the connect is not terminated, errorCode is set to EWOULDBLOCK
 *	and 0 is returned.
 *
 * Side effects:
 *	Processes socket events off the system queue.
 *	May process asynchroneous connect.
 *
 *----------------------------------------------------------------------
 */

static int
WaitForConnect(
    SocketInfo *infoPtr,	/* Information about this socket. */
    int *errorCodePtr,		/* Where to store errors? */
    int terminate_connect)	/* Should the connect be terminated? */
{
    int result;
    int oldMode;
    ThreadSpecificData *tsdPtr = TclThreadDataKeyGet(&dataKey);

    /*
     * Be sure to disable event servicing so we are truly modal.
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
	    /* Consume the connect event */
	    infoPtr->readyEvents &= ~(FD_CONNECT);

	    /*
	     * For blocking sockets disable async connect
	     * as we continue now synchoneously
	     */
	    if (! ( infoPtr->flags & TCP_ASYNC_SOCKET ) ) {
		infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
	    }

            /* Free list lock */
            SetEvent(tsdPtr->socketListLock);

	    /* continue connect */







|







1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
	    /* Consume the connect event */
	    infoPtr->readyEvents &= ~(FD_CONNECT);

	    /*
	     * For blocking sockets disable async connect
	     * as we continue now synchoneously
	     */
	    if ( terminate_connect ) {
		infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
	    }

            /* Free list lock */
            SetEvent(tsdPtr->socketListLock);

	    /* continue connect */
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
        /* Free list lock */
        SetEvent(tsdPtr->socketListLock);

	/*
	 * A non blocking socket waiting for an asyncronous connect
	 * returns directly an error
	 */
	if ( infoPtr->flags & TCP_ASYNC_SOCKET ) {
	    *errorCodePtr = EWOULDBLOCK;
	    return 0;
	}

	/*
	 * Wait until something happens.
	 */







|







1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
        /* Free list lock */
        SetEvent(tsdPtr->socketListLock);

	/*
	 * A non blocking socket waiting for an asyncronous connect
	 * returns directly an error
	 */
	if ( ! terminate_connect ) {
	    *errorCodePtr = EWOULDBLOCK;
	    return 0;
	}

	/*
	 * Wait until something happens.
	 */
2064
2065
2066
2067
2068
2069
2070
2071


2072
2073
2074
2075

2076
2077
2078
2079
2080
2081
2082
     */

    if (infoPtr->flags & SOCKET_EOF) {
	return 0;
    }

    /*
     * Check if there is an async connect to terminate


     */

    if ( (infoPtr->flags & SOCKET_REENTER_PENDING)
	    && !WaitForConnect(infoPtr, errorCodePtr)) {

	return -1;
    }

    /*
     * No EOF, and it is connected, so try to read more from the socket. Note
     * that we clear the FD_READ bit because read events are level triggered
     * so a new event will be generated if there is still data available to be







|
>
>



|
>







2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
     */

    if (infoPtr->flags & SOCKET_EOF) {
	return 0;
    }

    /*
     * Check if there is an async connect running.
     * For blocking sockets terminate connect, otherwise do one step.
     * For a non blocking socket return EWOULDBLOCK if connect not terminated
     */

    if ( (infoPtr->flags & SOCKET_REENTER_PENDING)
	    && !WaitForConnect(infoPtr, errorCodePtr,
		! ( infoPtr->flags & TCP_ASYNC_SOCKET ))) {
	return -1;
    }

    /*
     * No EOF, and it is connected, so try to read more from the socket. Note
     * that we clear the FD_READ bit because read events are level triggered
     * so a new event will be generated if there is still data available to be
2192
2193
2194
2195
2196
2197
2198
2199


2200
2201
2202
2203

2204
2205
2206
2207
2208
2209
2210

    if (!SocketsEnabled()) {
	*errorCodePtr = EFAULT;
	return -1;
    }

    /*
     * Check if there is an async connect to terminate


     */

    if ( (infoPtr->flags & SOCKET_REENTER_PENDING)
	    && !WaitForConnect(infoPtr, errorCodePtr)) {

	return -1;
    }

    while (1) {
	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
		(WPARAM) UNSELECT, (LPARAM) infoPtr);








|
>
>



|
>







2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252

    if (!SocketsEnabled()) {
	*errorCodePtr = EFAULT;
	return -1;
    }

    /*
     * Check if there is an async connect running.
     * For blocking sockets terminate connect, otherwise do one step.
     * For a non blocking socket return EWOULDBLOCK if connect not terminated
     */

    if ( (infoPtr->flags & SOCKET_REENTER_PENDING)
	    && !WaitForConnect(infoPtr, errorCodePtr,
		! ( infoPtr->flags & TCP_ASYNC_SOCKET ))) {
	return -1;
    }

    while (1) {
	SendMessage(tsdPtr->hwnd, SOCKET_SELECT,
		(WPARAM) UNSELECT, (LPARAM) infoPtr);

2414
2415
2416
2417
2418
2419
2420
2421


2422
2423

2424

2425
2426











2427
2428
2429
2430

2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442


2443
2444
2445
2446
2447
2448
2449
    sock = infoPtr->sockets->fd;
    if (optionName != NULL) {
	len = strlen(optionName);
    }

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


	/*
	 * Check if an asyncroneous connect is running

	 * and return ok

	 */
	if (infoPtr->flags & SOCKET_REENTER_PENDING) {











	    err = 0;
	} else {
	    int optlen;
	    int ret;


	    optlen = sizeof(int);
	    ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
		    (char *)&err, &optlen);
	    if (ret == SOCKET_ERROR) {
		err = WSAGetLastError();
	    }
	}
	if (err) {
	    TclWinConvertError(err);
	    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
	}


	return TCL_OK;
    }

    if (interp != NULL && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
	reverseDNS = NI_NUMERICHOST;
    }








|
>
>
|
|
>
|
>
|
|
>
>
>
>
>
>
>
>
>
>
>
|



>






<
<
|
|
|
|
>
>







2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494


2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
    sock = infoPtr->sockets->fd;
    if (optionName != NULL) {
	len = strlen(optionName);
    }

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

	if ( (infoPtr->flags & SOCKET_REENTER_PENDING) ) {

	    /*
	     * Asyncroneous connect is running.
	     * Process it one step without blocking.
	     * Return its error or nothing if connect not
	     * terminated.
	     */

	    int errorCode;
	    if (!WaitForConnect(infoPtr, &errorCode, 0)
		    && errorCode != EWOULDBLOCK) {
		/* connect terminated with error */
		Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(errorCode), -1);
	    }
	} else if (infoPtr->connectError != 0) {
	    /*
	     * An async connect error was not jet reported.
	     */
	    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(infoPtr->connectError), -1);
	    infoPtr->connectError = 0;
	} else {
	    int optlen;
	    int ret;
    	    DWORD err;

	    optlen = sizeof(int);
	    ret = TclWinGetSockOpt(sock, SOL_SOCKET, SO_ERROR,
		    (char *)&err, &optlen);
	    if (ret == SOCKET_ERROR) {
		err = WSAGetLastError();


		if (err) {
		    TclWinConvertError(err);
		    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(Tcl_GetErrno()), -1);
		}
	    }
	}
	return TCL_OK;
    }

    if (interp != NULL && Tcl_GetVar(interp, SUPPRESS_RDNS_VAR, 0) != NULL) {
	reverseDNS = NI_NUMERICHOST;
    }

2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
FindFDInList(
    SocketInfo *infoPtr,
    SOCKET socket)
{
    TcpFdList *fds;
    for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
        #ifdef DEBUGGING
	fprintf(stderr,"socket = %d, fd=%d",socket,fds);
	#endif
	if (fds->fd == socket) {
	    return 1;
	}
    }
    return 0;
}







|







3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
FindFDInList(
    SocketInfo *infoPtr,
    SOCKET socket)
{
    TcpFdList *fds;
    for (fds = infoPtr->sockets; fds != NULL; fds = fds->next) {
        #ifdef DEBUGGING
	fprintf(stderr,"socket = %d, fd=%d\n",socket,fds);
	#endif
	if (fds->fd == socket) {
	    return 1;
	}
    }
    return 0;
}