OSDN Git Service

Christopher Faylor <cgf@timesys.com>
authorcgf <cgf>
Fri, 3 Sep 2004 01:32:02 +0000 (01:32 +0000)
committercgf <cgf>
Fri, 3 Sep 2004 01:32:02 +0000 (01:32 +0000)
* autoload.cc (NtQueryInformationFile): Return nonzero on error.
* ntdll.h (FILE_PIPE_LOCAL_INFORMATION): Add.
(NtQueryInformationFile): Fix types for last two arguments.
* pipe.cc: Include stdlib.h, limits.h, and ntdll.h.
(create_selectable_pipe): New function to create a pipe that can be used with
NtQueryInformationFile for select.
(fhandler_pipe::create): Call create_selectable_pipe instead of CreatePipe.
(pipe): Use DEFAULT_PIPEBUFSIZE as argument to create_pipe.
* select.cc: Include limits.h and ntdll.h.
(peek_pipe): Add select_printf output.  Call NtQueryInformationFile to
implement select for write on pipes.
(fhandler_pipe::select_read): Reorder field assignments to be consistent with
fhandler_pipe::select_write.
(fhandler_pipe::select_write): Initialize startup, verify, cleanup, and
write_ready fields for select_record.
(fhandler_pipe::select_except): Tweak indentation to be consistent with
fhandler_pipe::select_write.

winsup/cygwin/ChangeLog
winsup/cygwin/autoload.cc
winsup/cygwin/ntdll.h
winsup/cygwin/pipe.cc
winsup/cygwin/select.cc

index 178ffc6..af0c5f1 100644 (file)
@@ -1,3 +1,25 @@
+2004-09-02  Bob Byrnes  <byrnes@curl.com>
+           Christopher Faylor  <cgf@timesys.com>
+
+       * autoload.cc (NtQueryInformationFile): Return nonzero on error.
+       * ntdll.h (FILE_PIPE_LOCAL_INFORMATION): Add.
+       (NtQueryInformationFile): Fix types for last two arguments.
+       * pipe.cc: Include stdlib.h, limits.h, and ntdll.h.
+       (create_selectable_pipe): New function to create a pipe that can be
+       used with NtQueryInformationFile for select.
+       (fhandler_pipe::create): Call create_selectable_pipe instead of
+       CreatePipe.
+       (pipe): Use DEFAULT_PIPEBUFSIZE as argument to create_pipe.
+       * select.cc: Include limits.h and ntdll.h.
+       (peek_pipe): Add select_printf output.  Call NtQueryInformationFile to
+       implement select for write on pipes.
+       (fhandler_pipe::select_read): Reorder field assignments to be
+       consistent with fhandler_pipe::select_write.
+       (fhandler_pipe::select_write): Initialize startup, verify, cleanup, and
+       write_ready fields for select_record.
+       (fhandler_pipe::select_except): Tweak indentation to be consistent with
+       fhandler_pipe::select_write.
+
 2004-08-30  Pierre Humblet <pierre.humblet@ieee.org>
 
        * fork.cc (fork_parent): Return the cygpid directly derived from the
index 7b1a569..e7de842 100644 (file)
@@ -379,7 +379,7 @@ LoadDLLfuncEx (NtCreateToken, 52, ntdll, 1)
 LoadDLLfuncEx (NtMapViewOfSection, 40, ntdll, 1)
 LoadDLLfuncEx (NtOpenFile, 24, ntdll, 1)
 LoadDLLfuncEx (NtOpenSection, 12, ntdll, 1)
-LoadDLLfuncEx (NtQueryInformationFile, 20, ntdll, 1)
+LoadDLLfuncEx2 (NtQueryInformationFile, 20, ntdll, 1, 1)
 LoadDLLfuncEx (NtQueryInformationProcess, 20, ntdll, 1)
 LoadDLLfuncEx2 (NtQueryObject, 20, ntdll, 1, 1)
 LoadDLLfuncEx (NtQuerySystemInformation, 16, ntdll, 1)
index 8e829f5..23ce468 100644 (file)
@@ -358,6 +358,20 @@ typedef struct _FILE_NAME_INFORMATION
   WCHAR FileName[MAX_PATH + 100];
 } FILE_NAME_INFORMATION;
 
+typedef struct _FILE_PIPE_LOCAL_INFORMATION
+{
+  ULONG NamedPipeType;
+  ULONG NamedPipeConfiguration;
+  ULONG MaximumInstances;
+  ULONG CurrentInstances;
+  ULONG InboundQuota;
+  ULONG ReadDataAvailable;
+  ULONG OutboundQuota;
+  ULONG WriteQuotaAvailable;
+  ULONG NamedPipeState;
+  ULONG NamedPipeEnd;
+} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
+
 typedef struct _FILE_COMPRESSION_INFORMATION
 {
   LARGE_INTEGER CompressedSize;
@@ -369,6 +383,7 @@ typedef struct _FILE_COMPRESSION_INFORMATION
 
 typedef enum _FILE_INFORMATION_CLASS
 {
+  FilePipeLocalInformation = 24,
   FileCompressionInformation = 28
 } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
 
@@ -404,7 +419,7 @@ extern "C"
                             PIO_STATUS_BLOCK, ULONG, ULONG);
   NTSTATUS NTAPI NtOpenSection (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
   NTSTATUS NTAPI NtQueryInformationFile (HANDLE, IO_STATUS_BLOCK *, VOID *,
-                                        DWORD, DWORD);
+                                        ULONG, FILE_INFORMATION_CLASS);
   NTSTATUS NTAPI NtQueryInformationProcess (HANDLE, PROCESSINFOCLASS,
                                            PVOID, ULONG, PULONG);
   NTSTATUS NTAPI NtQueryObject (HANDLE, OBJECT_INFORMATION_CLASS, VOID *,
index 19fdba0..2a0095f 100644 (file)
@@ -12,7 +12,9 @@ details. */
 
 #include "winsup.h"
 #include <unistd.h>
+#include <stdlib.h>
 #include <sys/socket.h>
+#include <limits.h>
 #include "cygerrno.h"
 #include "security.h"
 #include "path.h"
@@ -22,6 +24,7 @@ details. */
 #include "thread.h"
 #include "pinfo.h"
 #include "cygthread.h"
+#include "ntdll.h"
 
 static unsigned pipecount;
 static const NO_COPY char pipeid_fmt[] = "stupid_pipe.%u.%u";
@@ -211,15 +214,139 @@ leave:
   return res;
 }
 
+/* Create a pipe, and return handles to the read and write ends,
+   just like CreatePipe, but ensure that the write end permits
+   FILE_READ_ATTRIBUTES access, on later versions of win32 where
+   this is supported.  This access is needed by NtQueryInformationFile,
+   which is used to implement select and nonblocking writes.
+   Note that the return value is either NO_ERROR or GetLastError,
+   unlike CreatePipe, which returns a bool for success or failure.  */
+static int
+create_selectable_pipe (PHANDLE read_pipe_ptr,
+                        PHANDLE write_pipe_ptr,
+                        LPSECURITY_ATTRIBUTES sa_ptr,
+                        DWORD psize)
+{
+  /* Default to error. */
+  *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
+
+  HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
+
+  /* Ensure that there is enough pipe buffer space for atomic writes.  */
+  if (psize < PIPE_BUF)
+    psize = PIPE_BUF;
+
+  char pipename[CYG_MAX_PATH];
+
+  /* Retry CreateNamedPipe as long as the pipe name is in use.
+     Retrying will probably never be necessary, but we want
+     to be as robust as possible.  */
+  while (1)
+    {
+      static volatile LONG pipe_unique_id;
+
+      __small_sprintf (pipename, "\\\\.\\pipe\\cygwin-%d-%ld", myself->pid,
+                      InterlockedIncrement ((LONG *) &pipe_unique_id));
+
+      debug_printf ("CreateNamedPipe: name %s, size %lu", pipename, psize);
+
+      /* Use CreateNamedPipe instead of CreatePipe, because the latter
+         returns a write handle that does not permit FILE_READ_ATTRIBUTES
+         access, on versions of win32 earlier than WinXP SP2.
+         CreatePipe also stupidly creates a full duplex pipe, which is
+         a waste, since only a single direction is actually used.
+         It's important to only allow a single instance, to ensure that
+         the pipe was not created earlier by some other process, even if
+         the pid has been reused.  We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
+         because that is only available for Win2k SP2 and WinXP.  */
+      read_pipe = CreateNamedPipe (pipename,
+                                   PIPE_ACCESS_INBOUND,
+                                   PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+                                   1,       /* max instances */
+                                   psize,   /* output buffer size */
+                                   psize,   /* input buffer size */
+                                   NMPWAIT_USE_DEFAULT_WAIT,
+                                   sa_ptr);
+
+      if (read_pipe != INVALID_HANDLE_VALUE)
+        {
+          debug_printf ("pipe read handle %p", read_pipe);
+          break;
+        }
+
+      DWORD err = GetLastError ();
+      switch (err)
+        {
+        case ERROR_PIPE_BUSY:
+          /* The pipe is already open with compatible parameters.
+             Pick a new name and retry.  */
+          debug_printf ("pipe busy, retrying");
+          continue;
+        case ERROR_ACCESS_DENIED:
+          /* The pipe is already open with incompatible parameters.
+             Pick a new name and retry.  */
+          debug_printf ("pipe access denied, retrying");
+          continue;
+        case ERROR_CALL_NOT_IMPLEMENTED:
+          /* We are on an older Win9x platform without named pipes.
+             Return an anonymous pipe as the best approximation.  */
+          debug_printf ("CreateNamedPipe not implemented, resorting to "
+                        "CreatePipe size %lu", psize);
+          if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
+            {
+              debug_printf ("pipe read handle %p", *read_pipe_ptr);
+              debug_printf ("pipe write handle %p", *write_pipe_ptr);
+              return NO_ERROR;
+            }
+          err = GetLastError ();
+          debug_printf ("CreatePipe failed, %E");
+          return err;
+        default:
+          debug_printf ("CreateNamedPipe failed, %E");
+          return err;
+        }
+      /* NOTREACHED */
+    }
+
+  debug_printf ("CreateFile: name %s", pipename);
+
+  /* Open the named pipe for writing.
+     Be sure to permit FILE_READ_ATTRIBUTES access.  */
+  write_pipe = CreateFile (pipename,
+                           GENERIC_WRITE | FILE_READ_ATTRIBUTES,
+                           0,       /* share mode */
+                           sa_ptr,
+                           OPEN_EXISTING,
+                           0,       /* flags and attributes */
+                           0);      /* handle to template file */
+
+  if (write_pipe == INVALID_HANDLE_VALUE)
+    {
+      /* Failure. */
+      DWORD err = GetLastError ();
+      debug_printf ("CreateFile failed, %E");
+      CloseHandle (read_pipe);
+      return err;
+    }
+
+  debug_printf ("pipe write handle %p", write_pipe);
+
+  /* Success. */
+  *read_pipe_ptr = read_pipe;
+  *write_pipe_ptr = write_pipe;
+  return NO_ERROR;
+}
+
 int
 fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode, bool fifo)
 {
   HANDLE r, w;
   SECURITY_ATTRIBUTES *sa = (mode & O_NOINHERIT) ?  &sec_none_nih : &sec_none;
   int res = -1;
+  int ret;
 
-  if (!CreatePipe (&r, &w, sa, psize))
-    __seterrno ();
+  if ((ret = create_selectable_pipe (&r, &w, sa, psize)) != NO_ERROR)
+    __seterrno_from_win_error (ret);
   else
     {
       fhs[0] = (fhandler_pipe *) build_fh_dev (*piper_dev);
@@ -282,13 +409,16 @@ fhandler_pipe::ioctl (unsigned int cmd, void *p)
   return 0;
 }
 
+#define DEFAULT_PIPEBUFSIZE (4 * PIPE_BUF)
+
 extern "C" int
 pipe (int filedes[2])
 {
   extern DWORD binmode;
   fhandler_pipe *fhs[2];
-  int res = fhandler_pipe::create (fhs, 16384, (!binmode || binmode == O_BINARY)
-                                              ? O_BINARY : O_TEXT);
+  int res = fhandler_pipe::create (fhs, DEFAULT_PIPEBUFSIZE,
+                                  (!binmode || binmode == O_BINARY)
+                                  ? O_BINARY : O_TEXT);
   if (res == 0)
     {
       cygheap_fdnew fdin;
index b244469..e2d11a4 100644 (file)
@@ -28,7 +28,7 @@ details. */
 #include <winuser.h>
 #include <netdb.h>
 #include <unistd.h>
-#include <stdio.h>
+#include <limits.h>
 #define USE_SYS_TYPES_FD_SET
 #include <winsock.h>
 #include "select.h"
@@ -42,6 +42,7 @@ details. */
 #include "perthread.h"
 #include "tty.h"
 #include "cygthread.h"
+#include "ntdll.h"
 
 /*
  * All these defines below should be in sys/types.h
@@ -419,7 +420,7 @@ peek_pipe (select_record *s, bool from_select)
     {
       if (s->read_ready)
        {
-         select_printf ("already ready");
+         select_printf ("%s, already ready for read", fh->get_name ());
          gotone = 1;
          goto out;
        }
@@ -451,7 +452,8 @@ peek_pipe (select_record *s, bool from_select)
     }
 
   if (fh->get_device () == FH_PIPEW)
-    /* nothing */;
+    select_printf ("%s, select for read/except on write end of pipe",
+                  fh->get_name ());
   else if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL))
     {
       select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ());
@@ -489,21 +491,82 @@ peek_pipe (select_record *s, bool from_select)
     }
   if (n > 0 && s->read_selected)
     {
-      select_printf ("%s, ready for read", fh->get_name ());
+      select_printf ("%s, ready for read: avail %d", fh->get_name (), n);
       gotone += s->read_ready = true;
     }
   if (!gotone && s->fh->hit_eof ())
     {
       select_printf ("%s, saw EOF", fh->get_name ());
       if (s->except_selected)
-       gotone = s->except_ready = true;
+       gotone += s->except_ready = true;
       if (s->read_selected)
        gotone += s->read_ready = true;
-      select_printf ("saw eof on '%s'", fh->get_name ());
     }
 
 out:
-  return gotone || s->write_ready;
+  if (s->write_selected)
+    {
+      if (s->write_ready)
+        {
+          select_printf ("%s, already ready for write", fh->get_name ());
+          gotone++;
+        }
+      /* Do we need to do anything about SIGTTOU here? */
+      else if (fh->get_device () == FH_PIPER)
+       select_printf ("%s, select for write on read end of pipe",
+                      fh->get_name ());
+      else
+        {
+          /* We don't worry about the guard mutex, because that only applies
+             when from_select is false, and peek_pipe is never called that
+             way for writes.  */
+
+          IO_STATUS_BLOCK iosb = {0};
+          FILE_PIPE_LOCAL_INFORMATION fpli = {0};
+
+          if (NtQueryInformationFile (h,
+                                      &iosb,
+                                      &fpli,
+                                      sizeof (fpli),
+                                      FilePipeLocalInformation))
+            {
+              /* If NtQueryInformationFile fails, optimistically assume the
+                 pipe is writable.  This could happen on Win9x, because
+                 NtQueryInformationFile is not available, or if we somehow
+                 inherit a pipe that doesn't permit FILE_READ_ATTRIBUTES
+                 access on the write end.  */
+              select_printf ("%s, NtQueryInformationFile failed",
+                             fh->get_name ());
+              gotone += s->write_ready = true;
+            }
+          /* Ensure that enough space is available for atomic writes,
+             as required by POSIX.  Subsequent writes with size > PIPE_BUF
+             can still block, but most (all?) UNIX variants seem to work
+             this way (e.g., BSD, Linux, Solaris).  */
+          else if (fpli.WriteQuotaAvailable >= PIPE_BUF)
+            {
+              select_printf ("%s, ready for write: size %lu, avail %lu",
+                             fh->get_name (),
+                             fpli.OutboundQuota,
+                             fpli.WriteQuotaAvailable);
+              gotone += s->write_ready = true;
+            }
+          /* If we somehow inherit a tiny pipe (size < PIPE_BUF), then consider
+             the pipe writable only if it is completely empty, to minimize the
+             probability that a subsequent write will block.  */
+          else if (fpli.OutboundQuota < PIPE_BUF &&
+                   fpli.WriteQuotaAvailable == fpli.OutboundQuota)
+            {
+              select_printf ("%s, tiny pipe: size %lu, avail %lu",
+                             fh->get_name (),
+                             fpli.OutboundQuota,
+                             fpli.WriteQuotaAvailable);
+              gotone += s->write_ready = true;
+            }
+        }
+    }
+
+  return gotone;
 }
 
 static int start_thread_pipe (select_record *me, select_stuff *stuff);
@@ -603,9 +666,9 @@ fhandler_pipe::select_read (select_record *s)
   s->startup = start_thread_pipe;
   s->peek = peek_pipe;
   s->verify = verify_ok;
+  s->cleanup = pipe_cleanup;
   s->read_selected = true;
   s->read_ready = false;
-  s->cleanup = pipe_cleanup;
   return s;
 }
 
@@ -613,14 +676,13 @@ select_record *
 fhandler_pipe::select_write (select_record *s)
 {
   if (!s)
-    {
-      s = new select_record;
-      s->startup = no_startup;
-      s->verify = no_verify;
-    }
+    s = new select_record;
+  s->startup = start_thread_pipe;
   s->peek = peek_pipe;
+  s->verify = verify_ok;
+  s->cleanup = pipe_cleanup;
   s->write_selected = true;
-  s->write_ready = true;
+  s->write_ready = false;
   return s;
 }
 
@@ -628,7 +690,7 @@ select_record *
 fhandler_pipe::select_except (select_record *s)
 {
   if (!s)
-      s = new select_record;
+    s = new select_record;
   s->startup = start_thread_pipe;
   s->peek = peek_pipe;
   s->verify = verify_ok;