]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix hang when deleting a pipe process
authorEli Zaretskii <eliz@gnu.org>
Sat, 11 Dec 2021 18:15:53 +0000 (20:15 +0200)
committerEli Zaretskii <eliz@gnu.org>
Sat, 11 Dec 2021 18:15:53 +0000 (20:15 +0200)
* src/w32.h (FILE_DONT_CLOSE): New flag.
* src/w32.c (sys_close): Don't close descriptors used to read from
the pipe process.  Leave the FILE_DONT_CLOSE flag set in the
descriptor's info.
(register_aux_fd): Set the FILE_DONT_CLOSE flag in the
descriptor's info.
* src/w32proc.c (reader_thread): When exiting normally, close the
file descriptor used to read from a pipe process.  (Bug#52414)

src/w32.c
src/w32.h
src/w32proc.c

index 2b2f8aadf6ba123f8e7b2aec2e042b21d88dd185..1de148f0343d5eacea36e7875d8f14e702ae0b77 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -8548,7 +8548,7 @@ fcntl (int s, int cmd, int options)
 int
 sys_close (int fd)
 {
-  int rc;
+  int rc = -1;
 
   if (fd < 0)
     {
@@ -8603,14 +8603,31 @@ sys_close (int fd)
        }
     }
 
-  if (fd >= 0 && fd < MAXDESC)
-    fd_info[fd].flags = 0;
-
   /* Note that sockets do not need special treatment here (at least on
      NT and Windows 95 using the standard tcp/ip stacks) - it appears that
      closesocket is equivalent to CloseHandle, which is to be expected
      because socket handles are fully fledged kernel handles. */
-  rc = _close (fd);
+  if (fd < MAXDESC)
+    {
+      if ((fd_info[fd].flags & FILE_DONT_CLOSE) == 0)
+       {
+         fd_info[fd].flags = 0;
+         rc = _close (fd);
+       }
+      else
+       {
+         /* We don't close here descriptors open by pipe processes
+            for reading from the pipe, because the reader thread
+            might be stuck in _sys_read_ahead, and then we will hang
+            here.  If the reader thread exits normally, it will close
+            the descriptor; otherwise we will leave a zombie thread
+            hanging around.  */
+         rc = 0;
+         /* Leave the flag set for the reader thread to close the
+            descriptor.  */
+         fd_info[fd].flags = FILE_DONT_CLOSE;
+       }
+    }
 
   return rc;
 }
@@ -10898,6 +10915,7 @@ register_aux_fd (int infd)
     }
   fd_info[ infd ].cp = cp;
   fd_info[ infd ].hnd = (HANDLE) _get_osfhandle (infd);
+  fd_info[ infd ].flags |= FILE_DONT_CLOSE;
 }
 
 #ifdef HAVE_GNUTLS
index b31d66646c9605c60e89652bd8a98e3c127053c6..bb3ec40324aed86a72aaf805637e71ff447d5858 100644 (file)
--- a/src/w32.h
+++ b/src/w32.h
@@ -135,6 +135,7 @@ extern filedesc fd_info [ MAXDESC ];
 #define FILE_SOCKET             0x0200
 #define FILE_NDELAY             0x0400
 #define FILE_SERIAL             0x0800
+#define FILE_DONT_CLOSE         0x1000
 
 extern child_process * new_child (void);
 extern void delete_child (child_process *cp);
index 360f45e9e11d9e5a1db90bc43fa780c0a53a516e..bfe720eb62302af10c8b39550ac464c8881cfb98 100644 (file)
@@ -1206,6 +1206,7 @@ static DWORD WINAPI
 reader_thread (void *arg)
 {
   child_process *cp;
+  int fd;
 
   /* Our identity */
   cp = (child_process *)arg;
@@ -1220,12 +1221,13 @@ reader_thread (void *arg)
     {
       int rc;
 
-      if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_CONNECT) != 0)
-       rc = _sys_wait_connect (cp->fd);
-      else if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_LISTEN) != 0)
-       rc = _sys_wait_accept (cp->fd);
+      fd = cp->fd;
+      if (fd >= 0 && (fd_info[fd].flags & FILE_CONNECT) != 0)
+       rc = _sys_wait_connect (fd);
+      else if (fd >= 0 && (fd_info[fd].flags & FILE_LISTEN) != 0)
+       rc = _sys_wait_accept (fd);
       else
-       rc = _sys_read_ahead (cp->fd);
+       rc = _sys_read_ahead (fd);
 
       /* Don't bother waiting for the event if we already have been
         told to exit by delete_child.  */
@@ -1238,7 +1240,7 @@ reader_thread (void *arg)
         {
          DebPrint (("reader_thread.SetEvent(0x%x) failed with %lu for fd %ld (PID %d)\n",
                     (DWORD_PTR)cp->char_avail, GetLastError (),
-                    cp->fd, cp->pid));
+                    fd, cp->pid));
          return 1;
        }
 
@@ -1266,6 +1268,13 @@ reader_thread (void *arg)
       if (cp->status == STATUS_READ_ERROR)
        break;
     }
+  /* If this thread was reading from a pipe process, close the
+     descriptor used for reading, as sys_close doesn't in that case.  */
+  if (fd_info[fd].flags == FILE_DONT_CLOSE)
+    {
+      fd_info[fd].flags = 0;
+      _close (fd);
+    }
   return 0;
 }