Mountain/RunTime/
ApplicationRunTime.rs

1// File: Mountain/Source/RunTime/ApplicationRunTime.rs
2// Role: Defines the concrete, `Echo`-based `ApplicationRunTime`.
3// Responsibilities:
4//   - The core execution engine bridging `ActionEffect`s with the `Echo`
5//     scheduler.
6//   - Provides the `Run` method to execute any effect, supplying the required
7//     capability.
8//   - Orchestrates the graceful shutdown of application services.
9
10//! # ApplicationRunTime
11//!
12//! Defines the concrete, `Echo`-based `ApplicationRunTime` for the Mountain
13//! application. This is the core execution engine that bridges the declarative
14//! `ActionEffect` system with the high-performance `Echo` task scheduler.
15
16use std::sync::Arc;
17
18use Common::{
19	Effect::{ActionEffect::ActionEffect, ApplicationRunTime::ApplicationRunTime as ApplicationRunTimeTrait},
20	Environment::{Environment::Environment, HasEnvironment::HasEnvironment, Requires::Requires},
21	Error::CommonError::CommonError,
22	IPC::IPCProvider::IPCProvider,
23	Terminal::TerminalProvider::TerminalProvider as TerminalProviderTrait,
24};
25use Echo::Scheduler::Scheduler::Scheduler;
26use async_trait::async_trait;
27use log::{error, info};
28use tokio::sync::oneshot;
29
30use crate::Environment::MountainEnvironment::MountainEnvironment;
31
32/// A `RunTime` that uses a high-performance, work-stealing scheduler (`Echo`)
33/// to execute all `ActionEffect`s.
34#[derive(Clone)]
35pub struct ApplicationRunTime {
36	/// A shared handle to the application's central scheduler.
37	pub Scheduler:Arc<Scheduler>,
38
39	/// A shared handle to the application's `Environment`, providing all
40	/// necessary capabilities.
41	pub Environment:Arc<MountainEnvironment>,
42}
43
44impl ApplicationRunTime {
45	/// Creates a new `ApplicationRunTime` that is powered by an `Echo`
46	/// scheduler.
47	pub fn Create(Scheduler:Arc<Scheduler>, Environment:Arc<MountainEnvironment>) -> Self {
48		info!("[ApplicationRunTime] New Echo-based instance created.");
49
50		Self { Scheduler, Environment }
51	}
52
53	/// Orchestrates the graceful shutdown of all services.
54	pub async fn Shutdown(&self) {
55		info!("[ApplicationRunTime] Initiating graceful shutdown of services...");
56
57		// 1. Shutdown Cocoon
58		let IPCProvider:Arc<dyn IPCProvider> = self.Environment.Require();
59
60		if let Err(Error) = IPCProvider
61			.SendNotificationToSideCar("cocoon-main".to_string(), "$shutdown".to_string(), serde_json::Value::Null)
62			.await
63		{
64			error!("[ApplicationRunTime] Failed to send shutdown signal to Cocoon: {}", Error);
65		}
66
67		// Give Cocoon a moment to process the shutdown before we proceed.
68		tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
69
70		// 2. Dispose of all active terminals
71		let TerminalProvider:Arc<dyn TerminalProviderTrait> = self.Environment.Require();
72
73		let TerminalIds:Vec<u64> = {
74			let TerminalsGuard = self.Environment.ApplicationState.ActiveTerminals.lock().unwrap();
75
76			TerminalsGuard.keys().cloned().collect()
77		};
78
79		for id in TerminalIds {
80			if let Err(Error) = TerminalProvider.DisposeTerminal(id).await {
81				error!("[ApplicationRunTime] Failed to dispose terminal {}: {}", id, Error);
82			}
83		}
84
85		// TODO: Add final flush for storage services if they become debounced.
86
87		info!("[ApplicationRunTime] Service shutdown tasks complete.");
88	}
89}
90
91// Implement the marker trait to satisfy the bounds on ApplicationRunTimeTrait
92impl HasEnvironment for ApplicationRunTime {
93	type EnvironmentType = MountainEnvironment;
94
95	fn GetEnvironment(&self) -> Arc<Self::EnvironmentType> { self.Environment.clone() }
96}
97
98// The ApplicationRunTime is not an environment itself, but it needs this marker
99// to satisfy some complex generic bounds in the effect system.
100impl Environment for ApplicationRunTime {}
101
102#[async_trait]
103impl ApplicationRunTimeTrait for ApplicationRunTime {
104	/// The core integration logic between `Common::ActionEffect` and
105	/// `Echo::Scheduler`.
106	async fn Run<TCapabilityProvider, TError, TOutput>(
107		&self,
108
109		Effect:ActionEffect<Arc<TCapabilityProvider>, TError, TOutput>,
110	) -> Result<TOutput, TError>
111	where
112		TCapabilityProvider: ?Sized + Send + Sync + 'static,
113		Self::EnvironmentType: Requires<TCapabilityProvider>,
114		TError: From<CommonError> + Send + Sync + 'static,
115		TOutput: Send + Sync + 'static, {
116		let (ResultSender, ResultReceiver) = oneshot::channel::<Result<TOutput, TError>>();
117
118		let CapabilityProvider:Arc<TCapabilityProvider> = self.Environment.Require();
119
120		let Task = async move {
121			let Result = Effect.Apply(CapabilityProvider).await;
122
123			if ResultSender.send(Result).is_err() {
124				error!("[ApplicationRunTime] Failed to send effect result; receiver was dropped.");
125			}
126		};
127
128		self.Scheduler.Submit(Task, Echo::Task::Priority::Priority::Normal);
129
130		match ResultReceiver.await {
131			Ok(Result) => Result,
132
133			Err(RecvError) => {
134				let Message = format!("Effect execution canceled; oneshot channel closed. Error: {}", RecvError);
135
136				error!("{}", Message);
137
138				Err(CommonError::IPCError { Description:Message }.into())
139			},
140		}
141	}
142}