Reading the variable chainActive requires holding the mutex cs_main.
Prior to this commit the cs_main mutex was not held when accessing chainActive in:
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
Reading the variable chainActive requires holding the mutex cs_main.
Prior to this commit the cs_main mutex was not held when accessing chainActive in:
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
483 | + bool keepRunning; 484 | + { 485 | + LOCK(cs_main); 486 | + keepRunning = chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning(); 487 | + } 488 | + while (keepRunning)
My suggestion is to create a function like:
bool KeepRunning() {
LOCK(cs_main);
assert(chainActive.Tip());
return chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning();
}
And here just:
while (KeepRunning()) {
...
}
Concept ACK.
Restarted travis job.
Reading the variable `chainActive` requires holding the mutex `cs_main`.
Prior to this commit the `cs_main` mutex was not held when accessing
`chainActive` in:
```
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
```
The whole point of csBestBlock was that we shouldn't need cs_main to simply check the best block. It looks like the logic for that got lost somewhere, and I'm not sure we're even safe from races anymore. :(
Suggest instead of this, fixing csBestBlock so it is sufficient/works.
@promag Now using a lambda to keep it DRY - please re-review :-)
@luke-jr Do you mean that fixing this make things worse? If so, please describe why :-)
I'm not familiar with the csBestBlock issue you are describing, so I'm afraid I'll have to leave fixing that to someone else :-)
Pinging @luke-jr :-)
No reason to wait on a "better fix" before merging this. Lets just merge and then @luke-jr can fix it in the way he wants in a cleanup follow-up.
utACK 6e65f35f23337cf0b284acb2bb2ec038ae83aeca
@theuni i find the current code relatively readable, and I'm really not at all sure what "fixing csBestBlock" means, given a condition variable is allowed to wake up spuriously whenever it wants.
@TheBlueMatt as @luke-jr mentioned, presumably cvBlockChange was at one point paired with a uint256 reflecting the miner's best known hash, which at some point got removed. We shouldn't need to take cs_main to check whether the new tip matches the cached one.
If the scope of this grows beyond this PR's goal of fixing the missing cs_main lock I'm afraid I'll have to leave that for others to fix. Let me know when a fix for the issue described by @luke-jr is available and I'll close this PR.
If that is something that is unlikely to happen soon I'd vote for merging this fix while waiting for a future better fix.
@theuni "fixing" cvBlockChanged/csBestBlock probably means wholesale removing it and replacing it with appropriate event listeners, but for that, I think, we (realistically) need to beef up the infrastructure behind CValidationInterface so that we dont end up with wallet or other things blocking each other just because they're all on the validation interface (or add some new interface for this stuff that isnt "call into RPC stuff from inside ConnectBlock"), but unless you want to do that, I really think it shouldnt block a simple lock fix.
Ok, since we re-lock cs_main before returning anyway, and since cs_main being locked for any notable period of time after a legitimate cvBlockChange notification would likely indicate a rapid 2nd block anyway, I suppose no regressions would be introduced.
So, -0 to 6e65f35f23337cf0b284acb2bb2ec038ae83aeca. Looks correct to fix the unguarded access.
Any chance of getting this merged? :-)
483 | + auto KeepRunning = [&]() -> bool { 484 | + LOCK(cs_main); 485 | + assert(chainActive.Tip()); 486 | + return chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning(); 487 | + }; 488 | + while (KeepRunning())
Sorry but how about plain code?
while (IsRPCRunning()) {
{
LOCK(cs_main);
if (chainActive.Tip()->GetBlockHash() != hashWatchedChain) break;
}
...
478 | @@ -479,7 +479,12 @@ UniValue getblocktemplate(const JSONRPCRequest& request) 479 | checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); 480 | 481 | WaitableLock lock(csBestBlock); 482 | - while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) 483 | + auto KeepRunning = [&]() -> bool { 484 | + LOCK(cs_main); 485 | + assert(chainActive.Tip());
I would drop the assert, otherwise should we add all over where tip is used?
utACK 6e65f35, but I have 2 comments.
@promag Good suggestion. Please re-review! @TheBlueMatt @theuni Would you mind re-reviewing in light of the latest commit? :-)
Closing in favour of #12743 which is a better alternative :-)