Skip to content

Commit f1e20c3

Browse files
committed
Fix hangs
1 parent d2e4072 commit f1e20c3

7 files changed

Lines changed: 43 additions & 21 deletions

File tree

src/API.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ final class API extends AbstractAPI
5151
*
5252
* @var string
5353
*/
54-
public const RELEASE = '8.4.10';
54+
public const RELEASE = '8.4.11';
5555
/**
5656
* We're not logged in.
5757
*

src/Connection.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,6 @@ public function sendMessage(MTProtoOutgoingMessage $message): void
521521
{
522522
$message->connection = $this;
523523
$message->trySend();
524-
$promise = $message->getSendPromise();
525524
if (!$message->hasSerializedBody() || $message->shouldRefreshReferences()) {
526525
$body = $message->getBody();
527526
if ($message->shouldRefreshReferences()) {
@@ -563,7 +562,6 @@ public function sendMessage(MTProtoOutgoingMessage $message): void
563562
$this->mainPendingOutgoing->enqueue($message);
564563
}
565564
$this->flush();
566-
$promise->await();
567565
}
568566
/**
569567
* Flush pending packets.

src/Loop/Connection/WriteLoop.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public function encryptedWriteLoop(): void
197197
$arr[] = $msg;
198198
}
199199
$this->API->logger("Still missing {$list} on DC {$this->datacenter}, sending state request", Logger::ERROR);
200-
$this->connection->objectCall('msgs_state_req', ['msg_ids' => $msgIds, 'cancellation' => new \Amp\TimeoutCancellation(self::LONG_POLL_TIMEOUT)], $deferred);
200+
$this->connection->objectCallAsync('msgs_state_req', ['msg_ids' => $msgIds, 'cancellation' => new \Amp\TimeoutCancellation(self::LONG_POLL_TIMEOUT)], $deferred);
201201
$deferred->getFuture()->map(function (array|\Closure $result) use ($arr): void {
202202
try {
203203
if (\is_callable($result)) {
@@ -290,7 +290,7 @@ public function encryptedWriteLoop(): void
290290
}
291291

292292
$message_id = $message->getMsgId() ?? $this->connection->msgIdHandler->generateMessageId();
293-
$this->API->logger("Sending $message as encrypted message to DC $this->datacenter", Logger::ULTRA_VERBOSE);
293+
$this->API->logger("Sending $message as encrypted message with id $message_id to DC $this->datacenter", Logger::ULTRA_VERBOSE);
294294
$MTmessage = [
295295
'_' => 'MTmessage',
296296
'msg_id' => $message_id,
@@ -328,7 +328,7 @@ public function encryptedWriteLoop(): void
328328
'bytes' => \strlen($body),
329329
];
330330
$count++;
331-
unset($acks, $body);
331+
unset($body);
332332
}
333333
if ($this->connection->isHttp()) {
334334
$this->API->logger('Adding http_wait', Logger::ULTRA_VERBOSE);
@@ -345,13 +345,19 @@ public function encryptedWriteLoop(): void
345345
}
346346

347347
if ($count > 1 || $has_seq) {
348-
$this->API->logger("Wrapping in msg_container ({$count} messages of total size {$total_length}) as encrypted message for DC {$this->datacenter}", Logger::ULTRA_VERBOSE);
349348
$message_id = $this->connection->msgIdHandler->generateMessageId();
350-
$messages []= new Container($this->connection, $messages);
349+
$this->API->logger("Wrapping in msg_container ({$count} messages of total size {$total_length}, id $message_id) as encrypted message for DC {$this->datacenter}", Logger::ULTRA_VERBOSE);
350+
$messages []= $ct = new Container(
351+
$this->connection,
352+
$messages,
353+
$acks,
354+
);
351355
$this->connection->outgoingCtr?->inc();
352356
$message_data = $this->API->getTL()->serializeObject(['type' => ''], ['_' => 'msg_container', 'messages' => $MTmessages], 'container');
353357
$message_data_length = \strlen($message_data);
354358
$seq_no = $this->connection->generateOutSeqNo(false);
359+
$ct->setMsgId($message_id);
360+
$ct->setSeqNo($seq_no);
355361
} elseif ($count) {
356362
$message = $MTmessages[0];
357363
$message_data = $message['body'];

src/MTProto.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,19 @@ final class MTProto implements TLCallback, LoggerGetter, SettingsGetter
150150
* @internal
151151
* @var array
152152
*/
153-
public const BAD_MSG_ERROR_CODES = [16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the correct msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)', 17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)', 18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)', 19 => 'container msg_id is the same as msg_id of a previously received message (this must never happen)', 20 => 'message too old, and it cannot be verified whether the server has received a message with this msg_id or not', 32 => 'msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)', 33 => 'msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)', 34 => 'an even msg_seqno expected (irrelevant message), but odd received', 35 => 'odd msg_seqno expected (relevant message), but even received', 48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)', 64 => 'invalid container'];
153+
public const BAD_MSG_ERROR_CODES = [
154+
16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the correct msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)',
155+
17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)',
156+
18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)',
157+
19 => 'container msg_id is the same as msg_id of a previously received message (this must never happen)',
158+
20 => 'message too old, and it cannot be verified whether the server has received a message with this msg_id or not',
159+
32 => 'msg_seqno too low (the server has already received a message with a lower msg_id but with either a higher or an equal and odd seqno)',
160+
33 => 'msg_seqno too high (similarly, there is a message with a higher msg_id but with either a lower or an equal and odd seqno)',
161+
34 => 'an even msg_seqno expected (irrelevant message), but odd received',
162+
35 => 'odd msg_seqno expected (relevant message), but even received',
163+
48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)',
164+
64 => 'invalid container',
165+
];
154166
/**
155167
* Localized message info flags.
156168
*

src/MTProto/Container.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ final class Container extends MTProtoOutgoingMessage
3333
* Constructor.
3434
*
3535
* @param list<MTProtoOutgoingMessage> $msgs
36+
* @param list<int> $acks
3637
*/
37-
public function __construct(Connection $connection, public readonly array $msgs)
38-
{
38+
public function __construct(
39+
Connection $connection,
40+
public readonly array $msgs,
41+
public readonly array $acks,
42+
) {
3943
parent::__construct($connection, [], 'msg_container', '', false, false, null, null);
4044
}
4145
}

src/MTProto/MTProtoOutgoingMessage.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,10 @@ public function trySend(): void
191191
*/
192192
public function sent(): void
193193
{
194-
if ($this->resultDeferred !== null) {
195-
if ($this->unencrypted) {
196-
$this->connection->unencrypted_new_outgoing[$this->getMsgId()] = $this;
197-
} else {
198-
$this->connection->new_outgoing[$this->getMsgId()] = $this;
199-
}
194+
if ($this->unencrypted) {
195+
$this->connection->unencrypted_new_outgoing[$this->getMsgId()] = $this;
196+
} else {
197+
$this->connection->new_outgoing[$this->getMsgId()] = $this;
200198
}
201199
if ($this->sent === null && $this->isMethod) {
202200
$this->connection->inFlightGauge?->inc([
@@ -233,7 +231,6 @@ private function check(): void
233231
}
234232
$shared = $this->connection->getShared();
235233
$settings = $shared->getSettings();
236-
$global = $shared->getGenericSettings();
237234
$timeout = $settings->getTimeout();
238235
$this->checkTimer = EventLoop::delay(
239236
$timeout,

src/MTProtoSession/CallHandler.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function methodRecall(MTProtoOutgoingMessage $request, ?int $forceDatacen
5858
unset($this->new_outgoing[$id]);
5959
}
6060
if ($request instanceof Container) {
61+
$this->ack_queue = array_merge($request->acks, $this->ack_queue);
6162
foreach ($request->msgs as $msg) {
6263
$this->methodRecall($msg, $forceDatacenter, $defer);
6364
}
@@ -163,7 +164,10 @@ public function methodCallAsyncWrite(string $method, array $args): WrappedFuture
163164
throw new Exception("Could not find method $method!");
164165
}
165166
$encrypted = $methodInfo['encrypted'];
166-
$timeout = new TimeoutCancellation($args['timeout'] ?? ($this->drop ??= (float) $this->API->getSettings()->getRpc()->getRpcDropTimeout()));
167+
$timeout = new TimeoutCancellation(
168+
$args['timeout'] ?? ($this->drop ??= (float) $this->API->getSettings()->getRpc()->getRpcDropTimeout()),
169+
"Timeout while waiting for $method"
170+
);
167171
$cancellation = $cancellation !== null
168172
? new CompositeCancellation($cancellation, $timeout)
169173
: $timeout;
@@ -187,15 +191,16 @@ public function methodCallAsyncWrite(string $method, array $args): WrappedFuture
187191
$message->setMsgId($args['madelineMsgId']);
188192
}
189193
$this->sendMessage($message);
194+
$message->getSendPromise()->await($cancellation);
190195
return new WrappedFuture($response->getFuture());
191196
}
192197
/**
193-
* Send object and make sure it is asynchronously sent (generator).
198+
* Send object.
194199
*
195200
* @param string $object Object name
196201
* @param array $args Arguments
197202
*/
198-
public function objectCall(string $object, array $args, ?DeferredFuture $promise = null): void
203+
public function objectCallAsync(string $object, array $args, DeferredFuture $promise): void
199204
{
200205
$cancellation = $args['cancellation'] ?? null;
201206
$cancellation?->throwIfRequested();

0 commit comments

Comments
 (0)