Skip to content

Exception: Value of type null is not callable #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pczerkas opened this issue Dec 7, 2022 · 6 comments
Closed

Exception: Value of type null is not callable #244

pczerkas opened this issue Dec 7, 2022 · 6 comments
Labels

Comments

@pczerkas
Copy link

pczerkas commented Dec 7, 2022

image

This looks like something in PHP core. Does anyone have working configuration of PHP/ReactPHP versions, where such things work?

Single process, no forking app. Only crashes, when using 2 or more clients.

PHP 8.1.13 (also happens on 8.1.10)

"name": "react/async",
"version": "v4.0.0",

"name": "react/cache",
"version": "v1.1.1",

"name": "react/child-process",
"version": "v0.6.5",

"name": "react/dns",
"version": "v1.10.0",

"name": "react/event-loop",
"version": "v1.3.0",

"name": "react/http",
"version": "v1.8.0",

"name": "react/promise",
"version": "v2.9.0",

"name": "react/promise-stream",
"version": "v1.5.0",

"name": "react/promise-timer",
"version": "v1.9.0",

"name": "react/socket",
"version": "v1.12.0",

"name": "react/stream",
"version": "v1.2.0",

// Please add code examples if possible, so we can reproduce your steps

    public function __construct(
        LoopInterface $loop,
        TestSessionRepository $testSessionRepository,
        Env $env,
        UrlUtil $urlUtil,
        NodeCommanding $nodeCommanding
    ) {
        $this->loop = $loop;
        $this->testSessionRepository = $testSessionRepository;
        $this->env = $env;
        $this->urlUtil = $urlUtil;
        $this->nodeCommanding = $nodeCommanding;
    }

    public function __invoke(Request $request): PromiseInterface
    {
        $secretKey = $request->attributes->get('secret_key');

        $body = json_decode($request->getContent(), true);

        //TODO: better browser detection?
        $browser = $body['capabilities']['firstMatch'][0]['browserName'];

        $testSession = new TestSession();
        $testSession->secretKey = $secretKey;
        $testSession->browser = $browser;
        $testSession->settings = $body;
        $this->testSessionRepository->add($testSession, true);

        $promise = new Promise(
            function (
                callable $resolve,
                callable $reject
            ) use (
                $testSession
            ) {
                $this->loop->addPeriodicTimer(
                    1.0,
                    function (
                        $timer
                    ) use (
                        &$resolve,
                        $testSession
                    ) {
                        //TODO: check if raw access to DB may be needed here
                        $placedTestSession = $this->testSessionRepository
                            ->getSessionPlacedOnNode($testSession->id);

                        if ($placedTestSession) {
                            $this->loop->cancelTimer($timer);

                            if ($resolve) {
                                $resolve($placedTestSession);
                            }
                        }
                    }
                );
            }
        );

        return $promise
            ->then(
                function (
                    TestSession $testSession
                ) use (
                    $request,
                    $secretKey
                ) {
                    return $this->forwardRequest(
                        $request,
                        $secretKey,
                        $testSession
                    );
                },
                function (Throwable $e) {
                    die('HERE1: ' . $e->getMessage());
                }
            );
    }
@pczerkas pczerkas added the bug label Dec 7, 2022
@WyriHaximus
Copy link
Member

Why are you passing $resolve by reference, and later checking if it someone is true?

@pczerkas
Copy link
Author

pczerkas commented Dec 8, 2022

These two are remnants of debugging: if I change to not pass $resolve by ref and remove if clause the same error happens.

if also demonstrates something strange: how can null value of $resolve pass the if statement?

@pczerkas
Copy link
Author

pczerkas commented Dec 8, 2022

This app is basically a forwarding proxy, but waits in controller to calculate address of destination node, that is ready to take connection (hence periodic timer).

@SimonFrings
Copy link
Member

@pczerkas The error message you're seeing looks familiar, same has been reported in reactphp/async#16. Maybe it could be the case that you use an await() somewhere and forgot to wrap it into an async function. As the documentation of async says:

This function is specifically designed to complement the await() function. The await() function can be considered blocking from the perspective of the calling code. You can avoid this blocking behavior by wrapping it in an async() function call. Everything inside this function will still be blocked, but everything outside this function can be executed asynchronously without blocking::

Loop::addTimer(0.5, React\Async\async(function () {
    echo 'a';
    React\Async\await(React\Promise\Timer\sleep(1.0));
    echo 'c';
}));

Loop::addTimer(1.0, function () {
    echo 'b';
});

// prints "a" at t=0.5s
// prints "b" at t=1.0s
// prints "c" at t=1.5s

It's not explicitly written, but you can't use blocking code inside a promise or timer function. The same goes for await(), because it is also a blocking function. With the async() function this behavior can be avoided, but it only works in combination with await(), which means async() doesn't turn every blocking function magically into an async one.

You can read all about it in the reactphp/async readme.

Does this help?

@pczerkas
Copy link
Author

pczerkas commented Dec 8, 2022

@SimonFrings thanks! Yes, I've seen that issue, and also tried this ealier:

   $this->loop->addPeriodicTimer(
                    1.0,
                    async(function (
                    [...]

In the meantine I've downgraded react/async from v4.0.0 to v3.0.0, changed timer function signature to not pass to it whole object, but only string (testSessionId) and I'm seeing rock-solid stability now.

I guess I'll leave this configuration as it is.

@pczerkas pczerkas closed this as completed Dec 8, 2022
@clue clue added question and removed bug labels Sep 17, 2023
@mrAndersen
Copy link

I guess I am having the same type of issue.

react/async is 4.3.0

$response = await(
                $this->browser
                    ->withResponseBuffer(1024 * 1024 * 32)
                    ->withTimeout($timeout)
                    ->request($method, $url, $headers, $body)
            );

            if (!$response) {
                $this->tracerService->incScalar('http_error');

                throw new Exception('Empty response');
            }

I am getting Empty response Exception frequently and the following error in await:

AssertionError: assert(\is_callable($ret)) in /var/www/symfony/vendor/react/async/src/SimpleFiber.php:72

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants