You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
bug symfony#10420 [2.3][Process] Make Process::start non-blocking on Windows platform (romainneutron)
This PR was merged into the 2.3 branch.
Discussion
----------
[2.3][Process] Make Process::start non-blocking on Windows platform
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | symfony#9755
| License | MIT
This PR is a proposition that solves issue symfony#9755.
Let me sum-up what problems we have on Windows platform:
- We can not use pipes with `proc_open` on Windows because `stream_set_blocking` does not work on this platform, resulting in blocking pipes and a blocking `Process::start` (see https://bugs.php.net/bug.php?id=51800, https://bugs.php.net/bug.php?id=47918 and https://bugs.php.net/bug.php?id=50856).
- We can not use file handles for both `STDOUT` and `STDERR` as it might produce corrupted content (see https://bugs.php.net/bug.php?id=65650).
- We currently use file handles for `STDOUT` and pipe for `STDERR` but it makes `Process::start` blocking.
The solution I propose here is to use native redirections, write `STDERR` / `STDOUT` in temporary files, read these files, finally cleanup.
It works pretty well, all tests pass on Windows. Better : [previous tests that were specific to this platform](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Process/Tests/AbstractProcessTest.php#L720-725) disappear as unix one are now okay :).
The drawback of this is the need of using `taskkill`: When stopping a process (`Process::stop` or in case of a timeout reached) `proc_terminate` does not kill the underlying process properly, only the `cmd` parent. The underlying process run by Process still runs after the `proc_terminate` call, having a lock on the temporary files we're using for getting the output, avoiding PHP to remove them (or any user on system) until the underlying process has finish to run).
So I use the `taskkill` Windows command to terminate the underlying process. This works well but I've to admit it's not pretty. If we do not use this hack, let's face that the underlying process is not stopped.
Last but not least, let's deal with the case we're running on Windows platform with PHP having --enable-sigchild environment (I don't know if it can really happen)
In this case:
- we can not retrieve the `pid`
- we can not `taskkill` the underlying process
- it runs
- locks remain on temporary files, we might not be able to remove them
We can't really deal with this.
Feedback more than welcome
Commits
-------
1f5bf32 [Process] Make Process::start non-blocking on Windows platform
if (false === $this->fileHandles[Process::STDOUT]) {
52
-
thrownewRuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
thrownewRuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable');
54
+
}
53
55
}
54
56
$this->readBytes = array(
55
57
Process::STDOUT => 0,
58
+
Process::STDERR => 0,
56
59
);
57
60
}
58
61
}
59
62
60
63
publicfunction__destruct()
61
64
{
62
65
$this->close();
66
+
$this->removeFiles();
63
67
}
64
68
65
69
/**
@@ -105,11 +109,13 @@ public function closeUnixPipes()
105
109
publicfunctiongetDescriptors()
106
110
{
107
111
if ($this->useFiles) {
112
+
// We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800)
113
+
// We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650
114
+
// So we redirect output within the commandline and pass the nul device to the process
108
115
returnarray(
109
116
array('pipe', 'r'),
110
-
$this->fileHandles[Process::STDOUT],
111
-
// Use a file handle only for STDOUT. Using for both STDOUT and STDERR would trigger https://bugs.php.net/bug.php?id=65650
112
-
array('pipe', 'w'),
117
+
array('file', 'NUL', 'w'),
118
+
array('file', 'NUL', 'w'),
113
119
);
114
120
}
115
121
@@ -128,6 +134,20 @@ public function getDescriptors()
128
134
);
129
135
}
130
136
137
+
/**
138
+
* Returns an array of filenames indexed by their related stream in case these pipes use temporary files.
139
+
*
140
+
* @return array
141
+
*/
142
+
publicfunctiongetFiles()
143
+
{
144
+
if ($this->useFiles) {
145
+
return$this->files;
146
+
}
147
+
148
+
returnarray();
149
+
}
150
+
131
151
/**
132
152
* Reads data in file handles and pipes.
133
153
*
@@ -322,4 +342,17 @@ private function hasSystemCallBeenInterrupted()
322
342
// stream_select returns false when the `select` system call is interrupted by an incoming signal
323
343
returnisset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
0 commit comments