Skip to content

Exception "ProcessException: Received 0 of 5 expected bytes" when reading from stdout out of time #69

@PhantomArt

Description

@PhantomArt

I've been experimenting with Amp\Process to better understand how it works. I launched several processes, connecting them stdout -> stdin, simulated various situations in order to understand where errors could occur and how to handle them correctly, including so that there were no zombie processes left. I have encountered one problem, the nature of which is not clear to me.

Important: I am using the Windows 10 operating system and what I am working on will run on the same operating system.

I will give some synthetic examples. Most of them work correctly, but one works unexpectedly. In all examples, I will use the ImageMagick command to convert a PNG image to a JPEG.

Example 1 (works well):

\Amp\Process\Process::start([
  'magick',
  'image.png', // input file
  '-quality', '100',
  'JPEG:image.jpg' // output file
]);

\Revolt\EventLoop::run();

Now I modify this example to pass the input image to stdin.

Example 2 (works well):

$content = \file_get_contents('image.png');

$process = \Amp\Process\Process::start([
  'magick',
  '-', // input stdin
  '-quality', '100',
  'JPEG:image.jpg' // output file
]);

$process->getStdin()->write($content);
$process->getStdin()->end();

\Revolt\EventLoop::run();

Next, I change the example so that the output image is sent to stdout.

Example 3 (works well):

$content = \file_get_contents('image.png');

$process = \Amp\Process\Process::start([
  'magick',
  '-', // input stdin
  '-quality', '100',
  'JPEG:-' // output stdout
]);

$process->getStdin()->write($content);
$process->getStdin()->end();

$content = \Amp\ByteStream\buffer($process->getStdout());
\file_put_contents('image.jpg', $content);

\Revolt\EventLoop::run();

Now I remove the code that reads from stdout. I expect the script to hang forever on the \Revolt\EventLoop::run(); call. I realize this is stupid code, but it demonstrates the problem.

Example 4 (not working correctly):

$content = \file_get_contents('image.png');

$process = \Amp\Process\Process::start([
  'magick',
  '-', // input stdin
  '-quality', '100',
  'JPEG:-' // output stdout
]);

$process->getStdin()->write($content);
$process->getStdin()->end();

\Revolt\EventLoop::run();

The script does hang at the line \Revolt\EventLoop::run();, but every time after exactly 120 seconds, I get an exception:

Uncaught Amp\Process\ProcessException: Received 0 of 5 expected bytes in C:\test\vendor\amphp\process\src\Internal\Windows\SocketConnector.php:245

The exception text is always identical, it reports the expected 5 bytes.

Then I additionally remove the code that sends the input image to stdin.

Example 5 (works well):

\Amp\Process\Process::start([
  'magick',
  '-', // input stdin
  '-quality', '100',
  'JPEG:-' // output stdout
]);

\Revolt\EventLoop::run();

In this case, the script hangs indefinitely, as expected (perhaps not indefinitely, but certainly much longer than 120 seconds).

Then I try again to specify the input file as the command argument.

Example 6 (works well):

\Amp\Process\Process::start([
  'magick',
  'image.png', // input file
  '-quality', '100',
  'JPEG:-' // output stdout
]);

\Revolt\EventLoop::run();

In this example, the script still hangs forever, as expected.

In each of the examples I tried replacing \Revolt\EventLoop::run(); with $process->join();, this did not affect the results in any way.

Below I provide the full stack trace of the exception from example 4, but with $process->join();:

PHP Fatal error:  Uncaught Amp\Process\ProcessException: Received 0 of 5 expected bytes in C:\test\vendor\amphp\process\src\Internal\Windows\SocketConnector.php:245
Stack trace:
#0 C:\test\vendor\amphp\process\src\Internal\Windows\SocketConnector.php(219): Amp\Process\Internal\Windows\SocketConnector->read(Object(Amp\ByteStream\ReadableResourceStream), 5)
#1 C:\test\vendor\amphp\process\src\Internal\Windows\SocketConnector.php(102): Amp\Process\Internal\Windows\SocketConnector->readExitCode(Object(Amp\ByteStream\ReadableResourceStream))
#2 C:\test\vendor\amphp\amp\src\functions.php(34): Amp\Process\Internal\Windows\SocketConnector->Amp\Process\Internal\Windows\{closure}()
#3 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(425): Amp\{closure}(NULL, NULL, Array)
#4 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(616): Revolt\EventLoop\Internal\AbstractDriver->invokeMicrotasks()
#5 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#6 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\DriverSuspension.php(64): Fiber->resume(NULL)
#7 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(425): Revolt\EventLoop\Internal\DriverSuspension::Revolt\EventLoop\Internal\{closure}()
#8 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(616): Revolt\EventLoop\Internal\AbstractDriver->invokeMicrotasks()
#9 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#10 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(498): Fiber->start()
#11 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(553): Revolt\EventLoop\Internal\AbstractDriver->invokeCallbacks()
#12 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#13 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(94): Fiber->resume()
#14 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\DriverSuspension.php(117): Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#15 C:\test\vendor\amphp\amp\src\Future.php(251): Revolt\EventLoop\Internal\DriverSuspension->suspend()
#16 C:\test\vendor\amphp\process\src\Internal\Windows\WindowsRunner.php(128): Amp\Future->await(NULL)
#17 C:\test\vendor\amphp\process\src\Process.php(123): Amp\Process\Internal\Windows\WindowsRunner->join(Object(Amp\Process\Internal\Windows\WindowsHandle), NULL)
#18 C:\test\test.php(19): Amp\Process\Process->join()
#19 {main}

Next Amp\Future\UnhandledFutureError: Unhandled future: Amp\Process\ProcessException: "Received 0 of 5 expected bytes"; Await the Future with Future::await() before the future is destroyed or use Future::ignore() to suppress this exception. The future has been created at #0 C:\test\vendor\amphp\amp\src\functions.php:40 Amp\Internal\FutureState->__construct()
#1 C:\test\vendor\amphp\process\src\Internal\Windows\SocketConnector.php:100 Amp\async()
#2 C:\test\vendor\amphp\process\src\Internal\Windows\WindowsRunner.php:101 Amp\Process\Internal\Windows\SocketConnector->connectPipes()
#3 C:\test\vendor\amphp\process\src\Process.php:80 Amp\Process\Internal\Windows\WindowsRunner->start()
#4 C:\test\test.php:9 Amp\Process\Process::start() in C:\test\vendor\amphp\amp\src\Internal\FutureState.php:53
Stack trace:
#0 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(425): Amp\Internal\FutureState->__destruct()
#1 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(616): Revolt\EventLoop\Internal\AbstractDriver->invokeMicrotasks()
#2 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#3 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\DriverSuspension.php(64): Fiber->resume(NULL)
#4 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(425): Revolt\EventLoop\Internal\DriverSuspension::Revolt\EventLoop\Internal\{closure}()
#5 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(616): Revolt\EventLoop\Internal\AbstractDriver->invokeMicrotasks()
#6 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#7 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(498): Fiber->start()
#8 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(553): Revolt\EventLoop\Internal\AbstractDriver->invokeCallbacks()
#9 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#10 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(94): Fiber->resume()
#11 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\DriverSuspension.php(117): Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal\{closure}()
#12 C:\test\vendor\amphp\amp\src\Future.php(251): Revolt\EventLoop\Internal\DriverSuspension->suspend()
#13 C:\test\vendor\amphp\process\src\Internal\Windows\WindowsRunner.php(128): Amp\Future->await(NULL)
#14 C:\test\vendor\amphp\process\src\Process.php(123): Amp\Process\Internal\Windows\WindowsRunner->join(Object(Amp\Process\Internal\Windows\WindowsHandle), NULL)
#15 C:\test\test.php(19): Amp\Process\Process->join()
#16 {main}

Next Revolt\EventLoop\UncaughtThrowable: Uncaught Amp\Future\UnhandledFutureError thrown in event loop callback Amp\Internal\FutureState::Amp\Internal\{closure} defined in C:\test\vendor\amphp\amp\src\Internal\FutureState.php:54; use Revolt\EventLoop::setErrorHandler() to gracefully handle such exceptions: Unhandled future: Amp\Process\ProcessException: "Received 0 of 5 expected bytes"; Await the Future with Future::await() before the future is destroyed or use Future::ignore() to suppress this exception. The future has been created at #0 C:\test\vendor\amphp\amp\src\functions.php:40 Amp\Internal\FutureState->__construct()
#1 C:\test\vendor\amphp\process\src\Internal\Windows\SocketConnector.php:100 Amp\async()
#2 C:\test\vendor\amphp\process\src\Internal\Windows\WindowsRunner.php:101 Amp\Process\Internal\Windows\SocketConnector->connectPipes()
#3 C:\test\vendor\amphp\process\src\Process.php:80 Amp\Process\Internal\Windows\WindowsRunner->start()
#4 C:\test\test.php:9 Amp\Process\Process::start() in C:\test\vendor\revolt\event-loop\src\EventLoop\UncaughtThrowable.php:13
Stack trace:
#0 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\AbstractDriver.php(400): Revolt\EventLoop\UncaughtThrowable::throwingCallback(Object(Closure), Object(Amp\Future\UnhandledFutureError))
#1 C:\test\vendor\revolt\event-loop\src\EventLoop\Internal\DriverSuspension.php(127): Revolt\EventLoop\Internal\AbstractDriver::Revolt\EventLoop\Internal\{closure}()
#2 C:\test\vendor\amphp\amp\src\Future.php(251): Revolt\EventLoop\Internal\DriverSuspension->suspend()
#3 C:\test\vendor\amphp\process\src\Internal\Windows\WindowsRunner.php(128): Amp\Future->await(NULL)
#4 C:\test\vendor\amphp\process\src\Process.php(123): Amp\Process\Internal\Windows\WindowsRunner->join(Object(Amp\Process\Internal\Windows\WindowsHandle), NULL)
#5 C:\test\test.php(19): Amp\Process\Process->join()
#6 {main}
  thrown in C:\test\vendor\revolt\event-loop\src\EventLoop\UncaughtThrowable.php on line 13

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions