2019-10-01 21:01:44 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Amp\Loop;
|
|
|
|
|
|
|
|
use function Amp\Internal\formatStacktrace;
|
|
|
|
|
|
|
|
final class TracingDriver extends Driver
|
|
|
|
{
|
2020-09-25 05:17:13 +02:00
|
|
|
private Driver $driver;
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/** @var true[] */
|
2020-09-25 05:17:13 +02:00
|
|
|
private array $enabledWatchers = [];
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/** @var true[] */
|
2020-09-25 05:17:13 +02:00
|
|
|
private array $unreferencedWatchers = [];
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/** @var string[] */
|
2020-09-25 05:17:13 +02:00
|
|
|
private array $creationTraces = [];
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/** @var string[] */
|
2020-09-25 05:17:13 +02:00
|
|
|
private array $cancelTraces = [];
|
2019-10-01 21:01:44 +02:00
|
|
|
|
|
|
|
public function __construct(Driver $driver)
|
|
|
|
{
|
|
|
|
$this->driver = $driver;
|
|
|
|
}
|
|
|
|
|
2020-09-26 16:53:24 +02:00
|
|
|
public function createControl(): DriverControl
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
2020-09-26 16:53:24 +02:00
|
|
|
return $this->driver->createControl();
|
2019-10-01 21:01:44 +02:00
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function run(): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
$this->driver->run();
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function stop(): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
$this->driver->stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function defer(callable $callback, $data = null): string
|
|
|
|
{
|
|
|
|
$id = $this->driver->defer(function (...$args) use ($callback) {
|
|
|
|
$this->cancel($args[0]);
|
|
|
|
return $callback(...$args);
|
|
|
|
}, $data);
|
|
|
|
|
|
|
|
$this->creationTraces[$id] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
$this->enabledWatchers[$id] = true;
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function delay(int $delay, callable $callback, $data = null): string
|
|
|
|
{
|
|
|
|
$id = $this->driver->delay($delay, function (...$args) use ($callback) {
|
|
|
|
$this->cancel($args[0]);
|
|
|
|
return $callback(...$args);
|
|
|
|
}, $data);
|
|
|
|
|
|
|
|
$this->creationTraces[$id] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
$this->enabledWatchers[$id] = true;
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function repeat(int $interval, callable $callback, $data = null): string
|
|
|
|
{
|
|
|
|
$id = $this->driver->repeat($interval, $callback, $data);
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
$this->creationTraces[$id] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
$this->enabledWatchers[$id] = true;
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function onReadable($stream, callable $callback, $data = null): string
|
|
|
|
{
|
|
|
|
$id = $this->driver->onReadable($stream, $callback, $data);
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
$this->creationTraces[$id] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
$this->enabledWatchers[$id] = true;
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function onWritable($stream, callable $callback, $data = null): string
|
|
|
|
{
|
|
|
|
$id = $this->driver->onWritable($stream, $callback, $data);
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
$this->creationTraces[$id] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
$this->enabledWatchers[$id] = true;
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function onSignal(int $signo, callable $callback, $data = null): string
|
|
|
|
{
|
|
|
|
$id = $this->driver->onSignal($signo, $callback, $data);
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
$this->creationTraces[$id] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
$this->enabledWatchers[$id] = true;
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
2020-09-26 16:53:24 +02:00
|
|
|
public function enable(string $watcherId): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
$this->driver->enable($watcherId);
|
|
|
|
$this->enabledWatchers[$watcherId] = true;
|
|
|
|
} catch (InvalidWatcherError $e) {
|
|
|
|
throw new InvalidWatcherError(
|
|
|
|
$watcherId,
|
2019-10-26 16:20:56 +02:00
|
|
|
$e->getMessage() . "\r\n\r\n" . $this->getTraces($watcherId)
|
2019-10-01 21:01:44 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function cancel(string $watcherId): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
$this->driver->cancel($watcherId);
|
2019-10-26 16:20:56 +02:00
|
|
|
|
|
|
|
if (!isset($this->cancelTraces[$watcherId])) {
|
|
|
|
$this->cancelTraces[$watcherId] = formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS));
|
|
|
|
}
|
2019-10-01 21:39:23 +02:00
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
unset($this->enabledWatchers[$watcherId], $this->unreferencedWatchers[$watcherId]);
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function disable(string $watcherId): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
$this->driver->disable($watcherId);
|
|
|
|
unset($this->enabledWatchers[$watcherId]);
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function reference(string $watcherId): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
$this->driver->reference($watcherId);
|
|
|
|
unset($this->unreferencedWatchers[$watcherId]);
|
|
|
|
} catch (InvalidWatcherError $e) {
|
|
|
|
throw new InvalidWatcherError(
|
|
|
|
$watcherId,
|
2019-10-26 16:20:56 +02:00
|
|
|
$e->getMessage() . "\r\n\r\n" . $this->getTraces($watcherId)
|
2019-10-01 21:01:44 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function unreference(string $watcherId): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
$this->driver->unreference($watcherId);
|
|
|
|
$this->unreferencedWatchers[$watcherId] = true;
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function setErrorHandler(callable $callback = null): ?callable
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
return $this->driver->setErrorHandler($callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @inheritdoc */
|
|
|
|
public function getHandle()
|
|
|
|
{
|
|
|
|
$this->driver->getHandle();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function dump(): string
|
|
|
|
{
|
|
|
|
$dump = "Enabled, referenced watchers keeping the loop running: ";
|
|
|
|
|
|
|
|
foreach ($this->enabledWatchers as $watcher => $_) {
|
|
|
|
if (isset($this->unreferencedWatchers[$watcher])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$dump .= "Watcher ID: " . $watcher . "\r\n";
|
|
|
|
$dump .= $this->getCreationTrace($watcher);
|
|
|
|
$dump .= "\r\n\r\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
return \rtrim($dump);
|
|
|
|
}
|
|
|
|
|
2019-10-01 21:39:23 +02:00
|
|
|
public function getInfo(): array
|
|
|
|
{
|
|
|
|
return $this->driver->getInfo();
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
public function __debugInfo(): array
|
2019-10-01 21:39:23 +02:00
|
|
|
{
|
|
|
|
return $this->driver->__debugInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function now(): int
|
|
|
|
{
|
|
|
|
return $this->driver->now();
|
|
|
|
}
|
|
|
|
|
2020-09-25 05:17:13 +02:00
|
|
|
protected function error(\Throwable $exception): void
|
2019-10-01 21:39:23 +02:00
|
|
|
{
|
|
|
|
$this->driver->error($exception);
|
|
|
|
}
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2020-09-25 05:17:13 +02:00
|
|
|
protected function activate(array $watchers): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
// nothing to do in a decorator
|
|
|
|
}
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2020-09-25 05:17:13 +02:00
|
|
|
protected function dispatch(bool $blocking): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
// nothing to do in a decorator
|
|
|
|
}
|
|
|
|
|
2020-03-28 22:20:44 +01:00
|
|
|
/**
|
|
|
|
* @inheritdoc
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2020-09-25 05:17:13 +02:00
|
|
|
protected function deactivate(Watcher $watcher): void
|
2019-10-01 21:01:44 +02:00
|
|
|
{
|
|
|
|
// nothing to do in a decorator
|
|
|
|
}
|
|
|
|
|
2019-10-26 16:20:56 +02:00
|
|
|
private function getTraces(string $watcherId): string
|
|
|
|
{
|
|
|
|
return "Creation Trace:\r\n" . $this->getCreationTrace($watcherId) . "\r\n\r\n" .
|
|
|
|
"Cancellation Trace:\r\n" . $this->getCancelTrace($watcherId);
|
|
|
|
}
|
|
|
|
|
2019-10-01 21:01:44 +02:00
|
|
|
private function getCreationTrace(string $watcher): string
|
|
|
|
{
|
|
|
|
if (!isset($this->creationTraces[$watcher])) {
|
2019-10-26 16:20:56 +02:00
|
|
|
return 'No creation trace, yet.';
|
2019-10-01 21:01:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->creationTraces[$watcher];
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getCancelTrace(string $watcher): string
|
|
|
|
{
|
|
|
|
if (!isset($this->cancelTraces[$watcher])) {
|
2019-10-26 16:20:56 +02:00
|
|
|
return 'No cancellation trace, yet.';
|
2019-10-01 21:01:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->cancelTraces[$watcher];
|
|
|
|
}
|
|
|
|
}
|