Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/benchmark-ticks.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
$n = isset($argv[1]) ? (int) $argv[1] : 1000 * 100;

for ($i = 0; $i < $n; ++$i) {
EventLoop::defer(function () { });
EventLoop::defer(function () {
});
}

EventLoop::run();
3 changes: 2 additions & 1 deletion examples/benchmark-timers.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
$n = isset($argv[1]) ? (int) $argv[1] : 1000 * 100;

for ($i = 0; $i < $n; ++$i) {
EventLoop::delay(0, function () { });
EventLoop::delay(0, function () {
});
}

EventLoop::run();
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<testsuites>
<testsuite name="Main">
<directory>test</directory>
<directory suffix=".phpt">test</directory>
</testsuite>
</testsuites>
</phpunit>
4 changes: 4 additions & 0 deletions src/EventLoop/Driver/EvDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
// We need to clear all references to events manually, see
// https://bitbucket.org/osmanov/pecl-ev/issues/31/segfault-in-ev_timer_stop
$this->events = [];

// Reinitialize the loop handle due to indeterminate destruct order.
// See https://github.com/revoltphp/event-loop/issues/105
$this->handle = new \EvLoop();
}

/**
Expand Down Expand Up @@ -165,16 +169,16 @@
$this->handle->nowUpdate();
$now = $this->now();

foreach ($callbacks as $callback) {

Check warning on line 172 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedAssignment

src/EventLoop/Driver/EvDriver.php:172:32: MixedAssignment: Unable to determine the type that $callback is being assigned to (see https://psalm.dev/032)

Check warning on line 172 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedAssignment

src/EventLoop/Driver/EvDriver.php:172:32: MixedAssignment: Unable to determine the type that $callback is being assigned to (see https://psalm.dev/032)

Check warning on line 172 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedAssignment

src/EventLoop/Driver/EvDriver.php:172:32: MixedAssignment: Unable to determine the type that $callback is being assigned to (see https://psalm.dev/032)
if (!isset($this->events[$id = $callback->id])) {

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedPropertyFetch

src/EventLoop/Driver/EvDriver.php:173:44: MixedPropertyFetch: Cannot fetch property on mixed var $callback (see https://psalm.dev/034)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedAssignment

src/EventLoop/Driver/EvDriver.php:173:38: MixedAssignment: Unable to determine the type that $id is being assigned to (see https://psalm.dev/032)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:173:24: MixedArrayOffset: Cannot access value on variable $this->events using mixed offset (see https://psalm.dev/031)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedPropertyFetch

src/EventLoop/Driver/EvDriver.php:173:44: MixedPropertyFetch: Cannot fetch property on mixed var $callback (see https://psalm.dev/034)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedAssignment

src/EventLoop/Driver/EvDriver.php:173:38: MixedAssignment: Unable to determine the type that $id is being assigned to (see https://psalm.dev/032)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:173:24: MixedArrayOffset: Cannot access value on variable $this->events using mixed offset (see https://psalm.dev/031)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedPropertyFetch

src/EventLoop/Driver/EvDriver.php:173:44: MixedPropertyFetch: Cannot fetch property on mixed var $callback (see https://psalm.dev/034)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedAssignment

src/EventLoop/Driver/EvDriver.php:173:38: MixedAssignment: Unable to determine the type that $id is being assigned to (see https://psalm.dev/032)

Check warning on line 173 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:173:24: MixedArrayOffset: Cannot access value on variable $this->events using mixed offset (see https://psalm.dev/031)
if ($callback instanceof StreamReadableCallback) {
\assert(\is_resource($callback->stream));

$this->events[$id] = $this->handle->io($callback->stream, \Ev::READ, $this->ioCallback, $callback);

Check warning on line 177 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedPropertyTypeCoercion

src/EventLoop/Driver/EvDriver.php:177:21: MixedPropertyTypeCoercion: $this->events expects 'array<string, EvWatcher>', parent type `non-empty-array<array-key, EvWatcher>` provided (see https://psalm.dev/196)

Check warning on line 177 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:177:21: MixedArrayOffset: Cannot access value on variable $this->events[$id] using mixed offset (see https://psalm.dev/031)

Check warning on line 177 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedPropertyTypeCoercion

src/EventLoop/Driver/EvDriver.php:177:21: MixedPropertyTypeCoercion: $this->events expects 'array<string, EvWatcher>', parent type `non-empty-array<array-key, EvWatcher>` provided (see https://psalm.dev/196)

Check warning on line 177 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:177:21: MixedArrayOffset: Cannot access value on variable $this->events[$id] using mixed offset (see https://psalm.dev/031)

Check warning on line 177 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedPropertyTypeCoercion

src/EventLoop/Driver/EvDriver.php:177:21: MixedPropertyTypeCoercion: $this->events expects 'array<string, EvWatcher>', parent type `non-empty-array<array-key, EvWatcher>` provided (see https://psalm.dev/196)

Check warning on line 177 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:177:21: MixedArrayOffset: Cannot access value on variable $this->events[$id] using mixed offset (see https://psalm.dev/031)
} elseif ($callback instanceof StreamWritableCallback) {
\assert(\is_resource($callback->stream));

$this->events[$id] = $this->handle->io(

Check warning on line 181 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.1

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:181:21: MixedArrayOffset: Cannot access value on variable $this->events[$id] using mixed offset (see https://psalm.dev/031)

Check warning on line 181 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.2

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:181:21: MixedArrayOffset: Cannot access value on variable $this->events[$id] using mixed offset (see https://psalm.dev/031)

Check warning on line 181 in src/EventLoop/Driver/EvDriver.php

View workflow job for this annotation

GitHub Actions / PHP 8.3

MixedArrayOffset

src/EventLoop/Driver/EvDriver.php:181:21: MixedArrayOffset: Cannot access value on variable $this->events[$id] using mixed offset (see https://psalm.dev/031)
$callback->stream,
\Ev::WRITE,
$this->ioCallback,
Expand Down
6 changes: 5 additions & 1 deletion src/EventLoop/Driver/EventDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ public function __destruct()
/** @psalm-suppress RedundantPropertyInitializationCheck */
if (isset($this->handle)) {
$this->handle->free();
unset($this->handle);

// Reinitialize the loop handle due to indeterminate destruct order.
// See https://github.com/revoltphp/event-loop/issues/105
/** @psalm-suppress TooFewArguments https://github.com/JetBrains/phpstorm-stubs/pull/763 */
$this->handle = new \EventBase();
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/EventLoop/Driver/UvDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ public function cancel(string $callbackId): void
unset($this->events[$callbackId]);
}

public function __destruct()
{
$this->events = [];
$this->uvCallbacks = [];
$this->streams = [];

// Reinitialize the loop handle due to indeterminate destruct order.
// See https://github.com/revoltphp/event-loop/issues/105
$this->handle = \uv_loop_new();
}

/**
* @return \UVLoop|resource
*/
Expand Down
11 changes: 8 additions & 3 deletions src/EventLoop/Internal/AbstractDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,14 @@ private function invokeCallbacks(): void
{
while (!$this->microtaskQueue->isEmpty() || !$this->callbackQueue->isEmpty()) {
/** @noinspection PhpUnhandledExceptionInspection */
$yielded = $this->callbackFiber->isStarted()
? $this->callbackFiber->resume()
: $this->callbackFiber->start();
if ($this->callbackFiber->isSuspended()) {
$yielded = $this->callbackFiber->resume();
} else {
if ($this->callbackFiber->isTerminated()) {
$this->createCallbackFiber();
}
$yielded = $this->callbackFiber->start();
}

if ($yielded !== $this->internalSuspensionMarker) {
$this->createCallbackFiber();
Expand Down
9 changes: 8 additions & 1 deletion test/Driver/TimerQueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,16 @@ public function testHeapOrder(): void
$id = 'a';
$callbacks = [];
foreach ($values as $value) {
$callback = new TimerCallback($id++, $value, static function () {
$callback = new TimerCallback($id, $value, static function () {
}, $value);
$callbacks[] = $callback;

if (\PHP_VERSION_ID >= 80300) {
/** @psalm-suppress UndefinedFunction */
$id = \str_increment($id);
} else {
$id++;
}
}

$toRemove = $callbacks[$indexToRemove];
Expand Down
53 changes: 53 additions & 0 deletions test/event_loop_destruction_order_ev.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Issue #105: Ensure the callback fiber is always alive as long as the event loop lives (ev driver)
--SKIPIF--
<?php

if (PHP_VERSION_ID < 80400) {
echo 'skip PHP 8.4+ required';
}

if (!\extension_loaded('ev')) {
echo 'skip ev extension required';
}

?>
--FILE--
<?php

use Revolt\EventLoop;
use Revolt\EventLoop\Driver\EvDriver;

require 'vendor/autoload.php';

EventLoop::setDriver(new EvDriver());

final class a {
private static self $a;
public static function getInstance(): self {
return self::$a ??= new self;
}

public function __destruct()
{
echo "Destroying ", self::class, "\n";
$suspension = EventLoop::getSuspension();
EventLoop::delay(1.0, $suspension->resume(...));
$suspension->suspend();
echo "Finished " . self::class, "\n";
}
}

EventLoop::defer(function () {
echo "start\n";
});

a::getInstance();

EventLoop::run();

?>
--EXPECT--
start
Destroying a
Finished a
53 changes: 53 additions & 0 deletions test/event_loop_destruction_order_event.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Issue #105: Ensure the callback fiber is always alive as long as the event loop lives (event driver)
--SKIPIF--
<?php

if (PHP_VERSION_ID < 80400) {
echo 'skip PHP 8.4+ required';
}

if (!\extension_loaded('event')) {
echo 'skip event extension required';
}

?>
--FILE--
<?php

use Revolt\EventLoop;
use Revolt\EventLoop\Driver\EventDriver;

require 'vendor/autoload.php';

EventLoop::setDriver(new EventDriver());

final class a {
private static self $a;
public static function getInstance(): self {
return self::$a ??= new self;
}

public function __destruct()
{
echo "Destroying ", self::class, "\n";
$suspension = EventLoop::getSuspension();
EventLoop::delay(1.0, $suspension->resume(...));
$suspension->suspend();
echo "Finished " . self::class, "\n";
}
}

EventLoop::defer(function () {
echo "start\n";
});

a::getInstance();

EventLoop::run();

?>
--EXPECT--
start
Destroying a
Finished a
49 changes: 49 additions & 0 deletions test/event_loop_destruction_order_stream_select.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
Issue #105: Ensure the callback fiber is always alive as long as the event loop lives (stream_select driver)
--SKIPIF--
<?php

if (PHP_VERSION_ID < 80400) {
echo 'skip PHP 8.4+ required';
}

?>
--FILE--
<?php

use Revolt\EventLoop;
use Revolt\EventLoop\Driver\StreamSelectDriver;

require 'vendor/autoload.php';

EventLoop::setDriver(new StreamSelectDriver());

final class a {
private static self $a;
public static function getInstance(): self {
return self::$a ??= new self;
}

public function __destruct()
{
echo "Destroying ", self::class, "\n";
$suspension = EventLoop::getSuspension();
EventLoop::delay(1.0, $suspension->resume(...));
$suspension->suspend();
echo "Finished " . self::class, "\n";
}
}

EventLoop::defer(function () {
echo "start\n";
});

a::getInstance();

EventLoop::run();

?>
--EXPECT--
start
Destroying a
Finished a
53 changes: 53 additions & 0 deletions test/event_loop_destruction_order_uv.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Issue #105: Ensure the callback fiber is always alive as long as the event loop lives (uv driver)
--SKIPIF--
<?php

if (PHP_VERSION_ID < 80400) {
echo 'skip PHP 8.4+ required';
}

if (!\extension_loaded('uv')) {
echo 'skip uv extension required';
}

?>
--FILE--
<?php

use Revolt\EventLoop;
use Revolt\EventLoop\Driver\UvDriver;

require 'vendor/autoload.php';

EventLoop::setDriver(new UvDriver());

final class a {
private static self $a;
public static function getInstance(): self {
return self::$a ??= new self;
}

public function __destruct()
{
echo "Destroying ", self::class, "\n";
$suspension = EventLoop::getSuspension();
EventLoop::delay(1.0, $suspension->resume(...));
$suspension->suspend();
echo "Finished " . self::class, "\n";
}
}

EventLoop::defer(function () {
echo "start\n";
});

a::getInstance();

EventLoop::run();

?>
--EXPECT--
start
Destroying a
Finished a
Loading