diff --git a/library/Pdfexport/HeadlessChrome.php b/library/Pdfexport/HeadlessChrome.php index f40b6a2..a596eed 100644 --- a/library/Pdfexport/HeadlessChrome.php +++ b/library/Pdfexport/HeadlessChrome.php @@ -313,7 +313,7 @@ public function asyncToPdf(): PromiseInterface } $killer = Loop::addTimer(10, function (TimerInterface $timer) use ($chrome, $deferred) { - $chrome->terminate(6); // SIGABRT + $this->terminateProcess($chrome, 6); // SIGABRT Logger::error( 'Browser timed out after %d seconds without the expected output', @@ -346,7 +346,7 @@ public function asyncToPdf(): PromiseInterface Logger::error('Failed to print PDF. An error occurred: %s', $e); } - $chrome->terminate(); + $this->terminateProcess($chrome); if (! empty($pdf)) { $deferred->resolve($pdf); @@ -689,6 +689,26 @@ private function waitFor(Client $ws, $eventName, ?array $expectedParams = null) return $params; } + /** + * Terminate a process and close pipes inherited by subprocesses + * + * Chrome spawns sub-processes that inherit open pipes. Close them before termination + * so the event loop is not kept alive by inherited file descriptors. + * + * @param Process $process The process to terminate + * @param ?int $signal The signal passed to Process::terminate() + * + * @return void + */ + private function terminateProcess(Process $process, ?int $signal = null): void + { + foreach ($process->pipes as $pipe) { + $pipe->close(); + } + + $process->terminate($signal); + } + /** * Get the major version number of Chrome or false on failure *