This commit is contained in:
2026-07-08 11:39:27 +08:00
parent 1f443958e8
commit 27ffa05515
6 changed files with 221 additions and 30 deletions
+76 -18
View File
@@ -13,6 +13,8 @@ use Kiri\Server\Processes\OnProcessInterface;
use Psr\Log\LoggerInterface;
use Swoole\Event;
use Swoole\Process;
use const SIGKILL;
use const SIGTERM;
use const SIGUSR1;
class FileWatcher extends AbstractProcess implements OnProcessInterface
@@ -35,6 +37,7 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
private array $extensions = ['php'];
private string $strategy;
private bool $running = false;
private bool $scanning = false;
private $inotifyFd = null;
private array $inotifyWatchMap = [];
@@ -132,6 +135,10 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
public function process(Process|null $process): void
{
if (class_exists('\\Swoole\\Coroutine')) {
\Swoole\Coroutine::set(['enable_deadlock_check' => false]);
}
if ($this->running) {
return;
}
@@ -163,20 +170,14 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
$this->inotifyFd = null;
}
if (isset($this->fswatchPipes[1]) && is_resource($this->fswatchPipes[1])) {
@Event::del($this->fswatchPipes[1]);
@fclose($this->fswatchPipes[1]);
}
if (isset($this->fswatchPipes[2]) && is_resource($this->fswatchPipes[2])) {
@fclose($this->fswatchPipes[2]);
}
if ($this->fswatchProcess && is_resource($this->fswatchProcess)) {
@proc_terminate($this->fswatchProcess);
@proc_close($this->fswatchProcess);
$this->fswatchProcess = null;
foreach ($this->fswatchPipes as $pipe) {
if (is_resource($pipe)) {
@Event::del($pipe);
@fclose($pipe);
}
}
$this->fswatchPipes = [];
$this->stopFswatchProcess();
if ($this->pollTimer) {
\Swoole\Timer::clear($this->pollTimer);
@@ -191,6 +192,43 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
@Event::exit();
}
private function stopFswatchProcess(): void
{
if (!$this->fswatchProcess || !is_resource($this->fswatchProcess)) {
$this->fswatchProcess = null;
return;
}
$status = @proc_get_status($this->fswatchProcess);
if (($status['running'] ?? false) === true) {
@proc_terminate($this->fswatchProcess, SIGTERM);
for ($i = 0; $i < 10; $i++) {
usleep(100000);
$status = @proc_get_status($this->fswatchProcess);
if (($status['running'] ?? false) !== true) {
break;
}
}
}
$status = @proc_get_status($this->fswatchProcess);
if (($status['running'] ?? false) === true) {
@proc_terminate($this->fswatchProcess, SIGKILL);
for ($i = 0; $i < 5; $i++) {
usleep(100000);
$status = @proc_get_status($this->fswatchProcess);
if (($status['running'] ?? false) !== true) {
break;
}
}
}
$status = @proc_get_status($this->fswatchProcess);
if (($status['running'] ?? false) !== true) {
@proc_close($this->fswatchProcess);
}
$this->fswatchProcess = null;
}
private function isExcluded(string $path): bool
{
foreach ($this->excludePatterns as $pattern) {
@@ -377,6 +415,10 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
}
Event::add($this->inotifyFd, function ($fd) {
if (!$this->running) {
return;
}
$events = inotify_read($fd);
if ($events === false) {
return;
@@ -466,6 +508,10 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
stream_set_blocking($this->fswatchPipes[1], false);
Event::add($this->fswatchPipes[1], function ($pipe) {
if (!$this->running) {
return;
}
$data = fread($pipe, 8192);
if ($data === false || $data === '') {
return;
@@ -486,13 +532,25 @@ class FileWatcher extends AbstractProcess implements OnProcessInterface
$intervalMs = $this->pollInterval * 1000;
$this->pollTimer = \Swoole\Timer::tick($intervalMs, function () {
$newSnapshot = [];
$changedFiles = $this->compareSnapshots($newSnapshot);
if (!empty($changedFiles)) {
$this->triggerCallback($changedFiles);
if (!$this->running || $this->scanning) {
return;
}
$this->fileSnapshot = $newSnapshot;
$this->scanning = true;
try {
$newSnapshot = [];
$changedFiles = $this->compareSnapshots($newSnapshot);
if (!$this->running) {
return;
}
if (!empty($changedFiles)) {
$this->triggerCallback($changedFiles);
}
$this->fileSnapshot = $newSnapshot;
} finally {
$this->scanning = false;
}
});
}