From 47df6e7e2d754c6330e85d247987352f0a25e60a Mon Sep 17 00:00:00 2001
From: Teodor Sigaev <teodor@sigaev.ru>
Date: Fri, 13 Oct 2006 13:59:47 +0000
Subject: [PATCH] Fix infinite sleep and failes of send in Win32.

1) pgwin32_waitforsinglesocket(): WaitForMultipleObjectsEx now called with
finite timeout (100ms) in case of FP_WRITE and UDP socket. If timeout occurs
then pgwin32_waitforsinglesocket() tries to write empty packet goes to
WaitForMultipleObjectsEx again.

2) pgwin32_send(): add loop around WSASend and pgwin32_waitforsinglesocket().
The reason is: for overlapped socket, 'ok' result from
pgwin32_waitforsinglesocket() isn't guarantee that socket is still free,
it can become busy again and following WSASend call will fail with
WSAEWOULDBLOCK error.

See http://archives.postgresql.org/pgsql-hackers/2006-10/msg00561.php
---
 src/backend/port/win32/socket.c | 106 ++++++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 25 deletions(-)

diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index 47bb0d9cb85..6489c64daee 100644
--- a/src/backend/port/win32/socket.c
+++ b/src/backend/port/win32/socket.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.13 2006/10/04 00:29:56 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.14 2006/10/13 13:59:47 teodor Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,11 +102,23 @@ pgwin32_poll_signals(void)
 	return 0;
 }
 
+static int
+isDataGram(SOCKET s) {
+	int type;
+	int typelen = sizeof(type);
+
+	if ( getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &typelen) )
+		return 1;
+
+	return ( type == SOCK_DGRAM ) ? 1 : 0;
+}
+
 int
 pgwin32_waitforsinglesocket(SOCKET s, int what)
 {
 	static HANDLE waitevent = INVALID_HANDLE_VALUE;
 	static SOCKET current_socket = -1;
+	static int    isUDP = 0;
 	HANDLE		events[2];
 	int			r;
 
@@ -127,8 +139,12 @@ pgwin32_waitforsinglesocket(SOCKET s, int what)
 	 * socket from a previous call
 	 */
 
-	if (current_socket != s && current_socket != -1)
-		WSAEventSelect(current_socket, waitevent, 0);
+	if (current_socket != s) 
+	{
+		if ( current_socket != -1 )
+			WSAEventSelect(current_socket, waitevent, 0);
+		isUDP = isDataGram(s);
+	}
 
 	current_socket = s;
 
@@ -140,7 +156,46 @@ pgwin32_waitforsinglesocket(SOCKET s, int what)
 
 	events[0] = pgwin32_signal_event;
 	events[1] = waitevent;
-	r = WaitForMultipleObjectsEx(2, events, FALSE, INFINITE, TRUE);
+
+	/* 
+	 * Just a workaround of unknown locking problem with writing
+	 * in UDP socket under high load: 
+	 * Client's pgsql backend sleeps infinitely in 
+	 * WaitForMultipleObjectsEx, pgstat process sleeps in 
+	 * pgwin32_select().  So, we will wait with small 
+	 * timeout(0.1 sec) and if sockect is still blocked, 
+	 * try WSASend (see comments in pgwin32_select) and wait again.
+	 */
+	if ((what & FD_WRITE) && isUDP)
+	{
+		for(;;)
+		{
+			r = WaitForMultipleObjectsEx(2, events, FALSE, 100, TRUE);
+
+			if ( r == WAIT_TIMEOUT )
+			{
+				char        c;
+				WSABUF      buf;
+				DWORD       sent;
+
+				buf.buf = &c;
+				buf.len = 0;
+
+				r = WSASend(s, &buf, 1, &sent, 0, NULL, NULL);
+				if (r == 0)         /* Completed - means things are fine! */
+					return 1;
+				else if ( WSAGetLastError() != WSAEWOULDBLOCK )
+				{
+					TranslateSocketError();
+					return 0;
+				}
+			}
+			else
+				break;
+		}
+	}
+	else
+		r = WaitForMultipleObjectsEx(2, events, FALSE, INFINITE, TRUE);
 
 	if (r == WAIT_OBJECT_0 || r == WAIT_IO_COMPLETION)
 	{
@@ -280,30 +335,31 @@ pgwin32_send(SOCKET s, char *buf, int len, int flags)
 	wbuf.len = len;
 	wbuf.buf = buf;
 
-	r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL);
-	if (r != SOCKET_ERROR && b > 0)
-		/* Write succeeded right away */
-		return b;
-
-	if (r == SOCKET_ERROR &&
-		WSAGetLastError() != WSAEWOULDBLOCK)
-	{
-		TranslateSocketError();
-		return -1;
-	}
-
-	/* No error, zero bytes (win2000+) or error+WSAEWOULDBLOCK (<=nt4) */
+	/*
+	 * Readiness of socket to send data to UDP socket 
+	 * may be not true: socket can become busy again! So loop
+	 * until send or error occurs.
+	 */
+	for(;;) {
+		r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL);
+		if (r != SOCKET_ERROR && b > 0)
+			/* Write succeeded right away */
+			return b;
+
+		if (r == SOCKET_ERROR &&
+			WSAGetLastError() != WSAEWOULDBLOCK)
+		{
+			TranslateSocketError();
+			return -1;
+		}
 
-	if (pgwin32_waitforsinglesocket(s, FD_WRITE | FD_CLOSE) == 0)
-		return -1;
+		/* No error, zero bytes (win2000+) or error+WSAEWOULDBLOCK (<=nt4) */
 
-	r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL);
-	if (r == SOCKET_ERROR)
-	{
-		TranslateSocketError();
-		return -1;
+		if (pgwin32_waitforsinglesocket(s, FD_WRITE | FD_CLOSE) == 0)
+			return -1;
 	}
-	return b;
+
+	return -1;
 }
 
 
-- 
GitLab