amphp / amp Goto Github PK
View Code? Open in Web Editor NEWA non-blocking concurrency framework for PHP applications. ๐
Home Page: https://amphp.org/amp
License: MIT License
A non-blocking concurrency framework for PHP applications. ๐
Home Page: https://amphp.org/amp
License: MIT License
Useful for all structs with public properties. Could probably be combined with #96 into a helper document.
(function () {
if (false) {
yield;
}
throw new \Exception("rethrow test");
})();
Running that as a coroutine results in Cannot get return value of a generator that hasn't returned
Explain where it's useful (private methods for loop or promise callbacks without a new closure very time).
https://wiki.php.net/rfc/async_signals
I don't really know what this means, but hey, you guys probably know what they're talking about.
Hi @rdlowrey ,
I feel it is good to have a place for discussion. Or did you left it to make it on github itself ?
In order to use Aerys in front of my existing async application I have to convert all of my Guzzle and React promises to Amp promises, which means I have to invest a load of time writing boilerplate code before I can properly test out Aerys, which means I probably won't give it a proper try because that time will have been wasted if we subsequently decide not to use Aerys. Are there any plans to support thenables in the Amp coroutine implementation in order to make it easier to work with non-Amp libraries?
Line 112 in 8f903b6
try
/ catch
around the $callback
invocation, not around the foreach
.It doesn't matter for normal promises, but I think it matters for more advanced use cases where the promise API is just one way of consumption. Looking at a list of functions, onResolve
/ onComplete
are way more expressive than when
.
Currently there's a TODO comment in the docs.
Hello.
Need support systemd.socket. This will make, activation and reload demon save all open connections.
https://www.freedesktop.org/software/systemd/man/systemd.socket.html#
Is planned implement?
Thank.
{
"minimum-stability": "dev",
"require": {
"php": ">=7.0",
"amphp/amp": "dev-master"
}
}
composer update
Problem 1
- Installation request for amphp/amp dev-master -> satisfiable by amphp/amp[dev-master].
- amphp/amp dev-master requires async-interop/event-loop-implementation ^0.5 -> no matching package found.
Currently, InvalidYieldError
s are thrown back into the generator. I'm not sure whether that's a good idea. Maybe they should directly be forwarded to the error handler and the coroutine aborted?
Compiled extensions should be cached for reduced test run times.
Test results when running locally with PHP 7.1.3RC1 and uv
and event
extensions.
PHPUnit 6.0.8 by Sebastian Bergmann and contributors.
Error: No code coverage driver is available
............................................................... 63 / 624 ( 10%)
..........................................................SSSSS 126 / 624 ( 20%)
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS 189 / 624 ( 30%)
SSSSSSSSSSSSSSSSSSSSSSS..........S............................. 252 / 624 ( 40%)
.....S.....................SSS...............................S. 315 / 624 ( 50%)
.................................S.....................SSS..... 378 / 624 ( 60%)
..........................S.................................FS. 441 / 624 ( 70%)
....................SSS........................................ 504 / 624 ( 80%)
................................F.............................. 567 / 624 ( 90%)
......................................................... 624 / 624 (100%)
Time: 11.83 seconds, Memory: 37.01MB
There were 2 failures:
1) Amp\Test\Loop\UvDriverTest::testExecutionOrderGuarantees
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'01 02 03 04 05 05 05 05 05 05 05 05 10 11 12 13 13 13 13 20 21 21 21 21 30 40 41 '
+'01 02 03 04 05 05 05 05 05 10 11 12 13 05 20 21 21 21 21 30 05 05 40 13 13 13 41 '
2) Amp\Test\ProducerTest::testEmitReactBackPressure
Failed asserting that 299.91698265075684 is greater than 300.
/home/kelunik/GitHub/amphp/amp/test/ProducerTest.php:140
FAILURES!
Tests: 8814, Assertions: 1275, Failures: 2, Skipped: 106.
This excpetion is throw ยฑ 1 out of 10 times when running this test. The test itself will take a path of an image, scales it down and saves it about 10 times. Each downscaling is a separate Fork.
This is the testcode.
public function test_async() {
$url = 'img/image.jpeg';
$factory = new ResponsiveFactory(new DefaultConfigurator([
'publicPath' => $this->publicPath,
'engine' => 'gd',
'stepModifier' => 0.5,
'scaler' => 'filesize',
'enableCache' => false,
'async' => true,
]));
$responsiveImage = $factory->create($url);
$this->assertTrue(count($responsiveImage->getSrcset()) > 1);
$this->assertEquals("/{$url}", $responsiveImage->src());
$testCase = $this;
$responsiveImage->onSaved(function () use ($testCase, $responsiveImage) {
$fs = new Filesystem();
foreach ($responsiveImage->getSrcset() as $src) {
$src = trim($src, '/');
$testCase->assertTrue($fs->exists("{$testCase->publicPath}/{$src}"));
}
});
\Amp\wait($responsiveImage->getMainPromise());
}
And this is the exception trace.
PHPUnit 5.7.14 by Sebastian Bergmann and contributors.
E 1 / 1 (100%)
Time: 673 ms, Memory: 30.00MB
There was 1 error:
1) Brendt\Image\Tests\Phpunit\ResponsiveFactoryTest::test_async
Amp\Parallel\ChannelException: The socket unexpectedly closed
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/amphp/parallel/lib/Sync/ChannelledSocket.php:91
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/amphp/loop/lib/NativeLoop.php:113
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/amphp/loop/lib/NativeLoop.php:42
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/amphp/loop/lib/Loop.php:96
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/amphp/loop/lib/Loop.php:47
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/async-interop/event-loop/src/Loop.php:78
/Users/brent1/Documents/sites/brendt/responsive-image/vendor/amphp/amp/lib/functions.php:116
/Users/brent1/Documents/sites/brendt/responsive-image/tests/phpunit/ResponsiveFactoryTest.php:164
The code for downscaling images:
$factory = $this;
$promise = Fork::spawn(function () use ($factory, $imageObject, $width, $height, $scaledFilePath) {
$scaledImage = $imageObject->resize((int) $width, (int) $height)->encode($imageObject->extension);
$factory->saveImageFile($scaledFilePath, $scaledImage);
})->join();
return $promise;
All promises are combined into one using \Amp\all()
.
We should mention the most important changes in CHANGELOG
and probably create a UPGRADING-1.md
guide.
We have quite a few risky tests since the upgrade to PHPUnit 6.
Extension version requirements should be specified in composer.json
. As they're not required, it should at least be in the suggest
.
TBD in ./docs/event-loop-api.md
.
There must be a naming issue because this project fails when you try to download through composer. Maybe "Alert" instead of "alert"?
setErrorHandler
is currently executed as coroutine, I don't know whether that really makes sense, as exceptions either have to be rethrown to stop the loop or not rethrown to continue. Having a coroutine there, makes the loop always continue.
Might fit into #100, but not sure on that.
Listener + Iterator + Message
Hi,
using new Amp\UvReactor()
directly (instead of Amp\getReactor
) doesn't
check if php-uv
is loaded and then it dies with a 'Call to undefined
function' message.
What about throw a RuntimeException
if php-uv
is not loaded?
On this line https://github.com/amphp/amp/blob/master/lib/EvReactor.php#L370
evHandle is null in the IMMEDIATE state because he is unset. So when calling clear() is null, causing error. Do you know to right way to fix that?
Check if not null or ensure to call that before unsetting the handler?
We should use a monotonic time source where available. We might have one available in PHP 7.2 via php/php-src#2368. UV already uses it's uv_hrtime
which is monotonic.
For Ev
we should maybe have a look at our current implementation. reactphp/event-loop#83 (comment) mentions real time vs. wall time.
What's the purpose of Amp\Future
? It's documented to be used only in internal code, no public APIs?
There are already multiple drafts in our design repository, please add your ideas here and share feedback.
Nested loops can result in cases where multiple calls to stop are timed in such a way that the lower-level loop does not stop.
This code continues the base loop indefinitely, even though it seems it should be stopped.
Loop::run(function () {
Loop::repeat(1000, function () {
print microtime(true) . PHP_EOL;
});
Loop::defer(function () {
Loop::stop();
});
Loop::run(function () {
Loop::stop();
});
});
While this is an edge-case, I can see this potentially happening if multiple calls to wait()
are made and the promises are resolved in the same tick.
This happens because we ignore multiple calls to stop. Perhaps multiple calls should not be ignored, but rather stop the prior loop if running as a nested loop.
I feel like this is going to be a common pitfall so I'm creating an issue for tracking. I'm finding that not being able to synchronously wait within an asynchronous context is overly limiting. Contrived example:
\Amp\run(function() {
var_dump(\Amp\wait(\Amp\Dns\resolve('google.ca')));
});
This code throws
'LogicException' with message 'Cannot tick() recursively; event reactor already active'
More commonly when adapting old synchronous code is to have asynchronous promises higher in the call stack and various yields lower in the call stack. Its quite annoying to pass promises and chain the entire call stack.
I've created a gist with a more complete example of what I'm trying to accomplish: https://gist.github.com/chrisleavoy/9c2386e3b408399c4ba2e5a4db055888
Can someone recommend a better workaround?
The leading contender right now is to use Icicle's loop reactor, but then adapt promises so that i can use Amp\Dns instead of Icicle's DNS implementation. Kudos on Amp\Dns, which is the best library I've found when evaluating all of the php async packages for DNS.
Working example that uses Icicle but embeds Amp\Dns:
https://gist.github.com/chrisleavoy/18b4032402d7b2d7f688655fe28eab60
I would appreciate some insight into reasons why Amp throws this logic exception and whether there is a work around that doesn't involve converting the entire call stack into async. (php5.5 support required)
all, any, some, first
Parts can be extracted from the already existing managing-concurrency.md
Hi,
I am trying to figure out how to get some blocking tasks performed in parallel, the example is here:
https://gist.github.com/yoanisgil/a28d57abca781fd84af1
Roughly the script will launch 5 asynchronous tasks (asyncMultiply) each of which will perform a random sleep. When I run the script with:
time docker run -v $(PWD):/usr/src/myapp -ti --rm yoanisgil/php56-cli-composer php test.php
it takes about ~ 16 secs, which indicates that tasks are performed synchronously (each task performs a random sleep between 2 - 4 seconds).
Given the deferred/asynchronous nature of promises and that I'm using Amp\immediately
I would have expected about ~ 4 seconds of execution.
For sure I most be doing something wrong. Any ideas what that might be?
Bests,
Yoanis.
Emitter + Producer + Backpressure.
It would be nice to add a Reactor::schedule
method to the interface that takes two arguments:
* * * * * *
)Because the event reactors already make it possible to write long-running CLI applications, cron emulation will trivialize daemon-style tasks in PHP without resorting to task schedulers, cron, etc. Libraries currently exist in PHP to parse cron strings but I'd prefer not to introduce dependencies into such a low-level library. If anyone cares to help with adding this functionality please let me know. It's a nice feature but I'm very busy right now and probably won't have the time to do it myself for a while.
wrap, coroutine, call
https://github.com/cujojs/when/blob/master/docs/api.md#edge-cases
Maybe we want to introduce something similar when assertions are enabled logging warnings.
I'm rewriting a db driver to use Amp 1.x in Aerys. It's pretty straight forward, except that I hit a wall with how promises are implemented. Here's some example code that shows my issue:
function connect($options) {
/* connect */
Amp\onReadable($socket, function($watcherId) {
/* read data */
$inFlight[$token]->succeed($data); // $inFlight[$token] is the matching request for this query
}
function query($query) {
/* Send query */
$inFlight[$token] = new Amp\deferred();
$promise = $inFlight[$token]->promise();
return $promise->when(function($error, $data) use (/*lots of stuff from query send*/) {
/* transform $data into native objects */
}
}
function myCoRoutine() {
$data = yield query($query);
}
However, $data
in myCoRoutine
holds the results of the original response from the server, before being transformed. Do you have any tips on how to get the result from the when
in the query()
function, instead of from the watcher?
All combinators and helpers should be properly documented.
We support React promises in combinators, I think we should support them in those functions as well for consistency then?
I think it makes sense to support yielding arrays as implicit all combinator, especially with the changes in PHP 7.1 allowing []
for restructuring.
<?php
function coroutine() {
[$a, $b, $c] = yield [
asyncOperation1(),
asyncOperation2(),
asyncOperation3(),
];
};
Promise and Deferred are already in https://github.com/amphp/amp/blob/f7344f15d0d6694c711247f7d1beff48a9fe42e0/docs/managing-concurrency.md. That should be extracted into it's own document and additionally cover Failure
and Success
https://github.com/amphp/amp/blob/9df6b4508384bd5dfcf0fa5ca5965e813f775af7/docs/managing-concurrency.md currently contains a copy of the old documentation of v1 and needs an update for v2. The also already existing other two files have already been updated.
It looks like this function doesn't exist in 2.0?
However, this affects Aerys with php-uv reactor.
In native:
Amp\onReadable( $this->socket, function ( $watcherId, $socket ) use ( &$data ) {
$data[2] .= @fread( $socket, 1024 );
. . .
That will work, even if the fread
doesn't read all the data in one go, it will still call the function on the next tick. With php-uv, it will not. It appears that it has to wait until new data hits the socket. This means that a programmer would have to know the maximum buffers of the socket in the target environment, or risk missing the tail end of a transaction.
As always, there's the possibility of extreme user error, but the code below doesn't seem to be finishing for me, both on OSX, and in a Centos Vagrant box.
<?php
require_once(realpath(__DIR__).'/../vendor/autoload.php');
$reactor = new \Alert\NativeReactor();
$client = new \Artax\AsyncClient($reactor);
$client->setOption('connecttimeout', 3);
$client->setOption('keepalivetimeout', 3);
$client->setOption('transfertimeout', 3);
$client->setOption('usekeepalive', false);
$onResponse = function (\Artax\Response $response) {
echo "Response status = ".$response->getStatus()."\n";
};
$onError = function ($param) {
echo "Somethings went wrong ".var_dump($param)."\n";
};
$client->request("http://www.google.com", $onResponse, $onError);
$watcherId = null;
$secondsCount = 0;
$msInterval = 2000;
$watcherId = $reactor->repeat(function() use ($reactor, &$secondsCount, &$watcherId, $msInterval) {
$secondsCount += 1;
echo "- Time: ".(($secondsCount * $msInterval ) / 1000 )."\n";
}, $msInterval);
$reactor->run();
echo "fin.";
The count is currently up over 500 seconds.
Does anyone know how the backend extensions compare in terms of CPU usage, memory usage and wall time?
Several packages (rdlowrey/nbsock, amphp/dns, amphp/artax 2.x-dev, amphp/mysql to name a few) rely on 1.0.x-dev as a version dependency.
You can currently get around this by using a require inline alias, but maybe keeping 1.0.x-dev as a branch-alias in composer.json could keep those packages working until they've been updated?
@trowski @bwoebi @rdlowrey I reviewed Amp v2.0 and here are my comments.
Internal\WhenQueue
How important is the layer of indirection? If we're worried about the call stack, should we instead resolve promises asynchronously?
It could also be renamed to a more generic name like CallbackQueue
or CallQueue
.
Internal\Placeholder
Annotation for $onResolved
is callable|\Amp\Internal\WhenQueue|null
, should be ?WhenQueue
or ?WhenQueue|null
instead.
Coroutine
Why does it not accept a callable as constructor parameter in addition of generators?
Emitter
if ($this->resolved) {
return;
}
^ Does that ignore double resolution silently?
Pause
Do we really need $value there? What about keep_alive = false
?
Internal\LazyPromise
Why is this one internal? Why is Amp\lazy suggested to be used only? Why do we even have Amp\lazy
? Instead of using arguments to Amp\lazy, those parameters can just be use'd in a closure by the user instead of providing them as arguments.
Amp\wrap
vs. Amp\coroutine
It is weird to have both and usage will be confusion for users.
Amp\timeout
I think Amp\timeout
shouldn't keep the loop running. The watchers should probably be unreferenced.
Amp\lift
Do we need lift
? I always ask that when reading the docs regarding it.
Amp\filter
Amp\filter
filters observables now instead of promises.
https://github.com/amphp/stream && https://github.com/amphp/socket will be rewritten ?
What are your plans ?
Proposal: Add a "TODO" section in README.
Thank you. ๐
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.