-
-
Notifications
You must be signed in to change notification settings - Fork 49
Windows fix #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows fix #11
Changes from all commits
4531908
f160467
6b841d4
784fd3f
80a4bdd
37777d6
9c2429c
748431b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,8 @@ class Process extends EventEmitter | |
| private $stopSignal; | ||
| private $termSignal; | ||
|
|
||
| private $windowsWorkaround = false; | ||
|
|
||
| private static $sigchild; | ||
|
|
||
| /** | ||
|
|
@@ -82,6 +84,10 @@ public function start(LoopInterface $loop, $interval = 0.1) | |
| throw new \RuntimeException('Process is already running'); | ||
| } | ||
|
|
||
| if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->windowsWorkaround) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can safely remove the |
||
| return $this->startWindows($loop, $interval); | ||
| } | ||
|
|
||
| $cmd = $this->cmd; | ||
| $fdSpec = array( | ||
| array('pipe', 'r'), // stdin | ||
|
|
@@ -119,6 +125,67 @@ public function start(LoopInterface $loop, $interval = 0.1) | |
| }); | ||
| } | ||
|
|
||
| /** | ||
| * This is a special implementation of the start method for the Windows operating system. | ||
| * This is needed because windows has a broken implementation of stdout / stderr pipes that | ||
| * cannot be fixed by PHP. | ||
| * | ||
| * @param LoopInterface $loop Loop interface for stream construction | ||
| * @param float $interval Interval to periodically monitor process state (seconds) | ||
| * @throws RuntimeException If the process is already running or fails to start | ||
| */ | ||
| protected function startWindows(LoopInterface $loop, $interval = 0.1) | ||
| { | ||
| $cmd = $this->cmd; | ||
|
|
||
| $stdoutName = tempnam(sys_get_temp_dir(), "out"); | ||
| $stderrName = tempnam(sys_get_temp_dir(), "err"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps (somehow) expose an option to explicitly define the path to avoid privacy issues on multi user systems? |
||
|
|
||
| // Let's open 2 file pointers for both stdout and stderr | ||
| // One for writing, one for reading. | ||
| $stdout = fopen($stdoutName, "w"); | ||
| $stderr = fopen($stderrName, "w"); | ||
| $stdoutRead = fopen($stdoutName, "r"); | ||
| $stderrRead = fopen($stderrName, "r"); | ||
|
|
||
|
|
||
| $fdSpec = array( | ||
| array('pipe', 'r'), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if the analysis in #9 also applies to STDIN. Assuming it does, this means that we will still block when writing more data to the STDIN pipe as there's no way to detect when the receiving buffer is full? |
||
| $stdout, | ||
| $stderr, | ||
| ); | ||
|
|
||
| // Read exit code through fourth pipe to work around --enable-sigchild | ||
| if ($this->isSigchildEnabled() && $this->enhanceSigchildCompatibility) { | ||
| $fdSpec[] = array('pipe', 'w'); | ||
| $cmd = sprintf('(%s) 3>/dev/null; code=$?; echo $code >&3; exit $code', $cmd); | ||
| } | ||
|
|
||
| $this->process = proc_open($cmd, $fdSpec, $this->pipes, $this->cwd, $this->env, $this->options); | ||
|
|
||
| if (!is_resource($this->process)) { | ||
| throw new \RuntimeException('Unable to launch a new process.'); | ||
| } | ||
|
|
||
| $this->stdin = new Stream($this->pipes[0], $loop); | ||
| $this->stdin->pause(); | ||
| $this->stdout = new UnstopableStream($stdoutRead, $loop, $stdoutName); | ||
| $this->stderr = new UnstopableStream($stderrRead, $loop, $stderrName); | ||
|
|
||
| foreach ($this->pipes as $pipe) { | ||
| stream_set_blocking($pipe, 0); | ||
| } | ||
|
|
||
| $loop->addPeriodicTimer($interval, function (Timer $timer) use ($stdoutRead, $stderrRead) { | ||
| if (!$this->isRunning()) { | ||
| $this->close(); | ||
| $timer->cancel(); | ||
| $this->emit('exit', array($this->getExitCode(), $this->getTermSignal())); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Close the process. | ||
| * | ||
|
|
@@ -425,4 +492,12 @@ private function updateStatus() | |
| $this->exitCode = $this->status['exitcode']; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Sets whether the windows workaround mode should be used or not. | ||
| * @param bool $windowsWorkaround | ||
| */ | ||
| public function useWindowsWorkaround($windowsWorkaround = true) { | ||
| $this->windowsWorkaround = $windowsWorkaround; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <?php | ||
| namespace React\ChildProcess; | ||
|
|
||
| use React\Stream\Stream; | ||
| use React\EventLoop\LoopInterface; | ||
|
|
||
| /** | ||
| * This is a special kind of stream that does not end when reaching EOF. | ||
| * Useful for the Windows workaround of STDOUT and STDERR due to buggy PHP implementation. | ||
| */ | ||
| class UnstopableStream extends Stream | ||
| { | ||
| private $filename; | ||
|
|
||
| public function __construct($stream, LoopInterface $loop, $filename) | ||
| { | ||
| parent::__construct($stream, $loop); | ||
| $this->filename = $filename; | ||
| } | ||
|
|
||
| public function handleData($stream) | ||
| { | ||
| $data = fread($stream, $this->bufferSize); | ||
|
|
||
| $this->emit('data', array($data, $this)); | ||
|
|
||
| // Let's not stop on feof | ||
| if (!is_resource($stream)) { | ||
| $this->end(); | ||
| } | ||
| } | ||
|
|
||
| public function handleClose() | ||
| { | ||
| parent::handleClose(); | ||
| unlink($this->filename); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per the discussion we should consider if we might want to default this to
trueon Windows?