-
Notifications
You must be signed in to change notification settings - Fork 324
Delete splash screen widget early #605
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
|
Why is this so much more complicated than bitcoin/bitcoin#25146 (comment) ? |
Because this change makes the resulted code simpler. |
|
I think I have a similar question to luke. With this PR Now there are two different manual deletions for wallet handler in two different places. The // Delete wallet controller here manually, instead of relying on Qt object
// tracking (https://doc.qt.io/qt-5/objecttrees.html). This makes sure
// walletmodel m_handle_* notification handlers are deleted before wallets
// are unloaded, which can simplify wallet implementations. It also avoids
// these notifications having to be handled while GUI objects are being
// destroyed, making GUI code less fragile as well.
delete m_wallet_controller;
m_wallet_controller = nullptr;And the // If success, m_splash is no longer needed.
// Otherwise, it must be deleted prior to requesting shutdown
// to make sure the `SplashScreen::m_connected_wallet_handlers`
// is deleted before the wallet context is.
delete m_splash;
m_splash = nullptr;Shouldn't both wallet handler deletions be consolidated together? Or is there something that makes them different from each other? |
Correct. But indirectly, via deleting of objects, members of which are wallet handlers.
Those wallet handlers belong to very different objects, OTOH, the suggested approach eliminates duplication of |
|
I agree this the approach in this PR is clean and simplifies code. I think I'm just wondering if it is sufficient to only delete the splash screen object in initializeResult, or if it the splash screen should also be deleted in requestShutdown? Will initializeResult always happen before requestShutdown? Also wondering why requestShutdown is the right place to delete the wallet controller. The comments do a good job of explaining why it is important to delete these objects, but it's unclear why the objects are deleted in these places. It's possible there aren't concrete reasons, but I'm asking to learn if there are, and maybe improve the comments. |
|
Wouldn't make sense to unsubscribe from the core signals before the In other words, calling |
The universal entry point of the shutdown process is OTOH, |
|
re: #605 (comment)
So if If so, it seems like maybe we need to add re: #605 (comment)
Unclear if it would make things more complicated to partially shut down wallet controller and splash screen objects by disconnecting them from signals, and let Qt delete them later, than to simply delete these objects ourselves when we know they are no longer needed. But maybe it would be more targeted and not actually more complicated. Qt does seem to not really care whether we delete the objects or it will. Both approaches seem like they would be ok. |
Correct, although this scenario is unlikely. Nevertheless, the code is flawed.
Would it cleaner to introduce a dedicated |
Added a commit to prevent such a scenario. |
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.
Code review ACK d4227f7, but I tend to think it would be good to keep the first three commits, drop the fourth commit "Ignore QEvent::Quit until full initialization", and add a new commit with achow's suggestion also deleting the splash screen in requestShutdown() so requestShutdown() is less fragile. (See comments below)
Would it cleaner to introduce a dedicated
SplashScreen::releaseWalletHandler()member function, and leavem_splashobject lifetime management to Qt framework? The same approach could be used for wallet controller.
IMO it is cleanest just to delete the splash screen as soon as you don't need the splash screen, rather than keep the splash screen alive but remove the wallet handler underneath it. If it were actually helpful to the user to keep showing the splash screen while unloading, maybe a more complicated approach like this could be good, but otherwise it seems like it wouldn't improve things.
src/qt/bitcoin.cpp
Outdated
| bool BitcoinApplication::event(QEvent* e) | ||
| { | ||
| if (e->type() == QEvent::Quit) { | ||
| if (m_initialized && e->type() == QEvent::Quit) { |
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.
In commit "qt: Ignore QEvent::Quit until full initialization" (d4227f7)
This seems like a pretty indirect way of preventing requestShutdown() from running before initializeResult() so splashscreen will be deleted before the wallet and won't hold dangling references to the wallet.
It also seems pretty fragile, because this is just one requestShutdown() caller when there could be other callers now or in the future that fail to delete the splash screen before calling requestShutdown.
Also the commit seems to make code more complicated. There are no comments to explain the logic, and the commit message just has a vague message about requestShutdown being error prune, not saying why it is (or should be) error prone.
I think a better approach here would just to make the requestShutdown method delete splash screen and wallet at the same time, so requestShutdown calls are not error-prone and don't need to be avoided like this. Something like
delete m_splash;
m_splash = nullptr;
delete m_wallet_controller;
m_wallet_controller = nullptr;in requestShutdown like achow originally proposed bitcoin/bitcoin#25146 (comment) would make sense I think.
Additionally, it does seem good to delete the splash screen in initializeResult as you are doing in your first commit "qt: Delete splash screen widget early" (c7d2458), since that also makes sense and simplifiies code. In both cases I think it's good to delete the splash screen as soon as the splash screen is no longer needed.
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 following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsNo conflicts as of last run. |
d4227f7 to
d2cfc7d
Compare
|
Updated d4227f7 -> d2cfc7d (pr605.03 -> pr605.04):
Done. |
|
Code Review ACK d2cfc7d |
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.
tACK d2cfc7d
I have reviewed the code and I agree it can be merged. Additionally did testing where I quit in various scenarios where the splash screen was still loading and quiting afterwards as well.
In relation to bitcoin/bitcoin#26340, I still couldn't recreate the issue. But since the splash screen would be the only core window showing at this time, it's reasonable to think it could be related.
src/qt/bitcoin.cpp
Outdated
| #ifdef ENABLE_WALLET | ||
| // Delete wallet controller here manually, instead of relying on Qt object | ||
| // Delete splash screen and wallet controller here manually, instead of relying on Qt object | ||
| // tracking (https://doc.qt.io/qt-5/objecttrees.html). This makes sure | ||
| // walletmodel m_handle_* notification handlers are deleted before wallets | ||
| // are unloaded, which can simplify wallet implementations. It also avoids | ||
| // these notifications having to be handled while GUI objects are being | ||
| // destroyed, making GUI code less fragile as well. | ||
| delete m_wallet_controller; | ||
| m_wallet_controller = nullptr; | ||
| delete m_splash; | ||
| m_splash = nullptr; |
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 deletion should be placed outside of the #ifdef ENABLE_WALLET scope.
As GUI uses the splash screen even when it runs without a wallet (--disable-wallet), the object will not be deleted if a shutdown is requested during load.
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 object will not be deleted if a shutdown is requested during load.
It will, via Qt parent-child relation.
Early widget deletion is crucial when wallet code is active only.
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 object will not be deleted if a shutdown is requested during load.
It will, via Qt parent-child relation.
The SplashScreen has no parent. It's a standalone dialog (check the view constructor).
You can try it by adding a print inside the splash screen destructor and call requestShutdown at any time during load (blocking initializeResult execution).
This ensures that during shutdown, including failed initialization, the `SplashScreen::m_connected_wallet_handlers` is deleted before the wallet context is.
d2cfc7d to
1b22849
Compare
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.
ACK 1b22849
The failing CI task is pure bad luck.
|
ACK 1b22849 |
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.
ACK 1b22849
Fixes #604.
Fixes bitcoin/bitcoin#25146.
Fixes bitcoin/bitcoin#26340.
SplashScreen::deleteLater()does not guarantee deletion of them_splashobject prior to the wallet context deletion. If the latter happens first, the segfault follows.