Bre*_*aun 5 symfony symfony-process
我在某些机器上从 Symfony 进程命令获得退出代码 -1,而完全相同的代码返回退出代码 0,而在其他机器上除外。在这两种情况下,给定命令的输出都成功执行。
从 shell 运行相同的命令行会给出正确的退出代码 (0)。我制作了一个小测试用例来重现这个问题:
use Symfony\Component\Process\Process;
$process = new Process('./console');
$process->run();
echo $process->getExitCode();
Run Code Online (Sandbox Code Playgroud)
关于如何诊断这个的任何想法?
isRunning或),则可能会发生这种情况。getExitCode如果我们深入了解 Symfony Process 正在做什么,我们可以看到它正在使用proc_open。
在大多数情况下,退出状态代码-1更有可能来自进程管理器而不是进程本身,因此我们几乎可以肯定这不是真正的退出状态代码。
在 PHP 8.3 之前,如果我们调用proc_get_status()进程资源,它只会在第一次返回实际的退出代码。然后,该进程将被丢弃,任何后续调用都将返回-1。
我们可以通过添加断点proc_open并手动运行来看到这种行为proc_get_status($this->process)。
长话短说,Symfony Process 擅长避免这个问题,因为它在进程不再运行后立即退出,这可以防止它在进程上多次调用该方法,从而丢弃实际的退出状态代码。
我不知道可能触发此问题的所有方式,但我能够以至少一种方式一致地重现它,这是因素和赛车条件的结合。
我能够重现它的方式是:如果进程的输出回调isRunning本身调用诸如or之类的方法getExitCode,则会导致可能触发错误的竞争条件。竞争条件取决于生成输出和进程退出之间的时间。
这是一个孤立的 PoC:
<?php
if ( getenv( 'OUTPUT' ) ) {
#echo 'This should fail when "wait" gets large';
sleep( 2 );
echo 'This should fail even when "wait" is small';
die( 0 );
}
use Symfony\Component\Process\Process;
require_once __DIR__ . '/vendor/autoload.php';
do {
// Increasingly bigger waits.
static $wait = 0;
$wait += 100000;
echo sprintf( 'Wait: %s', $wait ) . PHP_EOL;
$p = new Process( [ 'php', __FILE__ ], __DIR__, [
'OUTPUT' => 1,
] );
$p->start( function ( string $type, string $out ) use ( $p ) {
echo $out . PHP_EOL;
/**
* Calling most methods in Symfony Process that triggers
* updateStatus() can potentially trigger the -1 bug.
*
* @see Process::updateStatus()
*/
echo sprintf( 'Is Running: %s', $p->isRunning() ? 'Yes' : 'No' ) . PHP_EOL;
echo sprintf( 'Exit Code: %s', $p->getExitCode() ) . PHP_EOL;
} );
while ( $p->isRunning() ) {
usleep( $wait );
}
if ( ! $p->isSuccessful() ) {
break;
}
} while ( true );
$is_started = $p->isStarted();
$is_running = $p->isRunning();
$exit_code = $p->getExitCode();
echo sprintf( 'Started: %s, Running: %s, Exit code: %s', $is_started, $is_running, $exit_code ) . PHP_EOL;
Run Code Online (Sandbox Code Playgroud)
这在 PHP 8.3 上不应该发生,因为这个问题get_proc_status已经被修复,根据更新日志注释:
多次执行 proc_get_status()
现在,在 POSIX 系统上执行
proc_get_status()多次将始终返回正确的值。以前,只有函数的第一次调用才会返回正确的值。proc_close()之后 执行proc_get_status()现在还将返回正确的退出代码。以前这将返回 -1。
PS:我现在没有时间向 Symfony Process 贡献 PR,但如果有人这样做,我只是承诺保持 PHP 的向后兼容性。
| 归档时间: |
|
| 查看次数: |
1376 次 |
| 最近记录: |