merged branch pulzarraider/windows_proces_hang_fix (PR #4069)
Commits
-------
e3296cb
fix php5.4 problem
c2405c0 fix hanging of unit tests on Windows
Discussion
----------
[WIP] [Process] Fix hanging of unit tests on Windows
Bug fix: yes
Feature addition: no
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: #3798
Todo: -
This PR tries to fix hanging unit tests on Windows platform. The problem is caused by known [PHP bug](https://bugs.php.net/bug.php?id=51800). PHP hangs forever while reading from STDOUT pipe if the output is too big.
I tried different combinatios and this is not ideal, but a working patch - STDOUT is sending to a file.
@Tobion, @drak and other developers working on Windows - can you please confirm, if this solution works for you, too?
---------------------------------------------------------------------------
by Seldaek at 2012-04-22T20:56:20Z
Tried it on 5.4.0 - I get the following when I checkout your branch:
```
SS..........ESSSSSSSSSS...E.S
Time: 0 seconds, Memory: 19.75Mb
There were 2 errors:
1) Symfony\Component\Process\Tests\ProcessTest::testProcessResponses with data set #0 ('output', 'getOutput', 'echo \'output\';')
Undefined offset: 1
C:\Users\seld\Web\symfony\framework\src\Symfony\Component\Process\Process.php:342
C:\Users\seld\Web\symfony\framework\src\Symfony\Component\Process\Process.php:168
C:\Users\seld\Web\symfony\framework\src\Symfony\Component\Process\Tests\ProcessTest.php:46
2) Symfony\Component\Process\Tests\ProcessTest::testIsRunning
Undefined offset: 1
C:\Users\seld\Web\symfony\framework\src\Symfony\Component\Process\Process.php:342
C:\Users\seld\Web\symfony\framework\src\Symfony\Component\Process\Tests\ProcessTest.php:106
```
When I remove the skipping of `ProcessTest::testProcessPipes` - it still hangs so it looks like your fix is not working.
Just for reference, with latest master checkout I get this:
```
SS...........SSSSSSSSSS.....S
Time: 2 seconds, Memory: 19.75Mb
OK, but incomplete or skipped tests!
```
Don't have time right now, but if I find time to look at your diff in more details I will, maybe it's possible to fix it.
---------------------------------------------------------------------------
by pulzarraider at 2012-04-22T22:23:05Z
@Seldaek Thanks, fixed php5.4 problem, my php5.3.8 passed every Process tests.
This patch isn't fixing problem with pipes in general. I don't think it's even possible from PHP code. We have to wait for PHP core developers to fix this problem. This patch only fix issue with hanging unit tests (not the one with pipes you mentioned). In my environment, symfony unit tests never finished and hangs on SwitchUserTest from SecurityBundle on 98%. Now with this patch, I can see final informations about all tests and their errors.
---------------------------------------------------------------------------
by Seldaek at 2012-04-23T07:44:16Z
Ok, it passes again on 5.4.
This commit is contained in:
commit
62005b9736
@ -47,6 +47,9 @@ class Process
|
||||
private $process;
|
||||
private $status = self::STATUS_READY;
|
||||
|
||||
private $fileHandles;
|
||||
private $readedBytes;
|
||||
|
||||
/**
|
||||
* Exit codes translation table.
|
||||
*
|
||||
@ -195,7 +198,18 @@ class Process
|
||||
$this->stdout = '';
|
||||
$this->stderr = '';
|
||||
$callback = $this->buildCallback($callback);
|
||||
$descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
|
||||
|
||||
//Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
|
||||
//Workaround for this problem is to use temporary files instead of pipes on Windows platform.
|
||||
//@see https://bugs.php.net/bug.php?id=51800
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$this->fileHandles = array(
|
||||
self::STDOUT => tmpfile(),
|
||||
);
|
||||
$descriptors = array(array('pipe', 'r'), $this->fileHandles[self::STDOUT], array('pipe', 'w'));
|
||||
} else {
|
||||
$descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
|
||||
}
|
||||
|
||||
$commandline = $this->commandline;
|
||||
|
||||
@ -255,6 +269,22 @@ class Process
|
||||
}
|
||||
}
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$fh = $this->fileHandles;
|
||||
foreach ($fh as $type => $fileHandle) {
|
||||
fseek($fileHandle, 0);
|
||||
$data = fread($fileHandle, 8192);
|
||||
$this->readedBytes[$type] = strlen($data);
|
||||
if (strlen($data) > 0) {
|
||||
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
|
||||
}
|
||||
if (false === $data) {
|
||||
fclose($fileHandle);
|
||||
unset($this->fileHandles[$type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($r as $pipe) {
|
||||
$type = array_search($pipe, $this->pipes);
|
||||
$data = fread($pipe, 8192);
|
||||
@ -288,7 +318,7 @@ class Process
|
||||
{
|
||||
$this->processInformation = proc_get_status($this->process);
|
||||
$callback = $this->buildCallback($callback);
|
||||
while ($this->pipes) {
|
||||
while ($this->pipes || (defined('PHP_WINDOWS_VERSION_BUILD') && $this->fileHandles)) {
|
||||
$r = $this->pipes;
|
||||
$w = null;
|
||||
$e = null;
|
||||
@ -304,6 +334,27 @@ class Process
|
||||
throw new \RuntimeException('The process timed out.');
|
||||
}
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
$fh = $this->fileHandles;
|
||||
foreach ($fh as $type => $fileHandle) {
|
||||
fseek($fileHandle, $this->readedBytes[$type]);
|
||||
$data = fread($fileHandle, 8192);
|
||||
if(isset($this->readedBytes)) {
|
||||
$this->readedBytes[$type] += strlen($data);
|
||||
} else {
|
||||
$this->readedBytes[$type] = strlen($data);
|
||||
}
|
||||
|
||||
if (strlen($data) > 0) {
|
||||
call_user_func($callback, $type == 1 ? self::OUT : self::ERR, $data);
|
||||
}
|
||||
if (false === $data) {
|
||||
fclose($fileHandle);
|
||||
unset($this->fileHandles[$type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($r as $pipe) {
|
||||
$type = array_search($pipe, $this->pipes);
|
||||
$data = fread($pipe, 8192);
|
||||
@ -517,6 +568,13 @@ class Process
|
||||
|
||||
$exitcode = proc_close($this->process);
|
||||
$this->exitcode = -1 === $this->processInformation['exitcode'] ? $exitcode : $this->processInformation['exitcode'];
|
||||
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
foreach ($this->fileHandles as $fileHandle) {
|
||||
fclose($fileHandle);
|
||||
}
|
||||
$this->fileHandles = array();
|
||||
}
|
||||
}
|
||||
$this->status = self::STATUS_TERMINATED;
|
||||
|
||||
@ -660,7 +718,10 @@ class Process
|
||||
|
||||
protected function updateOutput()
|
||||
{
|
||||
if (isset($this->pipes[self::STDOUT]) && is_resource($this->pipes[self::STDOUT])) {
|
||||
if (defined('PHP_WINDOWS_VERSION_BUILD') && isset($this->fileHandles[self::STDOUT]) && is_resource($this->fileHandles[self::STDOUT])) {
|
||||
fseek($this->fileHandles[self::STDOUT], $this->readedBytes[self::STDOUT]);
|
||||
$this->addOutput(stream_get_contents($this->fileHandles[self::STDOUT]));
|
||||
} elseif (isset($this->pipes[self::STDOUT]) && is_resource($this->pipes[self::STDOUT])) {
|
||||
$this->addOutput(stream_get_contents($this->pipes[self::STDOUT]));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user