Changes On Branch bug-b6d0d8cc2c
Not logged in

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

Changes In Branch bug-b6d0d8cc2c Excluding Merge-Ins

This is equivalent to a diff from ebdc28dd96 to b6d4029d2b

2020-04-27
11:08
small review Leaf check-in: b6d4029d2b user: sebres tags: bug-b6d0d8cc2c
10:45
fixes [b6d0d8cc2c]: on close firstly try graceful disconnect and don't linger if it succeeds (and pe... check-in: b960d1b71e user: sebres tags: bug-b6d0d8cc2c
2020-04-23
19:04
Argument conditions for Invalid() call were not always satisfied. check-in: 0200ddd3d4 user: dgp tags: core-8-5-branch
18:26
merge 8.5 check-in: d9f21300ed user: dgp tags: dgp-27944a3661
16:18
tests-perf/socket.perf.tcl: privides basic performance test construct Leaf check-in: eb479ac8f7 user: sebres tags: sock-perf-test
14:52
documentation: descibes the empty list creation (with reserved space) where objv is NULL, like Tcl_N... check-in: ebdc28dd96 user: sebres tags: core-8-5-branch
12:14
Fix [1004065]: UTF-8 encoding crashes in UCS-4 mode check-in: ed551cd16c user: jan.nijtmans tags: core-8-5-branch

Changes to win/tclWinSock.c.
155
156
157
158
159
160
161


162
163
164
165
166
167
168
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170







+
+








#define SOCKET_ASYNC		(1<<0)	/* The socket is in blocking mode. */
#define SOCKET_EOF		(1<<1)	/* A zero read happened on the
					 * socket. */
#define SOCKET_ASYNC_CONNECT	(1<<2)	/* This socket uses async connect. */
#define SOCKET_PENDING		(1<<3)	/* A message has been sent for this
					 * socket */
#define SOCKET_HSENT		(1<<5)	/* Socket had already sent data. */
#define SOCKET_HRECV		(1<<6)	/* Socket had already received data. */

typedef struct {
    HWND hwnd;			/* Handle to window for socket messages. */
    HANDLE socketThread;	/* Thread handling the window */
    Tcl_ThreadId threadId;	/* Parent thread. */
    HANDLE readyEvent;		/* Event indicating that a socket event is
				 * ready. Also used to indicate that the
800
801
802
803
804
805
806
807







808
809












































































810

811
812
813
814
815
816
817
818
819
820
821

822
823
824
825
826
827
828
829
802
803
804
805
806
807
808

809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
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
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893

894
895
896
897
898
899
900
901
902
903
904

905

906
907
908
909
910
911
912







-
+
+
+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+










-
+
-







     * WinSock ran before other exit handlers that want to use sockets.
     */

    if (SocketsEnabled()) {
	/*
	 * Clean up the OS socket handle. The default Windows setting for a
	 * socket is SO_DONTLINGER, which does a graceful shutdown in the
	 * background.
	 * background, this can cause a flood with pending sockets in TIME_WAIT
	 * state, so can result to exceeding of FD_SETSIZE, etc
	 * (see bug [b6d0d8cc2c]). So try do a graceful disconnect manually,
	 * if success don't linger otherwise lingering for 5 seconds, that should be
	 * enough per default to fullfil a shutdown (flush buffers etc).
	 * The port can remain longer in TIME_WAIT state, but windows could
	 * probably use it earlier in out of sockets situation.
	 */

	SOCKET s = infoPtr->socket;
	LINGER l;
	l.l_linger = 5; /* 0 would cause a hard reset, so use carefully */

	/* if server socket - simply close it */
	if (infoPtr->acceptProc) {
	    goto closeSocket;
	}
	/* if nothing was sent through this connection - execute a reset */
	if (!(infoPtr->flags & (SOCKET_HSENT|SOCKET_HRECV))) {
	    l.l_linger = 0; /* hard reset */
	    goto lingerSocket;
	}

	/* If FD_CLOSE has been not yet received */
	infoPtr->flags &= ~SOCKET_EOF;
	infoPtr->flags |= (infoPtr->readyEvents & FD_CLOSE) ? SOCKET_EOF : 0;
	if (!(infoPtr->flags & SOCKET_EOF)) {

	    WSAEVENT eventObj = WSACreateEvent();
	    int fdWrite = 0;

	    /* We'll made an attempt of graceful disconnect (if possible).
	     * Thereby FD_WRITE signaling that we'd avoid immediate disconnect. */
	    if ((infoPtr->flags & SOCKET_HSENT) && (infoPtr->readyEvents & FD_WRITE)) {
		fdWrite = FD_WRITE;
	    }
	    if (
		   WSAEventSelect(s, eventObj, (FD_CLOSE|fdWrite)) != SOCKET_ERROR
		&& (fdWrite || WSASendDisconnect(s, NULL) != SOCKET_ERROR)
	    ) {
		/* Wait a bit (1ms) for FD_CLOSE gets signalled (e. g. fast
		 * (local) connect or already pending FIN/RST from other peer) */
		while (WSAWaitForMultipleEvents(1, &eventObj, 0, 1, 0)
			== WSA_WAIT_EVENT_0
		) {

		    WSANETWORKEVENTS evv;
		    if (WSAEnumNetworkEvents(s, eventObj, &evv) == 0) {

			if (evv.lNetworkEvents & FD_CLOSE) {
			    infoPtr->flags |= SOCKET_EOF;
			    break;
			}
			if (fdWrite) {
			    /* send disconnect now (and reset it to stop repeat) */
			    fdWrite &= ~FD_WRITE;
			    /* don't need write anymore - send disconnect and repeat */
			    if (WSASendDisconnect(s, NULL) != SOCKET_ERROR) {
				continue;
			    }
			}
		    }
		    break;
		}
		(void) WSAEventSelect(s, NULL, 0);
	    }
	    WSACloseEvent(eventObj);
	}

	/* If attempt succeeded (noticed FD_CLOSE) */
	if (  (infoPtr->flags & SOCKET_EOF) 
	  || !(infoPtr->flags & SOCKET_HSENT)
	) { /* don't need lingering. */
	    int v = 0; /* the socket will not remain open */
	    setsockopt(s, SOL_SOCKET, SO_DONTLINGER,
			(const char *) &v, sizeof(v));
	} else {
lingerSocket:
	    l.l_onoff = 1; /* the socket will remain open for l_linger time */
	    setsockopt(s, SOL_SOCKET, SO_LINGER,
			(const char *) &l, sizeof(l));
	}

closeSocket:
	/* Now close it. */
	if (closesocket(infoPtr->socket) == SOCKET_ERROR) {
	if (closesocket(s) == SOCKET_ERROR) {
	    TclWinConvertWSAError((DWORD) WSAGetLastError());
	    errorCode = Tcl_GetErrno();
	}
    }

    /*
     * Clear an eventual tsd info list pointer.
     * This may be called, if an async socket connect fails or is closed
     * between connect and thread action callback.
     */
    if (tsdPtr->pendingSocketInfo != NULL
    if (tsdPtr->pendingSocketInfo == infoPtr) {
	    && tsdPtr->pendingSocketInfo == infoPtr) {

	/* get infoPtr lock, because this concerns the notifier thread */
	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);

	tsdPtr->pendingSocketInfo = NULL;

	/* Free list lock */
1245
1246
1247
1248
1249
1250
1251


1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
















1269
1270
1271
1272
1273
1274
1275
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352

1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375







+
+
















-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







                (LPARAM) infoPtr);
    
        SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
                (LPARAM) infoPtr);
    }

    while (1) {
	int rc;

	if (infoPtr->lastError) {
	    *errorCodePtr = infoPtr->lastError;
	    result = 0;
	    break;
	} else if (infoPtr->readyEvents & events) {
	    break;
	} else if (infoPtr->flags & SOCKET_ASYNC) {
	    *errorCodePtr = EWOULDBLOCK;
	    result = 0;
	    break;
	}

	/*
	 * Wait until something happens.
	 */

	WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
	rc = WaitForSingleObject(tsdPtr->readyEvent, INFINITE);
	if (rc) {
#if 0
	    if (rc == WAIT_TIMEOUT) {
		*errorCodePtr = ETIMEDOUT;
		result = 0;
		break;
	    }
#endif
	    if (rc == WAIT_FAILED) {
		TclWinConvertError(GetLastError());
		*errorCodePtr = infoPtr->lastError = errno;
		result = 0;
		break;
	    }
	}
    }

    (void) Tcl_SetServiceMode(oldMode);
    return result;
}

/*
1638
1639
1640
1641
1642
1643
1644
1645
1646


1647
1648
1649
1650
1651
1652
1653
1654
1655
1738
1739
1740
1741
1742
1743
1744


1745
1746


1747
1748
1749
1750
1751
1752
1753







-
-
+
+
-
-







	bytesRead = recv(infoPtr->socket, buf, toRead, 0);
	infoPtr->readyEvents &= ~(FD_READ);

	/*
	 * Check for end-of-file condition or successful read.
	 */

	if (bytesRead == 0) {
	    infoPtr->flags |= SOCKET_EOF;
	if (bytesRead != SOCKET_ERROR) {
	    infoPtr->flags |= (bytesRead == 0) ? SOCKET_EOF : SOCKET_HRECV;
	}
	if (bytesRead != SOCKET_ERROR) {
	    break;
	}

	/*
	 * If an error occurs after the FD_CLOSE has arrived, then ignore the
	 * error and report an EOF.
	 */
1755
1756
1757
1758
1759
1760
1761


1762
1763
1764
1765
1766
1767
1768
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868







+
+








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

	bytesWritten = send(infoPtr->socket, buf, toWrite, 0);
	if (bytesWritten != SOCKET_ERROR) {
	    /* Signal data sent to peer */
	    infoPtr->flags |= toWrite ? SOCKET_HSENT : 0;
	    /*
	     * Since Windows won't generate a new write event until we hit an
	     * overflow condition, we need to force the event loop to poll
	     * until the condition changes.
	     */

	    if (infoPtr->watchEvents & FD_WRITE) {
2346
2347
2348
2349
2350
2351
2352

2353
2354
2355
2356
2357
2358
2359
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460







+







	 * Check if there is a pending info structure not jet in the
	 * list
	 */
	if ( !info_found
		&& tsdPtr->pendingSocketInfo != NULL
		&& tsdPtr->pendingSocketInfo->socket ==socket ) {
	    infoPtr = tsdPtr->pendingSocketInfo;
	    tsdPtr->pendingSocketInfo = NULL;
	    info_found = 1;
	}
	if (info_found) {

	    /*
	     * Update the socket state.
	     *