lgui_core/memory/
registry.rs1use 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}