Mountain/RunTime/
ApplicationRunTime.rs1use 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#[derive(Clone)]
35pub struct ApplicationRunTime {
36 pub Scheduler:Arc<Scheduler>,
38
39 pub Environment:Arc<MountainEnvironment>,
42}
43
44impl ApplicationRunTime {
45 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 pub async fn Shutdown(&self) {
55 info!("[ApplicationRunTime] Initiating graceful shutdown of services...");
56
57 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 tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
69
70 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 info!("[ApplicationRunTime] Service shutdown tasks complete.");
88 }
89}
90
91impl HasEnvironment for ApplicationRunTime {
93 type EnvironmentType = MountainEnvironment;
94
95 fn GetEnvironment(&self) -> Arc<Self::EnvironmentType> { self.Environment.clone() }
96}
97
98impl Environment for ApplicationRunTime {}
101
102#[async_trait]
103impl ApplicationRunTimeTrait for ApplicationRunTime {
104 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}