Unregister wallet notifications before unloading wallets#360
Unregister wallet notifications before unloading wallets#360hebasto merged 1 commit intobitcoin-core:masterfrom
Conversation
This change was originally part of both bitcoin/bitcoin#10102 and bitcoin/bitcoin#19101 and is required for both because it avoids the IPC wallet implementation in bitcoin/bitcoin#10102 and the WalletContext implementation in bitcoin/bitcoin#19101 needing to deal with notification objects that have stale pointers to deleted wallets.
| // 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 |
There was a problem hiding this comment.
Delete wallet controller here manually... This makes sure walletmodel
m_handle_*notification handlers are deleted before wallets are unloaded, which can simplify wallet implementations.
Sorry, but I cannot get it.
To achieve the goal--notification handler deletion--the ~WalletModel destructor must be called. But I cannot see the way how deletion of the m_wallet_controller could works here, as an instance of the WalletController class does not own WalletModel objects, because WalletController::m_wallets is std::vector<WalletModel*>, not std::vector<std::unique_ptr<WalletModel>>.
Did I miss the point?
There was a problem hiding this comment.
re: #360 (comment)
To achieve the goal--notification handler deletion--the
~WalletModeldestructor must be called. But I cannot see the way how deletion of them_wallet_controllercould works here, as an instance of theWalletControllerclass does not ownWalletModelobjects, becauseWalletController::m_walletsisstd::vector<WalletModel*>, notstd::vector<std::unique_ptr<WalletModel>>.Did I miss the point?
No, this is a reasonable question because wallet controller's ownership of wallet models is implicit instead of explicit. But m_wallet vector usage is just emulating unique_ptr without using unique_ptr for some reason (probably no reason).
In the case where an m_wallet entry is erased, the pointer is deleted:
gui/src/qt/walletcontroller.cpp
Lines 179 to 186 in d67330d
In the case where the m_wallet vector is cleared, the pointers are deleted:
gui/src/qt/walletcontroller.cpp
Lines 108 to 114 in d67330d
And in the case where the controller object is destroyed, any models that weren't cleared or erased are destroyed:
gui/src/qt/walletcontroller.cpp
Lines 142 to 146 in d67330d
I could add more details to this comment, but I don't think it would be appropriate to mention implementation details of the wallet controller outside the wallet controller (especially when implementation seems unnecessarily complicated and could probably be simplified later). So hopefully this reply clears things up.
Thanks for looking into this!
hebasto
left a comment
There was a problem hiding this comment.
Approach ACK 93cc53a.
The forced destroying of m_wallet_controller indeed achieves the desired goals.
But I'm still uncomfortable with the suggested comment, because we still rely on Qt object ownership.
Setting a ~WalletModel as a breakpoint clearly proves the statement above:
$ lldb ./src/qt/bitcoin-qt -- -signet
(lldb) target create "./src/qt/bitcoin-qt"
Current executable set to '/home/hebasto/GitHub/gui/src/qt/bitcoin-qt' (x86_64).
(lldb) settings set -- target.run-args "-signet"
(lldb) br s -n ~WalletModel
Breakpoint 1: 2 locations.
(lldb) run
Process 116788 launched: '/home/hebasto/GitHub/gui/src/qt/bitcoin-qt' (x86_64)
Process 116788 stopped
* thread #1, name = 'bitcoin-qt', stop reason = breakpoint 1.2
frame #0: 0x000055555565c07a bitcoin-qt`WalletModel::~WalletModel(this=0x00005555571b4aa0) at walletmodel.cpp:61:1
(lldb) bt
* thread #1, name = 'bitcoin-qt', stop reason = breakpoint 1.2
* frame #0: 0x000055555565c07a bitcoin-qt`WalletModel::~WalletModel(this=0x00005555571b4aa0) at walletmodel.cpp:61:1
frame #1: 0x00007ffff7a6701e libQt5Core.so.5`QObjectPrivate::deleteChildren() + 110
frame #2: 0x00007ffff7a715ef libQt5Core.so.5`QObject::~QObject() + 1455
frame #3: 0x0000555555655157 bitcoin-qt`WalletController::~WalletController(this=0x0000555557358d10) at walletcontroller.cpp:61:1
frame #4: 0x00005555556551cf bitcoin-qt`WalletController::~WalletController(this=0x0000555557358d10) at walletcontroller.cpp:57:1
frame #5: 0x00005555555d9c63 bitcoin-qt`BitcoinApplication::requestShutdown(this=0x00007fffffffd730) at bitcoin.cpp:362:5
frame #6: 0x00005555555dc269 bitcoin-qt`GuiMain(argc=2, argv=<unavailable>) at bitcoin.cpp:650:17
frame #7: 0x00005555555d70ea bitcoin-qt`main(argc=<unavailable>, argv=<unavailable>) at main.cpp:21:43
frame #8: 0x00007ffff61fa0b3 libc.so.6`__libc_start_main + 243
frame #9: 0x00005555555d700e bitcoin-qt`_start + 46
A WalletModel object becomes a child of the WalletController object here:
gui/src/qt/walletcontroller.cpp
Line 143 in 03826ae
Hmm, maybe you have a suggestion to reword the comment. The comment "Delete wallet controller here manually, instead of relying on Qt object tracking" is not saying we no longer rely on Qt object tracking generally. It is just saying we no longer rely on it to delete the wallet controller object. The wallet model is a different object from the wallet controller. It would be nice to manage wallet model objects with smart pointer ownership, but not necessary. This PR is necessary and is blocking bitcoin/bitcoin#10102 and bitcoin/bitcoin#19101 because these PR's can't deal with the GUI having stale walletmodel m_handle_* notification handlers associated with unloaded wallets. |
You are right. |
This change was originally part of both bitcoin/bitcoin#10102 and bitcoin/bitcoin#19101 and is required for both because it avoids the IPC wallet implementation in bitcoin/bitcoin#10102 and the WalletContext implementation in bitcoin/bitcoin#19101 needing to deal with notification objects that have stale pointers to deleted wallets.