Skip to main content

lgui_core/memory/
registry.rs

1use std::{
2    collections::BTreeMap,
3    fmt,
4    sync::{Arc, Mutex, Weak},
5};
6
7use super::{CacheDomain, CacheScope, CacheUsage, TrimReason};
8
9#[cfg_attr(feature = "diagnostics-serde", derive(serde::Serialize))]
10#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
11pub struct DomainInstanceId(pub u64);
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct TrimRequest {
15    pub reason: TrimReason,
16    pub scope: CacheScope,
17    pub target_bytes: usize,
18}
19
20#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
21pub struct TrimResult {
22    pub before_bytes: usize,
23    pub after_bytes: usize,
24}
25
26impl TrimResult {
27    pub const fn released_bytes(self) -> usize {
28        self.before_bytes.saturating_sub(self.after_bytes)
29    }
30}
31
32#[derive(Clone)]
33pub struct CacheAdapter {
34    usage: Arc<dyn Fn() -> CacheUsage + Send + Sync>,
35    trim: Arc<dyn Fn(TrimRequest) -> TrimResult + Send + Sync>,
36    set_budget: Arc<dyn Fn(usize) + Send + Sync>,
37}
38
39impl CacheAdapter {
40    pub fn new(
41        usage: impl Fn() -> CacheUsage + Send + Sync + 'static,
42        trim: impl Fn(TrimRequest) -> TrimResult + Send + Sync + 'static,
43    ) -> Self {
44        Self {
45            usage: Arc::new(usage),
46            trim: Arc::new(trim),
47            set_budget: Arc::new(|_| {}),
48        }
49    }
50
51    pub fn managed(
52        usage: impl Fn() -> CacheUsage + Send + Sync + 'static,
53        trim: impl Fn(TrimRequest) -> TrimResult + Send + Sync + 'static,
54        set_budget: impl Fn(usize) + Send + Sync + 'static,
55    ) -> Self {
56        Self {
57            usage: Arc::new(usage),
58            trim: Arc::new(trim),
59            set_budget: Arc::new(set_budget),
60        }
61    }
62
63    pub(crate) fn usage(&self) -> CacheUsage {
64        (self.usage)()
65    }
66
67    pub(crate) fn trim(&self, request: TrimRequest) -> TrimResult {
68        (self.trim)(request)
69    }
70
71    pub(crate) fn set_budget(&self, budget_bytes: usize) {
72        (self.set_budget)(budget_bytes);
73    }
74}
75
76impl fmt::Debug for CacheAdapter {
77    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78        formatter.write_str("CacheAdapter(..)")
79    }
80}
81
82#[derive(Clone, Debug)]
83pub struct DomainRegistration {
84    pub domain: CacheDomain,
85    pub instance: DomainInstanceId,
86    pub owner: String,
87    pub adapter: CacheAdapter,
88}
89
90impl DomainRegistration {
91    pub fn new(
92        domain: CacheDomain,
93        instance: DomainInstanceId,
94        owner: impl Into<String>,
95        adapter: CacheAdapter,
96    ) -> Self {
97        Self {
98            domain,
99            instance,
100            owner: owner.into(),
101            adapter,
102        }
103    }
104}
105
106#[derive(Debug)]
107pub(crate) struct RegistryState {
108    pub next_registration: u64,
109    pub next_instance: u64,
110    pub entries: BTreeMap<u64, DomainRegistration>,
111}
112
113impl Default for RegistryState {
114    fn default() -> Self {
115        Self {
116            next_registration: 1,
117            next_instance: 1,
118            entries: BTreeMap::new(),
119        }
120    }
121}
122
123pub struct CacheRegistration {
124    id: u64,
125    registry: Weak<Mutex<RegistryState>>,
126    on_drop: Option<Box<dyn FnOnce() + Send + Sync>>,
127}
128
129impl CacheRegistration {
130    pub(crate) fn new(
131        id: u64,
132        registry: Weak<Mutex<RegistryState>>,
133        on_drop: impl FnOnce() + Send + Sync + 'static,
134    ) -> Self {
135        Self {
136            id,
137            registry,
138            on_drop: Some(Box::new(on_drop)),
139        }
140    }
141
142    pub const fn id(&self) -> u64 {
143        self.id
144    }
145}
146
147impl fmt::Debug for CacheRegistration {
148    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
149        formatter
150            .debug_struct("CacheRegistration")
151            .field("id", &self.id)
152            .finish()
153    }
154}
155
156impl Drop for CacheRegistration {
157    fn drop(&mut self) {
158        let removed = self.registry.upgrade().is_some_and(|registry| {
159            registry
160                .lock()
161                .expect("memory registry poisoned")
162                .entries
163                .remove(&self.id)
164                .is_some()
165        });
166        if removed {
167            if let Some(on_drop) = self.on_drop.take() {
168                on_drop();
169            }
170        }
171    }
172}