Skip to content

Commit b79c3e0

Browse files
authored
Resolve cs_main vs governance vs masternode-sync deadlocks (#4293)
1 parent 18fae16 commit b79c3e0

1 file changed

Lines changed: 50 additions & 34 deletions

File tree

src/masternode/masternode-sync.cpp

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -139,23 +139,22 @@ void CMasternodeSync::ProcessTick(CConnman& connman)
139139
}
140140

141141
nTimeLastProcess = GetTime();
142+
std::vector<CNode*> vNodesCopy = connman.CopyNodeVector(CConnman::FullyConnectedOnly);
142143

143144
// gradually request the rest of the votes after sync finished
144145
if(IsSynced()) {
145-
std::vector<CNode*> vNodesCopy = connman.CopyNodeVector(CConnman::FullyConnectedOnly);
146146
governance.RequestGovernanceObjectVotes(vNodesCopy, connman);
147147
connman.ReleaseNodeVector(vNodesCopy);
148148
return;
149149
}
150150

151+
{
151152
LOCK(cs);
152153
// Calculate "progress" for LOG reporting / GUI notification
153154
double nSyncProgress = double(nTriedPeerCount + (nCurrentAsset - 1) * 8) / (8*4);
154155
LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTriedPeerCount %d nSyncProgress %f\n", nTick, nCurrentAsset, nTriedPeerCount, nSyncProgress);
155156
uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress);
156157

157-
std::vector<CNode*> vNodesCopy = connman.CopyNodeVector(CConnman::FullyConnectedOnly);
158-
159158
for (auto& pnode : vNodesCopy)
160159
{
161160
CNetMsgMaker msgMaker(pnode->GetSendVersion());
@@ -256,37 +255,10 @@ void CMasternodeSync::ProcessTick(CConnman& connman)
256255
return;
257256
}
258257

259-
// only request obj sync once from each peer, then request votes on per-obj basis
258+
// only request obj sync once from each peer
260259
if(netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) {
261-
int nObjsLeftToAsk = governance.RequestGovernanceObjectVotes(pnode, connman);
262-
static int64_t nTimeNoObjectsLeft = 0;
263-
// check for data
264-
if(nObjsLeftToAsk == 0) {
265-
static int nLastTick = 0;
266-
static int nLastVotes = 0;
267-
if(nTimeNoObjectsLeft == 0) {
268-
// asked all objects for votes for the first time
269-
nTimeNoObjectsLeft = GetTime();
270-
}
271-
// make sure the condition below is checked only once per tick
272-
if(nLastTick == nTick) continue;
273-
if(GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS &&
274-
governance.GetVoteCount() - nLastVotes < std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)
275-
) {
276-
// We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS
277-
// after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS
278-
// (i.e. 1 per second) votes were received during the last tick.
279-
// We can be pretty sure that we are done syncing.
280-
LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- asked for all objects, nothing to do\n", nTick, nCurrentAsset);
281-
// reset nTimeNoObjectsLeft to be able to use the same condition on resync
282-
nTimeNoObjectsLeft = 0;
283-
SwitchToNextAsset(connman);
284-
connman.ReleaseNodeVector(vNodesCopy);
285-
return;
286-
}
287-
nLastTick = nTick;
288-
nLastVotes = governance.GetVoteCount();
289-
}
260+
// will request votes on per-obj basis from each node in a separate loop below
261+
// to avoid deadlocks here
290262
continue;
291263
}
292264
netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync");
@@ -296,11 +268,55 @@ void CMasternodeSync::ProcessTick(CConnman& connman)
296268

297269
SendGovernanceSyncRequest(pnode, connman);
298270

271+
break; //this will cause each peer to get one request each six seconds for the various assets we need
272+
}
273+
}
274+
}
275+
} // cs
276+
277+
278+
if (WITH_LOCK(cs, return nCurrentAsset != MASTERNODE_SYNC_GOVERNANCE)) {
279+
// looped through all nodes and not syncing governance yet/already, release them
280+
connman.ReleaseNodeVector(vNodesCopy);
281+
return;
282+
}
283+
284+
// request votes on per-obj basis from each node
285+
for (auto& pnode : vNodesCopy) {
286+
if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) {
287+
continue; // to early for this node
288+
}
289+
int nObjsLeftToAsk = governance.RequestGovernanceObjectVotes(pnode, connman);
290+
static int64_t nTimeNoObjectsLeft = 0;
291+
// check for data
292+
if(nObjsLeftToAsk == 0) {
293+
static int nLastTick = 0;
294+
static int nLastVotes = 0;
295+
if(nTimeNoObjectsLeft == 0) {
296+
// asked all objects for votes for the first time
297+
nTimeNoObjectsLeft = GetTime();
298+
}
299+
// make sure the condition below is checked only once per tick
300+
if(nLastTick == nTick) continue;
301+
if(GetTime() - nTimeNoObjectsLeft > MASTERNODE_SYNC_TIMEOUT_SECONDS &&
302+
governance.GetVoteCount() - nLastVotes < std::max(int(0.0001 * nLastVotes), MASTERNODE_SYNC_TICK_SECONDS)
303+
) {
304+
// We already asked for all objects, waited for MASTERNODE_SYNC_TIMEOUT_SECONDS
305+
// after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS
306+
// (i.e. 1 per second) votes were received during the last tick.
307+
// We can be pretty sure that we are done syncing.
308+
LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- asked for all objects, nothing to do\n", nTick, MASTERNODE_SYNC_GOVERNANCE);
309+
// reset nTimeNoObjectsLeft to be able to use the same condition on resync
310+
nTimeNoObjectsLeft = 0;
311+
SwitchToNextAsset(connman);
299312
connman.ReleaseNodeVector(vNodesCopy);
300-
return; //this will cause each peer to get one request each six seconds for the various assets we need
313+
return;
301314
}
315+
nLastTick = nTick;
316+
nLastVotes = governance.GetVoteCount();
302317
}
303318
}
319+
304320
// looped through all nodes, release them
305321
connman.ReleaseNodeVector(vNodesCopy);
306322
}

0 commit comments

Comments
 (0)