squizlabs / PHP_CodeSniffer

PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.
BSD 3-Clause "New" or "Revised" License
10.66k stars 1.48k forks source link

PHP_CodeSniffer gets inifinitely stuck with parallel processing enabled if the gRPC extension is loaded #3518

Closed morozov closed 2 years ago

morozov commented 2 years ago

Describe the bug If phpcs is run on PHP 8.1 with parallel processing enabled, the process never ends.

Custom ruleset N/A

To reproduce Run phpcs --parallel=2

Expected behavior PHP_CodeSniffer exits as soon as all files have been checked.

Versions (please complete the following information):

Additional context Quick debugging shows that although the child process exits with the exit code equal to its PID, pcntl_waitpid() keeps being executed in an infinite loop returning 0. https://github.com/squizlabs/PHP_CodeSniffer/blob/7eef328f4703620a6e7ebecb66b5fab9ca5015e3/src/Runner.php#L723-L724 When the same build runs on PHP 8.0, pcntl_waitpid returns the value of the child PID as soon as it exits.

According to the PHP documentation,

Exit statuses should be in the range 0 to 254, the exit status 255 is reserved by PHP and shall not be used.

Using PID as the exit code seems to be a violation of the API and may be the root cause. https://github.com/squizlabs/PHP_CodeSniffer/blob/7eef328f4703620a6e7ebecb66b5fab9ca5015e3/src/Runner.php#L539

morozov commented 2 years ago

UPD: it doesn't look related to the exit code. The same issue is reproducible without the exit code being PID:

<?php

$pid = pcntl_fork();
if ($pid === -1) {
    echo 'Failed to create child process', PHP_EOL;
} elseif ($pid !== 0) {
    echo 'Waiting for child PID ', $pid, PHP_EOL;
    $result = pcntl_waitpid($pid, $status);
    echo 'Received ', $result, PHP_EOL;
} else {
    echo 'In child', PHP_EOL;
}

Produces:

$ php -v
PHP 8.0.14 (cli) (built: Dec 16 2021 11:20:34) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.14, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.14, Copyright (c), by Zend Technologies
    with Xdebug v3.1.2, Copyright (c) 2002-2021, by Derick Rethans

$ php test.php
Waiting for child PID 99167
In child
Received 99167

$ php -v
PHP 8.1.1 (cli) (built: Dec 15 2021 09:54:28) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.1, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies
    with Xdebug v3.1.1, Copyright (c) 2002-2021, by Derick Rethans

$ php test.php
Waiting for child PID 99176
In child
^C⏎ // never exits

Note that regardless of the issue, it's not necessary to use PID as the child exit code. According to the documentation,

pcntl_waitpid() returns the process ID of the child which exited [...]

gsherwood commented 2 years ago

Only got an 8.1.0 here, but your sample code works fine for me and there isn't anything related in the 8.1.1 changelog.

$ php -v
PHP 8.1.0 (cli) (built: Dec 17 2021 08:56:50) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.0, Copyright (c) Zend Technologies

$ php temp.php
Waiting for child PID 18155
In child
Received 18155

PHPCS itself runs fine with the parallel setting for me as well.

I'd need more people to test this out and see if there is a generic problem here.

morozov commented 2 years ago

It must be related to macOS or PHP packaged by Brew. I cannot reproduce the issue in Docker either:

$ docker run -it php:8.1-cli bash

root@d6f3a4104add:/# docker-php-ext-install pcntl

root@d6f3a4104add:/# cat > test.php
<?php

$pid = pcntl_fork();
if ($pid === -1) {
    echo 'Failed to create child process', PHP_EOL;
} elseif ($pid !== 0) {
    echo 'Waiting for child PID ', $pid, PHP_EOL;
    $result = pcntl_waitpid($pid, $status);
    echo 'Received ', $result, PHP_EOL;
} else {
    echo 'In child', PHP_EOL;
}

root@d6f3a4104add:/# php -v
PHP 8.1.1 (cli) (built: Dec 18 2021 00:28:00) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.1, Copyright (c) Zend Technologies

root@d6f3a4104add:/# php test.php
Waiting for child PID 2518
In child
Received 2518

@gsherwood please feel free to close as there's doubtfully something can be fixed in PHP_CodeSniffer itself.

gsherwood commented 2 years ago

Closing for now

morozov commented 2 years ago

Alright, the issue was caused by https://github.com/grpc/grpc/issues/20250. Once I disabled the grpc extension, the issue disappeared.

jrfnl commented 2 years ago

Nice detective work @morozov !