Fix hang in socket closing.
authorWerner Koch <wk@gnupg.org>
Thu, 25 Apr 2013 11:00:16 +0000 (12:00 +0100)
committerWerner Koch <wk@gnupg.org>
Wed, 8 May 2013 18:38:50 +0000 (20:38 +0200)
* src/w32-io.c (destroy_reader): Call shutdown.
(reader): Do not print an error in the shutdown case.

src/w32-io.c

index 164205e..776e379 100644 (file)
@@ -316,6 +316,21 @@ reader (void *arg)
                 }
               else
                 {
+                  /* Check whether the shutdown triggered the error -
+                     no need to to print a warning in this case.  */
+                  if ( ctx->error_code == WSAECONNABORTED
+                       || ctx->error_code == WSAECONNRESET)
+                    {
+                      LOCK (ctx->mutex);
+                      if (ctx->stop_me)
+                        {
+                          UNLOCK (ctx->mutex);
+                          TRACE_LOG ("got shutdown");
+                          break;
+                        }
+                      UNLOCK (ctx->mutex);
+                    }
+
                   ctx->error = 1;
                   TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
                 }
@@ -357,6 +372,7 @@ reader (void *arg)
          UNLOCK (ctx->mutex);
          break;
         }
+
       TRACE_LOG1 ("got %u bytes", nread);
 
       ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
@@ -495,6 +511,26 @@ destroy_reader (struct reader_context_s *ctx)
     }
 #endif
 
+  /* The reader thread is usually blocking in recv or ReadFile.  If
+     the peer does not send an EOF or breaks the pipe the WFSO might
+     get stuck waiting for the termination of the reader thread.  This
+     happens quite often with sockets, thus we definitely need to get
+     out of the recv.  A shutdown does this nicely.  For handles
+     (i.e. pipes) it would also be nice to cancel the operation, but
+     such a feature is only available since Vista.  Thus we need to
+     dlopen that syscall.  */
+  if (ctx->file_hd != INVALID_HANDLE_VALUE)
+    {
+      /* Fixme: Call CancelSynchronousIo (handle_of_thread).  */
+    }
+  else if (ctx->file_sock != INVALID_SOCKET)
+    {
+      if (shutdown (ctx->file_sock, 2))
+        TRACE2 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+                "shutdown socket %d failed: %s",
+                ctx->file_sock, (int) WSAGetLastError ());
+    }
+
   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
          "waiting for termination of thread %p", ctx->thread_hd);
   WaitForSingleObject (ctx->stopped, INFINITE);