Skip to content

Commit 7a10375

Browse files
committed
trying fix: improve Windows process management and stream handling
1 parent 82ea2db commit 7a10375

File tree

1 file changed

+64
-44
lines changed

1 file changed

+64
-44
lines changed

src/ProcessManager.php

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
<?php
22

33
namespace VoltTest;
4-
54
use RuntimeException;
65

76
class ProcessManager
87
{
98
private string $binaryPath;
10-
119
private $currentProcess = null;
10+
private $isWindows;
1211

1312
public function __construct(string $binaryPath)
1413
{
1514
$this->binaryPath = $binaryPath;
16-
if (function_exists('pcntl_async_signals')) {
15+
$this->isWindows = PHP_OS_FAMILY === 'Windows';
16+
17+
// Only register signal handlers on non-Windows systems
18+
if (!$this->isWindows && function_exists('pcntl_async_signals')) {
1719
pcntl_async_signals(true);
1820
pcntl_signal(SIGINT, [$this, 'handleSignal']);
1921
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
@@ -34,23 +36,15 @@ public function execute(array $config, bool $streamOutput): string
3436
[$success, $process, $pipes] = $this->openProcess();
3537
$this->currentProcess = $process;
3638

37-
if (! $success || ! is_array($pipes)) {
39+
if (!$success || !is_array($pipes)) {
3840
throw new RuntimeException('Failed to start process of volt test');
3941
}
4042

4143
try {
4244
$this->writeInput($pipes[0], json_encode($config, JSON_PRETTY_PRINT));
4345
fclose($pipes[0]);
44-
45-
$output = $this->handleProcess($pipes, $streamOutput);
46-
47-
// Store stderr content before closing
48-
$stderrContent = '';
49-
if (isset($pipes[2]) && is_resource($pipes[2])) {
50-
rewind($pipes[2]);
51-
$stderrContent = stream_get_contents($pipes[2]);
52-
}
53-
46+
return $this->handleProcess($pipes, $streamOutput);
47+
} finally {
5448
// Clean up pipes
5549
foreach ($pipes as $pipe) {
5650
if (is_resource($pipe)) {
@@ -62,43 +56,38 @@ public function execute(array $config, bool $streamOutput): string
6256
$exitCode = $this->closeProcess($process);
6357
$this->currentProcess = null;
6458
if ($exitCode !== 0) {
65-
echo "\nError: " . trim($stderrContent) . "\n";
66-
67-
return '';
68-
}
69-
}
70-
71-
return $output;
72-
} finally {
73-
foreach ($pipes as $pipe) {
74-
if (is_resource($pipe)) {
75-
fclose($pipe);
59+
throw new RuntimeException('Process failed with exit code ' . $exitCode);
7660
}
7761
}
78-
if (is_resource($process)) {
79-
$this->closeProcess($process);
80-
$this->currentProcess = null;
81-
}
8262
}
8363
}
8464

8565
protected function openProcess(): array
8666
{
87-
$pipes = [];
67+
$descriptorspec = [
68+
0 => ['pipe', 'r'], // stdin
69+
1 => ['pipe', 'w'], // stdout
70+
2 => ['pipe', 'w'] // stderr
71+
];
72+
73+
// Windows-specific process options
74+
$options = $this->isWindows ? [
75+
'bypass_shell' => true,
76+
'create_process_group' => true // Important for Windows process management
77+
] : [
78+
'bypass_shell' => true
79+
];
80+
8881
$process = proc_open(
8982
$this->binaryPath,
90-
[
91-
0 => ['pipe', 'r'],
92-
1 => ['pipe', 'w'],
93-
2 => ['pipe', 'w'],
94-
],
83+
$descriptorspec,
9584
$pipes,
9685
null,
9786
null,
98-
['bypass_shell' => true]
87+
$options
9988
);
10089

101-
if (! is_resource($process)) {
90+
if (!is_resource($process)) {
10291
return [false, null, []];
10392
}
10493

@@ -108,17 +97,36 @@ protected function openProcess(): array
10897
private function handleProcess(array $pipes, bool $streamOutput): string
10998
{
11099
$output = '';
100+
$running = true;
111101

112-
while (true) {
102+
// Set streams to non-blocking mode
103+
foreach ($pipes as $pipe) {
104+
if (is_resource($pipe)) {
105+
stream_set_blocking($pipe, false);
106+
}
107+
}
108+
109+
while ($running) {
113110
$read = array_filter($pipes, 'is_resource');
114111
if (empty($read)) {
115112
break;
116113
}
117114

115+
// Windows-specific: Check process status
116+
if ($this->isWindows) {
117+
$status = proc_get_status($this->currentProcess);
118+
if (!$status['running']) {
119+
$running = false;
120+
}
121+
}
122+
118123
$write = null;
119124
$except = null;
120125

121-
if (stream_select($read, $write, $except, 1) === false) {
126+
// Use a shorter timeout on Windows
127+
$timeout = $this->isWindows ? 0.1 : 1;
128+
129+
if (stream_select($read, $write, $except, 0, $timeout * 1000000) === false) {
122130
break;
123131
}
124132

@@ -130,7 +138,6 @@ private function handleProcess(array $pipes, bool $streamOutput): string
130138
if (feof($pipe)) {
131139
fclose($pipe);
132140
unset($pipes[$type]);
133-
134141
continue;
135142
}
136143
}
@@ -139,9 +146,15 @@ private function handleProcess(array $pipes, bool $streamOutput): string
139146
$output .= $data;
140147
if ($streamOutput) {
141148
echo $data;
149+
if ($this->isWindows) {
150+
flush(); // Ensure output is displayed immediately on Windows
151+
}
142152
}
143153
} elseif ($type === 2 && $streamOutput) { // stderr
144154
fwrite(STDERR, $data);
155+
if ($this->isWindows) {
156+
flush();
157+
}
145158
}
146159
}
147160
}
@@ -153,21 +166,28 @@ protected function writeInput($pipe, string $input): void
153166
{
154167
if (is_resource($pipe)) {
155168
fwrite($pipe, $input);
169+
if ($this->isWindows) {
170+
fflush($pipe); // Ensure data is written immediately on Windows
171+
}
156172
}
157173
}
158174

159175
protected function closeProcess($process): int
160176
{
161-
if (! is_resource($process)) {
177+
if (!is_resource($process)) {
162178
return -1;
163179
}
164180

165181
$status = proc_get_status($process);
166182
if ($status['running']) {
167-
proc_terminate($process);
183+
// Windows-specific process termination
184+
if ($this->isWindows) {
185+
exec('taskkill /F /T /PID ' . $status['pid']);
186+
} else {
187+
proc_terminate($process);
188+
}
168189
}
169190

170-
171191
return proc_close($process);
172192
}
173-
}
193+
}

0 commit comments

Comments
 (0)