Fix infinite sleep and failes of send in Win32.
authorTeodor Sigaev
Fri, 13 Oct 2006 13:59:47 +0000 (13:59 +0000)
committerTeodor Sigaev
Fri, 13 Oct 2006 13:59:47 +0000 (13:59 +0000)
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

index 47bb0d9cb855b5f6372eb94903d54efd5011a033..6489c64daee5942e6d26499c4c7da7b9a0262a15 100644 (file)
@@ -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;
 }