1use std::future::Future;
18use std::num::NonZeroUsize;
19use std::pin::Pin;
20use std::sync::{Arc, Mutex, Weak};
21use super::{Attachment, Context as PluginContext, Error, Plugin};
25use crate::operations::{Contract, Operation, ServingError, WireTable, lifecycle};
26
27pub type Table<P> = Arc<Mutex<WireTable<<P as Plugin>::Context>>>;
34type Transition<P> = Pin<Box<dyn Future<Output = (P, Result<(), Error<P>>, bool)> + Send>>;
35
36pub struct Owner<P: Plugin> {
39 slot: Arc<Mutex<Slot<P>>>,
40 table: Weak<Mutex<WireTable<P::Context>>>,
41 contracts: Arc<[Contract]>,
42 observers: Arc<[crate::inventory::observers::Declaration]>,
43 retirement: Mutex<Retirement<P>>,
44}
45
46struct Initializing<P: Plugin> {
47 owner: Arc<Owner<P>>,
48 state: Option<State<P>>,
49}
50
51struct ShuttingDown<P: Plugin> {
52 owner: Arc<Owner<P>>,
53 state: Option<State<P>>,
54}
55
56struct Retirement<P: Plugin> {
57 state: Option<State<P>>,
58 active: bool,
59 finished: bool,
60}
61
62struct RetirementLoan<P: Plugin> {
63 owner: Arc<Owner<P>>,
64 state: Option<State<P>>,
65 finished: bool,
66}
67
68struct Slot<P: Plugin> {
69 plugin: Option<P>,
70 phase: Phase,
71 attachment: Option<Arc<dyn Attachment<P::Context>>>,
72 generation_context: Option<[u8; 32]>,
73}
74
75struct State<P: Plugin> {
76 slot: Arc<Mutex<Slot<P>>>,
77 table: Weak<Mutex<WireTable<P::Context>>>,
78 plugin: Option<P>,
79 work: Option<Transition<P>>,
80 result: Option<Result<(), Error<P>>>,
81 action: lifecycle::Action,
82 delivered: bool,
83 failure_reported: bool,
84 shutdown_attempted: bool,
85 context: Option<P::Context>,
86 attachment: Option<Arc<dyn Attachment<P::Context>>>,
87 generation_context: Option<[u8; 32]>,
88}
89
90#[derive(Clone, Copy, PartialEq, Eq)]
93enum Phase {
94 Empty,
95 Initializing,
96 Initialized,
97 Stopping,
98 Closed,
99}
100
101impl<P: Plugin> Owner<P> {
106 pub fn new(contracts: Vec<Contract>, capacity: NonZeroUsize) -> Result<(Arc<Self>, Table<P>), Error<P>> {
107 Self::new_with_observers(contracts, capacity, Vec::new())
108 }
109
110 pub fn new_with_observers(
111 contracts: Vec<Contract>, capacity: NonZeroUsize, observers: Vec<crate::inventory::observers::Declaration>,
112 ) -> Result<(Arc<Self>, Table<P>), Error<P>> {
113 let definitions: Vec<_> = contracts.iter().map(|contract| contract.operation().clone()).collect();
114 crate::inventory::operations::validate(&definitions).map_err(|error| Error::Inventory(Arc::new(error)))?;
115 let table = Arc::new(Mutex::new(WireTable::new(capacity)));
116 let owner = Arc::new(Self {
117 slot: Arc::new(Mutex::new(Slot { plugin: None, phase: Phase::Empty, attachment: None, generation_context: None })),
118 table: Arc::downgrade(&table),
119 contracts: contracts.into(),
120 observers: observers.into(),
121 retirement: Mutex::new(Retirement { state: None, active: false, finished: false }),
122 });
123 {
124 let mut bindings = table.lock().map_err(|_| Error::Poisoned)?;
125 lifecycle::bind::<P, Initializing<P>, ShuttingDown<P>, _>(&mut bindings, Arc::clone(&owner))
126 .map_err(|error| Error::Serving(Arc::new(error)))?;
127 }
128 Ok((owner, table))
129 }
130
131 pub async fn quiesce(&self) -> Result<(), Error<P>> {
132 let attachment = self.slot.lock().map_err(|_| Error::Poisoned)?.attachment.clone();
133 if let Some(attachment) = attachment {
134 guarded(async { attachment.quiesce().await.map_err(|error| Error::Serving(Arc::new(error))) }).await?;
135 }
136 Ok(())
137 }
138
139 pub async fn retire(self: &Arc<Self>) -> Result<(), Error<P>> {
140 let table = self.table.upgrade().ok_or(Error::Phase)?;
141 {
142 let table = table.lock().map_err(|_| Error::Poisoned)?;
143 if !table.is_settled() {
144 return Err(Error::Phase);
145 }
146 }
147 let mut loan = {
148 let mut retirement = self.retirement.lock().map_err(|_| Error::Poisoned)?;
149 if retirement.finished {
150 return Ok(());
151 }
152 if retirement.active {
153 return Err(Error::Phase);
154 }
155 let state = match retirement.state.take() {
156 Some(state) => state,
157 None => self.prepare_shutdown()?,
158 };
159 retirement.active = true;
160 RetirementLoan { owner: Arc::clone(self), state: Some(state), finished: false }
161 };
162 loan.state.as_mut().ok_or(Error::Phase)?.finalize().await?;
163 loan.finished = true;
164 Ok(())
165 }
166
167 fn state(&self, plugin: Option<P>, action: lifecycle::Action) -> State<P> {
168 State {
169 slot: Arc::clone(&self.slot),
170 table: self.table.clone(),
171 plugin,
172 work: None,
173 result: None,
174 action,
175 delivered: false,
176 failure_reported: false,
177 shutdown_attempted: false,
178 context: None,
179 attachment: None,
180 generation_context: None,
181 }
182 }
183}
184
185impl<P: Plugin> Drop for RetirementLoan<P> {
186 fn drop(&mut self) {
187 let mut retirement = self.owner.retirement.lock().unwrap_or_else(|error| error.into_inner());
188 retirement.active = false;
189 retirement.finished = self.finished;
190 if !self.finished {
191 retirement.state = self.state.take();
192 }
193 }
194}
195
196impl<P: Plugin> Owner<P> {
197 fn prepare_initialize(&self, request: &lifecycle::Initialize) -> Result<State<P>, Error<P>> {
198 let generation_context = if request.generation_context.is_empty() {
199 None
200 } else {
201 let token: [u8; 32] = request.generation_context.as_slice().try_into().map_err(|_| Error::GenerationContext)?;
202 if token == [0; 32] {
203 return Err(Error::GenerationContext);
204 }
205 Some(token)
206 };
207 let config = crate::decode_config(&request.config_json).map_err(|error| Error::Configuration(Arc::new(error)))?;
208 let plugin = P::prepare(config).map_err(|error| Error::Plugin(Arc::new(error)))?;
209 let mut slot = self.slot.lock().map_err(|_| Error::Poisoned)?;
210 if slot.phase != Phase::Empty {
211 return Err(Error::Phase);
212 }
213 slot.phase = Phase::Initializing;
214 let mut state = self.state(Some(plugin), lifecycle::Action::Initialize);
215 state.generation_context = generation_context;
216 Ok(state)
217 }
218
219 async fn initialize(&self, state: &mut State<P>, context: &P::Context) -> Result<(), Error<P>> {
220 if state.work.is_none() && state.result.is_none() {
221 let mut plugin = state.plugin.take().ok_or(Error::Phase)?;
222 let context = state.generation_context.map_or_else(|| context.clone(), |token| context.with_generation(token));
223 state.work = Some(Box::pin(async move {
224 let result = guarded(async { plugin.initialize(&context).await.map_err(|error| Error::Plugin(Arc::new(error))) }).await;
225 (plugin, result, false)
226 }));
227 }
228 state.finish().await?;
229 state.result.as_ref().ok_or(Error::Phase)?.clone()?;
230 if state.delivered {
231 return Ok(());
232 }
233 let inventory = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
234 let plugin = state.plugin.as_ref().ok_or(Error::Phase)?;
235 state.attachment = plugin.attachment();
236 if let Some(attachment) = &state.attachment {
237 let resources: Arc<dyn std::any::Any + Send + Sync> = plugin.resources();
238 if !Arc::ptr_eq(&resources, &attachment.resources()) {
239 return Err(Error::Serving(Arc::new(ServingError::Catalog)));
240 }
241 }
242 let actual = state.attachment.as_ref().map_or_else(Vec::new, |attachment| attachment.observers());
243 if actual.as_slice() != self.observers.as_ref() {
244 return Err(Error::Serving(Arc::new(ServingError::Catalog)));
245 }
246 Ok(())
247 }))
248 .map_err(|_| Error::Panicked)
249 .and_then(|result| result);
250 if let Err(error) = inventory {
251 state.result = Some(Err(error.clone()));
252 return Err(error);
253 }
254 let attachment = state.attachment.clone();
255 let table = self.table.upgrade().ok_or(Error::Phase)?;
256 let binding = {
257 let mut table = table.lock().map_err(|_| Error::Poisoned)?;
258 let plugin = state.plugin.as_ref().ok_or(Error::Phase)?;
259 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
260 plugin.bind_operations(&mut table).and_then(|()| table.validate_contracts(&self.contracts))
261 }))
262 .map_err(|_| Error::Panicked)
263 .and_then(|result| result.map_err(|error| Error::Serving(Arc::new(error))))
264 };
265 if let Err(error) = binding {
266 state.result = Some(Err(error.clone()));
267 return Err(error);
268 }
269 let mut slot = self.slot.lock().map_err(|_| Error::Poisoned)?;
270 slot.plugin = state.plugin.take();
271 slot.attachment = attachment;
272 slot.generation_context = state.generation_context;
273 slot.phase = Phase::Initialized;
274 state.delivered = true;
275 Ok(())
276 }
277
278 fn prepare_shutdown(&self) -> Result<State<P>, Error<P>> {
279 let mut slot = self.slot.lock().map_err(|_| Error::Poisoned)?;
280 if matches!(slot.phase, Phase::Empty | Phase::Closed) {
281 let mut state = self.state(None, lifecycle::Action::Shutdown);
282 state.result = Some(Ok(()));
283 return Ok(state);
284 }
285 if slot.phase != Phase::Initialized {
286 return Err(Error::Phase);
287 }
288 let plugin = slot.plugin.take().ok_or(Error::Phase)?;
289 slot.phase = Phase::Stopping;
290 let mut state = self.state(Some(plugin), lifecycle::Action::Shutdown);
291 state.attachment = slot.attachment.take();
292 state.generation_context = slot.generation_context;
293 Ok(state)
294 }
295}
296
297impl<P: Plugin> Operation for Initializing<P> {
298 type Declaration = lifecycle::Initialize;
299 type Resources = Owner<P>;
300 type Error = Error<P>;
301 type Endpoints = ();
302 type Context<'a> = &'a P::Context;
303
304 async fn initialize<'ctx>(owner: Arc<Owner<P>>, _: Self::Context<'ctx>) -> Result<Self, Error<P>> {
305 Ok(Self { owner, state: None })
306 }
307
308 async fn run<'ctx>(
309 &mut self, context: Self::Context<'ctx>, request: &mut lifecycle::Initialize, _endpoints: &mut Self::Endpoints,
310 ) -> Result<crate::operations::Response<()>, Error<P>> {
311 let result: ::core::result::Result<(), Self::Error> = async move {
312 if self.state.is_none() {
313 self.state = Some(self.owner.prepare_initialize(request)?);
314 }
315 self.owner.initialize(self.state.as_mut().ok_or(Error::Phase)?, context).await
316 }
317 .await;
318 result.map(::core::convert::Into::into)
319 }
320
321 async fn cleanup(&mut self) -> Result<(), Error<P>> {
322 match self.state.as_mut() {
323 Some(state) => state.finalize().await,
324 None => Ok(()),
325 }
326 }
327}
328
329impl<P: Plugin> Operation for ShuttingDown<P> {
330 type Declaration = lifecycle::Shutdown;
331 type Resources = Owner<P>;
332 type Error = Error<P>;
333 type Endpoints = ();
334 type Context<'a> = &'a P::Context;
335
336 async fn initialize<'ctx>(owner: Arc<Owner<P>>, _: Self::Context<'ctx>) -> Result<Self, Error<P>> {
337 Ok(Self { owner, state: None })
338 }
339
340 async fn run<'ctx>(
341 &mut self, context: Self::Context<'ctx>, _request: &mut lifecycle::Shutdown, _endpoints: &mut Self::Endpoints,
342 ) -> Result<crate::operations::Response<()>, Error<P>> {
343 let result: ::core::result::Result<(), Self::Error> = async move {
344 if self.state.is_none() {
345 self.state = Some(self.owner.prepare_shutdown()?);
346 }
347 let state = self.state.as_mut().ok_or(Error::Phase)?;
348 if state.work.is_none() && state.result.is_none() {
349 state.context = Some(state.generation_context.map_or_else(|| context.clone(), |token| context.with_generation(token)));
350 state.start_shutdown()?;
351 }
352 state.finish().await?;
353 state.result.as_ref().ok_or(Error::Phase)?.clone()
354 }
355 .await;
356 result.map(::core::convert::Into::into)
357 }
358
359 async fn cleanup(&mut self) -> Result<(), Error<P>> {
360 match self.state.as_mut() {
361 Some(state) => state.finalize().await,
362 None => Ok(()),
363 }
364 }
365}
366
367impl<P: Plugin> State<P> {
368 async fn finish(&mut self) -> Result<(), Error<P>> {
369 if let Some(work) = &mut self.work {
370 let (plugin, result, shutdown_attempted) = work.await;
371 self.shutdown_attempted = shutdown_attempted;
372 self.plugin = Some(plugin);
373 self.result = Some(result);
374 self.work = None;
375 }
376 Ok(())
377 }
378
379 fn start_shutdown(&mut self) -> Result<(), Error<P>> {
380 let attachment = self.attachment.clone().or_else(|| self.plugin.as_ref().and_then(Plugin::attachment));
381 let mut plugin = self.plugin.take().ok_or(Error::Phase)?;
382 let table = self.table.clone();
383 let context = self.context.clone();
384 let mut shutdown_attempted = self.shutdown_attempted;
385 self.result = None;
386 self.work = Some(Box::pin(async move {
387 let result = guarded(async {
388 if let Some(attachment) = attachment {
389 attachment.quiesce().await.map_err(|error| Error::Serving(Arc::new(error)))?;
390 }
391 let table = table.upgrade().ok_or(Error::Phase)?;
392 let settlement = {
393 let mut table = table.lock().map_err(|_| Error::Poisoned)?;
394 table.close_ordinary()
395 };
396 settlement.await.map_err(|error| Error::Serving(Arc::new(error)))?;
397 let retired = {
398 let mut table = table.lock().map_err(|_| Error::Poisoned)?;
399 table.release_ordinary().map_err(|error| Error::Serving(Arc::new(error)))?
400 };
401 drop(retired);
402 if !shutdown_attempted && let Some(context) = &context {
403 shutdown_attempted = true;
405 plugin.stop(context).await.map_err(|error| Error::Plugin(Arc::new(error)))?;
406 }
407 plugin.cleanup().await.map_err(|error| Error::Plugin(Arc::new(error)))
408 })
409 .await;
410 (plugin, result, shutdown_attempted)
411 }));
412 Ok(())
413 }
414
415 async fn finalize(&mut self) -> Result<(), Error<P>> {
416 if self.delivered {
417 return Ok(());
418 }
419 if self.action == lifecycle::Action::Initialize {
420 self.finish().await?;
421 self.action = lifecycle::Action::Shutdown;
422 self.result = None;
423 }
424 if let Some(Err(error)) = &self.result {
425 if !self.failure_reported {
426 self.failure_reported = true;
427 return Err(error.clone());
428 }
429 self.failure_reported = false;
430 self.start_shutdown()?;
431 } else if self.work.is_none() && self.result.is_none() {
432 self.start_shutdown()?;
433 }
434 self.finish().await?;
435 if let Some(Err(error)) = &self.result {
436 self.failure_reported = true;
437 return Err(error.clone());
438 }
439 self.plugin.take();
440 self.slot.lock().map_err(|_| Error::Poisoned)?.phase = Phase::Closed;
441 Ok(())
442 }
443}
444
445async fn guarded<P: Plugin>(future: impl Future<Output = Result<(), Error<P>>>) -> Result<(), Error<P>> {
451 let mut future = std::pin::pin!(future);
452 std::future::poll_fn(|context| match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| future.as_mut().poll(context))) {
453 Ok(result) => result,
454 Err(_) => std::task::Poll::Ready(Err(Error::Panicked)),
455 })
456 .await
457}
458
459