Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/RunTime/Shutdown/
ShutdownCocoonWithRetry.rs

1//! Send `$shutdown` over gRPC to Cocoon (3 attempts), then SIGKILL the child
2//! regardless of gRPC outcome. The hard-kill (Atom I6) is critical: a gRPC
3//! failure (transport error, broken pipe) used to leave the child orphaned,
4//! holding port 50052, and the next Mountain launch hit EADDRINUSE with the
5//! extension host stuck in degraded mode.
6
7use std::sync::Arc;
8
9use CommonLibrary::{Environment::Requires::Requires, Error::CommonError::CommonError, IPC::IPCProvider::IPCProvider};
10
11use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
12
13impl ApplicationRunTime {
14	pub async fn ShutdownCocoonWithRetry(&self) -> Result<(), CommonError> {
15		let IPCProvider:Arc<dyn IPCProvider> = self.Environment.Require();
16
17		let MaximumAttempts = 3;
18
19		let mut Attempts = 0;
20
21		let mut GracefulOk = false;
22
23		let mut LastError:Option<CommonError> = None;
24
25		while Attempts < MaximumAttempts {
26			match IPCProvider
27				.SendNotificationToSideCar("cocoon-main".to_string(), "$shutdown".to_string(), serde_json::Value::Null)
28				.await
29			{
30				Ok(()) => {
31					tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
32
33					GracefulOk = true;
34
35					break;
36				},
37
38				Err(Error) => {
39					Attempts += 1;
40
41					LastError = Some(Error.clone());
42
43					if Attempts < MaximumAttempts {
44						dev_log!(
45							"lifecycle",
46							"warn: [ApplicationRunTime] Cocoon shutdown attempt {} failed: {}. Retrying...",
47							Attempts,
48							Error
49						);
50
51						tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
52					}
53				},
54			}
55		}
56
57		// Mark the Vine gRPC client shutting down BEFORE the SIGKILL so any
58		// background tokio task firing `SendNotification` after this flips
59		// short-circuits to `Ok(())` instead of attempting a TCP connect to
60		// the dead socket and logging a false-positive `Connection refused`.
61		crate::Vine::Client::MarkShutdown::Fn();
62
63		// Atom I6: always reap the child after the graceful attempt. No-op if
64		// the child already exited from $shutdown.
65		crate::ProcessManagement::CocoonManagement::HardKillCocoon().await;
66
67		if GracefulOk {
68			Ok(())
69		} else {
70			Err(LastError.unwrap_or_else(|| {
71				CommonError::Unknown { Description:"Failed to shutdown Cocoon after maximum retries".to_string() }
72			}))
73		}
74	}
75}