spacegate_plugin/
instance.rs

1use std::{
2    any::{Any, TypeId},
3    collections::{HashMap, HashSet},
4    sync::{
5        atomic::{AtomicU64, Ordering},
6        Arc, Weak,
7    },
8};
9
10use serde::{Deserialize, Serialize};
11use spacegate_kernel::{helper_layers::function::FnLayer, BoxError, BoxLayer, BoxResult};
12use spacegate_model::PluginConfig;
13
14use crate::mount::{MountPoint, MountPointIndex};
15
16pub struct PluginInstance {
17    /// Raw config
18    pub config: PluginConfig,
19    /// The mount points of this plugin instance, just for display and debug
20    pub mount_points: HashMap<MountPointIndex, DropTracer>,
21    /// The hooks of this plugin instance
22    pub hooks: PluginInstanceHooks,
23    pub plugin_function: crate::layer::PluginFunction,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27pub(crate) struct DropMarker {
28    drop_signal: Arc<u64>,
29}
30
31#[derive(Debug, Clone, Default)]
32
33pub(crate) struct DropMarkerSet {
34    pub(crate) inner: HashSet<DropMarker>,
35}
36
37#[derive(Debug, Clone)]
38pub struct DropTracer {
39    drop_signal: Weak<u64>,
40}
41
42impl DropTracer {
43    pub fn all_dropped(&self) -> bool {
44        self.drop_signal.strong_count() == 0
45    }
46}
47
48pub(crate) fn drop_trace() -> (DropTracer, DropMarker) {
49    static COUNT: AtomicU64 = AtomicU64::new(0);
50    let drop_signal = Arc::new(COUNT.fetch_add(1, Ordering::SeqCst));
51    (
52        DropTracer {
53            drop_signal: Arc::downgrade(&drop_signal),
54        },
55        DropMarker { drop_signal: drop_signal.clone() },
56    )
57}
58
59type PluginInstanceHook = Box<dyn Fn(&PluginInstance) -> Result<(), BoxError> + Send + Sync + 'static>;
60
61#[derive(Default)]
62pub struct PluginInstanceResource {
63    inner: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
64}
65
66impl PluginInstanceResource {
67    pub fn get<T: 'static + Send + Sync>(&self) -> Option<&T> {
68        self.inner.get(&TypeId::of::<T>()).and_then(|v| v.downcast_ref())
69    }
70    pub fn get_mut<T: 'static + Send + Sync>(&mut self) -> Option<&mut T> {
71        self.inner.get_mut(&TypeId::of::<T>()).and_then(|v| v.downcast_mut())
72    }
73    pub fn insert<T: 'static + Send + Sync>(&mut self, value: T) {
74        self.inner.insert(TypeId::of::<T>(), Box::new(value));
75    }
76}
77
78impl std::fmt::Debug for PluginInstanceResource {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        f.debug_tuple(stringify!(PluginInstanceResource)).finish()
81    }
82}
83
84#[derive(Default)]
85pub struct PluginInstanceHooks {
86    pub after_create: Option<PluginInstanceHook>,
87    pub before_mount: Option<PluginInstanceHook>,
88    pub after_mount: Option<PluginInstanceHook>,
89    pub before_destroy: Option<PluginInstanceHook>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct PluginInstanceSnapshot {
94    pub config: PluginConfig,
95    pub mount_points: HashSet<MountPointIndex>,
96}
97
98macro_rules! expose_hooks {
99    ($($hook: ident, $setter: ident)*) => {
100        $(
101            pub(crate) fn $hook(&self) -> BoxResult<()> {
102                self.call_hook(&self.hooks.$hook)
103            }
104            pub(crate) fn $setter<M>(&mut self, hook: M)
105            where M: Fn(&PluginInstance) -> Result<(), BoxError> + Send + Sync + 'static
106            {
107                self.hooks.$hook = Some(Box::new(hook))
108            }
109        )*
110    };
111}
112#[allow(dead_code)]
113impl PluginInstance {
114    pub(crate) fn mount_at<M: MountPoint>(&mut self, mount_point: &mut M, index: MountPointIndex) -> Result<(), BoxError> {
115        let tracer = mount_point.mount(self)?;
116        self.mount_points.insert(index, tracer);
117        Ok(())
118    }
119    pub fn snapshot(&self) -> PluginInstanceSnapshot {
120        PluginInstanceSnapshot {
121            config: self.config.clone(),
122            mount_points: self.mount_points.iter().filter_map(|(index, tracer)| if !tracer.all_dropped() { Some(index.clone()) } else { None }).collect(),
123        }
124    }
125    pub(crate) fn call_hook(&self, hook: &Option<PluginInstanceHook>) -> BoxResult<()> {
126        if let Some(ref hook) = hook {
127            (hook)(self)
128        } else {
129            Ok(())
130        }
131    }
132    pub fn make(&self) -> BoxLayer {
133        BoxLayer::new(FnLayer::new(self.plugin_function.clone()))
134    }
135    // if we don't clean the mount_points, it may cause a slow memory leak
136    // we do it before new instance mounted
137    pub(crate) fn mount_points_gc(&mut self) {
138        self.mount_points.retain(|_, tracer| !tracer.all_dropped());
139    }
140    expose_hooks! {
141        after_create, set_after_create
142        before_mount, set_before_create
143        after_mount, set_after_mount
144        before_destroy, set_before_destroy
145    }
146}