-
Notifications
You must be signed in to change notification settings - Fork 37.8k
Stop Using cs_main for CNodeState/State() #9419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2b53e69
to
fcc8c2c
Compare
fcc8c2c
to
67bc69b
Compare
const vector<string>& categories = mapMultiArgs["-debug"]; | ||
ptrCategory.reset(new set<string>(categories.begin(), categories.end())); | ||
// thread_specific_ptr automatically deletes the set when the thread ends. | ||
if (mapMultiArgs.count("-debug")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not IsMultiArgSet
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer less diff? (note this is really from #9243, not strictly this PR).
@@ -412,6 +428,13 @@ bool SoftSetBoolArg(const std::string& strArg, bool fValue) | |||
return SoftSetArg(strArg, std::string("0")); | |||
} | |||
|
|||
void ForceSetArg(const std::string& strArg, const std::string& strValue) | |||
{ | |||
mapArgs[strArg] = strValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this not LOCK(cs_args)
?
(maybe I misinterpreted the meaning of force
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #9243
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. |
|
||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
.
return CNodeStateAccessor(); | ||
pstate = it->second; | ||
} | ||
return CNodeStateAccessor(pstate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add an std::move
here if you want to avoid a copy.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
|
||
CNodeStateAccessor(CNodeStateAccessor&& o) { pstate = o.pstate; o.pstate = NULL; } | ||
|
||
bool operator()() const { return (bool)pstate; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
explicit operator bool instead? Using the call operator for this seems strange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, switched to regular bool.
CNodeState* operator->() { return &(*pstate); } | ||
const CNodeState* operator->() const { return &(*pstate); } | ||
}; | ||
|
||
class NodeStateStorage { | ||
/** Map maintaining per-node state. Requires cs_main. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stale comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
std::shared_ptr<CNodeState> pstate; | ||
|
||
CNodeStateAccessor() : pstate() { } | ||
CNodeStateAccessor(std::shared_ptr<CNodeState>&& pstateIn) : pstate(std::move(pstateIn)) { ENTER_CRITICAL_SECTION(pstate->cs); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just have a lock as a member?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.