diff --git a/monero-harness/src/lib.rs b/monero-harness/src/lib.rs index 74963b910..498009d06 100644 --- a/monero-harness/src/lib.rs +++ b/monero-harness/src/lib.rs @@ -531,7 +531,7 @@ impl MoneroWallet { /// Get non-strict balance per subaddress for main account (index 0). pub async fn balance_per_subaddress(&self) -> Result> { - Ok(self.wallet.balance_per_subaddress().await) + self.wallet.balance_per_subaddress().await } pub async fn refresh(&self) -> Result<()> { diff --git a/monero-sys/src/lib.rs b/monero-sys/src/lib.rs index ad76e94c1..8c974bd93 100644 --- a/monero-sys/src/lib.rs +++ b/monero-sys/src/lib.rs @@ -17,7 +17,7 @@ pub use bridge::wallet_listener; pub use bridge::{TraceListener, WalletEventListener, WalletListenerBox}; pub use database::{Database, RecentWallet}; use std::panic::{AssertUnwindSafe, catch_unwind}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, Weak}; use std::{ any::Any, cmp::Ordering, collections::HashMap, fmt::Display, future::Future, ops::Deref, pin::Pin, time::Duration, @@ -682,10 +682,12 @@ impl WalletHandle { /// Returns a map of subaddress index -> balance (in atomic units). /// strict: If true, only includes confirmed and unlocked balance. /// If false, pending and unconfirmed transactions are also included. - pub async fn balance_per_subaddress(&self) -> std::collections::HashMap { + pub async fn balance_per_subaddress( + &self, + ) -> anyhow::Result> { self.call(move |wallet| wallet.balance_per_subaddress_sync()) .await - .expect("wallet thread closed while fetching balance per subaddress") + .context("Couldn't complete wallet call") } /// Check if the wallet is synchronized. @@ -3219,7 +3221,7 @@ impl Deref for TransactionInfoHandle { /// This listener does things on certain events like storing the wallet to disk. /// This is supposed to improve upon the behaviour of wallet2 pub struct WalletHandleListener { - wallet: Arc, + wallet: Weak, /// We need a handle to the runtime to be able to spawn tasks rt_handle: tokio::runtime::Handle, /// We throttle the saving of the wallet to disk to avoid storing the wallet too often @@ -3234,6 +3236,7 @@ impl WalletHandleListener { pub fn new(wallet: Arc) -> Self { // Get the current runtime handle let rt_handle = tokio::runtime::Handle::current(); + let wallet = Arc::downgrade(&wallet); // Create a throttle wrapper around the save job let store_job = { @@ -3241,7 +3244,10 @@ impl WalletHandleListener { let rt = rt_handle.clone(); move |()| { - let wallet = wallet.clone(); + let Some(wallet) = wallet.upgrade() else { + tracing::trace!("Skipping wallet store because wallet handle is gone"); + return; + }; let rt = rt.clone(); rt.spawn(async move { @@ -3291,7 +3297,10 @@ impl WalletEventListener for WalletHandleListener { // When the wallet finishes refreshing, we start the refresh thread again. // The purpose of this is to ensure that if the user does a rescan (restore height changed) // We start the refresh thread again after the rescan is complete. - let handle = self.wallet.clone(); + let Some(handle) = self.wallet.upgrade() else { + tracing::trace!("Skipping refresh thread restart because wallet handle is gone"); + return; + }; self.rt_handle.spawn(async move { if let Err(e) = handle.start_refresh_thread().await { tracing::error!(error=%e, "Failed to start refresh thread"); diff --git a/monero-wallet/src/listener.rs b/monero-wallet/src/listener.rs index 060f9ba3b..4714613f3 100644 --- a/monero-wallet/src/listener.rs +++ b/monero-wallet/src/listener.rs @@ -1,4 +1,7 @@ -use std::{sync::Arc, time::Duration}; +use std::{ + sync::{Arc, Weak}, + time::Duration, +}; use monero_sys::WalletEventListener; @@ -29,13 +32,17 @@ impl TauriWalletListener { pub async fn new(tauri_handle: TauriHandle, wallet: Arc) -> Self { let rt_handle = tokio::runtime::Handle::current(); + let wallet: Weak = Arc::downgrade(&wallet); let balance_job = { let wallet = wallet.clone(); let tauri = tauri_handle.clone(); let rt = rt_handle.clone(); move |()| { - let wallet = wallet.clone(); + let Some(wallet) = wallet.upgrade() else { + tracing::trace!("Skipping balance update because wallet handle is gone"); + return; + }; let tauri = tauri.clone(); let rt = rt.clone(); rt.spawn(async move { @@ -64,7 +71,10 @@ impl TauriWalletListener { let tauri = tauri_handle.clone(); let rt = rt_handle.clone(); move |()| { - let wallet = wallet.clone(); + let Some(wallet) = wallet.upgrade() else { + tracing::trace!("Skipping history update because wallet handle is gone"); + return; + }; let tauri = tauri.clone(); let rt = rt.clone(); rt.spawn(async move { @@ -86,7 +96,10 @@ impl TauriWalletListener { let tauri = tauri_handle.clone(); let rt = rt_handle.clone(); move |()| { - let wallet = wallet.clone(); + let Some(wallet) = wallet.upgrade() else { + tracing::trace!("Skipping sync update because wallet handle is gone"); + return; + }; let tauri = tauri.clone(); let rt = rt.clone(); rt.spawn(async move {