app_forge_kit_service/
observer.rs1use crate::Error;
2use crate::signal;
3use app_forge_kit_telemetry_tracing::debug;
4use async_trait::async_trait;
5use std::cell::RefCell;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicUsize, Ordering};
8use tokio::task::JoinSet;
9use tokio::time;
10
11#[async_trait]
12pub trait Observable: Send + Sync {
13 fn init(&self) -> Result<(), Error> {
14 Ok(())
15 }
16 async fn serve(&self) -> Result<(), Error> {
17 Ok(())
18 }
19 async fn signal(&self, _signal: &signal::Signal) -> Result<(), Error> {
20 Ok(())
21 }
22}
23
24#[derive(Default, PartialEq, Eq)]
25enum ObservableState {
26 #[default]
27 Registered,
28 Initialized,
29 Started,
30}
31
32pub struct Resource {
33 observable: Arc<Box<dyn Observable>>,
34 state: RefCell<ObservableState>,
35}
36
37impl Resource {
38 pub fn new(observable: Box<dyn Observable>) -> Self {
39 Resource {
40 observable: Arc::new(observable),
41 state: RefCell::new(ObservableState::default()),
42 }
43 }
44}
45
46pub struct Observer {
47 resources: RefCell<Vec<Resource>>,
48 signals: RefCell<Vec<signal::Signal>>,
49}
50
51const OBSERVER_RESOURCES_CAPACITY: usize = 8;
52const OBSERVER_SIGNALS_CAPACITY: usize = 4;
53
54const SIGNAL_LOOP_SLEEP_DURATION_MICROS: u64 = 10;
55
56#[allow(clippy::new_without_default)]
57impl Observer {
58 pub fn new() -> Self {
59 let instance = Self {
60 resources: RefCell::new(Vec::with_capacity(OBSERVER_RESOURCES_CAPACITY)),
61 signals: RefCell::new(Vec::with_capacity(OBSERVER_SIGNALS_CAPACITY)),
62 };
63
64 instance
65 .signals
66 .borrow_mut()
67 .extend([signal::Signal::SIGINT, signal::Signal::SIGTERM]);
68
69 instance
70 }
71
72 pub fn register(&self, resource: Box<dyn Observable>) -> Result<(), Error> {
73 self.resources.borrow_mut().push(Resource::new(resource));
74
75 Ok(())
76 }
77
78 fn init(&self) -> Result<(), Error> {
79 debug!("initializing");
80
81 self.resources
82 .borrow()
83 .iter()
84 .try_for_each(|resource| -> Result<(), Error> {
85 resource.observable.init()?;
86 resource.state.replace(ObservableState::Initialized);
87
88 Ok(())
89 })
90 }
91
92 fn register_signals_listeners(
93 signals: &[signal::Signal],
94 signal_catcher: &Arc<AtomicUsize>,
95 ) -> Result<(), Error> {
96 for signal in signals {
97 signal_hook::flag::register_usize(
98 signal.into(),
99 signal_catcher.clone(),
100 signal.into(),
101 )?;
102 }
103
104 Ok(())
105 }
106
107 async fn signal_catcher(
108 signals: &[signal::Signal],
109 observables: &[Arc<Box<dyn Observable>>],
110 ) -> Result<(), Error> {
111 let signal_catcher = Arc::new(AtomicUsize::new(signal::Signal::UNKNOWN.into()));
112
113 Self::register_signals_listeners(signals, &signal_catcher)?;
114
115 let mut sleep_duration = time::Duration::from_micros(0);
116
117 loop {
118 if !sleep_duration.is_zero() {
119 time::sleep(sleep_duration).await;
120 }
121
122 let signal_value: signal::Signal = signal_catcher.load(Ordering::Acquire).into();
123
124 if signal_value.is_unknown() {
125 if sleep_duration.is_zero() {
126 sleep_duration = time::Duration::from_micros(SIGNAL_LOOP_SLEEP_DURATION_MICROS);
127 }
128
129 continue;
130 }
131
132 debug!("catching signal: {}", signal_value);
133
134 for observable in observables.iter() {
135 observable.signal(&signal_value).await?;
136 }
137
138 if signal_value.is_terminate() {
139 debug!("terminating");
140
141 break;
142 }
143 }
144
145 Ok(())
146 }
147
148 fn spawn_signal_catcher(&self, join_set: &mut JoinSet<Result<(), Error>>) -> Result<(), Error> {
149 let signals: Arc<Vec<signal::Signal>> =
150 Arc::new(self.signals.borrow().iter().cloned().collect());
151
152 let observables: Arc<Vec<Arc<Box<dyn Observable>>>> = Arc::new(
153 self.resources
154 .borrow()
155 .iter()
156 .map(|resource| resource.observable.clone())
157 .collect(),
158 );
159
160 join_set.spawn(async move { Self::signal_catcher(&signals, &observables).await });
161
162 Ok(())
163 }
164
165 async fn join_handle(join_set: &mut JoinSet<Result<(), Error>>) -> Result<(), Error> {
166 let mut result: Result<(), Error> = Ok(());
167
168 while let Some(join_handle) = join_set.join_next().await {
169 let mut need_shutdown = false;
170
171 match join_handle {
172 Ok(join_handle_result) => {
173 if let Err(err) = join_handle_result {
174 result = Err(err);
175 need_shutdown = true;
176 }
177 }
178 Err(err) => {
179 if err.is_panic() {
180 result = Err(err.into());
181 }
182 need_shutdown = true;
183 }
184 }
185
186 if need_shutdown {
187 join_set.join_next().await;
188 break;
189 }
190 }
191
192 result
193 }
194
195 pub async fn run(&self) -> Result<(), Error> {
196 self.init()?;
197
198 let mut join_set: JoinSet<Result<(), Error>> = JoinSet::new();
199
200 debug!("starting");
201 self.spawn_signal_catcher(&mut join_set)?;
202
203 for resource in self.resources.borrow().iter() {
204 let observable = Arc::clone(&resource.observable);
205
206 join_set.spawn(async move { observable.serve().await });
207
208 resource.state.replace(ObservableState::Started);
209 }
210
211 Self::join_handle(&mut join_set).await
212 }
213}