-
Notifications
You must be signed in to change notification settings - Fork 35
Open
Description
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
Labels
No labels