Skip to content

Update block tip in the gui thread #177

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

Merged
merged 1 commit into from
Oct 25, 2022

Conversation

jarolrod
Copy link
Member

This takes commit cf8a278 from #38 and rebases it over changes on master; namely that we've dropped GUIUtil::ObjectInvoke in favor of QMetaObject::invokeMethod.

This avoids non-thread-safe errors. I ran into a segfault here while running some data intensive monkey-code related to blocks, this change prevented it. I'm not going to actually use the code that caused the segfault, just to illustrate that this is useful here anyways.

Windows
Intel macOS
Apple Silicon macOS
ARM64 Android

@johnny9
Copy link
Collaborator

johnny9 commented Sep 28, 2022

Does the handler need to have disconnect() called when the NodeModel cleans up possibly? I'm having a hard time seeing which object in the lambda execution would be invalid and cause the segfault.

Copy link
Contributor

@shaavan shaavan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code change looks acceptable to me. The reasoning for which is nicely explained in this Stackoverflow answer:

... The reason to use QMetaObject::invokeMethod if the recipient object might be in another thread is that attempting to call a slot directly on an object in another thread can lead to corruption or worse if it accesses or modifies non-thread-safe data.

However, I am curious about the seg fault error you mentioned in the description. Would you please add some points on what exactly you think caused the error?

@johnny9
Copy link
Collaborator

johnny9 commented Oct 4, 2022

I agree that this change is acceptable. It's ok to do simple setters on the gui thread. My guess is that doing so forces the juicy part of the lambda to only execute while the NodeModel object is still alive but there is also the NodeImpl that adds its own wrapper to the lambda so it could be that object's lifetime as well.

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK eca5252, I have reviewed the code and it looks OK, I agree it can be merged.

#38 (review)

@hebasto hebasto merged commit ea267eb into bitcoin-core:main Oct 25, 2022
@promag
Copy link
Contributor

promag commented Oct 25, 2022

but there is also the NodeImpl that adds its own wrapper to the lambda so it could be that object's lifetime as well.

@johnny9 I don't understand this, can you rephrase it?

@johnny9
Copy link
Collaborator

johnny9 commented Oct 25, 2022

@promag

fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()},
is the line of code I was referring to. It looks like the function takes the lambda that you pass into handleNotifyBlockTip as input (fn) and adds its own lambda around it before registering it to the handler so it can do "GuessVerificationProgress". I would think NodeImpl's lifetime would be longer than the NodeModel's though so it seemed less likely that is where the segfault is.

@promag
Copy link
Contributor

promag commented Oct 25, 2022

The segfault comes from not calling setBlockTipHeight and setVerificationProgress on the gui thread. There is no concurrency control, and (not sure) QML direct-connects to the signals blockTipHeightChanged verificationProgressChanged.

The alternative to having some mutex is not ideal because we don't want to block the GUI and vice-versa

So the approach is to just defer the update.

Also, with QMetaObject::invokeMethod(this, fn), Qt won't call fn if this is destroyed.

@johnny9
Copy link
Collaborator

johnny9 commented Oct 25, 2022

Does having NodeModel call m_handler_notify_block_tip.disconnect() in its deconstructor fix the segfault? If you have a reliable way to cause the error I'd like to try.

@promag
Copy link
Contributor

promag commented Oct 25, 2022

It's already called on the m_handler_notify_block_tip destructor. But that's not the issue, setBlockTipHeight and setVerificationProgress have to be called on the GUI thread.

@johnny9
Copy link
Collaborator

johnny9 commented Oct 25, 2022

Sorry, I guess I don't remember a reason you couldn't EMIT the signal from another thread, I thought that setting the value and emitting the signal across threads was just fine and QML engine would handle the signal within its own thread but I'm fuzzy on the details.

You are right about the handler deleting itself also. I doubled checked https://www.boost.org/doc/libs/1_57_0/doc/html/boost/signals2/scoped_connection.html.

hebasto pushed a commit to hebasto/gui-qml that referenced this pull request Jun 8, 2025
hebasto pushed a commit to hebasto/gui-qml that referenced this pull request Jun 9, 2025
hebasto pushed a commit to hebasto/gui-qml that referenced this pull request Jun 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants