Fix crashes in fast simultaneous readings.

Fixes #7264, fixes #7259.
This commit is contained in:
John Preston 2020-02-23 11:54:15 +04:00
parent 50bf4dad36
commit 496faef0b3
2 changed files with 48 additions and 44 deletions

View File

@ -21,7 +21,6 @@ namespace Data {
namespace { namespace {
constexpr auto kReadRequestTimeout = 3 * crl::time(1000); constexpr auto kReadRequestTimeout = 3 * crl::time(1000);
constexpr auto kReadRequestSent = std::numeric_limits<crl::time>::max();
} // namespace } // namespace
@ -135,15 +134,23 @@ void Histories::readInboxTill(
bool force) { bool force) {
Expects(IsServerMsgId(tillId) || (!tillId && !force)); Expects(IsServerMsgId(tillId) || (!tillId && !force));
if (!history->readInboxTillNeedsRequest(tillId) && !force) { const auto needsRequest = history->readInboxTillNeedsRequest(tillId);
if (!needsRequest && !force) {
return; return;
} else if (!history->trackUnreadMessages()) { } else if (!history->trackUnreadMessages()) {
return; return;
} else if (!force) { }
const auto maybeState = lookup(history); const auto maybeState = lookup(history);
if (maybeState && maybeState->readTill >= tillId) { if (maybeState && maybeState->sentReadTill >= tillId) {
return; return;
} else if (maybeState && maybeState->willReadTill >= tillId) {
if (force) {
sendPendingReadInbox(history);
} }
return;
} else if (!needsRequest
&& (!maybeState || !maybeState->willReadTill)) {
return;
} }
const auto stillUnread = history->countStillUnreadLocal(tillId); const auto stillUnread = history->countStillUnreadLocal(tillId);
if (!force if (!force
@ -153,22 +160,16 @@ void Histories::readInboxTill(
history->setInboxReadTill(tillId); history->setInboxReadTill(tillId);
return; return;
} }
auto &state = _states[history]; auto &state = maybeState ? *maybeState : _states[history];
if (state.readTillSent >= tillId) { state.willReadTill = tillId;
return;
} else {
state.readTillSent = 0;
}
const auto wasReadTill = state.readTill;
state.readTill = tillId;
if (force || !stillUnread || !*stillUnread) { if (force || !stillUnread || !*stillUnread) {
state.readWhen = 0; state.willReadWhen = 0;
sendReadRequests(); sendReadRequests();
if (!stillUnread) { if (!stillUnread) {
return; return;
} }
} else if (!wasReadTill) { } else if (!state.willReadWhen) {
state.readWhen = crl::now() + kReadRequestTimeout; state.willReadWhen = crl::now() + kReadRequestTimeout;
if (!_readRequestsTimer.isActive()) { if (!_readRequestsTimer.isActive()) {
_readRequestsTimer.callOnce(kReadRequestTimeout); _readRequestsTimer.callOnce(kReadRequestTimeout);
} }
@ -304,6 +305,12 @@ void Histories::dialogEntryApplied(not_null<History*> history) {
callback(); callback();
} }
} }
if (const auto state = lookup(history)) {
if (state->sentReadTill && state->sentReadDone) {
history->setInboxReadTill(base::take(state->sentReadTill));
checkEmptyState(history);
}
}
} }
void Histories::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) { void Histories::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) {
@ -372,10 +379,8 @@ void Histories::requestFakeChatListMessage(
void Histories::sendPendingReadInbox(not_null<History*> history) { void Histories::sendPendingReadInbox(not_null<History*> history) {
if (const auto state = lookup(history)) { if (const auto state = lookup(history)) {
if (state->readTill if (state->willReadTill && state->willReadWhen) {
&& state->readWhen state->willReadWhen = 0;
&& state->readWhen != kReadRequestSent) {
state->readWhen = 0;
sendReadRequests(); sendReadRequests();
} }
} }
@ -388,12 +393,12 @@ void Histories::sendReadRequests() {
const auto now = crl::now(); const auto now = crl::now();
auto next = std::optional<crl::time>(); auto next = std::optional<crl::time>();
for (auto &[history, state] : _states) { for (auto &[history, state] : _states) {
if (!state.readTill || state.readWhen == kReadRequestSent) { if (!state.willReadTill) {
continue; continue;
} else if (state.readWhen <= now) { } else if (state.willReadWhen <= now) {
sendReadRequest(history, state); sendReadRequest(history, state);
} else if (!next || *next > state.readWhen) { } else if (!next || *next > state.willReadWhen) {
next = state.readWhen; next = state.willReadWhen;
} }
} }
if (next.has_value()) { if (next.has_value()) {
@ -404,28 +409,26 @@ void Histories::sendReadRequests() {
} }
void Histories::sendReadRequest(not_null<History*> history, State &state) { void Histories::sendReadRequest(not_null<History*> history, State &state) {
const auto tillId = state.readTill; Expects(state.willReadTill > state.sentReadTill);
state.readWhen = kReadRequestSent;
state.readTillSent = tillId; const auto tillId = state.sentReadTill = base::take(state.willReadTill);
state.willReadWhen = 0;
state.sentReadDone = false;
sendRequest(history, RequestType::ReadInbox, [=](Fn<void()> finish) { sendRequest(history, RequestType::ReadInbox, [=](Fn<void()> finish) {
const auto finished = [=] { const auto finished = [=] {
const auto state = lookup(history); const auto state = lookup(history);
Assert(state != nullptr); Assert(state != nullptr);
Assert(state->readTill >= tillId); Assert(state->sentReadTill >= tillId);
if (history->unreadCountRefreshNeeded(tillId)) { if (state->sentReadTill == tillId) {
requestDialogEntry(history); state->sentReadDone = true;
} else if (state->readTillSent == tillId) { if (history->unreadCountRefreshNeeded(tillId)) {
state->readTillSent = 0; requestDialogEntry(history);
}
if (state->readWhen == kReadRequestSent) {
state->readWhen = 0;
if (state->readTill == tillId) {
state->readTill = 0;
} else { } else {
sendReadRequests(); state->sentReadTill = 0;
} }
} }
sendReadRequests();
finish(); finish();
}; };
if (const auto channel = history->peer->asChannel()) { if (const auto channel = history->peer->asChannel()) {
@ -456,8 +459,8 @@ void Histories::checkEmptyState(not_null<History*> history) {
return state.postponed.empty() return state.postponed.empty()
&& !state.postponedRequestEntry && !state.postponedRequestEntry
&& state.sent.empty() && state.sent.empty()
&& (state.readTill == 0) && (state.willReadTill == 0)
&& (state.readTillSent == 0); && (state.sentReadTill == 0);
}; };
const auto i = _states.find(history); const auto i = _states.find(history);
if (i != end(_states) && empty(i->second)) { if (i != end(_states) && empty(i->second)) {

View File

@ -86,9 +86,10 @@ private:
struct State { struct State {
base::flat_map<int, PostponedHistoryRequest> postponed; base::flat_map<int, PostponedHistoryRequest> postponed;
base::flat_map<int, SentRequest> sent; base::flat_map<int, SentRequest> sent;
crl::time readWhen = 0; MsgId willReadTill = 0;
MsgId readTill = 0; MsgId sentReadTill = 0;
MsgId readTillSent = 0; crl::time willReadWhen = 0;
bool sentReadDone = false;
bool postponedRequestEntry = false; bool postponedRequestEntry = false;
}; };