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};
56use log::{debug, error, info, warn};
57
58use crate::RunTime::ApplicationRunTime::ApplicationRunTime;
59
60impl ApplicationRunTime {
61	/// Orchestrates the graceful shutdown of all services.
62	pub async fn Shutdown(&self) {
63		info!("[ApplicationRunTime] Initiating graceful shutdown of services...");
64
65		let shutdown_result = self.ShutdownWithRecovery().await;
66
67		match shutdown_result {
68			Ok(()) => info!("[ApplicationRunTime] Service shutdown tasks completed successfully."),
69			Err(error) => error!("[ApplicationRunTime] Service shutdown completed with errors: {}", error),
70		}
71	}
72
73	/// Enhanced shutdown with comprehensive error handling and recovery.
74	pub async fn ShutdownWithRecovery(&self) -> Result<(), CommonError> {
75		info!("[ApplicationRunTime] Initiating robust shutdown with recovery...");
76
77		let mut shutdown_errors:Vec<String> = Vec::new();
78
79		// 1. Shutdown Cocoon with retry mechanism
80		match self.ShutdownCocoonWithRetry().await {
81			Ok(()) => debug!("[ApplicationRunTime] Cocoon shutdown successful"),
82			Err(error) => {
83				shutdown_errors.push(format!("Cocoon shutdown failed: {}", error));
84				warn!("[ApplicationRunTime] Cocoon shutdown failed, continuing with other services...");
85			},
86		}
87
88		// 2. Dispose of all active terminals with error handling
89		match self.DisposeTerminalsSafely().await {
90			Ok(()) => debug!("[ApplicationRunTime] Terminal disposal successful"),
91			Err(error) => {
92				shutdown_errors.push(format!("Terminal disposal failed: {}", error));
93				warn!("[ApplicationRunTime] Terminal disposal failed, continuing...");
94			},
95		}
96
97		// 3. Save application state
98		match self.SaveApplicationState().await {
99			Ok(()) => debug!("[ApplicationRunTime] Application state saved"),
100			Err(error) => {
101				shutdown_errors.push(format!("State save failed: {}", error));
102				warn!("[ApplicationRunTime] Failed to save application state, continuing...");
103			},
104		}
105
106		// 4. Flush any pending operations
107		self.FlushPendingOperations().await;
108
109		if !shutdown_errors.is_empty() {
110			Err(CommonError::Unknown {
111				Description:format!(
112					"Shutdown completed with {} errors: {:?}",
113					shutdown_errors.len(),
114					shutdown_errors
115				),
116			})
117		} else {
118			Ok(())
119		}
120	}
121
122	/// Shutdown Cocoon with retry mechanism.
123	pub async fn ShutdownCocoonWithRetry(&self) -> Result<(), CommonError> {
124		let IPCProvider:Arc<dyn IPCProvider> = self.Environment.Require();
125
126		let mut attempts = 0;
127		let max_attempts = 3;
128
129		while attempts < max_attempts {
130			match IPCProvider
131				.SendNotificationToSideCar("cocoon-main".to_string(), "$shutdown".to_string(), serde_json::Value::Null)
132				.await
133			{
134				Ok(()) => {
135					// Give Cocoon a moment to process the shutdown
136					tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
137					return Ok(());
138				},
139				Err(error) => {
140					attempts += 1;
141					if attempts == max_attempts {
142						return Err(error);
143					}
144
145					warn!(
146						"[ApplicationRunTime] Cocoon shutdown attempt {} failed: {}. Retrying...",
147						attempts, error
148					);
149
150					tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
151				},
152			}
153		}
154
155		Err(CommonError::Unknown { Description:"Failed to shutdown Cocoon after maximum retries".to_string() })
156	}
157
158	/// Safely dispose of all active terminals.
159	pub async fn DisposeTerminalsSafely(&self) -> Result<(), CommonError> {
160		let TerminalProvider:Arc<dyn TerminalProviderTrait> = self.Environment.Require();
161
162		let TerminalIds:Vec<u64> = {
163			let TerminalsGuard = self
164				.Environment
165				.ApplicationState
166				.Feature
167				.Terminals
168				.ActiveTerminals
169				.lock()
170				.map_err(|e| CommonError::StateLockPoisoned { Context:e.to_string() })?;
171
172			TerminalsGuard.keys().cloned().collect()
173		};
174
175		let mut disposal_errors:Vec<String> = Vec::new();
176
177		for id in TerminalIds {
178			match TerminalProvider.DisposeTerminal(id).await {
179				Ok(()) => debug!("[ApplicationRunTime] Terminal {} disposed successfully", id),
180				Err(error) => {
181					disposal_errors.push(format!("Terminal {}: {}", id, error));
182					warn!("[ApplicationRunTime] Failed to dispose terminal {}: {}", id, error);
183				},
184			}
185		}
186
187		if !disposal_errors.is_empty() {
188			Err(CommonError::Unknown {
189				Description:format!(
190					"Terminal disposal completed with {} errors: {:?}",
191					disposal_errors.len(),
192					disposal_errors
193				),
194			})
195		} else {
196			Ok(())
197		}
198	}
199
200	/// Save application state before shutdown.
201	pub async fn SaveApplicationState(&self) -> Result<(), CommonError> {
202		debug!("[ApplicationRunTime] Saving application state...");
203
204		// Save global memento
205		let global_memento_guard = self
206			.Environment
207			.ApplicationState
208			.Configuration
209			.MementoGlobalStorage
210			.lock()
211			.map_err(|e| CommonError::StateLockPoisoned { Context:e.to_string() })?;
212
213		let global_memento_path = &self.Environment.ApplicationState.GlobalMementoPath;
214
215		if let Some(parent) = global_memento_path.parent() {
216			if !parent.exists() {
217				std::fs::create_dir_all(parent)
218					.map_err(|e| CommonError::FileSystemIO { Path:parent.to_path_buf(), Description:e.to_string() })?;
219			}
220		}
221
222		let memento_json = serde_json::to_string_pretty(&*global_memento_guard)
223			.map_err(|e| CommonError::SerializationError { Description:e.to_string() })?;
224
225		std::fs::write(global_memento_path, memento_json)
226			.map_err(|e| CommonError::FileSystemIO { Path:global_memento_path.clone(), Description:e.to_string() })
227	}
228
229	/// Flush any pending operations.
230	pub async fn FlushPendingOperations(&self) {
231		debug!("[ApplicationRunTime] Flushing pending operations...");
232
233		// Flush pending UI requests
234		let mut pending_requests_guard = self
235			.Environment
236			.ApplicationState
237			.UI
238			.PendingUserInterfaceRequests
239			.lock()
240			.unwrap_or_else(|e| {
241				error!("[ApplicationRunTime] Failed to lock pending UI requests: {}", e);
242				e.into_inner()
243			});
244
245		for (_request_id, sender) in pending_requests_guard.drain() {
246			let _ = sender.send(Err(CommonError::Unknown {
247				Description:"Application shutting down".to_string(),
248			}));
249		}
250
251		debug!("[ApplicationRunTime] Pending operations flushed");
252	}
253}