Dark magic to allow serializing session to database on shutdown

This commit is contained in:
Daniil Gentili 2020-09-27 21:51:55 +02:00
parent 5560b140f5
commit 1cd41cc159
6 changed files with 53 additions and 6 deletions

View File

@ -255,7 +255,7 @@ class API extends InternalDoc
$this->API->destructing = true;
$this->API->unreference();
}
if (isset($this->wrapper)) {
if (isset($this->wrapper) && !Magic::$signaled) {
$this->logger->logger('Prompting final serialization...');
Tools::wait($this->wrapper->serialize());
$this->logger->logger('Done final serialization!');

View File

@ -204,6 +204,20 @@ class MTProto extends AsyncConstruct implements TLCallback
'msg_resend_ans_req',
];
const DEFAULT_GETUPDATES_PARAMS = ['offset' => 0, 'limit' => null, 'timeout' => 100];
/**
* Array of references to all instances of MTProto.
*
* This seems like a recipe for memory leaks, but this is actually required to allow saving the session on shutdown.
* When using a network I/O-based database+the EvDriver of AMPHP, calling die(); causes premature garbage collection of the event loop.
* This garbage collection happens always, even if a reference to the event handler is already present elsewhere (probably ev dark magic).
*
* Finally, this causes the process to hang on shutdown, since the database driver cannot receive a reply from the server, because the event loop is down.
*
* To avoid this, we store each MTProto instance in here (unreferencing on shutdown in unreference()), and call serialize() on all instances before calling die; in Magic.
*
* @var self[]
*/
public static array $references = [];
/**
* Instance of wrapper API.
*
@ -505,9 +519,23 @@ class MTProto extends AsyncConstruct implements TLCallback
return $data;
}
yield $this->session->offsetSet('data', $data);
var_dump("Saved!");
return $this->session;
}
/**
* Serialize all instances.
*
* CALLED ONLY ON SHUTDOWN.
*
* @return void
*/
public static function serializeAll(): void
{
Logger::log('Prompting final serialization (SHUTDOWN)...');
foreach (self::$references as $instance) {
Tools::wait($instance->wrapper->serialize());
}
Logger::log('Done final serialization (SHUTDOWN)!');
}
/**
* Constructor function.
@ -519,6 +547,7 @@ class MTProto extends AsyncConstruct implements TLCallback
*/
public function __magic_construct(SettingsAbstract $settings, APIWrapper $wrapper)
{
self::$references[\spl_object_hash($this)] = $this;
$this->wrapper = $wrapper;
$this->setInitPromise($this->__construct_async($settings));
}
@ -1023,6 +1052,8 @@ class MTProto extends AsyncConstruct implements TLCallback
*/
public function wakeup(SettingsAbstract $settings, APIWrapper $wrapper): \Generator
{
// Set reference to itself
self::$references[\spl_object_hash($this)] = $this;
// Set API wrapper
$this->wrapper = $wrapper;
// BC stuff
@ -1132,6 +1163,9 @@ class MTProto extends AsyncConstruct implements TLCallback
public function unreference(): void
{
$this->logger->logger("Will unreference instance");
if (isset(self::$references[\spl_object_hash($this)])) {
unset(self::$references[\spl_object_hash($this)]);
}
$this->stopLoops();
if (isset($this->seqUpdater)) {
$this->seqUpdater->signal(true);
@ -1153,7 +1187,6 @@ class MTProto extends AsyncConstruct implements TLCallback
$datacenter->disconnect();
}
$this->logger->logger("Unreferenced instance");
}
/**
* Destructor.

View File

@ -59,7 +59,7 @@ class Magic
*/
public static $isFork = false;
/**
* Whether this is an IPC worker
* Whether this is an IPC worker.
*/
public static bool $isIpcWorker = false;
/**
@ -234,7 +234,7 @@ class Magic
// Setup error reporting
\set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
\set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
self::$isIpcWorker = defined(\MADELINE_WORKER_TYPE::class) ? \MADELINE_WORKER_TYPE === 'madeline-ipc' : false;
self::$isIpcWorker = \defined(\MADELINE_WORKER_TYPE::class) ? \MADELINE_WORKER_TYPE === 'madeline-ipc' : false;
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
try {
\error_reporting(E_ALL);
@ -412,6 +412,7 @@ class Magic
$driver->unreference($key);
}
}
MTProto::serializeAll();
Loop::stop();
die($code);
}

View File

@ -425,6 +425,18 @@ class Connection extends SettingsAbstract
return $this;
}
/**
* Set proxies.
*
* @param array $proxies Proxies
*
* @return self
*/
public function setProxy(array $proxies): self
{
$this->proxy = $proxies;
return $this;
}
/**
* Clear proxies.
*

View File

@ -46,7 +46,7 @@ abstract class SettingsAbstract
!isset($defaults[$name])
|| $other->{$name} !== $defaults[$name] // Isn't equal to the default value
)
&& $other->{$name} !== $this->{"get$uc"}
&& $other->{$name} !== $this->{$name}
) {
$this->{"set$uc"}($other->{$name});
$this->changed = true;

View File

@ -52,6 +52,7 @@ class Shutdown
foreach (self::$callbacks as $callback) {
$callback();
}
Magic::shutdown(0);
}
/**
* Add a callback for script shutdown.