]> code.delx.au - gnu-emacs/commitdiff
Make piping to subprocesses more robust on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Tue, 12 Jan 2016 16:41:58 +0000 (18:41 +0200)
committerEli Zaretskii <eliz@gnu.org>
Tue, 12 Jan 2016 16:41:58 +0000 (18:41 +0200)
* src/w32.c (sys_write): Don't write to a pipe more stuff than its
buffer can hold.  Don't return -1 if something has been written to
the pipe.  Zero out 'errno' before calling '_write', to avoid
returning a stale value.  (Bug#22344)
* src/w32proc.c (syms_of_ntproc) <w32-pipe-buffer-size>: New variable.
* src/w32.c (pipe2): Use it to request a user-defined size for the
pipe being created.

* etc/NEWS: Mention 'w32-pipe-buffer-size'.

* doc/emacs/msdos.texi (Windows Processes): Document
'w32-pipe-buffer-size'.

doc/emacs/msdos.texi
etc/NEWS
src/w32.c
src/w32proc.c

index ea8a24d1cf7247f4713d9a1c04023d2199da4716..6ad12d646a1cd32e9511df00f0ad8871c15b318d 100644 (file)
@@ -655,7 +655,7 @@ and the right button generates @kbd{mouse-3} events.  If this variable
 is non-@code{nil}, the roles of these two buttons are reversed.
 
 @node Windows Processes
-@section Subprocesses on Windows 9X/ME and Windows NT/2K/XP
+@section Subprocesses on Windows 9X/ME and Windows NT/2K/XP/Vista/7/8/10
 @cindex subprocesses on MS-Windows
 
 @cindex DOS applications, running from Emacs
@@ -663,7 +663,8 @@ is non-@code{nil}, the roles of these two buttons are reversed.
 version) includes full support for asynchronous subprocesses.
 In the Windows version, synchronous and asynchronous subprocesses work
 fine on both
-Windows 9X/ME and Windows NT/2K/XP as long as you run only 32-bit Windows
+Windows 9X/ME and Windows NT/2K/XP/Vista/7/8/10 as long as you run
+only 32-bit or 64-bit Windows
 applications.  However, when you run a DOS application in a subprocess,
 you may encounter problems or be unable to run the application at all;
 and if you run two DOS applications at the same time in two
@@ -713,6 +714,15 @@ character.  If the value is a character, Emacs uses that character to escape
 any quote characters that appear; otherwise it chooses a suitable escape
 character based on the type of the program.
 
+@vindex w32-pipe-buffer-size
+  The variable @code{w32-pipe-buffer-size} controls the size of the
+buffer Emacs requests from the system when it creates pipes for
+communications with subprocesses.  The default value is zero, which
+lets the OS choose the size.  Any valid positive value will request a
+buffer of that size in bytes.  This can be used to tailor
+communications with subprocesses to programs that exhibit unusual
+behavior with respect to buffering pipe I/O.
+
 @ifnottex
 @findex w32-shell-execute
   The function @code{w32-shell-execute} can be useful for writing
index 85ec30ac033b843890f614ae2de4d21f5ef679ab..10fcb7e3fd49d2a0023cbe5e87ddb4783272d9f3 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1762,6 +1762,12 @@ this has no effect.
 ** The new function 'w32-application-type' returns the type of an
 MS-Windows application given the name of its executable program file.
 
+** New variable `w32-pipe-buffer-size'.
+It can be used to tune the size of the buffer of pipes created for
+communicating with subprocesses, when the program run by a subprocess
+exhibits unusual buffering behavior.  Default is zero, which lets the
+OS use its default size.
+
 \f
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
index 4770718f5e3009c7dd777335e9f6ef33ba9cc35c..ea3a9dafad5d48bb5bc313160ba55af9d834971b 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -8043,14 +8043,19 @@ pipe2 (int * phandles, int pipe2_flags)
 {
   int rc;
   unsigned flags;
+  unsigned pipe_size = 0;
 
   eassert (pipe2_flags == (O_BINARY | O_CLOEXEC));
 
+  /* Allow Lisp to override the default buffer size of the pipe.  */
+  if (w32_pipe_buffer_size > 0 && w32_pipe_buffer_size < UINT_MAX)
+    pipe_size = w32_pipe_buffer_size;
+
   /* make pipe handles non-inheritable; when we spawn a child, we
      replace the relevant handle with an inheritable one.  Also put
      pipes into binary mode; we will do text mode translation ourselves
      if required.  */
-  rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
+  rc = _pipe (phandles, pipe_size, _O_NOINHERIT | _O_BINARY);
 
   if (rc == 0)
     {
@@ -8632,15 +8637,35 @@ sys_write (int fd, const void * buffer, unsigned int count)
         http://thread.gmane.org/gmane.comp.version-control.git/145294
         in the git mailing list.  */
       const unsigned char *p = buffer;
-      const unsigned chunk = 30 * 1024 * 1024;
+      const bool is_pipe = (fd < MAXDESC
+                           && ((fd_info[fd].flags & (FILE_PIPE | FILE_NDELAY))
+                               == (FILE_PIPE | FILE_NDELAY)));
+      /* Some programs, notably Node.js's node.exe, seem to never
+        completely empty the pipe, so writing more than the size of
+        the pipe's buffer always returns ENOSPC, and we loop forever
+        between send_process and here.  As a workaround, write no
+        more than the pipe's buffer can hold.  */
+      DWORD pipe_buffer_size;
+      if (is_pipe)
+       {
+         if (!GetNamedPipeInfo ((HANDLE)_get_osfhandle (fd),
+                               NULL, &pipe_buffer_size, NULL, NULL))
+           {
+             DebPrint (("GetNamedPipeInfo: error %u\n", GetLastError ()));
+             pipe_buffer_size = 4096;
+           }
+       }
+      const unsigned chunk = is_pipe ? pipe_buffer_size : 30 * 1024 * 1024;
 
       nchars = 0;
+      errno = 0;
       while (count > 0)
        {
          unsigned this_chunk = count < chunk ? count : chunk;
          int n = _write (fd, p, this_chunk);
 
-         nchars += n;
+         if (n > 0)
+           nchars += n;
          if (n < 0)
            {
              /* When there's no buffer space in a pipe that is in the
@@ -8654,12 +8679,10 @@ sys_write (int fd, const void * buffer, unsigned int count)
                 avoiding deadlock whereby each side of the pipe is
                 blocked on write, waiting for the other party to read
                 its end of the pipe.  */
-             if (errno == ENOSPC
-                 && fd < MAXDESC
-                 && ((fd_info[fd].flags & (FILE_PIPE | FILE_NDELAY))
-                     == (FILE_PIPE | FILE_NDELAY)))
+             if (errno == ENOSPC && is_pipe)
                errno = EAGAIN;
-             nchars = n;
+             if (nchars == 0)
+               nchars = -1;
              break;
            }
          else if (n < this_chunk)
index a65f085fb3d9e9a75856d5ca7356824b134b665d..a89a98504660422ea413ffb39186cdc0d4691193 100644 (file)
@@ -3702,6 +3702,13 @@ of time slices to wait (effectively boosting the priority of the child
 process temporarily).  A value of zero disables waiting entirely.  */);
   w32_pipe_read_delay = 50;
 
+  DEFVAR_INT ("w32-pipe-buffer-size", w32_pipe_buffer_size,
+             doc: /* Size of buffer for pipes created to communicate with subprocesses.
+The size is in bytes, and must be non-negative.  The default is zero,
+which lets the OS use its default size, usually 4KB (4096 bytes).
+Any negative value means to use the default value of zero.  */);
+  w32_pipe_buffer_size = 0;
+
   DEFVAR_LISP ("w32-downcase-file-names", Vw32_downcase_file_names,
               doc: /* Non-nil means convert all-upper case file names to lower case.
 This applies when performing completions and file name expansion.