From defcd3d153074f8a7dad55dc66d0b2190d907aef Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 19 May 2026 22:21:12 +0300 Subject: [PATCH 01/11] tui: show server initialization status and error --- servers/src/common/types.rs | 11 +++++- servers/src/grin/server.rs | 31 +++++++++------ src/bin/cmd/server.rs | 48 +++++++++++------------ src/bin/tui/ui.rs | 77 +++++++++++++++++++++++++++++++------ 4 files changed, 119 insertions(+), 48 deletions(-) diff --git a/servers/src/common/types.rs b/servers/src/common/types.rs index ea83fecf82..bbcffa0fd7 100644 --- a/servers/src/common/types.rs +++ b/servers/src/common/types.rs @@ -19,7 +19,6 @@ use std::sync::Arc; use chrono::prelude::Utc; use rand::prelude::*; -use crate::api; use crate::chain; use crate::core::global::{ChainTypes, DEFAULT_FUTURE_TIME_LIMIT}; use crate::core::{core, libtx, pow}; @@ -28,6 +27,7 @@ use crate::p2p; use crate::pool; use crate::pool::types::DandelionConfig; use crate::store; +use crate::{api, Server}; /// Error type wrapping underlying module errors. #[derive(Debug)] @@ -405,3 +405,12 @@ impl DandelionEpoch { self.relay_peer.clone() } } + +/// Server initialization status. +pub enum ServerInitStatus { + LoadDatabase, + StartSync, + StartAPI, + FinishedLoading(Server), + ErrorLoading(Error), +} diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index d3510fe228..7285e85827 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -39,7 +39,7 @@ use crate::common::hooks::{init_chain_hooks, init_net_hooks}; use crate::common::stats::{ ChainStats, DiffBlock, DiffStats, PeerStats, ServerStateInfo, ServerStats, TxStats, }; -use crate::common::types::{Error, ServerConfig, StratumServerConfig}; +use crate::common::types::{Error, ServerConfig, ServerInitStatus, StratumServerConfig}; use crate::core::core::hash::{Hashed, ZERO_HASH}; use crate::core::ser::ProtocolVersion; use crate::core::{consensus, genesis, global, pow}; @@ -52,6 +52,7 @@ use crate::pool; use crate::util::file::get_first_line; use crate::util::{RwLock, StopState}; use futures::channel::oneshot; +use futures::SinkExt; use grin_util::logger::LogEntry; /// Arcified thread-safe TransactionPool with type parameters used by server components @@ -84,20 +85,16 @@ impl Server { /// Instantiates and starts a new server. Optionally takes a callback /// for the server to send an ARC copy of itself, to allow another process /// to poll info about the server status - pub fn start( + pub fn start( config: ServerConfig, - logs_rx: Option>, - mut info_callback: F, stop_state: Option>, + server_tx: Option>, api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>), - ) -> Result<(), Error> - where - F: FnMut(Server, Option>), - { + ) -> Result { let mining_config = config.stratum_mining_config.clone(); let enable_test_miner = config.run_test_miner; let test_miner_wallet_url = config.test_miner_wallet_url.clone(); - let serv = Server::new(config, stop_state, api_chan)?; + let serv = Server::new(config, stop_state, server_tx, api_chan)?; if let Some(c) = mining_config { let enable_stratum_server = c.enable_stratum_server; @@ -118,8 +115,7 @@ impl Server { } } - info_callback(serv, logs_rx); - Ok(()) + Ok(serv) } // Exclusive (advisory) lock_file to ensure we do not run multiple @@ -151,6 +147,7 @@ impl Server { pub fn new( config: ServerConfig, stop_state: Option>, + server_tx: Option>, api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>), ) -> Result { // Obtain our lock_file or fail immediately with an error. @@ -193,6 +190,10 @@ impl Server { info!("Starting server, genesis block: {}", genesis.hash()); + if let Some(server_tx) = server_tx { + let _ = server_tx.send(ServerInitStatus::LoadDatabase); + } + let shared_chain = Arc::new(chain::Chain::init( config.db_root.clone(), chain_adapter.clone(), @@ -220,6 +221,10 @@ impl Server { }; debug!("Capabilities: {:?}", capabilities); + if let Some(server_tx) = server_tx { + let _ = server_tx.send(ServerInitStatus::StartSync); + } + let p2p_server = Arc::new(p2p::Server::new( &config.db_root, capabilities, @@ -265,6 +270,10 @@ impl Server { } })?; + if let Some(server_tx) = server_tx { + let _ = server_tx.send(ServerInitStatus::StartAPI); + } + info!("Starting rest apis at: {}", &config.api_http_addr); let api_secret = get_first_line(config.api_secret_path.clone()); let foreign_api_secret = get_first_line(config.foreign_api_secret_path.clone()); diff --git a/src/bin/cmd/server.rs b/src/bin/cmd/server.rs index 6eb60f9f76..64a6a48170 100644 --- a/src/bin/cmd/server.rs +++ b/src/bin/cmd/server.rs @@ -28,6 +28,8 @@ use crate::tui::ui; use futures::channel::oneshot; use grin_p2p::msg::PeerAddrs; use grin_p2p::PeerAddr; +use grin_servers::common::types::{Error, ServerInitStatus}; +use grin_servers::Server; use grin_util::logger::LogEntry; use std::sync::mpsc; @@ -46,29 +48,25 @@ fn start_server_tui( logs_rx: Option>, api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>), ) { - // Run the UI controller.. here for now for simplicity to access - // everything it might need if config.run_tui.unwrap_or(false) { warn!("Starting GRIN in UI mode..."); - servers::Server::start( - config, - logs_rx, - |serv: servers::Server, logs_rx: Option>| { - let mut controller = ui::Controller::new(logs_rx.unwrap()).unwrap_or_else(|e| { - panic!("Error loading UI controller: {}", e); - }); - controller.run(serv); - }, - None, - api_chan, - ) - .unwrap(); + // Run the UI controller. + let (serv_tx, serv_rx) = mpsc::channel::(); + let mut controller = ui::Controller::new(logs_rx, serv_rx).unwrap_or_else(|e| { + panic!("Error loading UI controller: {}", e); + }); + let serv_tx_clone = serv_tx.clone(); + thread::spawn(move || { + match Server::start(config, None, Some(serv_tx_clone.clone()), api_chan) { + Ok(s) => serv_tx_clone.send(ServerInitStatus::FinishedLoading(s)), + Err(e) => serv_tx_clone.send(ServerInitStatus::ErrorLoading(e)), + } + }); + controller.run(); } else { warn!("Starting GRIN w/o UI..."); - servers::Server::start( - config, - logs_rx, - |serv: servers::Server, _: Option>| { + match Server::start(config, None, None, api_chan) { + Ok(s) => { let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); ctrlc::set_handler(move || { @@ -79,12 +77,12 @@ fn start_server_tui( thread::sleep(Duration::from_secs(1)); } warn!("Received SIGINT (Ctrl+C) or SIGTERM (kill)."); - serv.stop(); - }, - None, - api_chan, - ) - .unwrap(); + s.stop(); + } + Err(e) => { + error!("Error starting GRIN: {:?}", e); + } + } } } diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index 3021ebdf62..29797407a1 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -39,6 +39,8 @@ use crate::tui::constants::{ROOT_STACK, VIEW_BASIC_STATUS, VIEW_MINING, VIEW_PEE use crate::tui::types::{TUIStatusListener, UIMessage}; use crate::tui::{logs, menu, mining, peers, status, version}; use grin_core::global; +use grin_servers::common::types::{Error, ServerInitStatus}; +use grin_servers::ServerStats; use grin_util::logger::LogEntry; pub struct UI { @@ -46,7 +48,7 @@ pub struct UI { ui_rx: mpsc::Receiver, ui_tx: mpsc::Sender, controller_tx: mpsc::Sender, - logs_rx: mpsc::Receiver, + logs_rx: Option>, } fn modify_theme(theme: &mut Theme) { @@ -65,7 +67,7 @@ impl UI { /// Create a new UI pub fn new( controller_tx: mpsc::Sender, - logs_rx: mpsc::Receiver, + logs_rx: Option>, ) -> UI { let (ui_tx, ui_rx) = mpsc::channel::(); @@ -139,8 +141,10 @@ impl UI { return false; } - while let Some(message) = self.logs_rx.try_iter().next() { - logs::TUILogsView::update(&mut self.cursive, message); + if let Some(logs_rx) = &self.logs_rx { + while let Some(message) = logs_rx.try_iter().next() { + logs::TUILogsView::update(&mut self.cursive, message); + } } // Process any pending UI messages @@ -174,6 +178,8 @@ impl UI { pub struct Controller { rx: mpsc::Receiver, ui: UI, + serv_rx: mpsc::Receiver, + server: Option, } pub enum ControllerMessage { @@ -182,16 +188,52 @@ pub enum ControllerMessage { impl Controller { /// Create a new controller - pub fn new(logs_rx: mpsc::Receiver) -> Result { + pub fn new( + logs_rx: Option>, + serv_rx: mpsc::Receiver, + ) -> Result { let (tx, rx) = mpsc::channel::(); Ok(Controller { rx, ui: UI::new(tx, logs_rx), + serv_rx, + server: None, }) } + /// Server initialization status. + pub fn init_status(&mut self, text: &str, pop: bool) { + if pop { + self.ui.cursive.pop_layer(); + } + let content = StyledString::styled(text, Color::Light(BaseColor::Green)); + self.ui + .cursive + .add_layer(CircularFocus::new(Dialog::around(TextView::new(content))).wrap_tab()); + } + + /// Server initialization error. + pub fn init_error(&mut self, e: Error) { + let content = StyledString::styled(format!("{:?}", e), Color::Light(BaseColor::Red)); + self.ui.cursive.add_layer( + CircularFocus::new(Dialog::around(TextView::new(content)).button("Exit", |s| { + s.quit(); + })) + .wrap_tab(), + ); + } + + /// Server UI after initialization. + pub fn server(&mut self, server: &Server) { + if let Ok(stats) = server.get_server_stats() { + self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap(); + } + } + /// Run the controller - pub fn run(&mut self, server: Server) { + pub fn run(&mut self) { + self.init_status("Starting server...", false); + let stat_update_interval = 1; let mut next_stat_update = Utc::now().timestamp() + stat_update_interval; let delay = time::Duration::from_millis(50); @@ -201,20 +243,33 @@ impl Controller { ControllerMessage::Shutdown => { warn!("Shutdown in progress, please wait"); self.ui.stop(); - server.stop(); - return; + break; + } + } + } + + if let Some(m) = self.serv_rx.try_iter().next() { + match m { + ServerInitStatus::LoadDatabase => self.init_status("Loading database...", true), + ServerInitStatus::StartSync => self.init_status("Start syncing...", true), + ServerInitStatus::StartAPI => self.init_status("Starting API...", true), + ServerInitStatus::FinishedLoading(s) => { + self.ui.cursive.pop_layer(); + self.server = Some(s) } + ServerInitStatus::ErrorLoading(e) => self.init_error(e), } } if Utc::now().timestamp() > next_stat_update { next_stat_update = Utc::now().timestamp() + stat_update_interval; - if let Ok(stats) = server.get_server_stats() { - self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap(); + if let Some(server) = self.server.take() { + if let Ok(stats) = server.get_server_stats() { + self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap(); + } } } thread::sleep(delay); } - server.stop(); } } From 03eb5f4c2a332349d2fda8b37a674b4273632ddd Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 19 May 2026 22:25:49 +0300 Subject: [PATCH 02/11] fix: compilation issues --- servers/src/grin/server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index 7285e85827..eaa3a3217b 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -190,7 +190,7 @@ impl Server { info!("Starting server, genesis block: {}", genesis.hash()); - if let Some(server_tx) = server_tx { + if let Some(ref server_tx) = server_tx { let _ = server_tx.send(ServerInitStatus::LoadDatabase); } @@ -221,7 +221,7 @@ impl Server { }; debug!("Capabilities: {:?}", capabilities); - if let Some(server_tx) = server_tx { + if let Some(ref server_tx) = server_tx { let _ = server_tx.send(ServerInitStatus::StartSync); } @@ -270,7 +270,7 @@ impl Server { } })?; - if let Some(server_tx) = server_tx { + if let Some(ref server_tx) = server_tx { let _ = server_tx.send(ServerInitStatus::StartAPI); } From 9371d7a30f826e862994ae76f8e178983afe5f37 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 19 May 2026 22:34:21 +0300 Subject: [PATCH 03/11] fix: add documenting to status, remove unused imports --- servers/src/common/types.rs | 5 +++++ servers/src/grin/server.rs | 2 -- src/bin/cmd/server.rs | 2 +- src/bin/tui/ui.rs | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/servers/src/common/types.rs b/servers/src/common/types.rs index bbcffa0fd7..a428a5eb47 100644 --- a/servers/src/common/types.rs +++ b/servers/src/common/types.rs @@ -408,9 +408,14 @@ impl DandelionEpoch { /// Server initialization status. pub enum ServerInitStatus { + /// Database loading. LoadDatabase, + /// P2P server initialization. StartSync, + /// API server initialization. StartAPI, + /// Server instance after successful initialization. FinishedLoading(Server), + /// Error on initialization. ErrorLoading(Error), } diff --git a/servers/src/grin/server.rs b/servers/src/grin/server.rs index eaa3a3217b..f492ed17bb 100644 --- a/servers/src/grin/server.rs +++ b/servers/src/grin/server.rs @@ -52,8 +52,6 @@ use crate::pool; use crate::util::file::get_first_line; use crate::util::{RwLock, StopState}; use futures::channel::oneshot; -use futures::SinkExt; -use grin_util::logger::LogEntry; /// Arcified thread-safe TransactionPool with type parameters used by server components pub type ServerTxPool = Arc>>; diff --git a/src/bin/cmd/server.rs b/src/bin/cmd/server.rs index 64a6a48170..40e90b14c8 100644 --- a/src/bin/cmd/server.rs +++ b/src/bin/cmd/server.rs @@ -28,7 +28,7 @@ use crate::tui::ui; use futures::channel::oneshot; use grin_p2p::msg::PeerAddrs; use grin_p2p::PeerAddr; -use grin_servers::common::types::{Error, ServerInitStatus}; +use grin_servers::common::types::ServerInitStatus; use grin_servers::Server; use grin_util::logger::LogEntry; use std::sync::mpsc; diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index 29797407a1..8278890fab 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -40,7 +40,6 @@ use crate::tui::types::{TUIStatusListener, UIMessage}; use crate::tui::{logs, menu, mining, peers, status, version}; use grin_core::global; use grin_servers::common::types::{Error, ServerInitStatus}; -use grin_servers::ServerStats; use grin_util::logger::LogEntry; pub struct UI { From b829ba9a8d4624d0705b3b8e90d4fe1373f64103 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 19 May 2026 23:14:41 +0300 Subject: [PATCH 04/11] fix: do not empty server value --- src/bin/tui/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index 8278890fab..21d922d2ec 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -262,7 +262,7 @@ impl Controller { if Utc::now().timestamp() > next_stat_update { next_stat_update = Utc::now().timestamp() + stat_update_interval; - if let Some(server) = self.server.take() { + if let Some(server) = self.server { if let Ok(stats) = server.get_server_stats() { self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap(); } From 6c394f0d940b51da07f973392a27da6a720318cb Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 19 May 2026 23:56:03 +0300 Subject: [PATCH 05/11] fix: server ref --- src/bin/tui/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index 21d922d2ec..87146a5c41 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -262,7 +262,7 @@ impl Controller { if Utc::now().timestamp() > next_stat_update { next_stat_update = Utc::now().timestamp() + stat_update_interval; - if let Some(server) = self.server { + if let Some(server) = &self.server { if let Ok(stats) = server.get_server_stats() { self.ui.ui_tx.send(UIMessage::UpdateStatus(stats)).unwrap(); } From d2d5ae9a06122dc636bec64563f9ed0b58b8bdad Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 20 May 2026 00:02:39 +0300 Subject: [PATCH 06/11] tui: do not quit on q when another dialog is showing (progress or error) --- src/bin/tui/ui.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index 87146a5c41..dda7ac1d2b 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -123,6 +123,11 @@ impl UI { // Configure a callback (shutdown, for the first test) let controller_tx_clone = grin_ui.controller_tx.clone(); grin_ui.cursive.add_global_callback('q', move |c| { + let v = c.pop_layer(); + if v.is_some() { + c.add_layer(v.unwrap()); + return; + } let content = StyledString::styled("Shutting down...", Color::Light(BaseColor::Yellow)); c.add_layer(CircularFocus::new(Dialog::around(TextView::new(content))).wrap_tab()); controller_tx_clone From 3ee1b6f026c4e08f3b7fea9ee8a86429abbee0e0 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 25 May 2026 21:58:27 +0300 Subject: [PATCH 07/11] fix: server stop on tui shutdown --- src/bin/tui/ui.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index dda7ac1d2b..e8e48d1cbf 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -247,6 +247,9 @@ impl Controller { ControllerMessage::Shutdown => { warn!("Shutdown in progress, please wait"); self.ui.stop(); + if let Some(s) = self.server { + s.stop(); + } break; } } From 2d92fe545f5e77ebc7fb4162e32e64a4d9b73835 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 25 May 2026 22:11:24 +0300 Subject: [PATCH 08/11] fix: stop server if tui was stopped after start --- src/bin/cmd/server.rs | 15 ++++++++++++++- src/bin/tui/ui.rs | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/bin/cmd/server.rs b/src/bin/cmd/server.rs index 40e90b14c8..68a8d523a8 100644 --- a/src/bin/cmd/server.rs +++ b/src/bin/cmd/server.rs @@ -55,14 +55,27 @@ fn start_server_tui( let mut controller = ui::Controller::new(logs_rx, serv_rx).unwrap_or_else(|e| { panic!("Error loading UI controller: {}", e); }); + let tui_running = Arc::new(AtomicBool::new(true)); let serv_tx_clone = serv_tx.clone(); + let tui_running_clone = tui_running.clone(); thread::spawn(move || { match Server::start(config, None, Some(serv_tx_clone.clone()), api_chan) { - Ok(s) => serv_tx_clone.send(ServerInitStatus::FinishedLoading(s)), + Ok(s) => { + if !tui_running_clone.load(Ordering::Relaxed) { + s.stop(); + return serv_tx_clone.send(ServerInitStatus::ErrorLoading( + grin_servers::common::types::Error::General( + "TUI was already stopped".to_string(), + ), + )); + } + serv_tx_clone.send(ServerInitStatus::FinishedLoading(s)) + } Err(e) => serv_tx_clone.send(ServerInitStatus::ErrorLoading(e)), } }); controller.run(); + tui_running.store(false, Ordering::Relaxed); } else { warn!("Starting GRIN w/o UI..."); match Server::start(config, None, None, api_chan) { diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index e8e48d1cbf..c40a8ca1c7 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -247,7 +247,7 @@ impl Controller { ControllerMessage::Shutdown => { warn!("Shutdown in progress, please wait"); self.ui.stop(); - if let Some(s) = self.server { + if let Some(s) = self.server.take() { s.stop(); } break; From d48203303eb1f66e8200c49e7389af2c42e63468 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 25 May 2026 22:21:23 +0300 Subject: [PATCH 09/11] server: panic on error at non-tui mode like before with unwrap --- src/bin/cmd/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/cmd/server.rs b/src/bin/cmd/server.rs index 68a8d523a8..34b7643220 100644 --- a/src/bin/cmd/server.rs +++ b/src/bin/cmd/server.rs @@ -93,7 +93,7 @@ fn start_server_tui( s.stop(); } Err(e) => { - error!("Error starting GRIN: {:?}", e); + panic!("Error starting GRIN: {:?}", e); } } } From abcffab8683dc854a9223cc7909b6305614df417 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 25 May 2026 22:41:33 +0300 Subject: [PATCH 10/11] fix: pop dialog --- src/bin/tui/ui.rs | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/bin/tui/ui.rs b/src/bin/tui/ui.rs index c40a8ca1c7..fc8e242e95 100644 --- a/src/bin/tui/ui.rs +++ b/src/bin/tui/ui.rs @@ -15,6 +15,12 @@ //! Basic TUI to better output the overall system status and status //! of various subsystems +use super::constants::MAIN_MENU; +use crate::built_info; +use crate::servers::Server; +use crate::tui::constants::{ROOT_STACK, VIEW_BASIC_STATUS, VIEW_MINING, VIEW_PEER_SYNC}; +use crate::tui::types::{TUIStatusListener, UIMessage}; +use crate::tui::{logs, menu, mining, peers, status, version}; use chrono::prelude::Utc; use cursive::direction::Orientation; use cursive::theme::BaseColor::{Black, Blue, Cyan, White}; @@ -29,18 +35,12 @@ use cursive::views::{ CircularFocus, Dialog, LinearLayout, Panel, SelectView, StackView, TextView, ViewRef, }; use cursive::{CursiveRunnable, CursiveRunner}; -use std::sync::mpsc; -use std::{thread, time}; - -use super::constants::MAIN_MENU; -use crate::built_info; -use crate::servers::Server; -use crate::tui::constants::{ROOT_STACK, VIEW_BASIC_STATUS, VIEW_MINING, VIEW_PEER_SYNC}; -use crate::tui::types::{TUIStatusListener, UIMessage}; -use crate::tui::{logs, menu, mining, peers, status, version}; use grin_core::global; use grin_servers::common::types::{Error, ServerInitStatus}; use grin_util::logger::LogEntry; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{mpsc, Arc}; +use std::{thread, time}; pub struct UI { cursive: CursiveRunner, @@ -48,6 +48,7 @@ pub struct UI { ui_tx: mpsc::Sender, controller_tx: mpsc::Sender, logs_rx: Option>, + show_dialog: Arc, } fn modify_theme(theme: &mut Theme) { @@ -76,6 +77,7 @@ impl UI { ui_rx, controller_tx, logs_rx, + show_dialog: Arc::new(AtomicBool::new(false)), }; // Create UI objects, etc @@ -103,7 +105,7 @@ impl UI { built_info::PKG_VERSION, global::get_chain_type() ), - Color::Dark(BaseColor::Green), + Dark(BaseColor::Green), )); let main_layer = LinearLayout::new(Orientation::Vertical) @@ -118,22 +120,21 @@ impl UI { let mut theme = grin_ui.cursive.current_theme().clone(); modify_theme(&mut theme); grin_ui.cursive.set_theme(theme); + grin_ui.cursive.add_fullscreen_layer(main_layer); // Configure a callback (shutdown, for the first test) let controller_tx_clone = grin_ui.controller_tx.clone(); + let show_dialog_clone = grin_ui.show_dialog.clone(); grin_ui.cursive.add_global_callback('q', move |c| { - let v = c.pop_layer(); - if v.is_some() { - c.add_layer(v.unwrap()); + if show_dialog_clone.load(Ordering::Relaxed) { return; } let content = StyledString::styled("Shutting down...", Color::Light(BaseColor::Yellow)); c.add_layer(CircularFocus::new(Dialog::around(TextView::new(content))).wrap_tab()); - controller_tx_clone - .send(ControllerMessage::Shutdown) - .unwrap(); + let _ = controller_tx_clone.send(ControllerMessage::Shutdown); }); + grin_ui.cursive.set_fps(3); grin_ui } @@ -214,6 +215,7 @@ impl Controller { self.ui .cursive .add_layer(CircularFocus::new(Dialog::around(TextView::new(content))).wrap_tab()); + self.ui.show_dialog.store(true, Ordering::Relaxed); } /// Server initialization error. @@ -225,6 +227,7 @@ impl Controller { })) .wrap_tab(), ); + self.ui.show_dialog.store(true, Ordering::Relaxed); } /// Server UI after initialization. @@ -262,6 +265,7 @@ impl Controller { ServerInitStatus::StartAPI => self.init_status("Starting API...", true), ServerInitStatus::FinishedLoading(s) => { self.ui.cursive.pop_layer(); + self.ui.show_dialog.store(false, Ordering::Relaxed); self.server = Some(s) } ServerInitStatus::ErrorLoading(e) => self.init_error(e), From 8e3bb205ece3a18221d795450cdc632547c69968 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 25 May 2026 22:45:46 +0300 Subject: [PATCH 11/11] fix: do not return result on tx after server start --- src/bin/cmd/server.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/bin/cmd/server.rs b/src/bin/cmd/server.rs index 34b7643220..21e18a80d4 100644 --- a/src/bin/cmd/server.rs +++ b/src/bin/cmd/server.rs @@ -63,15 +63,13 @@ fn start_server_tui( Ok(s) => { if !tui_running_clone.load(Ordering::Relaxed) { s.stop(); - return serv_tx_clone.send(ServerInitStatus::ErrorLoading( - grin_servers::common::types::Error::General( - "TUI was already stopped".to_string(), - ), - )); + return; } - serv_tx_clone.send(ServerInitStatus::FinishedLoading(s)) + let _ = serv_tx_clone.send(ServerInitStatus::FinishedLoading(s)); + } + Err(e) => { + let _ = serv_tx_clone.send(ServerInitStatus::ErrorLoading(e)); } - Err(e) => serv_tx_clone.send(ServerInitStatus::ErrorLoading(e)), } }); controller.run();