1use std::any::{Any, TypeId};
25use std::collections::{BTreeMap, BTreeSet};
26use std::future::Future;
27use std::pin::Pin;
28use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
29use std::sync::{Arc, Mutex, Weak};
30use tokio::sync::watch;
32use super::caller::Identity;
34use super::instances::Instances;
35use super::{OperationGeneration, OperationInstanceId, OperationStatus};
36use crate::observers::ObserverDefinition;
37use crate::services::{ServiceDefinition, ServiceStatus};
38use crate::supervision::{self, Task, instance::Instance, instance::Phase};
39use crate::{Error, Result};
40
41static NEXT_SCOPE: AtomicU64 = AtomicU64::new(1);
44
45const ROOT: ScopeId = ScopeId(0);
46
47pub(crate) type Outcome = std::result::Result<(), Arc<Error>>;
52pub(crate) type Completion = Pin<Box<dyn Future<Output = Outcome> + Send>>;
53pub(crate) type Resources = Arc<dyn Any + Send + Sync>;
54
55pub struct Activation;
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
62pub struct ScopeId(u64);
63
64#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
66pub struct Key {
67 pub definition: String,
68 pub scope: ScopeId,
69}
70
71#[derive(Clone, Debug)]
72pub struct Snapshot {
73 pub instance: OperationInstanceId,
74 pub kind: Kind,
75 pub leases: usize,
76 pub lifecycle: OperationStatus,
77 pub health: Option<ServiceStatus>,
79 pub failure: Option<Arc<Error>>,
80}
81
82#[derive(Clone)]
83pub(crate) struct Registry {
84 shared: Arc<Shared>,
85}
86
87pub(crate) struct Shared {
88 identity: Arc<()>,
89 root_resources: Arc<()>,
90 index: Weak<Instances>,
91 root: Arc<Scope>,
92 state: Mutex<State>,
93 statuses: watch::Sender<BTreeMap<Key, Snapshot>>,
94 assembly: Mutex<Vec<Lease>>,
95}
96
97pub(crate) struct Scope {
98 owner: Weak<()>,
99 id: ScopeId,
100 address: usize,
101 resources: Resources,
102 closed: AtomicBool,
103 retirement: Mutex<Option<Outcome>>,
104 detached: Mutex<Vec<Lease>>,
105}
106
107pub(crate) struct Registration {
108 pub(crate) definition: Definition,
109 runner: Arc<dyn Runner>,
110 generation: OperationGeneration,
111 closing: AtomicBool,
112 scope: Option<ScopeId>,
113 internal: bool,
114 published: Arc<AtomicBool>,
115
116 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
117 plugin: Option<crate::plugin::PluginScope>,
118}
119
120pub(crate) struct Retirement {
123 outcome: Outcome,
124 _scope: Arc<Scope>,
125 _running: Vec<Running>,
126 _definitions: Vec<Arc<Registration>>,
127}
128
129#[must_use = "commit the resident catalog or drop it to release its reservations"]
132pub struct Staged {
133 owner: Weak<Shared>,
134 scope: Arc<Scope>,
135 entries: Vec<Arc<Registration>>,
136 published: Arc<AtomicBool>,
137}
138
139pub(crate) struct Start {
141 pub(crate) resources: Resources,
142 pub(crate) lifecycle: Arc<Instance>,
143 pub(crate) stopping: watch::Sender<bool>,
144 pub(crate) report: Report,
145 pub(crate) operations: super::OperationManager,
146}
147
148#[derive(Clone)]
149pub(crate) struct Running {
150 task: Arc<Task<Outcome>>,
151 settlement: Arc<dyn Settlement>,
152 report: Report,
153}
154
155#[derive(Clone)]
157pub(crate) struct Report {
158 owner: Weak<Shared>,
159 key: Key,
160 id: OperationInstanceId,
161 kind: Kind,
162}
163
164#[must_use = "a resident lease holds activation demand"]
165pub(crate) struct Lease {
166 running: Running,
167 demand: u64,
168 generation: OperationGeneration,
169 released: bool,
170 joins_release: bool,
171}
172
173struct State {
174 closed: bool,
175 retired_failure: Option<Arc<Error>>,
176 next: u64,
177 reserved: BTreeSet<String>,
178 definitions: BTreeMap<String, Arc<Registration>>,
179 scopes: BTreeMap<ScopeId, Arc<Scope>>,
180 addresses: BTreeMap<usize, ScopeId>,
181 instances: BTreeMap<Key, Entry>,
182}
183
184struct Entry {
185 snapshot: Snapshot,
186 internal: bool,
187 leases: BTreeSet<u64>,
188 running: Running,
189 stopping: bool,
190}
191
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
195pub enum Kind {
196 Observer,
197 Service,
198}
199
200#[derive(Clone, Debug)]
201pub enum Definition {
202 Observer(ObserverDefinition),
203 Service(ServiceDefinition),
204}
205
206enum Acquisition {
207 Lease(Lease),
208 Wait(OperationInstanceId, Running),
209}
210
211pub(crate) trait Runner: Send + Sync + 'static {
216 fn resources(&self) -> TypeId;
217
218 fn reactivates(&self) -> bool {
221 true
222 }
223
224 fn spawn(self: Arc<Self>, start: Start) -> (Task<Outcome>, Arc<dyn Settlement>);
225}
226
227pub(crate) trait Settlement: Send + Sync + 'static {
229 fn lifecycle(&self) -> &Arc<Instance>;
230
231 fn record(&self, _outcome: &Outcome) {}
233
234 fn retry(self: Arc<Self>, report: Report) -> Option<Completion>;
236}
237
238harmos_domain::postcard_product!(Activation {});
241
242impl crate::schema::Described for Activation {
243 const NAME: &'static str = "harmos::activation";
244 const FIELDS: &'static [crate::schema::declaration::Field] = &[];
245 const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
246 { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
247 >(Self::NAME, Self::FIELDS);
248}
249
250impl crate::MemorySize for Activation {
251 fn memory_size(&self) -> usize {
252 0
253 }
254}
255
256impl super::OperationDeclaration for Activation {
258 type Response = ();
259 type Input = super::NoStream;
260 type Output = super::NoStream;
261 const VERSION: u32 = 1;
262 const ID: &'static str = "";
263 const POLICY: super::OperationPolicy = super::OperationPolicy {
264 timeout: harmos_domain::operations::UNBOUNDED,
265 attempt_timeout: harmos_domain::operations::UNBOUNDED,
266 retries: super::RetryPolicy::Never,
267 restart: super::RestartPolicy::Never,
268 };
269}
270
271impl Key {
272 pub fn root(definition: impl Into<String>) -> Self {
274 Self { definition: definition.into(), scope: ROOT }
275 }
276}
277
278impl Kind {
279 pub fn domain(self) -> &'static str {
280 match self {
281 Self::Observer => "observer",
282 Self::Service => "service",
283 }
284 }
285
286 fn scope(self) -> &'static str {
287 match self {
288 Self::Observer => "observer scope",
289 Self::Service => "service scope",
290 }
291 }
292
293 fn definition(self) -> &'static str {
294 match self {
295 Self::Observer => "observer definition",
296 Self::Service => "service definition",
297 }
298 }
299
300 fn catalog(self) -> &'static str {
301 match self {
302 Self::Observer => "observer catalog",
303 Self::Service => "service catalog",
304 }
305 }
306}
307
308impl Definition {
309 pub fn id(&self) -> &str {
310 match self {
311 Self::Observer(definition) => &definition.id,
312 Self::Service(definition) => &definition.id,
313 }
314 }
315
316 pub fn kind(&self) -> Kind {
317 match self {
318 Self::Observer(_) => Kind::Observer,
319 Self::Service(_) => Kind::Service,
320 }
321 }
322}
323
324impl Registration {
325 fn is_published(&self) -> bool {
326 if !self.published.load(Ordering::Acquire) {
327 return false;
328 }
329 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
330 if self.plugin.as_ref().is_some_and(|scope| !scope.is_ready()) {
331 return false;
332 }
333 true
334 }
335}
336
337impl Registry {
338 pub(crate) fn new(index: &Arc<Instances>, reserved: BTreeSet<String>, identity: Arc<()>) -> Self {
339 let root_resources = Arc::new(());
340 let resources: Resources = Arc::clone(&root_resources) as Resources;
341 let root = Arc::new(Scope {
342 owner: Arc::downgrade(&identity),
343 id: ROOT,
344 address: address(&resources),
345 resources,
346 closed: AtomicBool::new(false),
347 retirement: Mutex::new(None),
348 detached: Mutex::new(Vec::new()),
349 });
350 let state = State {
351 closed: false,
352 retired_failure: None,
353 next: 0,
354 reserved,
355 definitions: BTreeMap::new(),
356 scopes: BTreeMap::from([(ROOT, Arc::clone(&root))]),
357 addresses: BTreeMap::from([(root.address, ROOT)]),
358 instances: BTreeMap::new(),
359 };
360 Self {
361 shared: Arc::new(Shared {
362 identity,
363 root_resources,
364 index: Arc::downgrade(index),
365 root,
366 state: Mutex::new(state),
367 statuses: watch::channel(BTreeMap::new()).0,
368 assembly: Mutex::new(Vec::new()),
369 }),
370 }
371 }
372
373 pub(crate) fn identity(&self) -> Arc<()> {
374 Arc::clone(&self.shared.identity)
375 }
376
377 pub(crate) fn root_resources(&self) -> Arc<()> {
378 Arc::clone(&self.shared.root_resources)
379 }
380
381 pub(crate) fn root(&self) -> Arc<Scope> {
382 Arc::clone(&self.shared.root)
383 }
384
385 fn state(&self) -> std::sync::MutexGuard<'_, State> {
386 self.shared.state.lock().expect("resident registry is healthy")
387 }
388
389 pub(crate) fn register(
391 &self, definition: Definition, runner: Arc<dyn Runner>, scope: Option<&Arc<Scope>>, internal: bool, reserved: bool,
392 ) -> Result<()> {
393 validate(&definition)?;
394 let registration = Arc::new(Registration {
395 definition,
396 runner,
397 generation: OperationGeneration::fresh()?,
398 closing: AtomicBool::new(false),
399 scope: scope.map(|scope| scope.id),
400 internal,
401 published: Arc::new(AtomicBool::new(true)),
402 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
403 plugin: None,
404 });
405 let mut state = self.state();
406 state.admit()?;
407 let id = registration.definition.id().to_owned();
408 if state.definitions.contains_key(&id) || (!reserved && state.reserved.contains(&id)) {
409 return Err(Error::AlreadyRegistered { domain: registration.definition.kind().domain(), name: id });
410 }
411 state.reserved.remove(&id);
412 state.definitions.insert(id, registration);
413 Ok(())
414 }
415
416 pub(crate) fn stage(
418 &self, scope: &Arc<Scope>, entries: Vec<(Definition, Arc<dyn Runner>)>,
419 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))] plugin: Option<crate::plugin::PluginScope>,
420 ) -> Result<Staged> {
421 self.check_scope(scope)?;
422 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
423 if plugin.as_ref().is_some_and(|scope| scope.is_closed()) {
424 return Err(Error::Closing { domain: "plugin scope" });
425 }
426 let published = Arc::new(AtomicBool::new(false));
427 let mut staged = Vec::new();
428 let mut ids = BTreeSet::new();
429 for (definition, runner) in entries {
430 validate(&definition)?;
431 if !ids.insert(definition.id().to_owned()) {
432 return Err(Error::AlreadyRegistered { domain: definition.kind().domain(), name: definition.id().to_owned() });
433 }
434 staged.push(Arc::new(Registration {
435 definition,
436 runner,
437 generation: OperationGeneration::fresh()?,
438 closing: AtomicBool::new(false),
439 scope: Some(scope.id),
440 internal: false,
441 published: Arc::clone(&published),
442 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
443 plugin: plugin.clone(),
444 }));
445 }
446
447 let mut state = self.state();
448 state.admit()?;
449 if scope.closed.load(Ordering::Acquire) {
450 return Err(Error::Closing { domain: "resident scope" });
451 }
452 if let Some(entry) = staged.iter().find(|entry| state.definitions.contains_key(entry.definition.id())) {
453 return Err(Error::AlreadyRegistered { domain: entry.definition.kind().domain(), name: entry.definition.id().to_owned() });
454 }
455 for entry in &staged {
456 state.definitions.insert(entry.definition.id().to_owned(), Arc::clone(entry));
457 }
458 Ok(Staged { owner: Arc::downgrade(&self.shared), scope: Arc::clone(scope), entries: staged, published })
459 }
460
461 pub(crate) fn definition(&self, id: &str) -> Option<Definition> {
462 self.state().definitions.get(id).filter(|item| item.is_published()).map(|item| item.definition.clone())
463 }
464
465 pub(crate) fn definitions(&self) -> Vec<Definition> {
466 self.state().definitions.values().filter(|item| item.is_published()).map(|item| item.definition.clone()).collect()
467 }
468
469 pub(crate) fn scope<R: Send + Sync + 'static>(&self, resources: Arc<R>) -> Result<Arc<Scope>> {
471 let resources: Resources = resources;
472 let address = address(&resources);
473 let mut state = self.state();
474 state.admit()?;
475 if let Some(scope) = state.addresses.get(&address).and_then(|id| state.scopes.get(id)) {
476 return Ok(Arc::clone(scope));
477 }
478 let id = ScopeId(
479 NEXT_SCOPE
480 .try_update(Ordering::Relaxed, Ordering::Relaxed, |value| value.checked_add(1))
481 .map_err(|_| Error::InvalidInput { field: "resident scope", reason: "identity space exhausted" })?,
482 );
483 let scope = Arc::new(Scope {
484 owner: Arc::downgrade(&self.shared.identity),
485 id,
486 address,
487 resources,
488 closed: AtomicBool::new(false),
489 retirement: Mutex::new(None),
490 detached: Mutex::new(Vec::new()),
491 });
492 state.scopes.insert(id, Arc::clone(&scope));
493 state.addresses.insert(address, id);
494 Ok(scope)
495 }
496
497 pub(crate) fn scope_of<R: Send + Sync + 'static>(&self, resources: &Arc<R>) -> Option<Arc<Scope>> {
498 let address = Arc::as_ptr(resources) as *const () as usize;
499 let state = self.state();
500 state.addresses.get(&address).and_then(|id| state.scopes.get(id)).cloned()
501 }
502
503 pub(super) async fn activate(
506 &self, operations: &super::OperationManager, id: &str, scope: &Arc<Scope>, resources: TypeId, holder: Identity, internal: bool,
507 ) -> Result<Lease> {
508 let mut joined = None;
509 loop {
510 match self.acquire(operations, id, scope, resources, joined, internal, &holder)? {
511 Acquisition::Lease(lease) => return Ok(lease),
512 Acquisition::Wait(instance, running) => {
513 let result = settle(vec![running.clone()]).await;
514 if !running.settlement.lifecycle().is_settled() {
515 return Err(result.err().unwrap_or(Error::NotReady { domain: "resident cleanup" }));
516 }
517 joined = Some(instance);
518 }
519 }
520 }
521 }
522
523 #[allow(clippy::too_many_arguments)]
524 fn acquire(
525 &self, operations: &super::OperationManager, id: &str, scope: &Arc<Scope>, resources: TypeId, joined: Option<OperationInstanceId>,
526 internal: bool, holder: &Identity,
527 ) -> Result<Acquisition> {
528 self.check_scope(scope)?;
529 let mut state = self.state();
530 let Some(registration) = state.definitions.get(id).cloned() else {
531 state.admit()?;
532 return Err(Error::NotRegistered { domain: "resident", name: id.to_owned() });
533 };
534 let kind = registration.definition.kind();
535 if state.closed {
536 return Err(Error::Closing { domain: kind.domain() });
537 }
538 if scope.closed.load(Ordering::Acquire) {
539 return Err(Error::Closing { domain: kind.scope() });
540 }
541 if registration.internal && !internal {
542 return Err(Error::InvalidInput { field: kind.definition(), reason: "infrastructure demand belongs to its runtime owner" });
543 }
544 if !registration.is_published() {
545 return Err(Error::NotReady { domain: kind.catalog() });
546 }
547 if registration.closing.load(Ordering::Acquire) {
548 return Err(Error::Closing { domain: kind.definition() });
549 }
550 if registration.scope.is_some_and(|owner| owner != scope.id) || registration.runner.resources() != resources {
551 return Err(Error::InvalidInput { field: kind.scope(), reason: "definition belongs to another resource scope or type" });
552 }
553 let key = Key { definition: id.to_owned(), scope: scope.id };
554 let demand = state.next()?;
555 if let Some(entry) = state.instances.get_mut(&key) {
556 let lifecycle = entry.running.settlement.lifecycle().status();
557 if !entry.stopping && lifecycle.outcome().is_none() && !lifecycle.phase().is_terminal() {
558 entry.leases.insert(demand);
559 entry.snapshot.leases = entry.leases.len();
560 let instance = entry.snapshot.instance;
561 let lease = Lease::new(entry.running.clone(), demand, registration.generation);
562 self.track(instance, entry.running.settlement.lifecycle(), holder);
563 self.shared.publish(&state);
564 return Ok(Acquisition::Lease(lease));
565 }
566 if joined != Some(entry.snapshot.instance) {
567 return Ok(Acquisition::Wait(entry.snapshot.instance, entry.running.clone()));
568 }
569 if !registration.runner.reactivates() {
570 return Err(Error::NotReady { domain: "retired assembly service" });
571 }
572 }
573
574 let instance = super::handler::fresh_instance()?;
576 let stopping = watch::channel(false).0;
577 let index = self.shared.index.clone();
578 let lifecycle = Arc::new(Instance::queued(stopping.clone()).resident().on_settled(move || {
579 if let Some(index) = index.upgrade() {
580 index.forget_resident(instance);
581 }
582 }));
583 let report = Report { owner: Arc::downgrade(&self.shared), key: key.clone(), id: instance, kind };
584 let (task, settlement) = Arc::clone(®istration.runner).spawn(Start {
585 resources: Arc::clone(&scope.resources),
586 lifecycle: Arc::clone(&lifecycle),
587 stopping,
588 report: report.clone(),
589 operations: operations.clone(),
590 });
591 let running = Running { task: Arc::new(task), settlement, report };
592 let entry = Entry {
593 snapshot: Snapshot {
594 instance,
595 kind: registration.definition.kind(),
596 leases: 1,
597 lifecycle: lifecycle.status(),
598 health: (registration.definition.kind() == Kind::Service).then_some(ServiceStatus::Starting),
599 failure: None,
600 },
601 internal: registration.internal,
602 leases: BTreeSet::from([demand]),
603 running: running.clone(),
604 stopping: false,
605 };
606 let lease = Lease::new(running, demand, registration.generation);
607 self.track(instance, &lifecycle, holder);
608 let retired = state.instances.insert(key, entry);
609 self.shared.publish(&state);
610 drop(state);
611 drop(retired);
612 Ok(Acquisition::Lease(lease))
613 }
614
615 fn track(&self, id: OperationInstanceId, lifecycle: &Arc<Instance>, holder: &Identity) {
616 if let Some(index) = self.shared.index.upgrade() {
617 index.track_resident(id, lifecycle, holder.clone());
618 }
619 }
620
621 pub(crate) fn start_once(
624 &self, operations: &super::OperationManager, definition: Definition, runner: Arc<dyn Runner>, internal: bool, reserved: bool,
625 ) -> Result<Lease> {
626 let id = definition.id().to_owned();
627 let resources = runner.resources();
628 self.register(definition, runner, Some(&self.root()), internal, reserved)?;
629 let root = self.root();
630 match self.acquire(operations, &id, &root, resources, None, internal, &Identity::Host)? {
631 Acquisition::Lease(lease) => Ok(lease),
632 Acquisition::Wait(..) => {
633 unreachable!("a newly registered resident definition has no instance")
634 }
635 }
636 }
637
638 pub(crate) fn hold(&self, lease: Lease) {
639 self.shared.assembly.lock().expect("resident assembly is healthy").push(lease);
640 }
641
642 pub(crate) fn status(&self, key: &Key) -> Option<Snapshot> {
643 self.shared.statuses.borrow().get(key).cloned()
644 }
645
646 pub(crate) fn statuses(&self) -> BTreeMap<Key, Snapshot> {
647 self.shared.statuses.borrow().clone()
648 }
649
650 pub(crate) fn subscribe(&self) -> watch::Receiver<BTreeMap<Key, Snapshot>> {
651 self.shared.statuses.subscribe()
652 }
653
654 pub(crate) async fn retire_scope(&self, scope: &Arc<Scope>) -> Result<Retirement> {
657 self.check_scope(scope)?;
658 if let Some(outcome) = scope.retirement.lock().expect("resident scope is healthy").clone() {
659 return Ok(Retirement { outcome, _scope: Arc::clone(scope), _running: Vec::new(), _definitions: Vec::new() });
660 }
661 let (running, definitions) = {
662 let mut state = self.state();
663 scope.closed.store(true, Ordering::Release);
664 let definitions: Vec<_> = state.definitions.values().filter(|entry| entry.scope == Some(scope.id)).cloned().collect();
665 for definition in &definitions {
666 definition.closing.store(true, Ordering::Release);
667 }
668 let running = state.stop(|key, _, _| key.scope == scope.id);
669 self.shared.publish(&state);
670 (running, definitions)
671 };
672 let outcome = settle(running.clone()).await.map_err(Arc::new);
673 let (original, detached) = {
674 let mut state = self.state();
675 if state.instances.iter().any(|(key, entry)| key.scope == scope.id && !entry.running.settlement.lifecycle().is_settled()) {
676 return Err(match &outcome {
677 Err(source) => Error::TaskFailed { domain: "resident cleanup", source: source.clone() },
678 Ok(()) => Error::NotReady { domain: "resident cleanup" },
679 });
680 }
681 if let Err(source) = &outcome
683 && running.iter().any(|running| running.report.kind == Kind::Observer)
684 {
685 state.retired_failure.get_or_insert_with(|| Arc::clone(source));
686 }
687 let original = state.scopes.remove(&scope.id).unwrap_or_else(|| Arc::clone(scope));
688 state.addresses.remove(&original.address);
689 state.instances.retain(|key, _| key.scope != scope.id);
690 for definition in &definitions {
691 if state.definitions.get(definition.definition.id()).is_some_and(|current| Arc::ptr_eq(current, definition)) {
692 state.definitions.remove(definition.definition.id());
693 }
694 }
695 self.shared.publish(&state);
696 let detached = std::mem::take(&mut *original.detached.lock().expect("resident scope is healthy"));
697 (original, detached)
698 };
699 drop(detached);
700 let outcome = scope.retirement.lock().expect("resident scope is healthy").get_or_insert(outcome).clone();
701 Ok(Retirement { outcome, _scope: original, _running: running, _definitions: definitions })
702 }
703
704 pub(crate) async fn unregister(&self, id: &str) -> Result<()> {
705 let (registration, running) = {
706 let mut state = self.state();
707 let registration =
708 state.definitions.get(id).cloned().ok_or_else(|| Error::NotRegistered { domain: "resident", name: id.to_owned() })?;
709 registration.closing.store(true, Ordering::Release);
710 let running = state.stop(|key, _, _| key.definition == id);
711 self.shared.publish(&state);
712 (registration, running)
713 };
714 let result = settle(running.clone()).await;
715 if running.iter().any(|running| !running.settlement.lifecycle().is_settled()) {
716 return result;
717 }
718 let mut state = self.state();
719 let mut retired = Vec::new();
720 if state.definitions.get(id).is_some_and(|current| Arc::ptr_eq(current, ®istration)) {
721 state.definitions.remove(id);
722 let keys: Vec<_> = state.instances.keys().filter(|key| key.definition == id).cloned().collect();
723 retired.extend(keys.into_iter().filter_map(|key| state.instances.remove(&key)));
724 self.shared.publish(&state);
725 }
726 drop(state);
727 drop((registration, retired));
728 result
729 }
730
731 pub(crate) fn cleanup_pending(&self, selected: impl Fn(Kind, bool) -> bool) -> bool {
732 self.state()
733 .instances
734 .values()
735 .any(|entry| selected(entry.snapshot.kind, entry.internal) && !entry.running.settlement.lifecycle().is_settled())
736 }
737
738 pub(crate) fn close_admission(&self) {
741 let scopes = {
742 let mut state = self.state();
743 state.closed = true;
744 for scope in state.scopes.values() {
745 scope.closed.store(true, Ordering::Release);
746 }
747 state.stop(|_, _, internal| !internal);
748 self.shared.publish(&state);
749 state.release_scopes()
750 };
751 drop(scopes);
753 }
754
755 pub(crate) async fn stop_kind(&self, kind: Kind) -> Result<()> {
758 let running = {
759 let mut state = self.state();
760 let running = state.stop(|_, selected, _| selected == kind);
761 self.shared.publish(&state);
762 running
763 };
764 settle(running).await
765 }
766
767 pub(crate) async fn shutdown_observers(&self) -> Result<()> {
768 self.close_admission();
769 let running = self.select(|kind, _| kind == Kind::Observer);
770 let result = settle(running).await;
771 let (definitions, scopes, retired_failure) = {
772 let mut state = self.state();
773 let ids: Vec<_> = state
774 .definitions
775 .iter()
776 .filter(|(id, registration)| {
777 registration.definition.kind() == Kind::Observer
778 && state
779 .instances
780 .iter()
781 .filter(|(key, _)| key.definition == **id)
782 .all(|(_, entry)| entry.running.settlement.lifecycle().is_settled())
783 })
784 .map(|(id, _)| id.clone())
785 .collect();
786 let definitions: Vec<_> = ids.into_iter().filter_map(|id| state.definitions.remove(&id)).collect();
787 (definitions, state.release_scopes(), state.retired_failure.clone())
788 };
789 drop((definitions, scopes));
792 result?;
793 match retired_failure {
794 Some(source) => Err(Error::TaskFailed { domain: "retired observer", source }),
795 None => Ok(()),
796 }
797 }
798
799 pub(crate) async fn shutdown_application(&self) -> Result<()> {
800 let running = {
801 let mut state = self.state();
802 state.closed = true;
803 let running = state.stop(|_, _, internal| !internal);
804 self.shared.publish(&state);
805 running
806 };
807 settle(running).await
808 }
809
810 pub(crate) async fn shutdown(&self) -> Result<()> {
811 let running = {
812 let mut state = self.state();
813 state.closed = true;
814 let running = state.stop(|_, _, _| true);
815 self.shared.publish(&state);
816 running
817 };
818 let retained = running.clone();
819 let result = settle(running).await;
820 if retained.iter().any(|running| !running.settlement.lifecycle().is_settled()) {
821 return result;
822 }
823 let (definitions, scopes) = {
824 let mut state = self.state();
825 for scope in state.scopes.values() {
826 scope.closed.store(true, Ordering::Release);
827 }
828 state.addresses.clear();
829 (std::mem::take(&mut state.definitions), std::mem::take(&mut state.scopes))
830 };
831 let assembly = std::mem::take(&mut *self.shared.assembly.lock().expect("resident assembly is healthy"));
832 let detached: Vec<_> =
833 scopes.values().flat_map(|scope| std::mem::take(&mut *scope.detached.lock().expect("resident scope is healthy"))).collect();
834 drop((definitions, scopes, assembly, detached));
835 result
836 }
837
838 fn select(&self, selected: impl Fn(Kind, bool) -> bool) -> Vec<Running> {
839 self.state()
840 .instances
841 .values()
842 .filter(|entry| selected(entry.snapshot.kind, entry.internal))
843 .map(|entry| entry.running.clone())
844 .collect()
845 }
846
847 fn check_scope(&self, scope: &Scope) -> Result<()> {
848 if !Weak::ptr_eq(&scope.owner, &Arc::downgrade(&self.shared.identity)) {
849 return Err(Error::InvalidInput { field: "resident scope", reason: "scope belongs to another manager" });
850 }
851 Ok(())
852 }
853}
854
855impl Shared {
856 fn publish(&self, state: &State) {
857 self.statuses.send_replace(
858 state
859 .instances
860 .iter()
861 .map(|(key, entry)| {
862 let mut snapshot = entry.snapshot.clone();
863 snapshot.lifecycle = entry.running.settlement.lifecycle().status();
864 (key.clone(), snapshot)
865 })
866 .collect(),
867 );
868 }
869
870 fn state(&self) -> std::sync::MutexGuard<'_, State> {
871 self.state.lock().expect("resident registry is healthy")
872 }
873
874 fn release(&self, key: &Key, id: OperationInstanceId, demand: u64) -> bool {
875 let mut state = self.state();
876 let Some(entry) = state.instances.get_mut(key).filter(|entry| entry.snapshot.instance == id) else {
877 return true;
878 };
879 entry.leases.remove(&demand);
880 entry.snapshot.leases = entry.leases.len();
881 let final_release = entry.leases.is_empty();
882 if final_release {
883 entry.stop();
884 }
885 self.publish(&state);
886 final_release
887 }
888
889 fn stop_instance(&self, key: &Key, id: OperationInstanceId) -> bool {
890 let mut state = self.state();
891 let Some(entry) = state.instances.get_mut(key).filter(|entry| entry.snapshot.instance == id) else {
892 return false;
893 };
894 entry.leases.clear();
895 entry.snapshot.leases = 0;
896 entry.stop();
897 self.publish(&state);
898 true
899 }
900}
901
902impl State {
903 fn admit(&self) -> Result<()> {
904 if self.closed { Err(Error::Closing { domain: "resident" }) } else { Ok(()) }
905 }
906
907 fn next(&mut self) -> Result<u64> {
908 self.next =
909 self.next.checked_add(1).ok_or(Error::InvalidInput { field: "resident identity", reason: "identity space exhausted" })?;
910 Ok(self.next)
911 }
912
913 fn stop(&mut self, selected: impl Fn(&Key, Kind, bool) -> bool) -> Vec<Running> {
914 self.instances
915 .iter_mut()
916 .filter(|(key, entry)| selected(key, entry.snapshot.kind, entry.internal))
917 .map(|(_, entry)| {
918 entry.leases.clear();
919 entry.snapshot.leases = 0;
920 entry.stop();
921 entry.running.clone()
922 })
923 .collect()
924 }
925
926 fn release_scopes(&mut self) -> Vec<Arc<Scope>> {
928 let ids: Vec<_> = self
929 .scopes
930 .keys()
931 .filter(|id| {
932 **id != ROOT
933 && self
934 .instances
935 .iter()
936 .filter(|(key, _)| key.scope == **id)
937 .all(|(_, entry)| entry.running.settlement.lifecycle().is_settled())
938 })
939 .copied()
940 .collect();
941 let scopes: Vec<_> = ids.into_iter().filter_map(|id| self.scopes.remove(&id)).collect();
942 for scope in &scopes {
943 self.addresses.remove(&scope.address);
944 }
945 scopes
946 }
947}
948
949impl Entry {
950 fn stop(&mut self) {
951 self.stopping = true;
952 if let Some(health) = &mut self.snapshot.health
953 && self.running.settlement.lifecycle().status().outcome().is_none()
954 {
955 *health = ServiceStatus::Stopping;
956 }
957 self.running.stop();
958 }
959}
960
961impl Running {
962 fn stop(&self) {
963 self.settlement.lifecycle().cancel();
964 }
965
966 async fn settle(&self) -> std::result::Result<Arc<Outcome>, Arc<tokio::task::JoinError>> {
967 if let Err(failure) = self.settlement.lifecycle().settle_dependents().await {
968 return Ok(Arc::new(Err(failure)));
969 }
970 let settlement = Arc::clone(&self.settlement);
971 let report = self.report.clone();
972 self.task.settle_retry(move || settlement.retry(report)).await
973 }
974
975 pub(crate) fn lifecycle(&self) -> &Arc<Instance> {
976 self.settlement.lifecycle()
977 }
978}
979
980impl Report {
981 pub(crate) fn id(&self) -> OperationInstanceId {
982 self.id
983 }
984
985 pub(crate) fn key(&self) -> &Key {
986 &self.key
987 }
988
989 fn with_entry(&self, update: impl FnOnce(&mut Entry) -> bool) {
990 let Some(owner) = self.owner.upgrade() else {
991 return;
992 };
993 let mut state = owner.state();
994 let changed = state.instances.get_mut(&self.key).filter(|entry| entry.snapshot.instance == self.id).map(update).unwrap_or(false);
995 if changed {
996 owner.publish(&state);
997 }
998 }
999
1000 pub(crate) fn transition(&self, phase: Phase) {
1002 self.with_entry(|entry| {
1003 if entry.stopping && !matches!(phase, Phase::Stopping | Phase::Finalizing) {
1004 return false;
1005 }
1006 entry.running.lifecycle().transition(phase);
1007 true
1008 });
1009 }
1010
1011 pub(crate) fn quiesce(&self) {
1013 self.with_entry(|entry| {
1014 entry.stopping = true;
1015 if let Some(health) = &mut entry.snapshot.health {
1016 *health = ServiceStatus::Stopping;
1017 }
1018 true
1019 });
1020 }
1021
1022 pub(crate) fn health(&self, status: ServiceStatus) {
1024 self.with_entry(|entry| {
1025 if entry.stopping && !matches!(status, ServiceStatus::Stopped | ServiceStatus::Inactive { .. }) {
1026 return false;
1027 }
1028 let lifecycle = entry.running.lifecycle();
1029 match &status {
1030 ServiceStatus::Running { .. } => lifecycle.transition(Phase::Running),
1031 ServiceStatus::BackingOff => lifecycle.transition(Phase::Restarting),
1032 _ => {}
1033 }
1034 entry.snapshot.health = Some(status);
1035 true
1036 });
1037 }
1038
1039 pub(crate) fn publish(&self) {
1040 self.with_entry(|_| true);
1041 }
1042
1043 pub(crate) fn complete(&self, outcome: &Outcome) {
1045 self.with_entry(|entry| {
1046 entry.running.settlement.record(outcome);
1047 entry.snapshot.failure = outcome.as_ref().err().cloned();
1048 if let Some(health) = &mut entry.snapshot.health {
1049 if outcome.is_err() && !matches!(health, ServiceStatus::Inactive { .. }) {
1050 *health = ServiceStatus::Inactive { cause: crate::services::ServiceExit::Panicked, history: Vec::new() };
1051 } else if outcome.is_ok() {
1052 *health = ServiceStatus::Stopped;
1053 }
1054 }
1055 true
1056 });
1057 }
1058}
1059
1060impl Lease {
1061 fn new(running: Running, demand: u64, generation: OperationGeneration) -> Self {
1062 Self { running, demand, generation, released: false, joins_release: false }
1063 }
1064
1065 pub(crate) fn id(&self) -> OperationInstanceId {
1066 self.running.report.id
1067 }
1068
1069 pub(crate) fn generation(&self) -> OperationGeneration {
1070 self.generation
1071 }
1072
1073 pub(crate) fn lifecycle(&self) -> Arc<Instance> {
1074 Arc::clone(self.running.lifecycle())
1075 }
1076
1077 pub(crate) fn settlement(&self) -> impl Future<Output = Result<()>> + Send + 'static + use<> {
1079 let running = self.running.clone();
1080 async move { settle(vec![running]).await }
1081 }
1082
1083 #[cfg(feature = "plugin-sidecar")]
1084 pub(crate) fn transition(&self, phase: Phase) {
1085 self.running.report.transition(phase);
1086 }
1087
1088 pub(crate) fn stop(&self) {
1090 let report = &self.running.report;
1091 let stopped = report.owner.upgrade().is_some_and(|owner| owner.stop_instance(&report.key, report.id));
1092 if !stopped {
1093 self.running.stop();
1094 }
1095 }
1096
1097 pub(crate) fn detach(self) {
1099 let report = &self.running.report;
1100 let Some(owner) = report.owner.upgrade() else {
1101 return;
1102 };
1103 let scope = owner.state().scopes.get(&report.key.scope).cloned();
1104 match scope {
1105 Some(scope) => scope.detached.lock().expect("resident scope is healthy").push(self),
1106 None => drop(self),
1107 }
1108 }
1109
1110 pub(crate) fn retire(&mut self) -> impl Future<Output = Result<()>> + Send + 'static + use<> {
1113 let completion = self.begin_release();
1114 let report = self.running.report.clone();
1115 let lifecycle = Arc::clone(self.running.lifecycle());
1116 async move {
1117 let result = completion.await;
1118 if !lifecycle.is_settled() {
1119 return result;
1120 }
1121 if let Some(owner) = report.owner.upgrade() {
1122 let mut state = owner.state();
1123 let owns = state.instances.get(&report.key).is_some_and(|entry| entry.snapshot.instance == report.id && entry.internal);
1124 let retired = owns.then(|| (state.instances.remove(&report.key), state.definitions.remove(&report.key.definition)));
1125 owner.publish(&state);
1126 drop(state);
1127 drop(retired);
1128 }
1129 result
1130 }
1131 }
1132
1133 pub(crate) async fn release(mut self) -> Result<()> {
1134 self.begin_release().await
1135 }
1136
1137 fn begin_release(&mut self) -> impl Future<Output = Result<()>> + Send + 'static + use<> {
1138 self.request();
1139 let running = self.joins_release.then(|| self.running.clone());
1140 async move { settle(running.into_iter().collect()).await }
1141 }
1142
1143 fn request(&mut self) -> bool {
1144 if self.released {
1145 return self.joins_release;
1146 }
1147 self.released = true;
1148 let report = &self.running.report;
1149 self.joins_release = match report.owner.upgrade() {
1150 Some(owner) => owner.release(&report.key, report.id, self.demand),
1151 None => {
1152 self.running.stop();
1153 true
1154 }
1155 };
1156 self.joins_release
1157 }
1158}
1159
1160impl Drop for Lease {
1161 fn drop(&mut self) {
1162 self.request();
1163 }
1164}
1165
1166impl Staged {
1167 pub fn commit(self) -> Result<()> {
1168 let owner = self.owner.upgrade().ok_or(Error::Closing { domain: "resident" })?;
1169 let state = owner.state();
1170 state.admit()?;
1171 if self.scope.closed.load(Ordering::Acquire) {
1172 return Err(Error::Closing { domain: "resident scope" });
1173 }
1174 for entry in &self.entries {
1175 #[cfg(any(feature = "plugin", feature = "plugin-guest", feature = "plugin-sidecar"))]
1176 if entry.plugin.as_ref().is_some_and(|scope| scope.is_closed()) {
1177 return Err(Error::Closing { domain: "plugin scope" });
1178 }
1179 if entry.closing.load(Ordering::Acquire)
1180 || !state.definitions.get(entry.definition.id()).is_some_and(|current| Arc::ptr_eq(current, entry))
1181 {
1182 return Err(Error::NotRegistered { domain: "resident reservation", name: entry.definition.id().to_owned() });
1183 }
1184 }
1185 self.published.store(true, Ordering::Release);
1186 Ok(())
1187 }
1188}
1189
1190impl Drop for Staged {
1191 fn drop(&mut self) {
1192 if self.published.load(Ordering::Acquire) {
1193 return;
1194 }
1195 let Some(owner) = self.owner.upgrade() else {
1196 return;
1197 };
1198 let mut state = owner.state();
1199 for entry in &self.entries {
1200 if state.definitions.get(entry.definition.id()).is_some_and(|current| Arc::ptr_eq(current, entry)) {
1201 state.definitions.remove(entry.definition.id());
1202 }
1203 }
1204 }
1206}
1207
1208impl Retirement {
1209 pub(crate) fn into_outcome(self) -> Outcome {
1210 self.outcome
1211 }
1212}
1213
1214impl Drop for Shared {
1215 fn drop(&mut self) {
1216 let state = self.state.get_mut().expect("resident registry is healthy");
1217 state.stop(|_, _, _| true);
1218 }
1219}
1220
1221pub(crate) async fn settle(running: Vec<Running>) -> Result<()> {
1226 let pending = running.into_iter().map(|running| async move {
1227 let domain = running.report.kind.domain();
1228 match running.settle().await {
1229 Ok(outcome) => outcome.as_ref().clone().map_err(|source| Error::TaskFailed { domain, source }),
1230 Err(source) => Err(Error::TaskFailed {
1231 domain: match running.report.kind {
1232 Kind::Observer => "observer supervisor",
1233 Kind::Service => "service supervisor",
1234 },
1235 source,
1236 }),
1237 }
1238 });
1239 supervision::settle_all(pending).await
1240}
1241
1242fn address(resources: &Resources) -> usize {
1243 Arc::as_ptr(resources) as *const () as usize
1244}
1245
1246fn validate(definition: &Definition) -> Result<()> {
1247 match definition {
1248 Definition::Observer(definition) => crate::observers::validate(definition),
1249 Definition::Service(definition) => crate::services::validate(definition),
1250 }
1251}
1252
1253