Fix possible deadlock.

Some unknown code (like getSession) was called while holding
_requestsByDcLock mutex which could lead to a deadlock.

Now all access points to _requestsByDc are simple.
This commit is contained in:
John Preston 2018-04-27 17:08:12 +04:00
parent 48e913bf2c
commit dc9483e07a
1 changed files with 82 additions and 88 deletions

View File

@ -128,8 +128,10 @@ private:
void configLoadDone(const MTPConfig &result); void configLoadDone(const MTPConfig &result);
bool configLoadFail(const RPCError &error); bool configLoadFail(const RPCError &error);
void cdnConfigLoadDone(const MTPCdnConfig &result); base::optional<ShiftedDcId> queryRequestByDc(
bool cdnConfigLoadFail(const RPCError &error); mtpRequestId requestId) const;
base::optional<ShiftedDcId> changeRequestByDc(
mtpRequestId requestId, DcId newdc);
// RPCError::NoError means do not toggle onError callback. // RPCError::NoError means do not toggle onError callback.
void clearCallbacks( void clearCallbacks(
@ -164,7 +166,7 @@ private:
// holds dcWithShift for request to this dc or -dc for request to main dc // holds dcWithShift for request to this dc or -dc for request to main dc
std::map<mtpRequestId, ShiftedDcId> _requestsByDc; std::map<mtpRequestId, ShiftedDcId> _requestsByDc;
QMutex _requestByDcLock; mutable QMutex _requestByDcLock;
// holds target dcWithShift for auth export request // holds target dcWithShift for auth export request
std::map<mtpRequestId, ShiftedDcId> _authExportRequests; std::map<mtpRequestId, ShiftedDcId> _authExportRequests;
@ -249,9 +251,7 @@ void Instance::Private::start(Config &&config) {
_checkDelayedTimer.setCallback([this] { checkDelayedRequests(); }); _checkDelayedTimer.setCallback([this] { checkDelayedRequests(); });
Assert((_mainDcId == Config::kNoneMainDc) == isKeysDestroyer()); Assert((_mainDcId == Config::kNoneMainDc) == isKeysDestroyer());
if (!isKeysDestroyer()) { requestConfig();
requestConfig();
}
} }
void Instance::Private::suggestMainDcId(DcId mainDcId) { void Instance::Private::suggestMainDcId(DcId mainDcId) {
@ -280,7 +280,7 @@ DcId Instance::Private::mainDcId() const {
} }
void Instance::Private::requestConfig() { void Instance::Private::requestConfig() {
if (_configLoader) { if (_configLoader || isKeysDestroyer()) {
return; return;
} }
_configLoader = std::make_unique<internal::ConfigLoader>(_instance, rpcDone([this](const MTPConfig &result) { _configLoader = std::make_unique<internal::ConfigLoader>(_instance, rpcDone([this](const MTPConfig &result) {
@ -301,7 +301,9 @@ void Instance::Private::requestCDNConfig() {
if (_cdnConfigLoadRequestId || _mainDcId == Config::kNoneMainDc) { if (_cdnConfigLoadRequestId || _mainDcId == Config::kNoneMainDc) {
return; return;
} }
_cdnConfigLoadRequestId = request(MTPhelp_GetCdnConfig()).done([this](const MTPCdnConfig &result) { _cdnConfigLoadRequestId = request(
MTPhelp_GetCdnConfig()
).done([this](const MTPCdnConfig &result) {
_cdnConfigLoadRequestId = 0; _cdnConfigLoadRequestId = 0;
Expects(result.type() == mtpc_cdnConfig); Expects(result.type() == mtpc_cdnConfig);
@ -374,8 +376,8 @@ void Instance::Private::ping() {
void Instance::Private::cancel(mtpRequestId requestId) { void Instance::Private::cancel(mtpRequestId requestId) {
if (!requestId) return; if (!requestId) return;
mtpMsgId msgId = 0; const auto shiftedDcId = queryRequestByDc(requestId);
_requestsDelays.erase(requestId); auto msgId = mtpMsgId(0);
{ {
QWriteLocker locker(&_requestMapLock); QWriteLocker locker(&_requestMapLock);
auto it = _requestMap.find(requestId); auto it = _requestMap.find(requestId);
@ -384,14 +386,10 @@ void Instance::Private::cancel(mtpRequestId requestId) {
_requestMap.erase(it); _requestMap.erase(it);
} }
} }
{ unregisterRequest(requestId);
QMutexLocker locker(&_requestByDcLock); if (shiftedDcId) {
auto it = _requestsByDc.find(requestId); if (const auto session = getSession(qAbs(*shiftedDcId))) {
if (it != _requestsByDc.end()) { session->cancel(requestId, msgId);
if (auto session = getSession(qAbs(it->second))) {
session->cancel(requestId, msgId);
}
_requestsByDc.erase(it);
} }
} }
clearCallbacks(requestId); clearCallbacks(requestId);
@ -399,10 +397,8 @@ void Instance::Private::cancel(mtpRequestId requestId) {
int32 Instance::Private::state(mtpRequestId requestId) { // < 0 means waiting for such count of ms int32 Instance::Private::state(mtpRequestId requestId) { // < 0 means waiting for such count of ms
if (requestId > 0) { if (requestId > 0) {
QMutexLocker locker(&_requestByDcLock); if (const auto shiftedDcId = queryRequestByDc(requestId)) {
auto i = _requestsByDc.find(requestId); if (auto session = getSession(qAbs(*shiftedDcId))) {
if (i != _requestsByDc.end()) {
if (auto session = getSession(qAbs(i->second))) {
return session->requestState(requestId); return session->requestState(requestId);
} }
return MTP::RequestConnecting; return MTP::RequestConnecting;
@ -647,6 +643,32 @@ bool Instance::Private::configLoadFail(const RPCError &error) {
return false; return false;
} }
base::optional<ShiftedDcId> Instance::Private::queryRequestByDc(
mtpRequestId requestId) const {
QMutexLocker locker(&_requestByDcLock);
auto it = _requestsByDc.find(requestId);
if (it != _requestsByDc.cend()) {
return it->second;
}
return base::none;
}
base::optional<ShiftedDcId> Instance::Private::changeRequestByDc(
mtpRequestId requestId,
DcId newdc) {
QMutexLocker locker(&_requestByDcLock);
auto it = _requestsByDc.find(requestId);
if (it != _requestsByDc.cend()) {
if (it->second < 0) {
it->second = -newdc;
} else {
it->second = shiftDcId(newdc, getDcIdShift(it->second));
}
return it->second;
}
return base::none;
}
void Instance::Private::checkDelayedRequests() { void Instance::Private::checkDelayedRequests() {
auto now = getms(true); auto now = getms(true);
while (!_delayedRequests.empty() && now >= _delayedRequests.front().second) { while (!_delayedRequests.empty() && now >= _delayedRequests.front().second) {
@ -654,15 +676,11 @@ void Instance::Private::checkDelayedRequests() {
_delayedRequests.pop_front(); _delayedRequests.pop_front();
auto dcWithShift = ShiftedDcId(0); auto dcWithShift = ShiftedDcId(0);
{ if (const auto shiftedDcId = queryRequestByDc(requestId)) {
QMutexLocker locker(&_requestByDcLock); dcWithShift = *shiftedDcId;
auto it = _requestsByDc.find(requestId); } else {
if (it != _requestsByDc.cend()) { LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId));
dcWithShift = it->second; continue;
} else {
LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId));
continue;
}
} }
auto request = mtpRequest(); auto request = mtpRequest();
@ -880,10 +898,8 @@ bool Instance::Private::hasAuthorization() {
} }
void Instance::Private::importDone(const MTPauth_Authorization &result, mtpRequestId requestId) { void Instance::Private::importDone(const MTPauth_Authorization &result, mtpRequestId requestId) {
QMutexLocker locker1(&_requestByDcLock); const auto shiftedDcId = queryRequestByDc(requestId);
if (!shiftedDcId) {
auto it = _requestsByDc.find(requestId);
if (it == _requestsByDc.end()) {
LOG(("MTP Error: auth import request not found in requestsByDC, requestId: %1").arg(requestId)); LOG(("MTP Error: auth import request not found in requestsByDC, requestId: %1").arg(requestId));
RPCError error(internal::rpcClientError("AUTH_IMPORT_FAIL", QString("did not find import request in requestsByDC, request %1").arg(requestId))); RPCError error(internal::rpcClientError("AUTH_IMPORT_FAIL", QString("did not find import request in requestsByDC, request %1").arg(requestId)));
if (_globalHandler.onFail && hasAuthorization()) { if (_globalHandler.onFail && hasAuthorization()) {
@ -891,7 +907,7 @@ void Instance::Private::importDone(const MTPauth_Authorization &result, mtpReque
} }
return; return;
} }
auto newdc = bareDcId(it->second); auto newdc = bareDcId(*shiftedDcId);
DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc)); DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
@ -904,23 +920,15 @@ void Instance::Private::importDone(const MTPauth_Authorization &result, mtpReque
LOG(("MTP Error: could not find request %1 for resending").arg(waitedRequestId)); LOG(("MTP Error: could not find request %1 for resending").arg(waitedRequestId));
continue; continue;
} }
auto dcWithShift = ShiftedDcId(newdc); const auto shiftedDcId = changeRequestByDc(waitedRequestId, newdc);
{ if (!shiftedDcId) {
auto k = _requestsByDc.find(waitedRequestId); LOG(("MTP Error: could not find request %1 by dc for resending").arg(waitedRequestId));
if (k == _requestsByDc.cend()) { continue;
LOG(("MTP Error: could not find request %1 by dc for resending").arg(waitedRequestId)); } else if (*shiftedDcId < 0) {
continue; _instance->setMainDcId(newdc);
}
if (k->second < 0) {
_instance->setMainDcId(newdc);
k->second = -newdc;
} else {
dcWithShift = shiftDcId(newdc, getDcIdShift(k->second));
k->second = dcWithShift;
}
DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(waitedRequestId).arg(k->second));
} }
if (auto session = getSession(dcWithShift)) { DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(waitedRequestId).arg(*shiftedDcId));
if (auto session = getSession(*shiftedDcId)) {
session->sendPrepared(it->second); session->sendPrepared(it->second);
} }
} }
@ -981,15 +989,12 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) { if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) {
if (!requestId) return false; if (!requestId) return false;
ShiftedDcId dcWithShift = 0, newdcWithShift = m.captured(2).toInt(); auto dcWithShift = ShiftedDcId(0);
{ auto newdcWithShift = ShiftedDcId(m.captured(2).toInt());
QMutexLocker locker(&_requestByDcLock); if (const auto shiftedDcId = queryRequestByDc(requestId)) {
auto it = _requestsByDc.find(requestId); dcWithShift = *shiftedDcId;
if (it == _requestsByDc.end()) { } else {
LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdcWithShift)); LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdcWithShift));
} else {
dcWithShift = it->second;
}
} }
if (!dcWithShift || !newdcWithShift) return false; if (!dcWithShift || !newdcWithShift) return false;
@ -1064,14 +1069,10 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
return true; return true;
} else if (code == 401 || (badGuestDc && _badGuestDcRequests.find(requestId) == _badGuestDcRequests.cend())) { } else if (code == 401 || (badGuestDc && _badGuestDcRequests.find(requestId) == _badGuestDcRequests.cend())) {
auto dcWithShift = ShiftedDcId(0); auto dcWithShift = ShiftedDcId(0);
{ if (const auto shiftedDcId = queryRequestByDc(requestId)) {
QMutexLocker locker(&_requestByDcLock); dcWithShift = *shiftedDcId;
auto it = _requestsByDc.find(requestId); } else {
if (it != _requestsByDc.end()) { LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
dcWithShift = it->second;
} else {
LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
}
} }
auto newdc = bareDcId(qAbs(dcWithShift)); auto newdc = bareDcId(qAbs(dcWithShift));
if (!newdc || newdc == mainDcId() || !hasAuthorization()) { if (!newdc || newdc == mainDcId() || !hasAuthorization()) {
@ -1106,14 +1107,10 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
request = it->second; request = it->second;
} }
auto dcWithShift = ShiftedDcId(0); auto dcWithShift = ShiftedDcId(0);
{ if (const auto shiftedDcId = queryRequestByDc(requestId)) {
QMutexLocker locker(&_requestByDcLock); dcWithShift = *shiftedDcId;
auto it = _requestsByDc.find(requestId); } else {
if (it == _requestsByDc.end()) { LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId));
LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId));
} else {
dcWithShift = it->second;
}
} }
if (!dcWithShift) return false; if (!dcWithShift) return false;
@ -1140,20 +1137,17 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
return false; return false;
} }
auto dcWithShift = ShiftedDcId(0); auto dcWithShift = ShiftedDcId(0);
{ if (const auto shiftedDcId = queryRequestByDc(requestId)) {
QMutexLocker locker(&_requestByDcLock); if (const auto afterDcId = queryRequestByDc(request->after->requestId)) {
auto it = _requestsByDc.find(requestId); dcWithShift = *shiftedDcId;
auto afterIt = _requestsByDc.find(request->after->requestId); if (*shiftedDcId != *afterDcId) {
if (it == _requestsByDc.end()) {
LOG(("MTP Error: could not find request %1 by dc").arg(requestId));
} else if (afterIt == _requestsByDc.end()) {
LOG(("MTP Error: could not find dependent request %1 by dc").arg(request->after->requestId));
} else {
dcWithShift = it->second;
if (it->second != afterIt->second) {
request->after = mtpRequest(); request->after = mtpRequest();
} }
} else {
LOG(("MTP Error: could not find dependent request %1 by dc").arg(request->after->requestId));
} }
} else {
LOG(("MTP Error: could not find request %1 by dc").arg(requestId));
} }
if (!dcWithShift) return false; if (!dcWithShift) return false;