GithubHelp home page GithubHelp logo

amphp / amp Goto Github PK

View Code? Open in Web Editor NEW
4.1K 132.0 249.0 2.6 MB

A non-blocking concurrency framework for PHP applications. ๐Ÿ˜

Home Page: https://amphp.org/amp

License: MIT License

PHP 100.00%
php async coroutines promises amphp asynchronous concurrency futures revolt

amp's People

Contributors

andrewcarteruk avatar bilge avatar bwoebi avatar daverandom avatar dependabot[bot] avatar descawed avatar douggr avatar enumag avatar foremtehan avatar gitter-badger avatar joshdifabio avatar jsor avatar kelunik avatar krlv avatar lt avatar lwlwilliam avatar lyrixx avatar mickaelandrieu avatar mmasiukevich avatar nevay avatar peehaa avatar prolic avatar rdlowrey avatar samnela avatar sedatsevgili avatar simpod avatar staabm avatar trowski avatar wyrihaximus avatar xpader 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  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

amp's Issues

Struct

Useful for all structs with public properties. Could probably be combined with #96 into a helper document.

Throwing coroutine results in error

(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

CallableMaker

Explain where it's useful (private methods for loop or promise callbacks without a new closure very time).

Consider supporting thenables

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?

Changing "when" to "onResolve" or "onComplete"

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.

How to install dev-master?

{
    "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.

Invalid yields

Currently, InvalidYieldErrors 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?

Fix failing tests

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.

Amp\Parallel\ChannelException: The socket unexpectedly closed

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().

Fix risky tests

We have quite a few risky tests since the upgrade to PHPUnit 6.

Package needs fixed on Packagist

There must be a naming issue because this project fails when you try to download through composer. Maybe "Alert" instead of "alert"?

Call to undefined function uv_loop_new()

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?

Why do we have Amp\Future

What's the purpose of Amp\Future? It's documented to be used only in internal code, no public APIs?

Logo

There are already multiple drafts in our design repository, please add your ideas here and share feedback.

Stopping nested loops

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.

Cannot tick() recursively; event reactor already active

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)

Promise Combinators

all, any, some, first

Parts can be extracted from the already existing managing-concurrency.md

Async programming with blocking operations

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.

Cron emulation

It would be nice to add a Reactor::schedule method to the interface that takes two arguments:

  • a callback
  • a cron format string (i.e. * * * * * *)

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.

Amp Pattern Question

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?

Implicit all combinator for coroutines

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(),
    ];
};

Bug in onReadable

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.

Native reactor doesn't exit

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.

Comparison of extensions

Does anyone know how the backend extensions compare in terms of CPU usage, memory usage and wall time?

Removal of 1.0.x-dev breaks dependent packages

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?

Version 2 Review

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

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.