amphp / dns

Async DNS resolution for PHP based on Amp.
https://amphp.org/dns
MIT License
157 stars 32 forks source link

Event loop stops without resolving promise #66

Closed kelunik closed 7 years ago

kelunik commented 7 years ago

In certain edge cases the loop stops without resolving the promise returned from BasicResolver::resolve(). Reproducible with the script from https://github.com/amphp/artax/issues/138 with a fix for the TLS hanging.

kelunik commented 7 years ago

Script used for reproduction:

<?php

use Amp\Loop;
use Amp\Artax\DefaultClient;

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

error_reporting(-1);

    print get_class(Loop::get()) . PHP_EOL;

    $urlAsFile = function ($url, $timeout, $allowRedirects = false): \Amp\Promise {
        $client = new DefaultClient();
        $request = new \Amp\Artax\Request($url);
        $totalTimeout = (int) (1000 * ($timeout));
        $promise = $client->request($request, [
            DefaultClient::OP_TRANSFER_TIMEOUT => $totalTimeout,
            DefaultClient::OP_MAX_REDIRECTS => $allowRedirects ? 10 : 0,
        ]);

        $deferred = new \Amp\Deferred();
        $promise2 = $deferred->promise();

        $promise->onResolve(function ($e, ?\Amp\Artax\Response $response) use ($deferred, $url) {
            if ($e) {
                $deferred->fail($e);
            } else {
                $tmpFile = 'foo.txt';
                try {
                    while (($chunk = yield $response->getBody()->read()) !== null)
                        print "*";
                    print PHP_EOL;
                    $deferred->resolve($tmpFile);
                } catch (\Throwable $e) {
                    $deferred->fail($e);
                }
            }
        });

        return $promise2;
    };

    while (true) {
        Loop::set((new Amp\Loop\DriverFactory)->create());
        try {
            Amp\Promise\wait(Amp\call(function () use ($urlAsFile) {
                $start = microtime(true);
                $f = yield $urlAsFile('https://i.jaumo.com/go/2651,766d972eda9dc4d0.jpg', 3);
                $end = microtime(true);
                $time = ($end - $start) * 1000;
                # echo $i . "= " . filesize($f) . "  " . sprintf("%0.3f", $time) . "ms\n";
                echo ".";
            }));
        } catch (\Error $e) {
            var_dump(Loop::getInfo());
            throw $e;
        }
    }

Usually takes quite some requests to get triggered. It's driver independent.

kelunik commented 7 years ago

Ok, I know why. People using multiple event loops deserve what they get from PHP's __destruct. Will post a PR to amphp/amp soon.

kelunik commented 7 years ago

Closing as amphp/amp#171 got merged.