diff --git a/chain/src/chain.rs b/chain/src/chain.rs index d8c7713d8..676c69d92 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -1216,21 +1216,23 @@ impl Chain { .map_err(|e| ErrorKind::StoreErr(e, "chain tail".to_owned()).into()) } - /// Tip (head) of the header chain if read lock can be acquired right now. - pub fn try_header_head(&self) -> Result, Error> { - match self.header_pmmr.try_read() { - Some(lock) => { - let hash = lock.head_hash()?; - let header = self.store.get_block_header(&hash)?; - Ok(Some(Tip::from_header(&header))) - } - None => Ok(None), - } + /// Tip (head) of the header chain if read lock can be acquired reasonably quickly. + /// Used by the TUI when updating stats to avoid locking the TUI up. + pub fn try_header_head(&self, timeout: Duration) -> Result, Error> { + self.header_pmmr + .try_read_for(timeout) + .map(|ref pmmr| self.read_header_head(pmmr).map(|x| Some(x))) + .unwrap_or(Ok(None)) } /// Tip (head) of the header chain. pub fn header_head(&self) -> Result { - let hash = self.header_pmmr.read().head_hash()?; + self.read_header_head(&self.header_pmmr.read()) + } + + /// Read head from the provided PMMR handle. + fn read_header_head(&self, pmmr: &txhashset::PMMRHandle) -> Result { + let hash = pmmr.head_hash()?; let header = self.store.get_block_header(&hash)?; Ok(Tip::from_header(&header)) } diff --git a/servers/src/common/stats.rs b/servers/src/common/stats.rs index ba44e04fb..2ca1743f7 100644 --- a/servers/src/common/stats.rs +++ b/servers/src/common/stats.rs @@ -63,7 +63,7 @@ pub struct ServerStats { /// Difficulty calculation statistics pub diff_stats: DiffStats, /// Transaction pool statistics - pub tx_stats: TxStats, + pub tx_stats: Option, /// Disk usage in GB pub disk_usage_gb: String, } diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index c623a76fd..68b1c760d 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -23,7 +23,7 @@ use std::path::Path; use std::sync::{mpsc, Arc}; use std::{ thread::{self, JoinHandle}, - time, + time::{self, Duration}, }; use fs2::FileExt; @@ -480,23 +480,16 @@ impl Server { .map(|p| PeerStats::from_peer(&p)) .collect(); - let tx_stats = { - let pool = self.tx_pool.try_read(); - match pool { - Some(pool) => TxStats { - tx_pool_size: pool.txpool.size(), - tx_pool_kernels: pool.txpool.kernel_count(), - stem_pool_size: pool.stempool.size(), - stem_pool_kernels: pool.stempool.kernel_count(), - }, - None => TxStats { - tx_pool_size: 0, - tx_pool_kernels: 0, - stem_pool_size: 0, - stem_pool_kernels: 0, - }, - } - }; + // Updating TUI stats should not block any other processing so only attempt to + // acquire various read locks with a timeout. + let read_timeout = Duration::from_millis(500); + + let tx_stats = self.tx_pool.try_read_for(read_timeout).map(|pool| TxStats { + tx_pool_size: pool.txpool.size(), + tx_pool_kernels: pool.txpool.kernel_count(), + stem_pool_size: pool.stempool.size(), + stem_pool_kernels: pool.stempool.kernel_count(), + }); let head = self.chain.head_header()?; let head_stats = ChainStats { @@ -506,16 +499,15 @@ impl Server { total_difficulty: head.total_difficulty(), }; - let header_stats = match self.chain.try_header_head()? { - Some(tip) => { - let header = self.chain.get_block_header(&tip.hash())?; + let header_stats = match self.chain.try_header_head(read_timeout)? { + Some(head) => self.chain.get_block_header(&head.hash()).map(|header| { Some(ChainStats { latest_timestamp: header.timestamp, height: header.height, last_block_h: header.prev_hash, total_difficulty: header.total_difficulty(), }) - } + })?, _ => None, }; diff --git a/src/bin/tui/status.rs b/src/bin/tui/status.rs index 90e1bb414..3e6e3099a 100644 --- a/src/bin/tui/status.rs +++ b/src/bin/tui/status.rs @@ -284,18 +284,20 @@ impl TUIStatusListener for TUIStatusView { t.set_content(header_stats.latest_timestamp.to_string()); }); } - c.call_on_id("tx_pool_size", |t: &mut TextView| { - t.set_content(stats.tx_stats.tx_pool_size.to_string()); - }); - c.call_on_id("stem_pool_size", |t: &mut TextView| { - t.set_content(stats.tx_stats.stem_pool_size.to_string()); - }); - c.call_on_id("tx_pool_kernels", |t: &mut TextView| { - t.set_content(stats.tx_stats.tx_pool_kernels.to_string()); - }); - c.call_on_id("stem_pool_kernels", |t: &mut TextView| { - t.set_content(stats.tx_stats.stem_pool_kernels.to_string()); - }); + if let Some(tx_stats) = &stats.tx_stats { + c.call_on_id("tx_pool_size", |t: &mut TextView| { + t.set_content(tx_stats.tx_pool_size.to_string()); + }); + c.call_on_id("stem_pool_size", |t: &mut TextView| { + t.set_content(tx_stats.stem_pool_size.to_string()); + }); + c.call_on_id("tx_pool_kernels", |t: &mut TextView| { + t.set_content(tx_stats.tx_pool_kernels.to_string()); + }); + c.call_on_id("stem_pool_kernels", |t: &mut TextView| { + t.set_content(tx_stats.stem_pool_kernels.to_string()); + }); + } } }