Skip to content

LibEventLoop and large messages #26

Description

@mbonneau

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.

Activity

  1. cboden commented on May 27, 2015

    @cboden
    Member

    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.

  2. mbonneau commented on May 28, 2015

    @mbonneau
    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

  3. added a commit that references this issue on May 29, 2015
  4. clue commented on Jun 7, 2015

    @clue
    Member

    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.

  5. mbonneau commented on Sep 10, 2015

    @mbonneau
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions