1
0
mirror of https://github.com/danog/MadelineProto.git synced 2025-01-11 23:58:26 +01:00
MadelineProto/src/GarbageCollector.php

190 lines
7.0 KiB
PHP
Raw Normal View History

2023-08-13 11:40:35 +02:00
<?php declare(strict_types=1);
2022-12-30 21:54:44 +01:00
2023-08-13 11:40:35 +02:00
/**
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2023 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
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;
use AssertionError;
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;
2023-08-15 13:16:08 +02:00
use function Amp\File\move;
use function Amp\File\read;
use function Amp\File\write;
2023-01-25 16:32:48 +01:00
/**
2023-02-16 18:38:47 +01:00
* @internal
*
2023-01-25 16:32:48 +01:00
* @psalm-suppress UndefinedConstant
*/
final class GarbageCollector
{
2020-06-16 17:52:55 +02:00
/**
2023-02-16 18:38:47 +01:00
* Ensure only one instance of GarbageCollector exists
* when multiple instances of MadelineProto are 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;
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
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
}
}));
2021-12-10 17:50:41 +01:00
if (!\defined('MADELINE_RELEASE_URL')) {
return;
}
$client = HttpClientBuilder::buildDefault();
$request = new Request(MADELINE_RELEASE_URL);
2023-01-26 14:33:30 +01:00
$id = null;
$cb = function () use ($client, $request, &$id): void {
2021-12-10 17:50:41 +01:00
try {
2023-01-15 22:13:21 +01:00
$latest = $client->request($request);
2023-07-25 16:12:42 +02:00
Magic::$latest_release = \trim($latest->getBody()->buffer());
if (API::RELEASE !== Magic::$latest_release) {
$old = API::RELEASE;
$new = Magic::$latest_release;
2023-01-28 14:53:07 +01:00
Logger::log("!!!!!!!!!!!!! An update of MadelineProto is required (old=$old, new=$new)! !!!!!!!!!!!!!", Logger::FATAL_ERROR);
$contents = $client->request(new Request("https://phar.madelineproto.xyz/phar.php?v=new".\rand(0, PHP_INT_MAX)))
->getBody()
->buffer();
2023-06-18 22:13:12 +02:00
if (!\str_starts_with($contents, '<?php')) {
throw new AssertionError("phar.php is not a PHP file!");
}
if ($contents !== read(MADELINE_PHP)) {
$unlock = Tools::flock(MADELINE_PHP.'.lock', LOCK_EX);
write(MADELINE_PHP.'.temp.php', $contents);
move(MADELINE_PHP.'.temp.php', MADELINE_PHP);
$unlock();
}
2023-08-15 13:16:08 +02:00
try {
\unlink(MADELINE_PHAR_VERSION);
} catch (Throwable) {
}
2023-01-15 22:13:21 +01:00
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
}
2023-08-05 21:26:58 +02:00
/** @var string */
2021-12-10 17:50:41 +01:00
foreach (\glob(MADELINE_PHAR_GLOB) as $path) {
$base = \basename($path);
2023-07-25 16:12:42 +02:00
if ($base === 'madeline-'.API::RELEASE.'.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) {
2023-01-28 17:13:29 +01:00
if ($e instanceof SignalException) {
throw $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-05-26 16:30:43 +02:00
EventLoop::unreference($id = EventLoop::repeat(3600.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;
}
}