amphp / amp

A non-blocking concurrency framework for PHP applications. 🐘
https://amphp.org/amp
MIT License
4.25k stars 257 forks source link

FiberError: Cannot switch fibers in current execution context on Laravel #412

Closed hellowork-mhlw closed 1 year ago

hellowork-mhlw commented 1 year ago

Hi i got error on GitHub Actions. I see this pretty frequently in the logs:

https://github.com/hellowork-mhlw/laravel/actions/runs/3901413617/jobs/6663232585

There was 1 error:

1) Tests\Feature\ExampleTest::test_amphp_vs_pmphp
FiberError: Cannot switch fibers in current execution context

/home/runner/work/laravel/laravel/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php:86
/home/runner/work/laravel/laravel/vendor/revolt/event-loop/src/EventLoop/Internal/DriverSuspension.php:97
/home/runner/work/laravel/laravel/vendor/amphp/amp/src/Future.php:251
/home/runner/work/laravel/laravel/app/Console/Commands/test.php:58

https://github.com/hellowork-mhlw/laravel/blob/9.x/app/Console/Commands/test.php#L31

    public function handle()
    {
        $future1 = async(function () {
            echo 'Hello ';

            // delay() is a non-blocking version of PHP's sleep() function,
            // which only pauses the current fiber instead of blocking the whole process.
            delay(2);

            echo 'the future! ';
        });

        $future2 = async(function () {
            echo 'World ';

            // Let's pause for only 1 instead of 2 seconds here,
            // so our text is printed in the correct order.
            delay(1);

            echo 'from ';
        });

        // Our functions have been queued, but won't be executed until the event-loop gains control.
        echo "Let's start: ";

        // Awaiting a future outside a fiber switches to the event loop until the future is complete.
        // Once the event loop gains control, it executes our already queued functions we've passed to async()
        $future1->await();
        $future2->await();

        echo PHP_EOL;
        return Command::SUCCESS;
    }
kelunik commented 1 year ago

Seems like you haven't provided the entire stack trace. Fiber switches are blocked in a few specific places, such as destructors, so there's likely something in your call stack, that's blocking the fiber switch.

hellowork-mhlw commented 1 year ago

Thank you:) https://github.com/hellowork-mhlw/laravel/actions/runs/3901650348/jobs/6663754802

I run it from artisan command. it works image

But PHPUnit is not working image

Run vendor/bin/phpunit
PHPUnit 9.5.27 by Sebastian Bergmann and contributors.

.E                                                                  2 / 2 (100%)Let's start: 

Time: 00:00.136, Memory: 26.00 MB

There was 1 error:

1) Tests\Feature\ExampleTest::test_amphp_vs_pmphp
FiberError: Cannot switch fibers in current execution context

/home/runner/work/laravel/laravel/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php:[8](https://github.com/hellowork-mhlw/laravel/actions/runs/3901650348/jobs/6663754802#step:12:9)6
/home/runner/work/laravel/laravel/vendor/revolt/event-loop/src/EventLoop/Internal/DriverSuspension.php:[9](https://github.com/hellowork-mhlw/laravel/actions/runs/3901650348/jobs/6663754802#step:12:10)7
/home/runner/work/laravel/laravel/vendor/amphp/amp/src/Future.php:251
/home/runner/work/laravel/laravel/app/Console/Commands/test.php:58
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Container/Util.php:41
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php:651
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Console/Command.php:182
/home/runner/work/laravel/laravel/vendor/symfony/console/Command/Command.php:312
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Console/Command.php:151
/home/runner/work/laravel/laravel/vendor/symfony/console/Application.php:[10](https://github.com/hellowork-mhlw/laravel/actions/runs/3901650348/jobs/6663754802#step:12:11)22
/home/runner/work/laravel/laravel/vendor/symfony/console/Application.php:3[14](https://github.com/hellowork-mhlw/laravel/actions/runs/3901650348/jobs/6663754802#step:12:15)
/home/runner/work/laravel/laravel/vendor/symfony/console/Application.php:168
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Console/Application.php:102
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Console/Application.php:193
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:336
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Testing/PendingCommand.php:[29](https://github.com/hellowork-mhlw/laravel/actions/runs/3901650348/jobs/6663754802#step:12:30)6
/home/runner/work/laravel/laravel/vendor/laravel/framework/src/Illuminate/Testing/PendingCommand.php:481
/home/runner/work/laravel/laravel/tests/Feature/ExampleTest.php:17

ERRORS!
Tests: 2, Assertions: 1, Errors: 1.
Error: Process completed with exit code 2.
hellowork-mhlw commented 1 year ago

Laravel execute code from the destructor;( Is there any workaround? @taylorotwell @GrahamCampbell @crynobone

https://github.com/illuminate/testing/blob/master/PendingCommand.php#L481

    /**
     * Handle the object's destruction.
     *
     * @return void
     */
    public function __destruct()
    {
        if ($this->hasExecuted) {
            return;
        }

        $this->run();
    }
hellowork-mhlw commented 1 year ago

I use Facades:) Thank you @kelunik

This is completely work. https://github.com/hellowork-mhlw/laravel/blob/9.x/tests/Feature/ExampleTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Console\Command\Command;

class ExampleTest extends TestCase
{

    /**
     * A basic test example.
     *
     * @return void
     */
    public function test_amphp_vs_pmphp()
    {
        $exitCode = Artisan::call('command:name');
        $this->assertSame($exitCode, Command::SUCCESS);
    }
}

image

image