-
Notifications
You must be signed in to change notification settings - Fork 38.8k
net: RecordBytesSent under cs_vSend lock #18784
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
The CNode member nSendBytes is incremented under the node's lock cs_vSend. However, RecordBytesSent is not. An RPC thread that acquires the cs_vSend lock puts the message handler thread on hold. While the thread is on hold, getnettotals returns "stale" values or values that don't add up. This can be fixed by making cs_vSend a "write lock" for the total bytes sent in connman. After this commit, both calls to RecordBytesSent are done under the LOCK(pnode->cs_vSend);
ffffedf to
faae6ca
Compare
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
|
Changed my mind, see below. |
Can you explain this a bit? What do the values not add up to? As far as I can tell, |
|
I've changed my mind on this one. However, I think a PR like this is needed to bring consistency between the two calls to |
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.
Concept ACK.
What is the motivation to have the lock when calling got it.RecordBytesSent?
Why are they separate? Let's assume we connect to a peer, send 100 bytes, disconnect. Send 100 bytes to another peer, then the total bytes sent should simply equal 200 bytes. |
Maybe I'm completely off-base here, I'm not trying to make a big statement. Isn't it impossible to keep these RPCs synced? They are two separate calls, something might happen in between them.
|
|
Ok, I see. The reason I created this pull was that a call to and then asserting that I haven't looked into #19673 so I am wondering if #19673 also fixes that issue. |
|
^ Woah that shouldn't happen. Will investigate. |
What is I don't understand the problem you're trying to solve. |
No, it is returning the value it returned in a previous call. But getpeerinfo already returned the new correct value. |
|
I agree with Wlad's assessment in that issue:
|
|
Ok, then the test is wrong and should be removed: diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 03c858c694..afb67891af 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -104,29 +104,15 @@ class NetTest(BitcoinTestFramework):
def test_getnettotals(self):
self.log.info("Test getnettotals")
- # getnettotals totalbytesrecv and totalbytessent should be
- # consistent with getpeerinfo. Since the RPC calls are not atomic,
- # and messages might have been recvd or sent between RPC calls, call
- # getnettotals before and after and verify that the returned values
- # from getpeerinfo are bounded by those values.
- net_totals_before = self.nodes[0].getnettotals()
peer_info = self.nodes[0].getpeerinfo()
- net_totals_after = self.nodes[0].getnettotals()
- assert_equal(len(peer_info), 2)
- peers_recv = sum([peer['bytesrecv'] for peer in peer_info])
- peers_sent = sum([peer['bytessent'] for peer in peer_info])
-
- assert_greater_than_or_equal(peers_recv, net_totals_before['totalbytesrecv'])
- assert_greater_than_or_equal(net_totals_after['totalbytesrecv'], peers_recv)
- assert_greater_than_or_equal(peers_sent, net_totals_before['totalbytessent'])
- assert_greater_than_or_equal(net_totals_after['totalbytessent'], peers_sent)
+ net_totals = self.nodes[0].getnettotals()
# test getnettotals and getpeerinfo by doing a ping
# the bytes sent/received should change
# note ping and pong are 32 bytes each
self.nodes[0].ping()
- self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_after['totalbytessent'] + 32 * 2), timeout=1)
- self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_after['totalbytesrecv'] + 32 * 2), timeout=1)
+ self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals['totalbytessent'] + 32 * 2), timeout=1)
+ self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals['totalbytesrecv'] + 32 * 2), timeout=1)
peer_info_after_ping = self.nodes[0].getpeerinfo()
for before, after in zip(peer_info, peer_info_after_ping): |
Done: #20258 |
|
thanks |
The
CNodemembernSendBytesis incremented under the node's lockcs_vSend. However,RecordBytesSentis not. An RPC thread that acquires thecs_vSendlock puts the message handler thread on hold. While the thread is on hold,getnettotalsreturns "stale" values or values that don't add up.This can be fixed by making
cs_vSenda "write lock" for the total bytes sent in connman.