Skip to content

Update block tip in the gui thread#177

Merged
hebasto merged 1 commit into
bitcoin-core:mainfrom
jarolrod:update-in-gui-thread
Oct 25, 2022
Merged

Update block tip in the gui thread#177
hebasto merged 1 commit into
bitcoin-core:mainfrom
jarolrod:update-in-gui-thread

Conversation

@jarolrod
Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
johnny9 pushed a commit to johnny9/bitcoin-core-app that referenced this pull request Jul 4, 2025
a8a7291 qml: Update block tip in the gui thread (João Barbosa)

Pull request description:

  This takes commit bitcoin-core/gui-qml@cf8a278 from bitcoin-core/gui-qml#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](https://img.shields.io/badge/OS-Windows-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/win64/insecure_win_gui.zip?branch=pull/177)
  [![Intel macOS](https://img.shields.io/badge/OS-Intel%20macOS-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/macos/insecure_mac_gui.zip?branch=pull/177)
  [![Apple Silicon macOS](https://img.shields.io/badge/OS-Apple%20Silicon%20macOS-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/macos_arm64/insecure_mac_arm64_gui.zip?branch=pull/177)
  [![ARM64 Android](https://img.shields.io/badge/OS-Android-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/android/insecure_android_apk.zip?branch=pull/177)

ACKs for top commit:
  hebasto:
    ACK a8a7291, I have reviewed the code and it looks OK, I agree it can be merged.

Tree-SHA512: 9faf9b486af15f29ace20c9cb55ba65f3e20d23f4b7dc58908589f9a104b47cebe575a2a88935ed5f1a2fafc8bc41088084f28803990db64715e235da4c6461f
tx-signer450 added a commit to tx-signer450/gui-qml that referenced this pull request Oct 20, 2025
a8a7291f744c0dc8c2bc9cf51766a4216b802fb9 qml: Update block tip in the gui thread (João Barbosa)

Pull request description:

  This takes commit bitcoin-core/gui-qml@cf8a278 from bitcoin-core/gui-qml#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](https://img.shields.io/badge/OS-Windows-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/win64/insecure_win_gui.zip?branch=pull/177)
  [![Intel macOS](https://img.shields.io/badge/OS-Intel%20macOS-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/macos/insecure_mac_gui.zip?branch=pull/177)
  [![Apple Silicon macOS](https://img.shields.io/badge/OS-Apple%20Silicon%20macOS-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/macos_arm64/insecure_mac_arm64_gui.zip?branch=pull/177)
  [![ARM64 Android](https://img.shields.io/badge/OS-Android-green)](https://api.cirrus-ci.com/v1/artifact/github/bitcoin-core/gui-qml/android/insecure_android_apk.zip?branch=pull/177)

ACKs for top commit:
  hebasto:
    ACK a8a7291f744c0dc8c2bc9cf51766a4216b802fb9, I have reviewed the code and it looks OK, I agree it can be merged.

Tree-SHA512: 9faf9b486af15f29ace20c9cb55ba65f3e20d23f4b7dc58908589f9a104b47cebe575a2a88935ed5f1a2fafc8bc41088084f28803990db64715e235da4c6461f
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