Giter VIP home page Giter VIP logo

event-loop's Introduction

EventLoop

CI status installs on Packagist

ReactPHP's core reactor event loop that libraries can use for evented I/O.

Development version: This branch contains the code for the upcoming v3 release. For the code of the current stable v1 release, check out the 1.x branch.

The upcoming v3 release will be the way forward for this package. However, we will still actively support v1 for those not yet on the latest version. See also installation instructions for more details.

In order for async based libraries to be interoperable, they need to use the same event loop. This component provides a common LoopInterface that any library can target. This allows them to be used in the same loop, with one single run() call that is controlled by the user.

Table of contents

Quickstart example

Here is an async HTTP server built with just the event loop.

<?php

use React\EventLoop\Loop;

require __DIR__ . '/vendor/autoload.php';

$server = stream_socket_server('tcp://127.0.0.1:8080');
stream_set_blocking($server, false);

Loop::addReadStream($server, function ($server) {
    $conn = stream_socket_accept($server);
    $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
    Loop::addWriteStream($conn, function ($conn) use (&$data) {
        $written = fwrite($conn, $data);
        if ($written === strlen($data)) {
            fclose($conn);
            Loop::removeWriteStream($conn);
        } else {
            $data = substr($data, $written);
        }
    });
});

Loop::addPeriodicTimer(5, function () {
    $memory = memory_get_usage() / 1024;
    $formatted = number_format($memory, 3).'K';
    echo "Current memory usage: {$formatted}\n";
});

See also the examples.

Usage

Typical applications would use the Loop class to use the default event loop like this:

use React\EventLoop\Loop;

$timer = Loop::addPeriodicTimer(0.1, function () {
    echo 'Tick' . PHP_EOL;
});

Loop::addTimer(1.0, function () use ($timer) {
    Loop::cancelTimer($timer);
    echo 'Done' . PHP_EOL;
});

As an alternative, you can also explicitly create an event loop instance at the beginning, reuse it throughout your program and finally run it at the end of the program like this:

$loop = React\EventLoop\Loop::get();

$timer = $loop->addPeriodicTimer(0.1, function () {
    echo 'Tick' . PHP_EOL;
});

$loop->addTimer(1.0, function () use ($loop, $timer) {
    $loop->cancelTimer($timer);
    echo 'Done' . PHP_EOL;
});

$loop->run();

While the former is more concise, the latter is more explicit. In both cases, the program would perform the exact same steps.

  1. The event loop instance is created at the beginning of the program. This is implicitly done the first time you call the Loop class (or by manually instantiating any of the loop implementations).
  2. The event loop is used directly or passed as an instance to library and application code. In this example, a periodic timer is registered with the event loop which simply outputs Tick every fraction of a second until another timer stops the periodic timer after a second.
  3. The event loop is run at the end of the program. This is automatically done when using the Loop class or explicitly with a single run() call at the end of the program.

As of v1.2.0, we highly recommend using the Loop class. The explicit loop instructions are still valid and may still be useful in some applications, especially for a transition period towards the more concise style.

Loop

The Loop class exists as a convenient global accessor for the event loop.

Loop methods

The Loop class provides all methods that exist on the LoopInterface as static methods:

If you're working with the event loop in your application code, it's often easiest to directly interface with the static methods defined on the Loop class like this:

use React\EventLoop\Loop;

$timer = Loop::addPeriodicTimer(0.1, function () {
    echo 'Tick' . PHP_EOL;
});

Loop::addTimer(1.0, function () use ($timer) {
    Loop::cancelTimer($timer);
    echo 'Done' . PHP_EOL;
});

On the other hand, if you're familiar with object-oriented programming (OOP) and dependency injection (DI), you may want to inject an event loop instance and invoke instance methods on the LoopInterface like this:

use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;

class Greeter
{
    private $loop;

    public function __construct(LoopInterface $loop)
    {
        $this->loop = $loop;
    }

    public function greet(string $name)
    {
        $this->loop->addTimer(1.0, function () use ($name) {
            echo 'Hello ' . $name . '!' . PHP_EOL;
        });
    }
}

$greeter = new Greeter(Loop::get());
$greeter->greet('Alice');
$greeter->greet('Bob');

Each static method call will be forwarded as-is to the underlying event loop instance by using the Loop::get() call internally. See LoopInterface for more details about available methods.

Loop autorun

When using the Loop class, it will automatically execute the loop at the end of the program. This means the following example will schedule a timer and will automatically execute the program until the timer event fires:

use React\EventLoop\Loop;

Loop::addTimer(1.0, function () {
    echo 'Hello' . PHP_EOL;
});

As of v1.2.0, we highly recommend using the Loop class this way and omitting any explicit run() calls. For BC reasons, the explicit run() method is still valid and may still be useful in some applications, especially for a transition period towards the more concise style.

If you don't want the Loop to run automatically, you can either explicitly run() or stop() it. This can be useful if you're using a global exception handler like this:

use React\EventLoop\Loop;

Loop::addTimer(10.0, function () {
    echo 'Never happens';
});

set_exception_handler(function (Throwable $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
    Loop::stop();
});

throw new RuntimeException('Demo');

get()

The get(): LoopInterface method can be used to get the currently active event loop instance.

This method will always return the same event loop instance throughout the lifetime of your application.

use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;

$loop = Loop::get();

assert($loop instanceof LoopInterface);
assert($loop === Loop::get());

This is particularly useful if you're using object-oriented programming (OOP) and dependency injection (DI). In this case, you may want to inject an event loop instance and invoke instance methods on the LoopInterface like this:

use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;

class Greeter
{
    private $loop;

    public function __construct(LoopInterface $loop)
    {
        $this->loop = $loop;
    }

    public function greet(string $name)
    {
        $this->loop->addTimer(1.0, function () use ($name) {
            echo 'Hello ' . $name . '!' . PHP_EOL;
        });
    }
}

$greeter = new Greeter(Loop::get());
$greeter->greet('Alice');
$greeter->greet('Bob');

See LoopInterface for more details about available methods.

Loop implementations

In addition to the LoopInterface, there are a number of event loop implementations provided.

All of the event loops support these features:

  • File descriptor polling
  • One-off timers
  • Periodic timers
  • Deferred execution on future loop tick

For most consumers of this package, the underlying event loop implementation is an implementation detail. You should use the Loop class to automatically create a new instance.

Advanced! If you explicitly need a certain event loop implementation, you can manually instantiate one of the following classes. Note that you may have to install the required PHP extensions for the respective event loop implementation first or they will throw a BadMethodCallException on creation.

StreamSelectLoop

A stream_select() based event loop.

This uses the stream_select() function and is the only implementation that works out of the box with PHP.

This event loop works out of the box on PHP 5.3 through PHP 8+ and HHVM. This means that no installation is required and this library works on all platforms and supported PHP versions. Accordingly, the Loop class will use this event loop by default if you do not install any of the event loop extensions listed below.

Under the hood, it does a simple select system call. This system call is limited to the maximum file descriptor number of FD_SETSIZE (platform dependent, commonly 1024) and scales with O(m) (m being the maximum file descriptor number passed). This means that you may run into issues when handling thousands of streams concurrently and you may want to look into using one of the alternative event loop implementations listed below in this case. If your use case is among the many common use cases that involve handling only dozens or a few hundred streams at once, then this event loop implementation performs really well.

If you want to use signal handling (see also addSignal() below), this event loop implementation requires ext-pcntl. This extension is only available for Unix-like platforms and does not support Windows. It is commonly installed as part of many PHP distributions. If this extension is missing (or you're running on Windows), signal handling is not supported and throws a BadMethodCallException instead.

This event loop is known to rely on wall-clock time to schedule future timers when using any version before PHP 7.3, because a monotonic time source is only available as of PHP 7.3 (hrtime()). While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). This means that if you schedule a timer to trigger in 30s on PHP < 7.3 and then adjust your system time forward by 20s, the timer may trigger in 10s. See also addTimer() for more details.

ExtEventLoop

An ext-event based event loop.

This uses the event PECL extension, that provides an interface to libevent library. libevent itself supports a number of system-specific backends (epoll, kqueue).

This loop is known to work with PHP 5.4 through PHP 8+.

ExtEvLoop

An ext-ev based event loop.

This loop uses the ev PECL extension, that provides an interface to libev library. libev itself supports a number of system-specific backends (epoll, kqueue).

This loop is known to work with PHP 5.4 through PHP 8+.

ExtUvLoop

An ext-uv based event loop.

This loop uses the uv PECL extension, that provides an interface to libuv library. libuv itself supports a number of system-specific backends (epoll, kqueue).

This loop is known to work with PHP 7+.

LoopInterface

run()

The run(): void method can be used to run the event loop until there are no more tasks to perform.

For many applications, this method is the only directly visible invocation on the event loop. As a rule of thumb, it is usually recommended to attach everything to the same loop instance and then run the loop once at the bottom end of the application.

$loop->run();

This method will keep the loop running until there are no more tasks to perform. In other words: This method will block until the last timer, stream and/or signal has been removed.

Likewise, it is imperative to ensure the application actually invokes this method once. Adding listeners to the loop and missing to actually run it will result in the application exiting without actually waiting for any of the attached listeners.

This method MUST NOT be called while the loop is already running. This method MAY be called more than once after it has explicitly been stop()ped or after it automatically stopped because it previously did no longer have anything to do.

stop()

The stop(): void method can be used to instruct a running event loop to stop.

This method is considered advanced usage and should be used with care. As a rule of thumb, it is usually recommended to let the loop stop only automatically when it no longer has anything to do.

This method can be used to explicitly instruct the event loop to stop:

$loop->addTimer(3.0, function () use ($loop) {
    $loop->stop();
});

Calling this method on a loop instance that is not currently running or on a loop instance that has already been stopped has no effect.

addTimer()

The addTimer(float $interval, callable $callback): TimerInterface method can be used to enqueue a callback to be invoked once after the given interval.

The second parameter MUST be a timer callback function that accepts the timer instance as its only parameter. If you don't use the timer instance inside your timer callback function you MAY use a function which has no parameters at all.

The timer callback function MUST NOT throw an Exception. The return value of the timer callback function will be ignored and has no effect, so for performance reasons you're recommended to not return any excessive data structures.

This method returns a timer instance. The same timer instance will also be passed into the timer callback function as described above. You can invoke cancelTimer to cancel a pending timer. Unlike addPeriodicTimer(), this method will ensure the callback will be invoked only once after the given interval.

$loop->addTimer(0.8, function () {
    echo 'world!' . PHP_EOL;
});

$loop->addTimer(0.3, function () {
    echo 'hello ';
});

See also example #1.

If you want to access any variables within your callback function, you can bind arbitrary data to a callback closure like this:

function hello($name, LoopInterface $loop)
{
    $loop->addTimer(1.0, function () use ($name) {
        echo "hello $name\n";
    });
}

hello('Tester', $loop);

This interface does not enforce any particular timer resolution, so special care may have to be taken if you rely on very high precision with millisecond accuracy or below. Event loop implementations SHOULD work on a best effort basis and SHOULD provide at least millisecond accuracy unless otherwise noted. Many existing event loop implementations are known to provide microsecond accuracy, but it's generally not recommended to rely on this high precision.

Similarly, the execution order of timers scheduled to execute at the same time (within its possible accuracy) is not guaranteed.

This interface suggests that event loop implementations SHOULD use a monotonic time source if available. Given that a monotonic time source is only available as of PHP 7.3 by default, event loop implementations MAY fall back to using wall-clock time. While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). This means that if you schedule a timer to trigger in 30s and then adjust your system time forward by 20s, the timer SHOULD still trigger in 30s. See also event loop implementations for more details.

addPeriodicTimer()

The addPeriodicTimer(float $interval, callable $callback): TimerInterface method can be used to enqueue a callback to be invoked repeatedly after the given interval.

The second parameter MUST be a timer callback function that accepts the timer instance as its only parameter. If you don't use the timer instance inside your timer callback function you MAY use a function which has no parameters at all.

The timer callback function MUST NOT throw an Exception. The return value of the timer callback function will be ignored and has no effect, so for performance reasons you're recommended to not return any excessive data structures.

This method returns a timer instance. The same timer instance will also be passed into the timer callback function as described above. Unlike addTimer(), this method will ensure the callback will be invoked infinitely after the given interval or until you invoke cancelTimer.

$timer = $loop->addPeriodicTimer(0.1, function () {
    echo 'tick!' . PHP_EOL;
});

$loop->addTimer(1.0, function () use ($loop, $timer) {
    $loop->cancelTimer($timer);
    echo 'Done' . PHP_EOL;
});

See also example #2.

If you want to limit the number of executions, you can bind arbitrary data to a callback closure like this:

function hello($name, LoopInterface $loop)
{
    $n = 3;
    $loop->addPeriodicTimer(1.0, function ($timer) use ($name, $loop, &$n) {
        if ($n > 0) {
            --$n;
            echo "hello $name\n";
        } else {
            $loop->cancelTimer($timer);
        }
    });
}

hello('Tester', $loop);

This interface does not enforce any particular timer resolution, so special care may have to be taken if you rely on very high precision with millisecond accuracy or below. Event loop implementations SHOULD work on a best effort basis and SHOULD provide at least millisecond accuracy unless otherwise noted. Many existing event loop implementations are known to provide microsecond accuracy, but it's generally not recommended to rely on this high precision.

Similarly, the execution order of timers scheduled to execute at the same time (within its possible accuracy) is not guaranteed.

This interface suggests that event loop implementations SHOULD use a monotonic time source if available. Given that a monotonic time source is only available as of PHP 7.3 by default, event loop implementations MAY fall back to using wall-clock time. While this does not affect many common use cases, this is an important distinction for programs that rely on a high time precision or on systems that are subject to discontinuous time adjustments (time jumps). This means that if you schedule a timer to trigger in 30s and then adjust your system time forward by 20s, the timer SHOULD still trigger in 30s. See also event loop implementations for more details.

Additionally, periodic timers may be subject to timer drift due to re-scheduling after each invocation. As such, it's generally not recommended to rely on this for high precision intervals with millisecond accuracy or below.

cancelTimer()

The cancelTimer(TimerInterface $timer): void method can be used to cancel a pending timer.

See also addPeriodicTimer() and example #2.

Calling this method on a timer instance that has not been added to this loop instance or on a timer that has already been cancelled has no effect.

futureTick()

The futureTick(callable $listener): void method can be used to schedule a callback to be invoked on a future tick of the event loop.

This works very much similar to timers with an interval of zero seconds, but does not require the overhead of scheduling a timer queue.

The tick callback function MUST be able to accept zero parameters.

The tick callback function MUST NOT throw an Exception. The return value of the tick callback function will be ignored and has no effect, so for performance reasons you're recommended to not return any excessive data structures.

If you want to access any variables within your callback function, you can bind arbitrary data to a callback closure like this:

function hello($name, LoopInterface $loop)
{
    $loop->futureTick(function () use ($name) {
        echo "hello $name\n";
    });
}

hello('Tester', $loop);

Unlike timers, tick callbacks are guaranteed to be executed in the order they are enqueued. Also, once a callback is enqueued, there's no way to cancel this operation.

This is often used to break down bigger tasks into smaller steps (a form of cooperative multitasking).

$loop->futureTick(function () {
    echo 'b';
});
$loop->futureTick(function () {
    echo 'c';
});
echo 'a';

See also example #3.

addSignal()

The addSignal(int $signal, callable $listener): void method can be used to register a listener to be notified when a signal has been caught by this process.

This is useful to catch user interrupt signals or shutdown signals from tools like supervisor or systemd.

The second parameter MUST be a listener callback function that accepts the signal as its only parameter. If you don't use the signal inside your listener callback function you MAY use a function which has no parameters at all.

The listener callback function MUST NOT throw an Exception. The return value of the listener callback function will be ignored and has no effect, so for performance reasons you're recommended to not return any excessive data structures.

$loop->addSignal(SIGINT, function (int $signal) {
    echo 'Caught user interrupt signal' . PHP_EOL;
});

See also example #4.

Signaling is only available on Unix-like platforms, Windows isn't supported due to operating system limitations. This method may throw a BadMethodCallException if signals aren't supported on this platform, for example when required extensions are missing.

Note: A listener can only be added once to the same signal, any attempts to add it more than once will be ignored.

removeSignal()

The removeSignal(int $signal, callable $listener): void method can be used to remove a previously added signal listener.

$loop->removeSignal(SIGINT, $listener);

Any attempts to remove listeners that aren't registered will be ignored.

addReadStream()

Advanced! Note that this low-level API is considered advanced usage. Most use cases should probably use the higher-level readable Stream API instead.

The addReadStream(resource $stream, callable $callback): void method can be used to register a listener to be notified when a stream is ready to read.

The first parameter MUST be a valid stream resource that supports checking whether it is ready to read by this loop implementation. A single stream resource MUST NOT be added more than once. Instead, either call removeReadStream() first or react to this event with a single listener and then dispatch from this listener. This method MAY throw an Exception if the given resource type is not supported by this loop implementation.

The second parameter MUST be a listener callback function that accepts the stream resource as its only parameter. If you don't use the stream resource inside your listener callback function you MAY use a function which has no parameters at all.

The listener callback function MUST NOT throw an Exception. The return value of the listener callback function will be ignored and has no effect, so for performance reasons you're recommended to not return any excessive data structures.

If you want to access any variables within your callback function, you can bind arbitrary data to a callback closure like this:

$loop->addReadStream($stream, function ($stream) use ($name) {
    echo $name . ' said: ' . fread($stream);
});

See also example #11.

You can invoke removeReadStream() to remove the read event listener for this stream.

The execution order of listeners when multiple streams become ready at the same time is not guaranteed.

Some event loop implementations are known to only trigger the listener if the stream becomes readable (edge-triggered) and may not trigger if the stream has already been readable from the beginning. This also implies that a stream may not be recognized as readable when data is still left in PHP's internal stream buffers. As such, it's recommended to use stream_set_read_buffer($stream, 0); to disable PHP's internal read buffer in this case.

addWriteStream()

Advanced! Note that this low-level API is considered advanced usage. Most use cases should probably use the higher-level writable Stream API instead.

The addWriteStream(resource $stream, callable $callback): void method can be used to register a listener to be notified when a stream is ready to write.

The first parameter MUST be a valid stream resource that supports checking whether it is ready to write by this loop implementation. A single stream resource MUST NOT be added more than once. Instead, either call removeWriteStream() first or react to this event with a single listener and then dispatch from this listener. This method MAY throw an Exception if the given resource type is not supported by this loop implementation.

The second parameter MUST be a listener callback function that accepts the stream resource as its only parameter. If you don't use the stream resource inside your listener callback function you MAY use a function which has no parameters at all.

The listener callback function MUST NOT throw an Exception. The return value of the listener callback function will be ignored and has no effect, so for performance reasons you're recommended to not return any excessive data structures.

If you want to access any variables within your callback function, you can bind arbitrary data to a callback closure like this:

$loop->addWriteStream($stream, function ($stream) use ($name) {
    fwrite($stream, 'Hello ' . $name);
});

See also example #12.

You can invoke removeWriteStream() to remove the write event listener for this stream.

The execution order of listeners when multiple streams become ready at the same time is not guaranteed.

removeReadStream()

The removeReadStream(resource $stream): void method can be used to remove the read event listener for the given stream.

Removing a stream from the loop that has already been removed or trying to remove a stream that was never added or is invalid has no effect.

removeWriteStream()

The removeWriteStream(resource $stream): void method can be used to remove the write event listener for the given stream.

Removing a stream from the loop that has already been removed or trying to remove a stream that was never added or is invalid has no effect.

Install

The recommended way to install this library is through Composer. New to Composer?

Once released, this project will follow SemVer. At the moment, this will install the latest development version:

composer require react/event-loop:^3@dev

See also the CHANGELOG for details about version upgrades.

This project aims to run on any platform and thus does not require any PHP extensions and supports running on legacy PHP 5.3 through current PHP 8+ and HHVM. It's highly recommended to use the latest supported PHP version for this project.

Installing any of the event loop extensions is suggested, but entirely optional. See also event loop implementations for more details.

Tests

To run the test suite, you first need to clone this repo and then install all dependencies through Composer:

composer install

To run the test suite, go to the project root and run:

vendor/bin/phpunit

License

MIT, see LICENSE file.

More

event-loop's People

Contributors

cameronjacobson avatar carusogabriel avatar cboden avatar cebe avatar clue avatar dandelionred avatar e3betht avatar greevex avatar igorw avatar ivkalita avatar jmalloc avatar jsor avatar kelunik avatar kooldev avatar lcobucci avatar lt avatar lucasnetau avatar mkrauser avatar nawarian avatar nhedger avatar nrk avatar ocramius avatar ondrejmirtes avatar pablokowalczyk avatar pborreli avatar reedy avatar seregazhuk avatar simonfrings avatar steverhoades avatar wyrihaximus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

event-loop's Issues

Why callbacks cannot throw exceptions?

I'd like to use the event loop to limit the max execution time of a given function, instead of the native set_time_limit. However, the documentation says the callback function should not throw exceptions.

Is there any other way to do this?

$loop->addTimer(0.1, function () {
    throw new TimeoutException('Max execution of 100ms');
});

Possibility to resume the loop

Hi,

You can read my original post here. I described issue with using block-react lib since there is no way to resume the stopped loop. @clue advised to use second loop (which I use now and it's only non-perfect, working solution), but as i read in issues and PRs here, You're aiming to make one global loop, so this won't work anymore. My idea is to make possible to resume React loop and method implementing this should only set running to true (exactly opposite what stop method does). This shouldn't break anything, just adds new possibility :)

I also understand what @clue mentioned that we shouldn't mix async and sync code, but we're not living in perfect world ;)

Any thoughts appreciated.

Multiple event loop

How can I have multiple event loop in a script??Without blocking each other . I want to use select and event

Call to undefined method in memory benchmark

kelunik@kelunik โฏ ~/GitHub/reactphp/event-loop โฏ 16:13:08 โฏ master
$ time php examples/95-benchmark-memory.php 
PHP Version:		7.2.0
Loop			React\EventLoop\StreamSelectLoop
Time			Fri, 22 Dec 2017 15:13:19 +0000
--------------------------------------------------
PHP Fatal error:  Uncaught Error: Call to undefined method React\EventLoop\Timer\Timer::cancel() in /home/kelunik/GitHub/reactphp/event-loop/examples/95-benchmark-memory.php:39
Stack trace:
#0 [internal function]: {closure}(Object(React\EventLoop\Timer\Timer))
#1 /home/kelunik/GitHub/reactphp/event-loop/src/Timer/Timers.php(99): call_user_func(Object(Closure), Object(React\EventLoop\Timer\Timer))
#2 /home/kelunik/GitHub/reactphp/event-loop/src/StreamSelectLoop.php(182): React\EventLoop\Timer\Timers->tick()
#3 /home/kelunik/GitHub/reactphp/event-loop/examples/95-benchmark-memory.php(59): React\EventLoop\StreamSelectLoop->run()
#4 {main}
  thrown in /home/kelunik/GitHub/reactphp/event-loop/examples/95-benchmark-memory.php on line 39

real	0m1,065s
user	0m0,127s
sys	0m0,012s

HHVM test fails because of arbitrary speed not matching.

1) React\Tests\EventLoop\StreamSelectLoopTest::testStopShouldPreventRunFromBlocking
Failed asserting that 0.010394096374512 is less than 0.005.

The purpose of the test is to make sure a timer is not still executed after stop is called.
The timer is set to 1 second, so the test should still work if the speed is adjusted for more HHVM headroom, like 0.05 instead of 0.005.

Make loop classes extendable

Hallo,
I currently working on an multi-processing library (https://github.com/RogerWaters/react-thread-pool) compatible with react. My problem is that I require some additional functionality on the loop classes and everything is private here. Best way will be to extend the loops and add the functions (afterForkParent, afterForkChild).
Would you prefer that I create an pull request with all the functions and variables protected within the classes, or would you prefer that I add those functions directly to the classes? The only alternative will be to reflect those classes and this will result in poor performance and bad readable code. Maybe anyone have another solution?

Thanks for your help
RogerWaters

Suggest values in composer.json

in composer.json the suggest attributes have version "constraints".

According to the composer documentation:
"The format is like package links above, except that the values are free text and not version constraints."
Generally it's saying that it will not fail when a wrong version is install.

Proposed solutions:

  1. Maybe it's more useful to add a useful description so people know which extension is best to install. The version constraints can still be included in that description.
  2. Maybe it's useful to write separate "driver" packages with the extensions as requirements. And make use of the provide to implement a virtual packages, which is "suggested" or "required" in this package.

The latter is of course the more demanding proposal.

LibEventLoop doesn't notifies userland code immediately

I'm working on a PHP keylogger using the Linux evdev API (characted devices in /dev/input/).

I'm working on the React interface of the library, which is a specialization of my main class.
Using my main (non-React) class and logging the events, I can get this type of log when I press the space key one time (simplified output) :

-- handleData --   -> handleData called
key=57 value=1     -> keydown on space
-- handleData --
key=57 value=0     -> keyup on space

OK. I pressed space one time and I instantly get two events related to the space key. All good.

Now, using the React extend of my keylogger and doing the same key press :

-- handleData --
key=57 value=1     -> keydown on space

You can notice I don't get the keyup event. Nothing happens until I press another key. So, let's press space another time :

-- handleData --   -> previous log
key=57 value=1     -> previous log
-- handleData --
key=57 value=0     -> it is displayed only now!!
-- handleData --
key=57 value=1     -> ok, expected new keydown
                   -> missing new keyup... 'til I press another key

I was using EventLoop\Factory::create() to get the event loop. It was returning a LibEventLoop. I tried to force the loop class used by replacing the factory with the plain-PHP StreamSelectLoop. And it works perfectly with this loop class.

I can't try with LibEvLoop or ExtEventLoop since these libraries aren't installed on my system.

Maybe it's a quirk of LibEventLoop on character files?
I hope I'm clear enough !

Test failing depending o Kernel and OS configurations

Hi, all!

I was running the tests at my local environment because I wanted to improve somethings and I like to start by making sure everything is running properly ^^
Thing is that the test \React\Tests\EventLoop\AbstractLoopTest::testRemoveReadAndWriteStreamFromLoopOnceResourceClosesOnEndOfFileEndsLoop is failing for all implementations with the following message:

fwrite(): send of 8192 bytes failed with errno=35 Resource temporarily unavailable

This happens at line 231, which runs this: fwrite($other, str_repeat('.', 60000));

I did some basic research and looks like Mac OS can have very low values for buffer sizes, but changing those values via sysctl didn't take much effect.

According to this errors list errno=35 Resource temporarily unavailable means kPOSIXErrorEAGAIN and according to here the EAGAIN error says that possibly it would work in further attempts (not desirable for unit testing) and can be caused because a) a resource could be doing a blocking operation and was set as non-blocking before, which is plausible because for both streams we call stream_set_blocking with false or b) due to a resource shortage, which for me would make sense with the sysctl stuff. Documentation says that maybe delaying certain operations would help.

I suppose this kind of error would be super hard to reproduce, but I have something else that may help:

nawarian@phoenix event-loop (master) $ sysctl -a | egrep "space|maxsockbuf"
kern.ipc.maxsockbuf: 8388608
net.local.stream.recvspace: 8192
net.local.stream.sendspace: 8192
net.local.dgram.recvspace: 4096
net.inet.tcp.sendspace: 131072
net.inet.tcp.recvspace: 131072
net.inet.udp.recvspace: 196724
net.inet.raw.recvspace: 8192
net.stats.sendspace: 2048
net.stats.recvspace: 8192

It might be possible to reproduce such error if you run something like sysctl -w variable.name=new-value using those keys and values. But again: I'm not sure.

Could also be useful:

nawarian@phoenix event-loop (master) $ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.12.6
BuildVersion:	16G1114

Oh yes, almost forgot: lowering the string length at fwrite($other, str_repeat('.', 60000)); solve the issue.

How to read response from smtp server?

I connecting to SMTP server. (alt1.gmail-smtp-in.l.google.com:25 for example)
How can i read response when i write $connection->write('HELO alt1.gmail-smtp-in.l.google.com');?

Event::set(): supplied resource is not a valid

There is a heavy script which is running 24x7. Before it worked on php5.6 + libevent + php libevent + react. After update to php 7.1 + libevent + php event (React\EventLoop\ExtEventLoop) + react I got periodical crashes with stack below. I am trying to debug issue, but it is difficult cause issue happens relatively rarely. Any help or ideas would be great.

> [2017-12-11 13:45:47] prod.ALERT: ErrorException: Event::set(): supplied resource is not a valid Socket resource in /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php:264
> Stack trace:
> #0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'Event::set(): s...', '/home/projects/...', 264, Array)
> #1 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(264): Event->set(Object(EventBase), Resource id #118542, 20, Object(Closure))
> #2 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(96): React\EventLoop\ExtEventLoop->unsubscribeStreamEvent(Resource id #118542, 2)
> #3 /home/projects/application/vendor/react/stream/src/DuplexResourceStream.php(105): React\EventLoop\ExtEventLoop->removeReadStream(Resource id #118542)
> #4 /home/projects/application/vendor/react/stream/src/DuplexResourceStream.php(139): React\Stream\DuplexResourceStream->pause()
> #5 /home/projects/application/vendor/react/socket/src/Connection.php(116): React\Stream\DuplexResourceStream->close()
> #6 /home/projects/application/vendor/react/http-client/src/Request.php(218): React\Socket\Connection->close()
> #7 /home/projects/application/vendor/react/http-client/src/Request.php(205): React\HttpClient\Request->close()
> #8 /home/projects/application/vendor/react/http-client/src/Request.php(178): React\HttpClient\Request->closeError(Object(RuntimeException))
> #9 /home/projects/application/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\HttpClient\Request->handleEnd()
> #10 /home/projects/application/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit('end', Array)
> #11 /home/projects/application/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\Stream\Util::React\Stream\{closure}()
> #12 /home/projects/application/vendor/react/stream/src/DuplexResourceStream.php(196): Evenement\EventEmitter->emit('end')
> #13 [internal function]: React\Stream\DuplexResourceStream->handleData(Resource id #118542)
> #14 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(299): call_user_func(Array, Resource id #118542)
> #15 [internal function]: React\EventLoop\ExtEventLoop->React\EventLoop\{closure}(Resource id #118542, 6, NULL)
> #16 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(182): EventBase->loop(3)
> #17 /home/projects/application/library/Main.php(163): React\EventLoop\ExtEventLoop->run()
> 
> Next Exception: Expected either valid PHP stream or valid PHP socket resource in /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php:264
> Stack trace:
> #0 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(264): Event->set(Object(EventBase), Resource id #118542, 20, Object(Closure))
> #1 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(96): React\EventLoop\ExtEventLoop->unsubscribeStreamEvent(Resource id #118542, 2)
> #2 /home/projects/application/vendor/react/stream/src/DuplexResourceStream.php(105): React\EventLoop\ExtEventLoop->removeReadStream(Resource id #118542)
> #3 /home/projects/application/vendor/react/stream/src/DuplexResourceStream.php(139): React\Stream\DuplexResourceStream->pause()
> #4 /home/projects/application/vendor/react/socket/src/Connection.php(116): React\Stream\DuplexResourceStream->close()
> #5 /home/projects/application/vendor/react/http-client/src/Request.php(218): React\Socket\Connection->close()
> #6 /home/projects/application/vendor/react/http-client/src/Request.php(205): React\HttpClient\Request->close()
> #7 /home/projects/application/vendor/react/http-client/src/Request.php(178): React\HttpClient\Request->closeError(Object(RuntimeException))
> #8 /home/projects/application/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\HttpClient\Request->handleEnd()
> #9 /home/projects/application/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit('end', Array)
> #10 /home/projects/application/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\Stream\Util::React\Stream\{closure}()
> #11 /home/projects/application/vendor/react/stream/src/DuplexResourceStream.php(196): Evenement\EventEmitter->emit('end')
> #12 [internal function]: React\Stream\DuplexResourceStream->handleData(Resource id #118542)
> #13 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(299): call_user_func(Array, Resource id #118542)
> #14 [internal function]: React\EventLoop\ExtEventLoop->React\EventLoop\{closure}(Resource id #118542, 6, NULL)
> #15 /home/projects/application/vendor/react/event-loop/src/ExtEventLoop.php(182): EventBase->loop(3)
> #16 /home/projects/application/library/Main.php(163): React\EventLoop\ExtEventLoop->run()
> 

1.0 release

Event loop 0.4 is very stable and there aren't any big BC breaks planned (maybe #30) so what is stopping us from putting a list of small things together we want in 1.0 and work towards that so we can tag 1.0 and push any major redesigns to 2.0?

[Question] How to prevent the amount of ram used from rising in the loop

Hi, I have a problem. I have a loop with a futuretick that connects to the database only once and begins to make requests and update data, the problem is that the percentage of ram on the server does not stop uploading. It goes on and on with each iteration of the loop.

What are the recommendations so that this loop can keep the server running so that this does not happen?
I have to clean the cache?
destroy objects?
or is there something else that makes this happen?

Recommanded Loop implementation for php7

Hi,

i'm trying to upgrade my app to php7. I used libevent and the matching pecl extension which is not available anymore for php7

What would you recommend ?

Did some of you make the switch already ?
What solution did you choose ?

Thank you for the effort in ReactPHP

Add timer within launched loop doesn't work

Hey,

I'm making face to a problem, i need to dynamcally attach timer. I'm on the context of websocket server (on top of ratchet) and i would use timer to emit message.

So i have created a component to attach timer on specific connection. But the callback is just played once :(

It's the expected behavior ? Am i on the wrong to achieve that ?

Thanks

Speed up StreamSelectLoop

There is very simple way to speed up StreamSelectLoop, because usleep(0) not skipping sleep, cuz it function call;

$t = microtime(true);
usleep(0);
$r = microtime(true);
echo ($r-$t)*1000000; //69.856643676758

vs

$t = microtime(true);
0 && usleep(0);
$r = microtime(true);
echo ($r-$t)*1000000; //5.9604644775391

So in the cycle with timeout in variable

$timeout = 0;
$start = microtime(true);
for($i = 0; $i < 100000; $i++) {
    usleep($timeout);
}
echo microtime(true) - $t; //6.6996

vs

$timeout = 0;
$start = microtime(true);
for($i = 0; $i < 100000; $i++) {
    $timeout && usleep($timeout); //skip null and 0 sleeps
}
echo microtime(true) - $t; //0.026

Remove misleading Composer suggest keywords

For example, if I have ext-ev installed, Composer still suggest installing ext-libevent while it's unclear from a consumer's perspective how much value this provides.

Note that #113 has improved the suggest keyword description for each extension somewhat, but I believe this is more an issue with how Composer suggests these dependencies and now really something that can be addressed in this package.

As an alternative, I suggest simply dropping the suggest keyword completely. Is much gained by having this in place? Instead, I would suggest updating our install instructions to detail why having either extension can be a good thing (under what circumstances anyway?).

Roadmap to stable v1.0.0

Let's face it, this project is stable and has been used in production for years :shipit:

However, we're currently following a v0.X.Y release scheme (http://sentimentalversioning.org/).

We should finally make this explicit and fully adhere to SemVer and release a stable v1.0.0.

To a large extend, a stable v1.0.0 helps making BC breaks more explicit and thus the whole project more reliable from a consumer perspective. This project is actively maintained and has received some major updates in the last weeks and has some major updates planned in the next weeks. Given our current versioning scheme, we'd like to ensure all anticipated BC breaks will be merged before the planned v1.0.0 release.

As such, I've set up a roadmap that enlists only the major changes for each version among with planned release dates towards a stable v1.0.0 release:

v0.4.3 โœ…

  • Released 2017-04-27
  • Bugfix release

v0.5.0 โœ…

  • Released 2018-04-05
  • Signal handling support
  • Documentation overhaul
  • Reduce public API
  • Throw on error

v1.0.0

  • Planned 2018-Q2?
  • No new changes planned, this should merely mark the previous release as "stable"

This ticket aims to serve as a basic overview and does not contain every single change. Please also see the milestone links and the CHANGELOG for more details.

Obviously, this roadmap is subject to change and I'll try to keep it updated as we progress. In order to avoid cluttering this, please keep discussion in this ticket to a minimum and consider reaching out to us through new tickets or Twitter etc.

PHP Warning: stream_select(): 26 is not a valid stream resource

This occurs when you try to close a resource already sent to stream_select (I assume this would be the same for the other implementations).

A possible solution would be to add a entry point right before you start another "wait for stream to change" session so that you can execute a resource close avoiding those nasty warnings.

Also: "PHP Warning: stream_select(): No stream arrays were passed"

All happens in StreamSelectLoop.php on line 255

Priority issues with multiple timers

If you run the following code

$loop = \React\EventLoop\Factory::create();
     for ($counter = 1; $counter <= 5; $counter++) {

         echo "Counter before loop {$counter}\n";
         $loop->addTimer(0.001, function () use ($counter) {
             echo "Counter in loop {$counter}\n";
         });
     }

$loop->run();

You get this output

Counter before loop 1
Counter before loop 2
Counter before loop 3
Counter before loop 4
Counter before loop 5
Counter in loop 1
Counter in loop 5
Counter in loop 4
Counter in loop 3
Counter in loop 2

The counter within the loop should be in the same order as the counter before the loop.

It looks like the issue is with line 33 in Timers.php. It adds the interval to $this->getTime(), which doesn't change.

A simple fix is to replace getTime() with updateTime(), which works as expected, but I'm not sure it that causes issues anywhere else.

$scheduledAt = $interval + $this->updateTime();

Let me know if you don't think it'll cause any other issues and I'll submit a PR.

Possible of dealing with SignalEvent

Usually a script using event loop should be able to deal with signals. Instead of using pcntl functions, can we open up the support for SignalEvent?

However as the loop is declared as private, there is no way of implement that by using either timer or tick queue.

Any thought?

StreamSelectLoop: Int overflow if the timer interval is big on 32-bit systems

if $this->getNextEventTimeInMicroSeconds() in StreamSelectLoop.php returns a float value greater than max int value, it will give you this error

stream_select(): The microseconds parameter must be greater than 0

This will happen if the next event interval is greater than 2148. Or, in other words you can't have a periodic timer with interval greater than 2148 seconds.

Workarounds: have a dummy periodic timer with interval less than 2148 seconds.

Support reference / unreference of watchers

Are there any plans to support referencing / unreferencing watchers as discussed in async-interop/event-loop? It's quite helpful to exit the loop when no further watchers exist, but cache expiration watchers or similar "background" watchers prevent this. If a watcher can be unreferenced, the loop exits if only unreferenced watchers exist anymore, instead of any watchers. It would thus be possible to have a cache expiration watcher that regularly checks items in a list for expiration and deletes them without having it interfere with the loop exit behavior.

ExtEventLoop causes segfault for pg_socket() resources

How can I addReadStream to pg_socket?

I try the following code on PHP 7.0

$loop = React\EventLoop\Factory::create();
$connection = pg_connect("host=localhost port=5432 dbname=mary");
$socket = pg_socket($connection);
$loop->addReadStream($socket , function (){
  echo "READ EVENT\n";
});

pg_send_query($connection , "select * from authors;");
$loop->run();

The code works well when I choose Select But when I run code with Event extension I have Segmentation fault

More details on http://php.net/manual/en/function.pg-socket.php

Multiple function calls asynchronously

Hi ! I need to execute multiple calls to same function asynchronously. I have 2 projects (project1 and project2):

Project 1:

/**
* Loop an array of urls and call sub function.
**/
public function startFunction() {
  $finalResponse = [];
  $urls = ['www.google.es', 'www.github.com', 'www.bitbucket.org'];

  foreach ($urls as $url) {
    $res = $this->subFunction($url);    // subfunction call ( **IT MAY TAKE A LONG TIME !!** )
    $finalResponse[] = $res;
 }

 return $finalResponse;
}
/**
* Uses Factory loop to get the Promise returned by finalRequest function.
**/
private function subFunction($url) {
  $loop = \React\EventLoop\Factory::create();
  $classA = new Project2\ClassA();
  $finalResponse = null;

  // project 2 function call
  $classA->finalRequest($url)->then(function($response) use(   
     &$finalResponse
  ) {
     $finalResponse = $response;
  })
  
  return $finalResponse;
}

Project 2:

/**
* Makes an React\HttpClient request (GET) to sent url and return his value inside a Promise.
**/
public function finalRequest($url) {
   $generalDeferred = new Deferred();
   $generalPromise = $generalDeferred->promise();

   // make React\HttpClient request
   $request = $client->request('GET', $url);
   $request->on('response', function ($response) use($generalDeferred) {
   $response->on('data', function ($response) {
        $generalDeferred->resolve($response);
      });
   });
  $request->end();

  return $generalPromise;
}

Problem is that on every subFunction($url) call, the program stops until the sub Function gets the response, but I need to do this asynchronously because this subFunction could take many seconds.
So I would like to launch all subFunction($url) calls at the same time, and get all responses asynchronously.

It's possible solve this problem? Thanks.

Autoloading in `class_exists`

Hi. Just found an issue in the Factory class on master concerning the behavior of class_exists. I'd like to suggest changing class_exists(<classname>) to class_exists(<classname>, FALSE) to prevent autoloading when the class is not defined.

Prioritizing events in the loop

I am just starting with react so my question might be dumb. Pardon me for that, but here it is:

I have a Socket server with React. The use case of this server is to receive objects from its clients (which would mostly be my own web app backend) and dispatch them to external services via standard APIs - like sending email, sending SMS, sending messages to slack channel, push notifications, etc.

I do something like this:

require_once 'autoload.php';
	use \App\Experiment\SenderService;

	/** @var \React\EventLoop\StreamSelectLoop $loop */
	$loop = React\EventLoop\Factory::create();
	$server = new React\Socket\Server($loop);

	$connectionPool = new \SplObjectStorage();

	$server->on('connection', function ($conn) use ($connectionPool, $loop) {
		$strDataRead = 'xyz';
		/** @var \React\Socket\Connection $conn */
		$conn->on('data', function ($data) use ($connectionPool, $conn, &$strDataRead) {
			$connectionPool->attach($conn);

			$obj = unserialize($data);
			
			$strDataRead = serialize($data);
		});


		$conn->on('end', function () use ($connectionPool, $conn, &$strDataRead, $loop) {
			$connectionPool->detach($conn);

			$loop->futureTick(function () use ($strDataRead) {
				SenderService\Dispatcher::Dispatch($strDataRead);
			});
		});
	});

	echo "Socket server listening on port 4000.\n";
	echo "You can connect to it by running: telnet localhost 4000\n";
	$server->listen(4000);
	$loop->run();

The problem here is that when I send burst connections to the server using another script, the initial 30-40 requests get queued up real fast and then the server slows down and starts accepting requests at only about 1 request per second and that is very slow and defeats the purpose of the very service that I am trying to create.

My understanding is that it stats ticking using the tick function:

public function tick()
    {
        $this->nextTickQueue->tick();

        $this->futureTickQueue->tick();

        $this->timers->tick();

        $this->waitForStreamActivity(0);
    }

And there, it waits for stream activity only after it has processed all the ticks. Since there are now about 30-40 requests queued up, it gets slow. (I might be wrong in my assumption; please tell me if I am).

I want to prioritize the incoming connections to the server over the dispatcher so that the service is always ultra responsive in accepting new requests and goes on dispatching when it gets the time. How do I do that?

StreamSelectLoop: Timers with very small intervals do not work correctly

Within StreamSelectLoop::run() is the following code:

} elseif ($scheduledAt = $this->timers->getFirst()) { // float
    $timeout = $scheduledAt - $this->timers->getTime(); // float - float
    if ($timeout < 0) {
        $timeout = 0;
    } else {
        $timeout *= self::MICROSECONDS_PER_SECOND; // float * int
    }
}

In the case of a periodic timer with an interval equal to Timer::MIN_INTERVAL (0.000001), the above code, in conjunction with the Timers class, essentially does something like the following:

$currentTime = microtime(true);
$ourTimerInterval = 0.000001;
$nextTimerScheduledAt = $currentTime + $ourTimerInterval;
// $timeout is for stream_select()/usleep() call
$timeout = $nextTimerScheduledAt - $currentTime; // This is NOT equal to 0.000001 due to float error [1]
$timeout *= 1000000; // This looks like it should be (int)1, but it is actually approximately (float)0.95

$timeout is then passed to stream_select() and usleep(), both of which expect int and so cast (float)0.95 to (int)0, not waiting at all when the timer was intended to delay for 1 microsecond. Furthermore, the fix in #37 is bypassed when this issue occurs as (float)0.95 casts to boolean true.

A quick fix would be to apply rounding to $timeout *= self::MICROSECONDS_PER_SECOND;.

[1] https://3v4l.org/FvioX

StreamSelectLoop:Timers added during stream events are not accurate

New timers are scheduled relative to a local time kept in the timers class, which is only updated when Timers::tick() is run. However, when there are no scheduled tick callbacks, StreamSelectLoop will wait until the next scheduled timer (or infinite, if there are none) for stream activity, which prevents Timers::tick() from being called regularly and updating its local time. If a new timer is added in response to a stream event, it will be shortened by whatever period of time was spent waiting for stream activity (or, if the new timer's length is shorter than was spent waiting, the timer will be resolved immediately).

I believe a potential solution is:

diff --git a/src/Timer/Timers.php b/src/Timer/Timers.php
index c183a63..bebe552 100644
--- a/src/Timer/Timers.php
+++ b/src/Timer/Timers.php
@@ -30,7 +30,7 @@ class Timers
     public function add(TimerInterface $timer)
     {
         $interval = $timer->getInterval();
-        $scheduledAt = $interval + $this->getTime();
+        $scheduledAt = $interval + microtime(true);

         $this->timers->attach($timer, $scheduledAt);
         $this->scheduler->insert($timer, -$scheduledAt);

I can provide example reproducing scripts if preferred.

addPeriodicTimer() blocks the execution of my code

Hi folks!

I have a code like this:

      if ($message->content == 'startrare') {
          $loop->run($timer);
      } 

When if = true, loop runs and my code stops executing. Acts like a traditional php while/foreach...

Loop code looks like this:

$loop = React\EventLoop\Factory::create();
$i = 0;
$timer = $loop->addPeriodicTimer(5, function() use (&$myvar0, $loop, &$myvar1, &$myvar2, &$myvar3) {
echo 'OK';
...

I receive 'OK' each 5 seconds, but other code after (or before) loop not working until loop stops :/

Optionally inject EventBase into ExtEventLoop

I am working on a project that I think could be complementary to your project:
https://github.com/cameronjacobson/Spas

** Look at examples/* files for a quick glimpse into what I'm trying to do.

Not as a component of React, but by using react as an injectable component in my project. Similar to how "Request" variables are injected into callbacks in Silex. I would write a ControllerResolver that injected additional components. The reason I'm writing this note is because my project assumes its own EventBase (using EventHttp) is already instantiated. And since using React as an injectable component would be optional, it wouldn't necessarily make sense to do all the React initialization up front. So if a user could inject a React event-loop (using the EventBase already instantiated) the user could use React inside the Silex controller seamlessly.

When reviewing the event-loop project I noted the 'private' visibility for the EventBase. I understand that my use-case may not be something you have thought of, but I think it's a valid one and I think would be pretty powerful way to utilize React.

I see that another option is for me to just implement the LoopInterface in my own implementation of an EventLoop for your project, and perhaps you feel better going that route, but given the new use case it seems it could open alot of possibilities for your project if either visibility was increased / getters / setters, and / or perhaps EventBase was injectable into the event-loop itself.

how can I get out of the loop (futuretick)

Hi how can I get out of the loop (futuretick) correctly with a command, key or typing in the console

example code

function fooBar($loop) {
    return function () use ($loop) {
       // my code 

// end my code
sleep(1);
        $loop->futureTick(fooBar($loop));
    };
}

$loop->futureTick(fooBar($loop));
$loop->run();

As I exit this loop correctly and without a Ctrl + C program I interrupt the running program

LibEventLoop and large messages

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.

Error in the "Usage" sample code

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

$server = stream_socket_server('tcp://127.0.0.1:8080');
stream_set_blocking($server, 0);
$loop->addReadStream($server, function ($server) use ($loop) {
    $conn = stream_socket_accept($server);
    $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
    $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
        $written = fwrite($conn, $data);
        if ($written === strlen($data)) {
            fclose($conn);
            $loop->removeStream($conn);
        } else {
// ==========================================
            $data = substr($data, 0, $written);
// Shouldn't it be substr($data, $written) ?
// ==========================================
        }
    });
});

$loop->addPeriodicTimer(5, function () {
    $memory = memory_get_usage() / 1024;
    $formatted = number_format($memory, 3) . 'K';
    echo "Current memory usage: {$formatted}\n";
});

$loop->run();

No way to add data to a timer in StreamSelectLoop

LoopInterface.php

    public function addTimer($interval, $callback);
    public function addPeriodicTimer($interval, $callback);

StreamSelectLoop.php

    public function addTimer($interval, $callback);
    public function addPeriodicTimer($interval, $callback);

I need to add a timer with associated data to my loop. I can create my own Timer object, but I have no way to inject it into the list maintained in StreamSelectLoop

I can PR something, just not sure how you want to approach it.

[libevent] is currently not work in php7

It all start with:

/vendor/react/event-loop/LibEventLoop.php on line 34

After trying to make it work, it come to:

pecl/libevent requires PHP (version >= 5.3.0, version <= 6.0.0, excluded versions: 6.0.0), installed version is 7.0.1-2+deb.sury.org~wily+1

No valid packages found

Not sure if I report this here is right since it depend on libevent

If tick is called in futureTickQueue we got exception

Hi,

On our project we call futureTick() in case in stream handler we need to wait for new data to populate. It has internal call to tick() that eventually clears current futureTick queue. In FutureTickQueue in tick() we simply call dequeue() exact amount of times equal to queue length at the tick() beginning. The problem is that we don't check if queue is already empty.

Allow multiple IO callbacks or throw

Currently, multiple IO callbacks are ignored, only the first one will work. #86 fixes this to be consistent, but silently ignoring should be changed to either throw exceptions or call all handlers and not ignore them.

Use monotonic time when available

Currently, PHP doesn't support and built-in way to access the system's monotonic time. That might change in PHP 7.2. I plan to implement a hrtime() function providing access to the high-resolution monotonic system time. If UV is available, but another loop is used, uv_hrtime() could be used in the meantime.

This issue exists to keep track of any improvements in PHP itself and to not forget implementing hrtime() in the future.

https://externals.io/thread/592#email-14248 updated: https://externals.io/message/97502

PHPUnit Test Fail, Windows 10, PHP 7

Not sure if this is known, trying to debug an issue elsewhere in React, so running the test suites on all installed components... Please ignore/close this issue if unhelpful/not required...

PHPUnit 4.8.32 by Sebastian Bergmann and contributors.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS  63 / 145 ( 43%)
SSSSSSSSSSSSSSSSSSSSSSS..SSSSSS...........................SSSSS 126 / 145 ( 86%)
SSSSSSSSSSSSSF.....

Time: 846 ms, Memory: 8.00MB

There was 1 failure:

1) React\Tests\EventLoop\Timer\StreamSelectTimerTest::testAddTimer
Expectation failed for method name is equal to <string:__invoke> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

FAILURES!
Tests: 145, Assertions: 44, Failures: 1, Skipped: 110.

Using version 0.4.2 (dev-master has this error along with another as shown below:

PHPUnit 4.8.32 by Sebastian Bergmann and contributors.

SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS  63 / 146 ( 43%)
SSSSSSSSSSSSSSSSSSSSSSS.SSSSSS............................SSSSS 126 / 146 ( 86%)
SSSSSSSSSSSSSFF.....

Time: 1.84 seconds, Memory: 8.00MB

There were 2 failures:

1) React\Tests\EventLoop\Timer\StreamSelectTimerTest::testAddTimer
Expectation failed for method name is equal to <string:__invoke> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

2) React\Tests\EventLoop\Timer\StreamSelectTimerTest::testAddPeriodicTimer
Expectation failed for method name is equal to <string:__invoke> when invoked 3 time(s).
Method was expected to be called 3 times, actually called 2 times.

FAILURES!
Tests: 146, Assertions: 44, Failures: 2, Skipped: 110.

Can addPeriodicTimer() run on absolute time intervals?

Can I tell addPeriodicTimer() to run on intervals, that are exactly 1 second apart?
Currently, if I run the following code, I get the following output:

$this->loop->addPeriodicTimer(1, function() {
    echo microtime(); 
});

Output:
0.37149400 1407178007
0.37158300 1407178008
0.37231200 1407178009
0.37331500 1407178010
0.37484600 1407178011
0.37504500 1407178012
0.37549600 1407178013
0.37581600 1407178014
0.37665200 1407178015
0.37732800 1407178016
0.37813900 1407178017
0.37861000 1407178018
0.37891700 1407178019
0.37892400 1407178020
0.37978700 1407178021
0.38012900 1407178022
0.38106900 1407178023
0.38199600 1407178024
0.38300500 1407178025
0.38403600 1407178026
0.38425800 1407178027
0.38606900 1407178028

Im guessing addPeriodicTimer() is acting as a recursive addTimer() and the bit of time it takes to run is responsible for the 'skew' in time of each iteration. Can you give me advice on how to make it run uniformly? Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.