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

View file

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