aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-08-03 09:01:56 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-08-03 09:01:56 +0200
commit31b87d3d695e35c1daf1c88d2b5d6ddebec3e62b (patch)
tree2494e235616c3bf5dcd15c5ef6780585ed5ef8c0
parent614d23129684f96ff0c5d77ee9c5a29be1f44272 (diff)
Implement faster emulation of Windows NUL via temporary file
-rw-r--r--butl/fdstream18
-rw-r--r--butl/fdstream.cxx34
-rw-r--r--butl/process.cxx6
3 files changed, 55 insertions, 3 deletions
diff --git a/butl/fdstream b/butl/fdstream
index 0caac5c..71de6a0 100644
--- a/butl/fdstream
+++ b/butl/fdstream
@@ -414,8 +414,26 @@ namespace butl
// Note that it's the caller's responsibility to close the returned file
// descriptor.
//
+ // On Windows the null device is NUL and writing anything substantial to it
+ // (like redirecting a process' output) is extremely slow, as in, an order
+ // of magnitude slower than writing to disk. If you are using the descriptor
+ // yourself this can be mitigated by setting the binary mode (already done
+ // by fdopen()) and using a buffer of around 64K. However, sometimes you
+ // have no control of how the descriptor will be used. For instance, it can
+ // be used to redirect a child's stdout and the way the child sets up its
+ // stdout is out of your control (on Windows). For such cases, there is an
+ // emulation via a temporary file. Mostly it functions as a proper null
+ // device with the file automatically removed once the descriptor is
+ // closed. One difference, however, would be if you were to both write to
+ // and read from the descriptor.
+ //
+#ifndef _WIN32
LIBBUTL_EXPORT int
fdnull () noexcept;
+#else
+ LIBBUTL_EXPORT int
+ fdnull (bool temp = false) noexcept;
+#endif
}
#include <butl/fdstream.ixx>
diff --git a/butl/fdstream.cxx b/butl/fdstream.cxx
index f761e7b..cff9b59 100644
--- a/butl/fdstream.cxx
+++ b/butl/fdstream.cxx
@@ -21,6 +21,7 @@
#include <errno.h> // errno, E*
#include <ios> // ios_base::openmode, ios_base::failure
+#include <new> // bad_alloc
#include <limits> // numeric_limits
#include <cassert>
#include <exception> // uncaught_exception()
@@ -542,9 +543,38 @@ namespace butl
}
int
- fdnull () noexcept
+ fdnull (bool temp) noexcept
{
- return _sopen ("nul", _O_RDWR, _SH_DENYNO);
+ // No need to translate /r/n before sending it to void.
+ //
+ if (!temp)
+ return _sopen ("nul", _O_RDWR | _O_BINARY, _SH_DENYNO);
+
+ try
+ {
+ // We could probably implement a Windows-specific version of getting
+ // the temporary file that avoid any allocations and exceptions.
+ //
+ path p (path::temp_path ("null")); // Can throw.
+ return _sopen (p.string ().c_str (),
+ (_O_CREAT |
+ _O_RDWR |
+ _O_BINARY | // Don't translate.
+ _O_TEMPORARY | // Remove on close.
+ _O_SHORT_LIVED), // Don't flush to disk.
+ _SH_DENYNO,
+ _S_IREAD | _S_IWRITE);
+ }
+ catch (const bad_alloc&)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ catch (const system_error& e)
+ {
+ errno = e.code ().value ();
+ return -1;
+ }
}
fdstream_mode
diff --git a/butl/process.cxx b/butl/process.cxx
index b78321c..b207ee5 100644
--- a/butl/process.cxx
+++ b/butl/process.cxx
@@ -451,7 +451,11 @@ namespace butl
auto create_null = [&get_osfhandle, &fail](auto_handle& n)
{
- auto_fd fd (fdnull ());
+ // Note that we are using a faster, temporary file-based emulation of
+ // NUL since we have no way of making sure the child buffers things
+ // properly (and by default they seem no to).
+ //
+ auto_fd fd (fdnull (true));
if (fd.get () == -1)
fail ();