Stop Using cs_main for CNodeState/State()#9419
Stop Using cs_main for CNodeState/State()#9419TheBlueMatt wants to merge 8 commits intobitcoin:masterfrom
Conversation
2b53e69 to
fcc8c2c
Compare
fcc8c2c to
67bc69b
Compare
src/util.cpp
Outdated
There was a problem hiding this comment.
Prefer less diff? (note this is really from #9243, not strictly this PR).
src/util.cpp
Outdated
There was a problem hiding this comment.
Why is this not LOCK(cs_args)?
(maybe I misinterpreted the meaning of force)
cs_vSend is used for two purposes - to lock the datastructures used to queue messages to place on the wire and to only call SendMessages once at a time per-node. I believe SendMessages used to access some of the vSendMsg stuff, but it doesn't anymore, so these locks do not need to be on the same mutex, and also make deadlocking much more likely.
|
Rebased after #9243 merge. |
67bc69b to
8a5ebaa
Compare
|
Wouldn't it be easier to use a shared_ptr wrapping for cleanup-after-last-use, instead of implementing refcounting yourself? You can still use a manual wrapper to lock/unlock. |
|
Hmm, I kinda prefer to not introduce shared_ptr abstractions unless they're optimizing a copy that we don't want to do. I don't think the manual refcounting is that bad.
…On December 27, 2016 7:46:52 PM GMT+01:00, Pieter Wuille ***@***.***> wrote:
Wouldn't it be easier to use a shared_ptr wrapping for
cleanup-after-last-use, instead of implementing refcounting yourself?
You can still use a manual wrapper to lock/unlock.
--
You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub:
#9419 (comment)
|
8a5ebaa to
32dd1c3
Compare
|
OK, @sipa found a better way to use shared_ptrs here so I went ahead and did that. It means removing some asserts, but I think its worth it. |
src/net_processing.cpp
Outdated
|
|
||
| void AddStateForNode(NodeId nodeid, const CAddress& addr, const std::string& addrName) { | ||
| LOCK(cs_main); | ||
| mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName))); |
There was a problem hiding this comment.
The std::move(addrMany) here won't do what you want, as addrName is a const reference. You can't destroy addrName anyway, so just remove the std::move.
src/net_processing.cpp
Outdated
| return CNodeStateAccessor(); | ||
| pstate = it->second; | ||
| } | ||
| return CNodeStateAccessor(pstate); |
There was a problem hiding this comment.
You should add an std::move here if you want to avoid a copy.
src/net_processing.cpp
Outdated
| std::shared_ptr<CNodeState> pstate; | ||
|
|
||
| CNodeStateAccessor() : pstate() { } | ||
| CNodeStateAccessor(std::shared_ptr<CNodeState> pstateIn) : pstate(pstateIn) { ENTER_CRITICAL_SECTION(pstate->cs); } |
There was a problem hiding this comment.
Add a std::move around pstateIn to avoid a copy.
|
Went down the rabbit hole here trying to track down a deadlock and then realized that it was already prevented. Note that (very much on purpose) cs_main is still used prior to all CNodeStateAccessors except for a few where we only call Misbehaving() or otherwise sure we are only locking one CNodeState at a time (to avoid deadlocks where we lock them in different orders). |
32dd1c3 to
227e394
Compare
|
Addressed @sipa's comments. |
227e394 to
79c5a21
Compare
src/net_processing.cpp
Outdated
There was a problem hiding this comment.
explicit operator bool instead? Using the call operator for this seems strange.
There was a problem hiding this comment.
I dunno I dont really like the bool operator. If you really prefer it I'm happy to change it.
There was a problem hiding this comment.
explicit bool matches the semantics of smart pointers, so I think it's a good fit here. But more importantly, the call operator makes it look as though something is happening to the accessor, so I'd really rather not use that.
There was a problem hiding this comment.
OK, switched to regular bool.
src/net_processing.cpp
Outdated
src/net_processing.cpp
Outdated
There was a problem hiding this comment.
Why not just have a lock as a member?
There was a problem hiding this comment.
I didnt realize we had an RAII lock in sync.h that would still do DEBUG_LOCKORDER checks. Will fix with CMutexLock.
There was a problem hiding this comment.
Hmm, looking again using CMutexLock means adding __FILE__, __LINE__, etc macros in net_processing for parameters to CMutexLock, instead of letting ENTER_CRITICAL_SECTION figure it out. If you prefer I could try to add some crazy defines like CONSTRUCT_CMUTEXLOCK(mutex, lockName) which fills in a constructor for use in the CNodeStateAccessor constructor?
There was a problem hiding this comment.
Hmm, I think you actually want these coming from net_processing anyway, no? I think if you make the ctor inline and pass __LINE__ to CMutexLock, you'd end up seeing the source of the lock. Otherwise, you're always seeing it come from the same place I should think.
Taking that a step further, I think it would work to change CMutexLock's ctors to take default __FILE__ and __LINE__ args?
|
Wanted to make sure my nits so far were reasonable, so I went ahead and patched 'em in. Feel free to take from theuni@058193e |
This removes reliance on net in net_processing for maintaining the refcount == 0 invariant when calling FinalizeNode This also removes a few asserts which used to be checked when there were no more CNodeStates remaining, which we can no longer check for. These should be pretty rarely checked anyway, so probably didn't serve much use.
79c5a21 to
e195459
Compare
|
Fixed a few of @theuni's comments, rebased diff-tree is at https://0bin.net/paste/nkpqzdxyoAXkoX7B#STuXxIS32GXf3gMP1JbiIPrufL9gq1hcSH+g6yXSKdq |
|
@TheBlueMatt Maybe I'm missing something big-picture here, but would it not be possible to hold a new global mutex rather than cs_main to serialize CNodeStateAccessor access? Or is there a reason that they must share the same lock? |
|
@theuni I think that's the plan longer term.
|
|
I suppose I'm trying to figure out, for the sake of reviewing this PR, what remains to be done before that can happen. I'm certainly not insinuating that we should hold this up until that point, I'd just like to get an idea of what other issues still need to be solved. So if, for ex, we naively made CNodeState::cs static and dropped the cs_main guards, what kind of carnage would be expected? |
|
Aside from the places that do, actually, require cs_main, a global order between various CNodeState::cs locks needs to be defined (or, equivalently, we could say that you may not hold multiple CNodeState::cs locks at once). |
|
Without #9488 (which I do not think should make 0.14 at this juncture), this should not go in for 0.14. |
|
Closing for now. Will make access exclusive and work towards a more whole solution for 0.15. |
Based on #9243 and the top commit off of #9375 ("Make CBlockIndex*es in net_processing const"), this no longer requires cs_main to call State() and removes a few cs_main's around Misbehaving() for an easy win.