1
0
mirror of https://github.com/danog/MadelineProto.git synced 2025-01-12 02:38:17 +01:00
MadelineProto/src/GarbageCollector.php

169 lines
5.9 KiB
PHP
Raw Normal View History

2022-12-30 21:54:44 +01:00
<?php
declare(strict_types=1);
namespace danog\MadelineProto;
2021-12-10 17:50:41 +01:00
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
2023-01-15 16:12:12 +01:00
use Amp\SignalException;
2023-01-24 14:28:49 +01:00
use danog\Loop\PeriodicLoop;
2023-01-24 19:12:25 +01:00
use ReflectionFiber;
2022-12-30 21:43:58 +01:00
use Revolt\EventLoop;
2022-12-30 19:21:36 +01:00
use Throwable;
2023-01-24 19:12:25 +01:00
use WeakMap;
2022-12-30 19:21:36 +01:00
use const LOCK_EX;
use const LOCK_NB;
use function Amp\File\move;
use function Amp\File\read;
use function Amp\File\write;
2023-01-25 16:32:48 +01:00
/**
* @psalm-suppress UndefinedConstant
*/
final class GarbageCollector
{
2020-06-16 17:52:55 +02:00
/**
* Ensure only one instance of GarbageCollector
2022-12-30 19:21:36 +01:00
* when multiple instances of MadelineProto running.
2020-06-16 17:52:55 +02:00
*/
2023-01-20 15:47:40 +01:00
private static bool $started = false;
2020-06-16 17:52:55 +02:00
/**
* Next cleanup will be triggered when memory consumption will increase by this amount.
*/
public static int $memoryDiffMb = 1;
/**
* Memory consumption after last cleanup.
*/
private static int $memoryConsumption = 0;
2021-12-10 17:50:41 +01:00
/**
* Phar cleanup loop.
*/
private static PeriodicLoop $cleanupLoop;
2020-06-16 17:52:55 +02:00
public static function start(): void
{
2023-01-20 15:47:40 +01:00
if (self::$started) {
2020-06-16 17:52:55 +02:00
return;
}
2023-01-20 15:47:40 +01:00
self::$started = true;
2020-06-16 17:52:55 +02:00
2023-01-26 14:33:30 +01:00
EventLoop::unreference(EventLoop::repeat(1, static function (): void {
$currentMemory = self::getMemoryConsumption();
if ($currentMemory > self::$memoryConsumption + self::$memoryDiffMb) {
2020-06-16 17:52:55 +02:00
\gc_collect_cycles();
self::$memoryConsumption = self::getMemoryConsumption();
$cleanedMemory = $currentMemory - self::$memoryConsumption;
if (!Magic::$suspendPeriodicLogging) {
Logger::log("gc_collect_cycles done. Cleaned memory: $cleanedMemory Mb", Logger::VERBOSE);
}
2020-06-16 17:52:55 +02:00
}
2023-01-26 14:33:30 +01:00
}));
2021-12-10 17:50:41 +01:00
if (!\defined('MADELINE_RELEASE_URL')) {
return;
}
$client = HttpClientBuilder::buildDefault();
$request = new Request(MADELINE_RELEASE_URL);
$madelinePhpContents = null;
2023-01-26 14:33:30 +01:00
$id = null;
$cb = function () use ($client, $request, &$madelinePhpContents, &$id): void {
2021-12-10 17:50:41 +01:00
try {
$madelinePhpContents ??= read(MADELINE_PHP);
$contents = $client->request(new Request("https://phar.madelineproto.xyz/phar.php?v=new".\rand(0, PHP_INT_MAX)))
->getBody()
->buffer();
if ($contents !== $madelinePhpContents) {
$unlock = Tools::flock(MADELINE_PHP.'.lock', LOCK_EX);
write(MADELINE_PHP.'.temp.php', $contents);
move(MADELINE_PHP.'.temp.php', MADELINE_PHP);
$unlock();
$madelinePhpContents = $contents;
}
2023-01-15 22:13:21 +01:00
$latest = $client->request($request);
2023-01-22 15:39:10 +01:00
Magic::$version_latest = \trim($latest->getBody()->buffer());
2023-01-15 22:13:21 +01:00
if (Magic::$version !== Magic::$version_latest) {
Logger::log('!!!!!!!!!!!!! An update of MadelineProto is required, shutting down worker! !!!!!!!!!!!!!', Logger::FATAL_ERROR);
write(MADELINE_PHAR_VERSION, '');
if (Magic::$isIpcWorker) {
throw new SignalException('!!!!!!!!!!!!! An update of MadelineProto is required, shutting down worker! !!!!!!!!!!!!!');
}
2023-01-26 14:33:30 +01:00
if ($id) {
EventLoop::cancel($id);
}
return;
2023-01-15 22:13:21 +01:00
}
2021-12-10 17:50:41 +01:00
foreach (\glob(MADELINE_PHAR_GLOB) as $path) {
$base = \basename($path);
2023-01-04 15:13:55 +01:00
if ($base === 'madeline-'.Magic::$version.'.phar') {
2021-12-10 17:50:41 +01:00
continue;
}
2021-12-15 00:25:53 +01:00
$f = \fopen("$path.lock", 'c');
2021-12-10 17:50:41 +01:00
if (\flock($f, LOCK_EX|LOCK_NB)) {
\fclose($f);
\unlink($path);
2021-12-15 00:25:53 +01:00
\unlink("$path.lock");
2021-12-10 17:50:41 +01:00
} else {
\fclose($f);
}
}
2022-12-30 19:21:36 +01:00
} catch (Throwable $e) {
2021-12-13 14:27:00 +01:00
Logger::log("An error occurred in the phar cleanup loop: $e", Logger::FATAL_ERROR);
2021-12-10 17:50:41 +01:00
}
2023-01-22 15:39:10 +01:00
};
$cb();
2023-01-26 14:33:30 +01:00
EventLoop::unreference($id = EventLoop::repeat(60.0, $cb));
2020-06-16 17:52:55 +02:00
}
2023-01-24 19:12:25 +01:00
/** @var \WeakMap<\Fiber, true> */
public static WeakMap $map;
public static function registerFiber(\Fiber $fiber): void
{
self::$map ??= new WeakMap;
self::$map[$fiber] = true;
}
2020-06-16 17:52:55 +02:00
private static function getMemoryConsumption(): int
{
$memory = \round(\memory_get_usage()/1024/1024, 1);
if (!Magic::$suspendPeriodicLogging) {
2023-01-25 15:53:28 +01:00
/*$k = 0;
foreach (self::$map as $fiber => $_) {
if ($k++ === 0) {
continue;
}
if ($fiber->isTerminated()) {
continue;
}
if (!$fiber->isStarted()) {
continue;
}
2023-01-24 19:12:25 +01:00
$reflection = new ReflectionFiber($fiber);
2023-01-25 15:53:28 +01:00
$tlTrace = '';
foreach ($reflection->getTrace() as $k => $frame) {
$tlTrace .= isset($frame['file']) ? \str_pad(\basename($frame['file']).'('.$frame['line'].'):', 20)."\t" : '';
$tlTrace .= isset($frame['function']) ? $frame['function'].'(' : '';
$tlTrace .= isset($frame['args']) ? \substr(\json_encode($frame['args']) ?: '', 1, -1) : '';
$tlTrace .= ')';
$tlTrace .= "\n";
}
\var_dump($tlTrace);
}
2023-01-25 16:32:48 +01:00
Logger::log("Memory consumption: $memory Mb", Logger::ULTRA_VERBOSE);
2023-01-25 15:53:28 +01:00
$fibers = self::$map->count();
2023-01-25 16:32:48 +01:00
$maps = '~'.\substr_count(\file_get_contents('/proc/self/maps'), "\n");
Logger::log("Running fibers: $fibers, maps: $maps", Logger::ULTRA_VERBOSE);*/
}
2020-06-16 17:52:55 +02:00
return (int) $memory;
}
}