Skip to content

Commit 7db72f6

Browse files
committed
WIP
1 parent ce1234f commit 7db72f6

File tree

1 file changed

+70
-70
lines changed

1 file changed

+70
-70
lines changed

src/ProcessManager.php

Lines changed: 70 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,18 @@ class ProcessManager
88
private string $binaryPath;
99
private $currentProcess = null;
1010
private bool $debug;
11+
private int $timeout = 30; // timeout in seconds
1112

12-
public function __construct(string $binaryPath, bool $debug = true)
13+
public function __construct(string $binaryPath, bool $debug = false)
1314
{
14-
// Normalize path for Windows
1515
$this->binaryPath = str_replace('/', '\\', $binaryPath);
1616
$this->debug = $debug;
1717

18-
// Verify binary exists and is executable
1918
if (!file_exists($this->binaryPath)) {
2019
throw new RuntimeException("Binary not found at: {$this->binaryPath}");
2120
}
2221

23-
if (!is_executable($this->binaryPath)) {
24-
throw new RuntimeException("Binary is not executable: {$this->binaryPath}");
25-
}
26-
27-
$this->debugLog("ProcessManager initialized with verified binary: {$this->binaryPath}");
22+
$this->debugLog("ProcessManager initialized with binary: {$this->binaryPath}");
2823
}
2924

3025
private function debugLog(string $message): void
@@ -39,107 +34,110 @@ public function execute(array $config, bool $streamOutput): string
3934
{
4035
$this->debugLog("Starting execution");
4136

42-
// Create temporary file for input
43-
$tmpfname = tempnam(sys_get_temp_dir(), 'volt_');
44-
$this->debugLog("Created temporary file: $tmpfname");
45-
46-
// Write config to temp file
47-
file_put_contents($tmpfname, json_encode($config, JSON_PRETTY_PRINT));
48-
49-
// Build command with proper escaping
37+
// Prepare command
5038
$cmd = escapeshellarg($this->binaryPath);
51-
$this->debugLog("Executing command: $cmd");
39+
$this->debugLog("Command: $cmd");
5240

53-
// Setup process
41+
// Prepare config
42+
$configJson = json_encode($config, JSON_PRETTY_PRINT);
43+
$this->debugLog("Config prepared: " . substr($configJson, 0, 100) . "...");
44+
45+
// Start process
5446
$descriptorspec = [
55-
0 => ['pipe', 'r'], // stdin
56-
1 => ['pipe', 'w'], // stdout
57-
2 => ['pipe', 'w'] // stderr
47+
0 => ['pipe', 'r'],
48+
1 => ['pipe', 'w'],
49+
2 => ['pipe', 'w']
5850
];
5951

60-
$cwd = dirname($this->binaryPath);
61-
$env = ['VOLT_TEST_DEBUG' => '1'];
62-
63-
$this->debugLog("Opening process in directory: $cwd");
64-
65-
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env, [
52+
$this->debugLog("Opening process");
53+
$process = proc_open($cmd, $descriptorspec, $pipes, null, null, [
6654
'bypass_shell' => true,
6755
'create_process_group' => true
6856
]);
6957

7058
if (!is_resource($process)) {
71-
unlink($tmpfname);
72-
throw new RuntimeException("Failed to start process: $cmd");
59+
throw new RuntimeException("Failed to start process");
7360
}
7461

7562
$this->currentProcess = $process;
76-
$this->debugLog("Process started successfully");
63+
$this->debugLog("Process started");
64+
65+
// Set streams to non-blocking mode
66+
foreach ($pipes as $pipe) {
67+
stream_set_blocking($pipe, false);
68+
}
7769

7870
try {
79-
// Write config to process
71+
// Write config to stdin
8072
$this->debugLog("Writing config to process");
81-
$configContent = file_get_contents($tmpfname);
82-
fwrite($pipes[0], $configContent);
83-
fclose($pipes[0]);
84-
unlink($tmpfname);
73+
$written = fwrite($pipes[0], $configJson);
74+
if ($written === false) {
75+
throw new RuntimeException("Failed to write config to process");
76+
}
77+
$this->debugLog("Wrote $written bytes to process");
8578

86-
// Set up non-blocking reads
87-
stream_set_blocking($pipes[1], false);
88-
stream_set_blocking($pipes[2], false);
79+
// Important: flush and close stdin
80+
fflush($pipes[0]);
81+
fclose($pipes[0]);
82+
$this->debugLog("Closed stdin pipe");
8983

9084
$output = '';
91-
$processRunning = true;
92-
93-
$this->debugLog("Starting output reading loop");
85+
$startTime = time();
86+
$lastDataTime = time();
9487

95-
while ($processRunning) {
88+
while (true) {
9689
$status = proc_get_status($process);
97-
$processRunning = $status['running'];
98-
99-
$read = array_filter([$pipes[1], $pipes[2]], 'is_resource');
100-
if (empty($read)) {
90+
if (!$status['running']) {
91+
$this->debugLog("Process has finished");
10192
break;
10293
}
10394

95+
// Check timeout
96+
if (time() - $startTime > $this->timeout) {
97+
throw new RuntimeException("Process timed out after {$this->timeout} seconds");
98+
}
99+
100+
// Check for data timeout (no data received)
101+
if (time() - $lastDataTime > 5) {
102+
$this->debugLog("No data received for 5 seconds, checking process status");
103+
$lastDataTime = time(); // Reset timer
104+
}
105+
106+
$read = [$pipes[1], $pipes[2]];
104107
$write = null;
105108
$except = null;
106109

107-
if (stream_select($read, $write, $except, 0, 100000)) {
110+
// Short timeout for select
111+
if (stream_select($read, $write, $except, 0, 200000)) {
108112
foreach ($read as $pipe) {
109-
$data = fread($pipe, 4096);
110-
111-
if ($data === false || $data === '') {
113+
$data = fread($pipe, 8192);
114+
if ($data === false) {
112115
continue;
113116
}
114-
115-
if ($pipe === $pipes[1]) {
116-
$output .= $data;
117-
if ($streamOutput) {
118-
fwrite(STDOUT, $data);
117+
if ($data !== '') {
118+
$lastDataTime = time();
119+
if ($pipe === $pipes[1]) {
120+
$output .= $data;
121+
if ($streamOutput) {
122+
fwrite(STDOUT, $data);
123+
flush();
124+
}
125+
} else {
126+
fwrite(STDERR, $data);
119127
flush();
120128
}
121-
} else {
122-
fwrite(STDERR, $data);
123-
flush();
124129
}
125130
}
126131
}
127-
128-
// Check if process has exited
129-
if (!$processRunning) {
130-
$this->debugLog("Process has finished");
131-
break;
132-
}
133132
}
134133

135134
// Close remaining pipes
136-
foreach ($pipes as $pipe) {
137-
if (is_resource($pipe)) {
138-
fclose($pipe);
135+
foreach ([1, 2] as $i) {
136+
if (isset($pipes[$i]) && is_resource($pipes[$i])) {
137+
fclose($pipes[$i]);
139138
}
140139
}
141140

142-
// Get exit code and close process
143141
$exitCode = proc_close($process);
144142
$this->debugLog("Process closed with exit code: $exitCode");
145143

@@ -152,18 +150,20 @@ public function execute(array $config, bool $streamOutput): string
152150
} catch (\Exception $e) {
153151
$this->debugLog("Error occurred: " . $e->getMessage());
154152

155-
// Clean up
153+
// Clean up pipes
156154
foreach ($pipes as $pipe) {
157155
if (is_resource($pipe)) {
158156
fclose($pipe);
159157
}
160158
}
161159

160+
// Terminate process if still running
162161
if (is_resource($process)) {
163162
$status = proc_get_status($process);
164163
if ($status['running']) {
164+
// Force kill on Windows
165165
exec("taskkill /F /T /PID {$status['pid']} 2>&1", $output, $resultCode);
166-
$this->debugLog("Taskkill result: " . implode("\n", $output) . " (code: $resultCode)");
166+
$this->debugLog("Taskkill result code: $resultCode");
167167
}
168168
proc_close($process);
169169
}

0 commit comments

Comments
 (0)