Skip to content

Commit 6c711ee

Browse files
committed
Force waiting for child on shutdown
1 parent 943352b commit 6c711ee

5 files changed

Lines changed: 57 additions & 20 deletions

File tree

src/Internal/Posix/PosixHandle.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static function (string $callbackId, $stream) use (&$status, $deferred, $stdin,
5353

5454
\fclose($stream);
5555

56-
self::waitPid($shellPid);
56+
self::asyncWaitPid($shellPid);
5757
},
5858
));
5959
}
@@ -72,13 +72,13 @@ public function unreference(): void
7272
}
7373
}
7474

75-
private static function waitPid(int $pid): void
75+
private static function asyncWaitPid(int $pid): void
7676
{
7777
if (self::hasChildExited($pid)) {
7878
return;
7979
}
8080

81-
EventLoop::unreference(EventLoop::defer(static fn () => self::waitPid($pid)));
81+
EventLoop::unreference(EventLoop::defer(static fn () => self::asyncWaitPid($pid)));
8282
}
8383

8484
private static function hasChildExited(int $pid): bool
@@ -97,6 +97,13 @@ public function __destruct()
9797
return;
9898
}
9999

100-
self::waitPid($this->shellPid);
100+
self::asyncWaitPid($this->shellPid);
101+
}
102+
103+
public function wait(): void
104+
{
105+
if (\extension_loaded('pcntl')) {
106+
\pcntl_waitpid($this->pid, $status);
107+
}
101108
}
102109
}

src/Internal/ProcHolder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ final class ProcHolder
1212
use ForbidSerialization;
1313

1414
public function __construct(
15-
private readonly ProcessRunner $runner,
16-
private readonly ProcessHandle $handle
15+
public readonly ProcessRunner $runner,
16+
public readonly ProcessHandle $handle,
1717
) {
1818
}
1919

src/Internal/ProcessHandle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,6 @@ public function __construct($proc)
3737
$this->joinDeferred = new DeferredFuture;
3838
$this->originalParentPid = \getmypid();
3939
}
40+
41+
abstract public function wait(): void;
4042
}

src/Internal/Windows/WindowsHandle.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@ public function __construct($proc)
3535

3636
$this->startBarrier = new Barrier(4);
3737
}
38+
39+
public function wait(): void
40+
{
41+
// Nothing to do.
42+
}
3843
}

src/Process.php

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ final class Process
2626

2727
private static \WeakMap $procHolder;
2828

29+
private static \WeakMap $streamHolder;
30+
2931
/**
3032
* Starts a new process.
3133
*
3234
* @param string|list<string> $command Command to run.
3335
* @param string|null $workingDirectory Working directory, or an empty string to use the working directory of the
3436
* parent.
35-
* @param array<string, string> $environment Environment variables, or use an empty array to inherit from the parent.
37+
* @param array<string, string> $environment Environment variables, or use an empty array to inherit from the
38+
* parent.
3639
* @param array<string, bool> $options Options for {@see proc_open()}.
3740
*
3841
* @throws ProcessException If starting the process fails.
@@ -59,14 +62,6 @@ public static function start(
5962
? \implode(" ", \array_map(escapeArgument(...), $command))
6063
: $command;
6164

62-
$driver = EventLoop::getDriver();
63-
64-
/** @psalm-suppress RedundantPropertyInitializationCheck */
65-
self::$driverRunner ??= new \WeakMap();
66-
self::$driverRunner[$driver] ??= \PHP_OS_FAMILY === 'Windows'
67-
? new WindowsProcessRunner()
68-
: new PosixProcessRunner();
69-
7065
if (!$workingDirectory) {
7166
$cwd = \getcwd();
7267
if ($cwd === false) {
@@ -76,7 +71,10 @@ public static function start(
7671
$workingDirectory = $cwd;
7772
}
7873

79-
$runner = self::$driverRunner[$driver];
74+
$driver = EventLoop::getDriver();
75+
76+
$runner = self::$driverRunner[$driver] ??= self::initialize();
77+
8078
$context = $runner->start(
8179
$command,
8280
$cancellation ?? new NullCancellation(),
@@ -90,13 +88,38 @@ public static function start(
9088

9189
$procHolder = new ProcHolder($runner, $handle);
9290

91+
self::$streamHolder[$streams->stdin] = $procHolder;
92+
self::$streamHolder[$streams->stdout] = $procHolder;
93+
self::$streamHolder[$streams->stderr] = $procHolder;
94+
95+
self::$procHolder[$procHolder] = $handle->pid;
96+
97+
return new self($runner, $handle, $streams, $command, $workingDirectory, $envVars, $options);
98+
}
99+
100+
private static function initialize(): ProcessRunner
101+
{
102+
/** @psalm-suppress RedundantPropertyInitializationCheck */
103+
self::$driverRunner ??= new \WeakMap();
104+
93105
/** @psalm-suppress RedundantPropertyInitializationCheck */
94106
self::$procHolder ??= new \WeakMap();
95-
self::$procHolder[$streams->stdin] = $procHolder;
96-
self::$procHolder[$streams->stdout] = $procHolder;
97-
self::$procHolder[$streams->stderr] = $procHolder;
98107

99-
return new self($runner, $handle, $streams, $command, $workingDirectory, $envVars, $options);
108+
/** @psalm-suppress RedundantPropertyInitializationCheck */
109+
self::$streamHolder ??= new \WeakMap();
110+
111+
$runner = \PHP_OS_FAMILY === 'Windows'
112+
? new WindowsProcessRunner()
113+
: new PosixProcessRunner();
114+
115+
\register_shutdown_function(static function (): void {
116+
/** @var ProcHolder $procHolder */
117+
foreach (self::$procHolder as $procHolder => $pid) {
118+
$procHolder->handle->wait();
119+
}
120+
});
121+
122+
return $runner;
100123
}
101124

102125
/**

0 commit comments

Comments
 (0)