Windows: map ERROR_NO_DATA to EPIPE, rather than EINVAL
authorSimon Marlow <marlowsd@gmail.com>
Wed, 15 Sep 2010 14:26:18 +0000 (14:26 +0000)
committerSimon Marlow <marlowsd@gmail.com>
Wed, 15 Sep 2010 14:26:18 +0000 (14:26 +0000)
WriteFile() returns ERROR_NO_DATA when writing to a pipe that is
"closing", however by default the write() wrapper in the CRT maps this
to EINVAL so we get confusing things like

  hPutChar: invalid argument (Invalid Argumnet)

when piping the output of a Haskell program into something that closes
the pipe early.  This was happening in the testsuite in a few place.

The solution is to map ERROR_NO_DATA to EPIPE correctly, as we
explicitly check for EPIPE on stdout (in GHC.TopHandler) so we can
exit without an error in this case.

GHC/IO/FD.hs
cbits/Win32Utils.c

index c67615d..2242ee6 100644 (file)
@@ -582,7 +582,19 @@ blockingWriteRawBufferPtr loc fd buf off len
   = fmap fromIntegral $ throwErrnoIfMinus1Retry loc $
         if fdIsSocket fd
            then c_safe_send  (fdFD fd) (buf `plusPtr` off) len 0
-           else c_safe_write (fdFD fd) (buf `plusPtr` off) len
+           else do
+             r <- c_safe_write (fdFD fd) (buf `plusPtr` off) len
+             when (r == -1) c_maperrno
+             return r
+      -- we don't trust write() to give us the correct errno, and
+      -- instead do the errno conversion from GetLastError()
+      -- ourselves.  The main reason is that we treat ERROR_NO_DATA
+      -- (pipe is closing) as EPIPE, whereas write() returns EINVAL
+      -- for this case.  We need to detect EPIPE correctly, because it
+      -- shouldn't be reported as an error when it happens on stdout.
+
+foreign import ccall unsafe "maperrno"             -- in Win32Utils.c
+   c_maperrno :: IO ()
 
 -- NOTE: "safe" versions of the read/write calls for use by the threaded RTS.
 -- These calls may block, but that's ok.
index 0f4eb52..fd4d1eb 100644 (file)
@@ -61,7 +61,10 @@ static struct errentry errtable[] = {
         {  ERROR_ALREADY_EXISTS,         EEXIST    },  /* 183 */
         {  ERROR_FILENAME_EXCED_RANGE,   ENOENT    },  /* 206 */
         {  ERROR_NESTING_NOT_ALLOWED,    EAGAIN    },  /* 215 */
-        {  ERROR_NOT_ENOUGH_QUOTA,       ENOMEM    }    /* 1816 */
+           /* Windows returns this when the read end of a pipe is
+            * closed (or closing) and we write to it. */
+        {  ERROR_NO_DATA,                EPIPE     },  /* 232 */
+        {  ERROR_NOT_ENOUGH_QUOTA,       ENOMEM    }  /* 1816 */
 };
 
 /* size of the table */