Skip to content

Commit

Permalink
Global event-loop accessor part four
Browse files Browse the repository at this point in the history
  • Loading branch information
WyriHaximus committed May 18, 2021
1 parent 8bd064c commit 22c9972
Show file tree
Hide file tree
Showing 18 changed files with 233 additions and 97 deletions.
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ single [`run()`](#run) call that is controlled by the user.

* [Quickstart example](#quickstart-example)
* [Usage](#usage)
* [Loop](#loop)
* [get()](#get)
* [Factory](#factory)
* [create()](#create)
* [Loop implementations](#loop-implementations)
Expand Down Expand Up @@ -45,32 +47,32 @@ single [`run()`](#run) call that is controlled by the user.
Here is an async HTTP server built with just the event loop.

```php
$loop = React\EventLoop\Factory::create();
use React\EventLoop\Loop;

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

$loop->addReadStream($server, function ($server) use ($loop) {
Loop::get()->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, $loop) {
Loop::get()->addWriteStream($conn, function ($conn) use (&$data) {
$written = fwrite($conn, $data);
if ($written === strlen($data)) {
fclose($conn);
$loop->removeWriteStream($conn);
Loop::get()->removeWriteStream($conn);
} else {
$data = substr($data, $written);
}
});
});

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

$loop->run();
Loop::get()->run();
```

See also the [examples](examples).
Expand Down Expand Up @@ -110,6 +112,28 @@ $loop->run();
purposes.
3. The loop is run with a single [`$loop->run()`](#run) call at the end of the program.

### Loop

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

#### get()

The `get(): LoopInterface` method is the preferred way to get and use the event loop. With
it there is no need to always pass the loop around anymore.

```php
use React\EventLoop\Loop;

Loop::get()->addTimer(0.02, function () {
echo 'World!';
});
Loop::get()->addTimer(0.01, function () {
echo 'Hello ';
});

Loop::get()->run();
```

### Factory

The `Factory` class exists as a convenient way to pick the best available
Expand Down
10 changes: 5 additions & 5 deletions examples/01-timers.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?php

require __DIR__ . '/../vendor/autoload.php';
use React\EventLoop\Loop;

$loop = React\EventLoop\Factory::create();
require __DIR__ . '/../vendor/autoload.php';

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

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

$loop->run();
Loop::get()->run();
12 changes: 6 additions & 6 deletions examples/02-periodic.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?php

require __DIR__ . '/../vendor/autoload.php';
use React\EventLoop\Loop;

$loop = React\EventLoop\Factory::create();
require __DIR__ . '/../vendor/autoload.php';

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

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

$loop->run();
Loop::get()->run();
10 changes: 5 additions & 5 deletions examples/03-ticks.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?php

require __DIR__ . '/../vendor/autoload.php';
use React\EventLoop\Loop;

$loop = React\EventLoop\Factory::create();
require __DIR__ . '/../vendor/autoload.php';

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

$loop->run();
Loop::get()->run();
10 changes: 5 additions & 5 deletions examples/04-signals.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<?php

use React\EventLoop\Loop;

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

if (!defined('SIGINT')) {
fwrite(STDERR, 'Not supported on your platform (ext-pcntl missing or Windows?)' . PHP_EOL);
exit(1);
}

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

$loop->addSignal(SIGINT, $func = function ($signal) use ($loop, &$func) {
Loop::get()->addSignal(SIGINT, $func = function ($signal) use (&$func) {
echo 'Signal: ', (string)$signal, PHP_EOL;
$loop->removeSignal(SIGINT, $func);
Loop::get()->removeSignal(SIGINT, $func);
});

echo 'Listening for SIGINT. Use "kill -SIGINT ' . getmypid() . '" or CTRL+C' . PHP_EOL;

$loop->run();
Loop::get()->run();
10 changes: 4 additions & 6 deletions examples/11-consume-stdin.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

use React\EventLoop\Factory;
use React\EventLoop\Loop;

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

Expand All @@ -9,16 +9,14 @@
exit(1);
}

$loop = Factory::create();

// read everything from STDIN and report number of bytes
// for illustration purposes only, should use react/stream instead
$loop->addReadStream(STDIN, function ($stream) use ($loop) {
Loop::get()->addReadStream(STDIN, function ($stream) {
$chunk = fread($stream, 64 * 1024);

// reading nothing means we reached EOF
if ($chunk === '') {
$loop->removeReadStream($stream);
Loop::get()->removeReadStream($stream);
stream_set_blocking($stream, true);
fclose($stream);
return;
Expand All @@ -27,4 +25,4 @@
echo strlen($chunk) . ' bytes' . PHP_EOL;
});

$loop->run();
Loop::get()->run();
10 changes: 5 additions & 5 deletions examples/12-generate-yes.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use React\EventLoop\Loop;

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

// data can be given as first argument or defaults to "y"
Expand All @@ -8,22 +10,20 @@
// repeat data X times in order to fill around 200 KB
$data = str_repeat($data, round(200000 / strlen($data)));

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

if (!defined('STDOUT') || stream_set_blocking(STDOUT, false) !== true) {
fwrite(STDERR, 'ERROR: Unable to set STDOUT non-blocking (not CLI or Windows?)' . PHP_EOL);
exit(1);
}

// write data to STDOUT whenever its write buffer accepts data
// for illustrations purpose only, should use react/stream instead
$loop->addWriteStream(STDOUT, function ($stdout) use ($loop, &$data) {
Loop::get()->addWriteStream(STDOUT, function ($stdout) use (&$data) {
// try to write data
$r = fwrite($stdout, $data);

// nothing could be written despite being writable => closed
if ($r === 0) {
$loop->removeWriteStream($stdout);
Loop::get()->removeWriteStream($stdout);
fclose($stdout);
stream_set_blocking($stdout, true);
fwrite(STDERR, 'Stopped because STDOUT closed' . PHP_EOL);
Expand All @@ -38,4 +38,4 @@
}
});

$loop->run();
Loop::get()->run();
10 changes: 4 additions & 6 deletions examples/13-http-client-blocking.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<?php

use React\EventLoop\Factory;
use React\EventLoop\Loop;

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

$loop = Factory::create();

// connect to www.google.com:80 (blocking call!)
// for illustration purposes only, should use react/socket instead
$stream = stream_socket_client('tcp://www.google.com:80');
Expand All @@ -18,18 +16,18 @@
fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n");

// wait for HTTP response
$loop->addReadStream($stream, function ($stream) use ($loop) {
Loop::get()->addReadStream($stream, function ($stream) {
$chunk = fread($stream, 64 * 1024);

// reading nothing means we reached EOF
if ($chunk === '') {
echo '[END]' . PHP_EOL;
$loop->removeReadStream($stream);
Loop::get()->removeReadStream($stream);
fclose($stream);
return;
}

echo $chunk;
});

$loop->run();
Loop::get()->run();
17 changes: 8 additions & 9 deletions examples/14-http-client-async.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<?php

use React\EventLoop\Factory;
use React\EventLoop\Loop;

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

$loop = Factory::create();

// resolve hostname before establishing TCP/IP connection (resolving DNS is still blocking here)
// for illustration purposes only, should use react/socket or react/dns instead!
$ip = gethostbyname('www.google.com');
Expand All @@ -24,14 +23,14 @@

// print progress every 10ms
echo 'Connecting';
$timer = $loop->addPeriodicTimer(0.01, function () {
$timer = Loop::get()->addPeriodicTimer(0.01, function () {
echo '.';
});

// wait for connection success/error
$loop->addWriteStream($stream, function ($stream) use ($loop, $timer) {
$loop->removeWriteStream($stream);
$loop->cancelTimer($timer);
Loop::get()->addWriteStream($stream, function ($stream) use ($timer) {
Loop::get()->removeWriteStream($stream);
Loop::get()->cancelTimer($timer);

// check for socket error (connection rejected)
if (stream_socket_get_name($stream, true) === false) {
Expand All @@ -45,13 +44,13 @@
fwrite($stream, "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n");

// wait for HTTP response
$loop->addReadStream($stream, function ($stream) use ($loop) {
Loop::get()->addReadStream($stream, function ($stream) {
$chunk = fread($stream, 64 * 1024);

// reading nothing means we reached EOF
if ($chunk === '') {
echo '[END]' . PHP_EOL;
$loop->removeReadStream($stream);
Loop::get()->removeReadStream($stream);
fclose($stream);
return;
}
Expand All @@ -60,4 +59,4 @@
});
});

$loop->run();
Loop::get()->run();
14 changes: 7 additions & 7 deletions examples/21-http-server.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

require __DIR__ . '/../vendor/autoload.php';
use React\EventLoop\Loop;

$loop = React\EventLoop\Factory::create();
require __DIR__ . '/../vendor/autoload.php';

// start TCP/IP server on localhost:8080
// for illustration purposes only, should use react/socket instead
Expand All @@ -13,24 +13,24 @@
stream_set_blocking($server, false);

// wait for incoming connections on server socket
$loop->addReadStream($server, function ($server) use ($loop) {
Loop::get()->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, $loop) {
Loop::get()->addWriteStream($conn, function ($conn) use (&$data) {
$written = fwrite($conn, $data);
if ($written === strlen($data)) {
fclose($conn);
$loop->removeWriteStream($conn);
Loop::get()->removeWriteStream($conn);
} else {
$data = substr($data, $written);
}
});
});

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

$loop->run();
Loop::get()->run();
8 changes: 3 additions & 5 deletions examples/91-benchmark-ticks.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<?php

use React\EventLoop\Factory;
use React\EventLoop\Loop;

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

$loop = Factory::create();

$n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100;

for ($i = 0; $i < $n; ++$i) {
$loop->futureTick(function () { });
Loop::get()->futureTick(function () { });
}

$loop->run();
Loop::get()->run();
Loading

0 comments on commit 22c9972

Please sign in to comment.