1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Make run-tests.php check for tcp fwrite edge cases (#9242)

When the recipient is busy or the payload is large, fwrite can block
or return a value smaller than the length of the stream.

workers in run-tests.php communicates over tcp sockets with the manager.

https://cirrus-ci.com/task/5315675320221696?logs=tests#L130
showed notices for fwrite/unserialize

This is a similar approach to the approach used in
https://github.com/phan/phan/blob/v5/src/Phan/LanguageServer/ProtocolStreamWriter.php
for the tcp language server writing.
This commit is contained in:
Tyson Andre
2022-10-07 20:02:22 -04:00
committed by GitHub
parent 9961f98079
commit d498908ec4

View File

@@ -1647,11 +1647,47 @@ escape:
}
}
/**
* Calls fwrite and retries when network writes fail with errors such as "Resource temporarily unavailable"
*
* @param resource $stream the stream to fwrite to
* @param string $data
* @return int|false
*/
function safe_fwrite($stream, string $data)
{
// safe_fwrite was tested by adding $message['unused'] = str_repeat('a', 20_000_000); in send_message()
// fwrites on tcp sockets can return false or less than strlen if the recipient is busy.
// (e.g. fwrite(): Send of 577 bytes failed with errno=35 Resource temporarily unavailable)
$bytes_written = 0;
while ($bytes_written < strlen($data)) {
$n = @fwrite($stream, substr($data, $bytes_written));
if ($n === false) {
$write_streams = [$stream];
$read_streams = [];
$except_streams = [];
/* Wait for up to 10 seconds for the stream to be ready to write again. */
$result = stream_select($read_streams, $write_streams, $except_streams, 10);
if (!$result) {
echo "ERROR: send_message() stream_select() failed\n";
return false;
}
$n = @fwrite($stream, substr($data, $bytes_written));
if ($n === false) {
echo "ERROR: send_message() Failed to write chunk after stream_select: " . error_get_last()['message'] . "\n";
return false;
}
}
$bytes_written += $n;
}
return $bytes_written;
}
function send_message($stream, array $message): void
{
$blocking = stream_get_meta_data($stream)["blocked"];
stream_set_blocking($stream, true);
fwrite($stream, base64_encode(serialize($message)) . "\n");
safe_fwrite($stream, base64_encode(serialize($message)) . "\n");
stream_set_blocking($stream, $blocking);
}