Page MenuHomePhabricator

D11688.id28194.diff
No OneTemporary

D11688.id28194.diff

diff --git a/src/future/exec/ExecFuture.php b/src/future/exec/ExecFuture.php
--- a/src/future/exec/ExecFuture.php
+++ b/src/future/exec/ExecFuture.php
@@ -45,6 +45,10 @@
private $profilerCallID;
private $killedByTimeout;
+ private $useWindowsFileStreams = false;
+ private $windowsStdoutTempFile = null;
+ private $windowsStderrTempFile = null;
+
private static $descriptorSpec = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
@@ -229,6 +233,21 @@
}
+ /**
+ * Set whether to use non-blocking streams on Windows.
+ *
+ * @param bool Whether to use non-blocking streams.
+ * @return this
+ * @task config
+ */
+ public function setUseWindowsFileStreams($use_streams) {
+ if (phutil_is_windows()) {
+ $this->useWindowsFileStreams = $use_streams;
+ }
+ return $this;
+ }
+
+
/* -( Interacting With Commands )------------------------------------------ */
@@ -564,7 +583,24 @@
}
do {
- $data = fread($stream, min($length, 64 * 1024));
+ $real_length = $length;
+
+ // On Windows, we must check to see if we can read without blocking
+ // from the stream, and even then we must read only 1 byte at a time.
+ if ($this->useWindowsFileStreams) {
+ $r = array($stream);
+ $w = array();
+ $e = array();
+ if (false === stream_select($r, $w, $e, 0)) {
+ throw new Exception('stream_select() failed');
+ }
+ $real_length = 0;
+ if (in_array($stream, $r)) {
+ $real_length = 1;
+ }
+ }
+
+ $data = fread($stream, min($real_length, 64 * 1024));
if (false === $data) {
throw new Exception('Failed to read from '.$description);
}
@@ -662,13 +698,50 @@
} else {
$trap = null;
}
+
+ $stdout_target = null;
+ $stderr_target = null;
+
+ $spec = self::$descriptorSpec;
+ if ($this->useWindowsFileStreams) {
+ $this->windowsStdoutTempFile = new TempFile();
+ $this->windowsStderrTempFile = new TempFile();
+
+ $spec = array(
+ 0 => self::$descriptorSpec[0], // stdin
+ 1 => fopen($this->windowsStdoutTempFile, 'wb'), // stdout
+ 2 => fopen($this->windowsStderrTempFile, 'wb'), // stderr
+ );
+
+ if (!$spec[1] || !$spec[2]) {
+ throw new Exception(
+ 'Unable to create temporary files for '.
+ 'Windows stdout / stderr streams');
+ }
+ }
$proc = @proc_open(
$unmasked_command,
- self::$descriptorSpec,
+ $spec,
$pipes,
$cwd,
$env);
+
+ if ($this->useWindowsFileStreams) {
+ fclose($spec[1]);
+ fclose($spec[2]);
+ $pipes = array(
+ 0 => head($pipes), // stdin
+ 1 => fopen($this->windowsStdoutTempFile, 'rb'), // stdout
+ 2 => fopen($this->windowsStderrTempFile, 'rb'), // stderr
+ );
+
+ if (!$pipes[1] || !$pipes[2]) {
+ throw new Exception(
+ 'Unable to open temporary files for '.
+ 'reading Windows stdout / stderr streams');
+ }
+ }
if ($trap) {
$err = $trap->getErrorsAsString();
@@ -685,12 +758,12 @@
$this->proc = $proc;
list($stdin, $stdout, $stderr) = $pipes;
-
+
if (!phutil_is_windows()) {
- // On Windows, there's no such thing as nonblocking interprocess I/O.
- // Just leave the sockets blocking and hope for the best. Some features
- // will not work.
+ // On Windows, we redirect process standard output and standard error
+ // through temporary files, and then use stream_select to determine
+ // if there's more data to read.
if ((!stream_set_blocking($stdout, false)) ||
(!stream_set_blocking($stderr, false)) ||
@@ -732,14 +805,14 @@
$status = $this->procGetStatus();
$read_buffer_size = $this->readBufferSize;
-
+
$max_stdout_read_bytes = PHP_INT_MAX;
$max_stderr_read_bytes = PHP_INT_MAX;
if ($read_buffer_size !== null) {
$max_stdout_read_bytes = $read_buffer_size - strlen($this->stdout);
$max_stderr_read_bytes = $read_buffer_size - strlen($this->stderr);
}
-
+
if ($max_stdout_read_bytes > 0) {
$this->stdout .= $this->readAndDiscard(
$stdout,
@@ -755,8 +828,13 @@
'stderr',
$max_stderr_read_bytes);
}
-
+
if (!$status['running']) {
+ if ($this->useWindowsFileStreams) {
+ fclose($stdout);
+ fclose($stderr);
+ }
+
$this->result = array(
$status['exitcode'],
$this->stdout,

File Metadata

Mime Type
text/plain
Expires
Thu, Oct 31, 6:33 AM (2 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6755067
Default Alt Text
D11688.id28194.diff (4 KB)

Event Timeline