Fix memory leaks when downloading files to callback. (#816)
This commit is contained in:
parent
83802fe944
commit
d5b2cbefd3
@ -22,12 +22,12 @@ namespace danog\MadelineProto;
|
|||||||
use Amp\Dns\Resolver;
|
use Amp\Dns\Resolver;
|
||||||
use Amp\File\StatCache;
|
use Amp\File\StatCache;
|
||||||
use Amp\Http\Client\HttpClient;
|
use Amp\Http\Client\HttpClient;
|
||||||
use Amp\Loop;
|
|
||||||
use danog\MadelineProto\Async\AsyncConstruct;
|
use danog\MadelineProto\Async\AsyncConstruct;
|
||||||
use danog\MadelineProto\Loop\Generic\PeriodicLoop;
|
use danog\MadelineProto\Loop\Generic\PeriodicLoop;
|
||||||
use danog\MadelineProto\Loop\Update\FeedLoop;
|
use danog\MadelineProto\Loop\Update\FeedLoop;
|
||||||
use danog\MadelineProto\Loop\Update\SeqLoop;
|
use danog\MadelineProto\Loop\Update\SeqLoop;
|
||||||
use danog\MadelineProto\Loop\Update\UpdateLoop;
|
use danog\MadelineProto\Loop\Update\UpdateLoop;
|
||||||
|
use danog\MadelineProto\MTProtoTools\GarbageCollector;
|
||||||
use danog\MadelineProto\MTProtoTools\CombinedUpdatesState;
|
use danog\MadelineProto\MTProtoTools\CombinedUpdatesState;
|
||||||
use danog\MadelineProto\MTProtoTools\MinDatabase;
|
use danog\MadelineProto\MTProtoTools\MinDatabase;
|
||||||
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
|
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
|
||||||
@ -465,6 +465,8 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
yield from $this->getConfig([], ['datacenter' => $this->datacenter->curdc]);
|
yield from $this->getConfig([], ['datacenter' => $this->datacenter->curdc]);
|
||||||
$this->startUpdateSystem(true);
|
$this->startUpdateSystem(true);
|
||||||
$this->v = self::V;
|
$this->v = self::V;
|
||||||
|
|
||||||
|
GarbageCollector::start();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sleep function.
|
* Sleep function.
|
||||||
@ -921,6 +923,8 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
yield $this->updaters[false]->resume();
|
yield $this->updaters[false]->resume();
|
||||||
}
|
}
|
||||||
$this->updaters[false]->start();
|
$this->updaters[false]->start();
|
||||||
|
|
||||||
|
GarbageCollector::start();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Unreference instance, allowing destruction.
|
* Unreference instance, allowing destruction.
|
||||||
|
@ -499,7 +499,7 @@ trait Files
|
|||||||
$cb = [$bridge, 'callback'];
|
$cb = [$bridge, 'callback'];
|
||||||
$read = $this->uploadFromCallable($reader, $size, $mime, '', $cb, true, $encrypted);
|
$read = $this->uploadFromCallable($reader, $size, $mime, '', $cb, true, $encrypted);
|
||||||
$write = $this->downloadToCallable($media, $writer, null, true, 0, -1, $chunk_size);
|
$write = $this->downloadToCallable($media, $writer, null, true, 0, -1, $chunk_size);
|
||||||
list($res) = yield \danog\MadelineProto\Tools::all([$read, $write]);
|
[$res] = yield \danog\MadelineProto\Tools::all([$read, $write]);
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1038,7 +1038,7 @@ trait Files
|
|||||||
if (\count($range) == 1) {
|
if (\count($range) == 1) {
|
||||||
$range[1] = '';
|
$range[1] = '';
|
||||||
}
|
}
|
||||||
list($size_unit, $range_orig) = $range;
|
[$size_unit, $range_orig] = $range;
|
||||||
if ($size_unit == 'bytes') {
|
if ($size_unit == 'bytes') {
|
||||||
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
|
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
|
||||||
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
|
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
|
||||||
@ -1046,7 +1046,7 @@ trait Files
|
|||||||
if (\count($list) == 1) {
|
if (\count($list) == 1) {
|
||||||
$list[1] = '';
|
$list[1] = '';
|
||||||
}
|
}
|
||||||
list($range, $extra_ranges) = $list;
|
[$range, $extra_ranges] = $list;
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
'serve' => false,
|
'serve' => false,
|
||||||
@ -1061,7 +1061,7 @@ trait Files
|
|||||||
if (\count($listseek) == 1) {
|
if (\count($listseek) == 1) {
|
||||||
$listseek[1] = '';
|
$listseek[1] = '';
|
||||||
}
|
}
|
||||||
list($seek_start, $seek_end) = $listseek;
|
[$seek_start, $seek_end] = $listseek;
|
||||||
|
|
||||||
$seek_end = empty($seek_end) ? ($messageMedia['size'] - 1) : \min(\abs(\intval($seek_end)), $messageMedia['size'] - 1);
|
$seek_end = empty($seek_end) ? ($messageMedia['size'] - 1) : \min(\abs(\intval($seek_end)), $messageMedia['size'] - 1);
|
||||||
|
|
||||||
@ -1274,7 +1274,7 @@ trait Files
|
|||||||
$time = 0;
|
$time = 0;
|
||||||
$speed = 0;
|
$speed = 0;
|
||||||
$origCb = $cb;
|
$origCb = $cb;
|
||||||
$cb = function () use ($cb, $count, &$time, &$speed) {
|
$cb = static function () use ($cb, $count, &$time, &$speed) {
|
||||||
static $cur = 0;
|
static $cur = 0;
|
||||||
$cur++;
|
$cur++;
|
||||||
\danog\MadelineProto\Tools::callFork($cb($cur * 100 / $count, $time, $speed));
|
\danog\MadelineProto\Tools::callFork($cb($cur * 100 / $count, $time, $speed));
|
||||||
|
59
src/danog/MadelineProto/MTProtoTools/GarbageCollector.php
Normal file
59
src/danog/MadelineProto/MTProtoTools/GarbageCollector.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace danog\MadelineProto\MTProtoTools;
|
||||||
|
|
||||||
|
use Amp\Loop;
|
||||||
|
use danog\MadelineProto\Logger;
|
||||||
|
|
||||||
|
class GarbageCollector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Ensure only one instance of GarbageCollector
|
||||||
|
* when multiple instances of MadelineProto running.
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public static bool $lock = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often will check memory
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static int $checkIntervalMs = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Next cleanup will be triggered when memory consumption will increase by this amount
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static int $memoryDiffMb = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory consumption after last cleanup
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private static int $memoryConsumption = 0;
|
||||||
|
|
||||||
|
public static function start(): void
|
||||||
|
{
|
||||||
|
if (static::$lock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static::$lock = true;
|
||||||
|
|
||||||
|
Loop::repeat(static::$checkIntervalMs, static function() {
|
||||||
|
$currentMemory = static::getMemoryConsumption();
|
||||||
|
if ($currentMemory > static::$memoryConsumption + static::$memoryDiffMb) {
|
||||||
|
gc_collect_cycles();
|
||||||
|
static::$memoryConsumption = static::getMemoryConsumption();
|
||||||
|
$cleanedMemory = $currentMemory - static::$memoryConsumption;
|
||||||
|
Logger::log("gc_collect_cycles done. Cleaned memory: $cleanedMemory Mb", Logger::NOTICE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getMemoryConsumption(): int
|
||||||
|
{
|
||||||
|
$memory = round(memory_get_usage()/1024/1024, 1);
|
||||||
|
Logger::log("Memory consumption: $memory Mb", Logger::VERBOSE);
|
||||||
|
return (int) $memory;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user