Skip to content

Resumptions in final cycle collection after EventLoop return in DriverSuspension are ignored #90

Description

@bwoebi

Consider these test cases (the one case immediately resumes in cycle collection, the other schedules something on the event loop):

    public function testSuspensionResumptionWithQueueInGarbageCollection(): void
    {
        $suspension = EventLoop::getSuspension();

        $class = new class($suspension) {
            public function __construct(public Suspension $suspension) {}
            public function __destruct() { $this->suspension->resume(true); }
        };
        $cycle = [$class, &$cycle];
        unset($class, $cycle);

        $ended = $suspension->suspend();

        $this->assertTrue($ended);
    }

    public function testEventLoopResumptionWithQueueInGarbageCollection(): void
    {
        $suspension = EventLoop::getSuspension();

        $class = new class($suspension) {
            public function __construct(public Suspension $suspension) {}
            public function __destruct() { EventLoop::queue($this->suspension->resume(...), true); }
        };
        $cycle = [$class, &$cycle];
        unset($class, $cycle);

        $ended = $suspension->suspend();

        $this->assertTrue($ended);
    }

These cases both will fail with:

Error: Event loop terminated without resuming the current suspension (the cause is either a fiber deadlock, or an incorrectly unreferenced/canceled watcher):

(The trailing colon is also weird, it means that no other fibers exist, maybe write that out.)

I was initially very confused when this happened in my code as dumping the event loop showed a pending queue() call, while the EventLoop reported as finished.

I would expect that, after the gc_collect_cycles():

\gc_collect_cycles(); // Collect any circular references before dumping pending suspensions.

the EventLoop is checked for any active, referenced watchers or microtasks, and if yes, is resumed. Similarly, if a suspension was resumed within that cycle collection, it will leave the code path leading to the error being thrown.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions