Skip to main content

Mountain/RunTime/Shutdown/
Shutdown.rs

1//! # Shutdown (RunTime::Shutdown)
2//!
3//! ## RESPONSIBILITIES
4//!
5//! Service shutdown and lifecycle management for graceful application
6//! termination. Coordinates cleanup of all application services with error
7//! recovery.
8//!
9//! ## ARCHITECTURAL ROLE
10//!
11//! The lifecycle manager in Mountain's architecture that ensures clean
12//! application termination and state persistence.
13//!
14//! ## KEY COMPONENTS
15//!
16//! - **Shutdown**: Main shutdown orchestration
17//! - **ShutdownWithRecovery**: Enhanced shutdown with error recovery
18//! - **ShutdownCocoonWithRetry**: Cocoon sidecar shutdown with retry
19//! - **DisposeTerminalsSafely**: Terminal cleanup
20//! - **SaveApplicationState**: State persistence
21//! - **FlushPendingOperations**: Cleanup pending operations
22//!
23//! ## ERROR HANDLING
24//!
25//! All shutdown operations use error recovery to continue cleanup even when
26//! individual services fail. Errors are collected and reported without
27//! crashing. Multi-attempt retry for critical operations like Cocoon shutdown.
28//!
29//! ## LOGGING
30//!
31//! Uses log crate with appropriate severity levels:
32//! - `info`: Shutdown initiation and completion
33//! - `debug`: Detailed operation steps
34//! - `warn`: Recoverable errors during shutdown
35//! - `error`: Failed operations (but continues shutdown)
36//!
37//! ## PERFORMANCE CONSIDERATIONS
38//!
39//! - Shutdown operations are optimized to complete quickly
40//! - Sequential cleanup to avoid race conditions
41//! - Minimal blocking during state persistence
42//! - Uses timeout recovery to prevent hanging
43//!
44//! ## TODO
45//!
46//! None
47
48use std::sync::Arc;
49
50use CommonLibrary::{
51	Environment::Requires::Requires,
52	Error::CommonError::CommonError,
53	IPC::IPCProvider::IPCProvider,
54	Terminal::TerminalProvider::TerminalProvider as TerminalProviderTrait,
55};
56
57use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
58
59impl ApplicationRunTime {
60	/// Orchestrates the graceful shutdown of all services.
61	pub async fn Shutdown(&self) {
62		dev_log!("lifecycle", "[ApplicationRunTime] Initiating graceful shutdown of services...");
63
64		let shutdown_result = self.ShutdownWithRecovery().await;
65
66		match shutdown_result {
67			Ok(()) => {
68				dev_log!(
69					"lifecycle",
70					"[ApplicationRunTime] Service shutdown tasks completed successfully."
71				)
72			},
73			Err(error) => {
74				dev_log!(
75					"lifecycle",
76					"error: [ApplicationRunTime] Service shutdown completed with errors: {}",
77					error
78				)
79			},
80		}
81	}
82
83	/// Enhanced shutdown with comprehensive error handling and recovery.
84	pub async fn ShutdownWithRecovery(&self) -> Result<(), CommonError> {
85		dev_log!("lifecycle", "[ApplicationRunTime] Initiating robust shutdown with recovery...");
86
87		let mut shutdown_errors:Vec<String> = Vec::new();
88
89		// 1. Shutdown Cocoon with retry mechanism
90		match self.ShutdownCocoonWithRetry().await {
91			Ok(()) => dev_log!("lifecycle", "[ApplicationRunTime] Cocoon shutdown successful"),
92			Err(error) => {
93				shutdown_errors.push(format!("Cocoon shutdown failed: {}", error));
94				dev_log!(
95					"lifecycle",
96					"warn: [ApplicationRunTime] Cocoon shutdown failed, continuing with other services..."
97				);
98			},
99		}
100
101		// 2. Dispose of all active terminals with error handling
102		match self.DisposeTerminalsSafely().await {
103			Ok(()) => dev_log!("lifecycle", "[ApplicationRunTime] Terminal disposal successful"),
104			Err(error) => {
105				shutdown_errors.push(format!("Terminal disposal failed: {}", error));
106				dev_log!(
107					"lifecycle",
108					"warn: [ApplicationRunTime] Terminal disposal failed, continuing..."
109				);
110			},
111		}
112
113		// 3. Save application state
114		match self.SaveApplicationState().await {
115			Ok(()) => dev_log!("lifecycle", "[ApplicationRunTime] Application state saved"),
116			Err(error) => {
117				shutdown_errors.push(format!("State save failed: {}", error));
118				dev_log!(
119					"lifecycle",
120					"warn: [ApplicationRunTime] Failed to save application state, continuing..."
121				);
122			},
123		}
124
125		// 4. Flush any pending operations
126		self.FlushPendingOperations().await;
127
128		if !shutdown_errors.is_empty() {
129			Err(CommonError::Unknown {
130				Description:format!(
131					"Shutdown completed with {} errors: {:?}",
132					shutdown_errors.len(),
133					shutdown_errors
134				),
135			})
136		} else {
137			Ok(())
138		}
139	}
140
141	/// Shutdown Cocoon with retry mechanism.
142	pub async fn ShutdownCocoonWithRetry(&self) -> Result<(), CommonError> {
143		let IPCProvider:Arc<dyn IPCProvider> = self.Environment.Require();
144
145		let mut attempts = 0;
146		let max_attempts = 3;
147
148		while attempts < max_attempts {
149			match IPCProvider
150				.SendNotificationToSideCar("cocoon-main".to_string(), "$shutdown".to_string(), serde_json::Value::Null)
151				.await
152			{
153				Ok(()) => {
154					// Give Cocoon a moment to process the shutdown
155					tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
156					return Ok(());
157				},
158				Err(error) => {
159					attempts += 1;
160					if attempts == max_attempts {
161						return Err(error);
162					}
163
164					dev_log!(
165						"lifecycle",
166						"warn: [ApplicationRunTime] Cocoon shutdown attempt {} failed: {}. Retrying...",
167						attempts,
168						error
169					);
170
171					tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
172				},
173			}
174		}
175
176		Err(CommonError::Unknown { Description:"Failed to shutdown Cocoon after maximum retries".to_string() })
177	}
178
179	/// Safely dispose of all active terminals.
180	pub async fn DisposeTerminalsSafely(&self) -> Result<(), CommonError> {
181		let TerminalProvider:Arc<dyn TerminalProviderTrait> = self.Environment.Require();
182
183		let TerminalIds:Vec<u64> = {
184			let TerminalsGuard = self
185				.Environment
186				.ApplicationState
187				.Feature
188				.Terminals
189				.ActiveTerminals
190				.lock()
191				.map_err(|e| CommonError::StateLockPoisoned { Context:e.to_string() })?;
192
193			TerminalsGuard.keys().cloned().collect()
194		};
195
196		let mut disposal_errors:Vec<String> = Vec::new();
197
198		for id in TerminalIds {
199			match TerminalProvider.DisposeTerminal(id).await {
200				Ok(()) => dev_log!("lifecycle", "[ApplicationRunTime] Terminal {} disposed successfully", id),
201				Err(error) => {
202					disposal_errors.push(format!("Terminal {}: {}", id, error));
203					dev_log!(
204						"lifecycle",
205						"warn: [ApplicationRunTime] Failed to dispose terminal {}: {}",
206						id,
207						error
208					);
209				},
210			}
211		}
212
213		if !disposal_errors.is_empty() {
214			Err(CommonError::Unknown {
215				Description:format!(
216					"Terminal disposal completed with {} errors: {:?}",
217					disposal_errors.len(),
218					disposal_errors
219				),
220			})
221		} else {
222			Ok(())
223		}
224	}
225
226	/// Save application state before shutdown.
227	pub async fn SaveApplicationState(&self) -> Result<(), CommonError> {
228		dev_log!("lifecycle", "[ApplicationRunTime] Saving application state...");
229
230		// Save global memento
231		let global_memento_guard = self
232			.Environment
233			.ApplicationState
234			.Configuration
235			.MementoGlobalStorage
236			.lock()
237			.map_err(|e| CommonError::StateLockPoisoned { Context:e.to_string() })?;
238
239		let global_memento_path = self
240			.Environment
241			.ApplicationState
242			.GlobalMementoPath
243			.lock()
244			.map_err(|e| CommonError::StateLockPoisoned { Context:e.to_string() })?
245			.clone();
246
247		if let Some(parent) = global_memento_path.parent() {
248			if !parent.exists() {
249				std::fs::create_dir_all(parent)
250					.map_err(|e| CommonError::FileSystemIO { Path:parent.to_path_buf(), Description:e.to_string() })?;
251			}
252		}
253
254		let memento_json = serde_json::to_string_pretty(&*global_memento_guard)
255			.map_err(|e| CommonError::SerializationError { Description:e.to_string() })?;
256
257		std::fs::write(&global_memento_path, memento_json)
258			.map_err(|e| CommonError::FileSystemIO { Path:global_memento_path.clone(), Description:e.to_string() })
259	}
260
261	/// Flush any pending operations.
262	pub async fn FlushPendingOperations(&self) {
263		dev_log!("lifecycle", "[ApplicationRunTime] Flushing pending operations...");
264
265		// Flush pending UI requests
266		let mut pending_requests_guard = self
267			.Environment
268			.ApplicationState
269			.UI
270			.PendingUserInterfaceRequest
271			.lock()
272			.unwrap_or_else(|e| {
273				dev_log!(
274					"lifecycle",
275					"error: [ApplicationRunTime] Failed to lock pending UI requests: {}",
276					e
277				);
278				e.into_inner()
279			});
280
281		for (_request_id, sender) in pending_requests_guard.drain() {
282			let _ = sender.send(Err(CommonError::Unknown {
283				Description:"Application shutting down".to_string(),
284			}));
285		}
286
287		dev_log!("lifecycle", "[ApplicationRunTime] Pending operations flushed");
288	}
289}