rpc: Add back missing cs_main lock in getrawmempool #12273

pull MarcoFalke wants to merge 1 commits into bitcoin:master from MarcoFalke:Mf1801-rpcMempoolGetLock changing 1 files +9 −3
  1. MarcoFalke commented at 2:33 AM on January 26, 2018: member

    The getrawmempool rpc should wait for ATMP to completely return before sending back the pool contents. Otherwise, the syncwithvalidationinterface rpc might race against ATMP and be a noop, even though it shouldn't.

    When writing to ATMP, the cs_main lock is acquired. So we can wait for to release of cs_main to be sure that ATMP is done.

    Effectively reverts #8244

  2. rpc: Add back missing cs_main lock in getrawmempool fabac46d05
  3. in src/rpc/blockchain.cpp:457 in fabac46d05
     454 | +
     455 | +    const UniValue txpool_json{mempoolToJSON(fVerbose)};
     456 |  
     457 | -    return mempoolToJSON(fVerbose);
     458 | +    // Wait for ATMP calling thread to release the write lock:
     459 | +    LOCK(cs_main);
    


    TheBlueMatt commented at 2:39 AM on January 26, 2018:

    This needs to go before mempoolToJONS, no? ie just do a { LOCK(cs_main); }.


    MarcoFalke commented at 3:05 AM on January 26, 2018:

    Acquiring the lock and immediately giving it back to the ATMP calling thread will not solve any races. (At least on my machine I can still see them.)

    I guess, I could hold it for the whole duration of getrawmempool, if that is what you like.


    TheBlueMatt commented at 3:12 AM on January 26, 2018:

    Ah, oops, yes, sorry, indeed, wrong direction. I guess this is fine for now, but we really need to kill the mempool.cs/cs_main garbage.


    promag commented at 4:03 PM on January 27, 2018:

    I don't understand why does it matter waiting for cs_main when the result is already determined.


    MarcoFalke commented at 5:07 PM on January 27, 2018:
  4. TheBlueMatt commented at 2:48 AM on January 26, 2018: member

    A bit more background - we (essentially) use mempool.cs as a read lock and cs_main as a write lock for the mempool. However, we don't wait for cs_main in getrawmempool so you can have a situation where you start a ATMP call, it holds cs_main, and then getrawmempool is completely unsychronized with anything going on, confusing some tests. Ideally we'd move away from the rather-confused read-write-but-not-upgradeable mempool.cs, but a simple { LOCK(cs_main); } should fix the issue for now.

  5. MarcoFalke added the label RPC/REST/ZMQ on Jan 26, 2018
  6. MarcoFalke commented at 3:22 AM on January 26, 2018: member

    This was hit twice on travis, you might find one of the logs here https://travis-ci.org/bitcoin/bitcoin/jobs/332197296 (might disappear soon, on reset)

  7. promag commented at 4:07 PM on January 27, 2018: member

    A bit more background - we (essentially) use mempool.cs as a read lock and cs_main as a write lock for the mempool.

    Why not just use mempool.cs for both read and write? Stopping a lot of stuff just to dump the mempool sounds bad.

  8. MarcoFalke commented at 5:09 PM on January 27, 2018: member

    @promag Pull request welcome, but I'd prefer not to do major refactoring in a bug-fix pull request.

  9. promag commented at 10:17 PM on January 27, 2018: member

    Thanks for the explanation @MarcoFalke.

    IMHO, and to cover other possible cases, cs_main could be locked right before returning in SyncWithValidationInterfaceQueue:

    void SyncWithValidationInterfaceQueue() {
        AssertLockNotHeld(cs_main);
        // Block until the validation queue drains
        std::promise<void> promise;
        CallFunctionInValidationInterfaceQueue([&promise] {
            promise.set_value();
        });
        promise.get_future().wait();
        // Block until other tasks holding cs_main finish
        LOCK(cs_main);
    }
    

    Or in syncwithvalidationinterface RPC after SyncWithValidationInterfaceQueue().

    This alternative acknowledges it's not a getrawmempool bug.

  10. TheBlueMatt commented at 3:50 PM on January 28, 2018: member

    No, we should not take a cs_main in SyncWithValidationInterface. The bug here is mempool.cs - we're using it as a read lock but not holding cs_main during the whole "write" in ATMP. The fix here is fine - sync getrawmempool with ATMP (as it should be). Alternatively we could hold cs_main for longer in ATMP, but adding a cs_main to validationinterface to fix a mempool-specific bug is way overkill.

    Medium-term (ie post-0.16) we should look into making cs_main a read/write/upgrade lock for real and then this should fall out.

    On January 27, 2018 10:17:34 PM UTC, "João Barbosa" notifications@github.com wrote:

    Thanks for the explanation @MarcoFalke.

    IMHO, and to cover other possible cases, cs_main could be locked right before returning in SyncWithValidationInterfaceQueue:

    void SyncWithValidationInterfaceQueue() {
       AssertLockNotHeld(cs_main);
       // Block until the validation queue drains
       std::promise<void> promise;
       CallFunctionInValidationInterfaceQueue([&promise] {
           promise.set_value();
       });
       promise.get_future().wait();
       // Block until other tasks holding cs_main finish
       LOCK(cs_main);
    }
    

    Or in syncwithvalidationinterface RPC after SyncWithValidationInterfaceQueue().

    This alternative acknowledges it's not a getrawmempool bug.

    -- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/bitcoin/bitcoin/pull/12273#issuecomment-361019806

  11. devrandom commented at 9:10 PM on February 3, 2018: none

    The question for me is whether the production (i.e. non-test) use of getrawmempool benefits from the added synchronization. If taking the lock is only to synchronize against the test-only syncwithvalidationinterface, then it doesn't seem right to pay the lock cost in a production path.

    Also, looking at the code, it does seem like cs_main is held for all of ATMP, so I don't understand the comment from @TheBlueMatt.

    It seems to me that { LOCK(cs_main); } at the start of syncwithvalidationinterface should work fine (i.e. lock and immediate release).

  12. promag commented at 9:19 PM on February 3, 2018: member

    The idea is to guarantee the result has transactions already processed and are not in the queue to be processed. Once the lock is acquired before returning means that ATMP finished.

  13. devrandom commented at 9:29 PM on February 3, 2018: none

    How does the user of getrawmempool use that synchronization guarantee? i.e. what followup would be invalid if the tx is not completely processed?

    (we can move to IRC if this is too much back and forth)

  14. promag commented at 9:52 PM on February 3, 2018: member

    @TheBlueMatt explains above #12273#pullrequestreview-91735276.

    I don't think this is a production/mainnet concern.

  15. devrandom commented at 10:32 PM on February 3, 2018: none

    I understand the comment you linked. It still seems a bit better to solve the test issue by synchronizing an RPC call that is never used in production rather than getrawmempool, which could be used in production. But the performance difference is likely very small, so it's not a strong concern.

  16. TheBlueMatt commented at 4:14 PM on February 6, 2018: member

    I disagree wholly that this is not a "production concern" - we could absolutely have users who are doing a getrawmempool and then calling wallet functions based on the result, introducing a new race for them. I believe we should be marking this 0.16 as a regression.

  17. devrandom commented at 4:18 PM on February 6, 2018: none

    Wouldn't all such followup actions take cs_main, so would be safe?

  18. TheBlueMatt commented at 4:19 PM on February 6, 2018: member

    Err, sorry, not wallet calls, sendrawtransaction followed by a getrawmempool to verify its there - if we can hit a realistic race in testing we probably should consider it a potentially-production-issue.

  19. laanwj added this to the milestone 0.16.1 on Feb 6, 2018
  20. promag commented at 4:26 PM on February 6, 2018: member

    Err, sorry, not wallet calls, sendrawtransaction followed by a getrawmempool to verify its there

    sendrawtransaction fails if rejected by mempool. But it's a regression like you said.

  21. TheBlueMatt commented at 4:32 PM on February 6, 2018: member

    One naive usage (which is already at least somewhat racy, but...) would be to do a getrawmempool to cheaply look up a transaction you just sent's fee. Not a massive deal, but definitely a regression worth fixing.

  22. devrandom commented at 4:59 PM on February 6, 2018: none

    With the proposed fix, sendrawtransaction followed by a getrawmempool is not guaranteed to return the sent transaction, since the lock is taken after the results are computed.

  23. TheBlueMatt commented at 5:51 PM on February 6, 2018: member

    Oh, I seem to have confused myself an mis-remembered the issue here - you cannot hit this purely from RPC, but can hit wallet errors: If you're polling getrawmempool to wait for a transaction to appear in your mempool, you can then race ATMP and, thus, see a balance that is as-of an old mempool and not the one you just got out of getrawmempool

  24. MarcoFalke removed this from the milestone 0.16.1 on Feb 6, 2018
  25. MarcoFalke added this to the milestone 0.16.0 on Feb 6, 2018
  26. MarcoFalke commented at 6:50 PM on February 6, 2018: member

    Changed milestone to 0.16

  27. TheBlueMatt referenced this in commit 85aa8398f5 on Feb 6, 2018
  28. MarcoFalke commented at 6:55 PM on February 6, 2018: member

    There has been too much discussion around this simple fix.

    Closing in favor of #12368

  29. MarcoFalke closed this on Feb 6, 2018

  30. MarcoFalke deleted the branch on Feb 6, 2018
  31. laanwj referenced this in commit d57d10ee96 on Feb 8, 2018
  32. laanwj referenced this in commit b8947554dd on Feb 8, 2018
  33. Willtech referenced this in commit d5a183ae19 on Feb 11, 2018
  34. hkjn referenced this in commit 7b4edfbcf4 on Feb 12, 2018
  35. HashUnlimited referenced this in commit b0149c57b4 on Mar 16, 2018
  36. ccebrecos referenced this in commit fca2771ede on Sep 14, 2018
  37. lionello referenced this in commit b88296a6e1 on Nov 7, 2018
  38. PastaPastaPasta referenced this in commit 22ee217b06 on Mar 14, 2020
  39. PastaPastaPasta referenced this in commit 9c9a1ab8f7 on Mar 14, 2020
  40. PastaPastaPasta referenced this in commit 78d303c3fd on Mar 15, 2020
  41. ckti referenced this in commit d2f38bd658 on Mar 28, 2021
  42. DrahtBot locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-13 15:15 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me