Skip to content

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

@bwoebi

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.

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