Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibEventLoop and large messages #26

Closed
mbonneau opened this issue Mar 21, 2015 · 4 comments
Closed

LibEventLoop and large messages #26

mbonneau opened this issue Mar 21, 2015 · 4 comments
Labels

Comments

@mbonneau
Copy link

I ran into an issue when sending messages that are larger than the bufferSize in Stream (4096).

It appears that if the size of the data in the underlying stream is larger than the buffer, handleData only gets called once. The data remains there until there is another read event triggered on the stream.

I wrote something to reproduce it:

<?php
require_once __DIR__ . "/vendor/autoload.php";

$loop = \React\EventLoop\Factory::create();

list($sockA, $sockB) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);

$streamA = new \React\Stream\Stream($sockA, $loop);
$streamB = new \React\Stream\Stream($sockB, $loop);

$testString = str_repeat("*", $streamA->bufferSize + 1);

$i = 0;
$buffer = "";
$streamB->on('data', function ($data, $streamB) use (&$i, &$buffer, &$testString) {
    $i++;
    $buffer .= $data;
    echo "Call " . $i . ": " . strlen($data) . " bytes - buffer is " . strlen($buffer) . " bytes\n";
    if (strlen($testString) == strlen($buffer)) {
        $streamB->close();
    }
});

$streamA->on('close', function ($streamA) {
    $streamA->close();
});

$streamA->write($testString);

$loop->run();

With StreamSelectLoop, I get the results I expect:

Call 1: 4096 bytes - buffer is 4096 bytes
Call 2: 1 bytes - buffer is 4097 bytes

With LibEventLoop:

Call 1: 4096 bytes - buffer is 4096 bytes

And then it hangs.

I have run this test with the same results on Mac OS 10.10.2 with php 5.6.5 and also on Centos 6.5 with 5.5.20.

@cboden cboden added the bug label May 27, 2015
@cboden
Copy link
Member

cboden commented May 27, 2015

Related to reactphp/reactphp#240. Replacing fread with stream_socket_recvfrom fixes this test case (but bypasses stream filters). I'm not sure if this is a Stream or EventLoop related bug. This test case fails on all non StreamSelectLoop loop implementations.

@mbonneau
Copy link
Author

I think this behavior is happening because of php stream buffers.

What is happening is that the fread in Stream is using the default buffering in PHP (which appears to be 8k in my version). So PHP actually reads all of the 4097 bytes into the stream buffer and returns 4096 because that is the max we said we wanted, figuring it will give that last byte to us next time.

This prevents event_loop from seeing anything available to read (because at that level, there isn't) and hangs there until the next read event.

This can be fixed by telling PHP not to buffer:

stream_set_read_buffer($sockB, 0);

Here is why it doesn't do this with stream_select: https://github.com/php/php-src/blob/f3dde29394831c59fc927bd5e7a7800a3fbe8519/ext/standard/streamsfuncs.c#L791

cboden added a commit to reactphp/stream that referenced this issue May 29, 2015
@clue
Copy link
Member

clue commented Jun 7, 2015

Awesome, thanks for spotting and proving a simple test case to reproduce this! 👍

Afaict this is a serious issue in (only) the stream component (see reactphp/stream#20) and there's little we need to do about this in the event-loop component.

@mbonneau
Copy link
Author

This is fixed in reactphp/stream#20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants