grafeo_common/memory/buffer/
grant.rs1use super::region::MemoryRegion;
4use std::sync::Arc;
5use std::sync::atomic::{AtomicUsize, Ordering};
6
7pub trait GrantReleaser: Send + Sync {
12 fn release(&self, size: usize, region: MemoryRegion);
14
15 fn try_allocate_raw(&self, size: usize, region: MemoryRegion) -> bool;
17}
18
19pub struct MemoryGrant {
24 releaser: Arc<dyn GrantReleaser>,
26 size: AtomicUsize,
28 region: MemoryRegion,
30 consumed: bool,
32}
33
34impl MemoryGrant {
35 pub(crate) fn new(releaser: Arc<dyn GrantReleaser>, size: usize, region: MemoryRegion) -> Self {
37 Self {
38 releaser,
39 size: AtomicUsize::new(size),
40 region,
41 consumed: false,
42 }
43 }
44
45 #[must_use]
47 pub fn size(&self) -> usize {
48 self.size.load(Ordering::Relaxed)
49 }
50
51 #[must_use]
53 pub fn region(&self) -> MemoryRegion {
54 self.region
55 }
56
57 pub fn resize(&mut self, new_size: usize) -> bool {
62 let current = self.size.load(Ordering::Relaxed);
63
64 match new_size.cmp(¤t) {
65 std::cmp::Ordering::Greater => {
66 let diff = new_size - current;
68 if self.releaser.try_allocate_raw(diff, self.region) {
69 self.size.store(new_size, Ordering::Relaxed);
70 true
71 } else {
72 false
73 }
74 }
75 std::cmp::Ordering::Less => {
76 let diff = current - new_size;
78 self.releaser.release(diff, self.region);
79 self.size.store(new_size, Ordering::Relaxed);
80 true
81 }
82 std::cmp::Ordering::Equal => true,
83 }
84 }
85
86 pub fn split(&mut self, amount: usize) -> Option<MemoryGrant> {
90 let current = self.size.load(Ordering::Relaxed);
91 if amount > current {
92 return None;
93 }
94
95 self.size.store(current - amount, Ordering::Relaxed);
96 Some(MemoryGrant {
97 releaser: Arc::clone(&self.releaser),
98 size: AtomicUsize::new(amount),
99 region: self.region,
100 consumed: false,
101 })
102 }
103
104 pub fn merge(&mut self, other: MemoryGrant) {
112 assert_eq!(
113 self.region, other.region,
114 "Cannot merge grants from different regions"
115 );
116
117 let other_size = other.consume();
118 let current = self.size.load(Ordering::Relaxed);
119 self.size.store(current + other_size, Ordering::Relaxed);
120 }
121
122 pub fn consume(mut self) -> usize {
127 self.consumed = true;
128 self.size.load(Ordering::Relaxed)
129 }
130
131 #[must_use]
133 pub fn is_consumed(&self) -> bool {
134 self.consumed
135 }
136
137 #[must_use]
139 pub fn is_empty(&self) -> bool {
140 self.size.load(Ordering::Relaxed) == 0
141 }
142}
143
144impl Drop for MemoryGrant {
145 fn drop(&mut self) {
146 if !self.consumed {
147 let size = self.size.load(Ordering::Relaxed);
148 if size > 0 {
149 self.releaser.release(size, self.region);
150 }
151 }
152 }
153}
154
155impl std::fmt::Debug for MemoryGrant {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 f.debug_struct("MemoryGrant")
158 .field("size", &self.size.load(Ordering::Relaxed))
159 .field("region", &self.region)
160 .field("consumed", &self.consumed)
161 .finish()
162 }
163}
164
165#[derive(Default)]
167pub struct CompositeGrant {
168 grants: Vec<MemoryGrant>,
169}
170
171impl CompositeGrant {
172 #[must_use]
174 pub fn new() -> Self {
175 Self { grants: Vec::new() }
176 }
177
178 pub fn add(&mut self, grant: MemoryGrant) {
180 self.grants.push(grant);
181 }
182
183 #[must_use]
185 pub fn total_size(&self) -> usize {
186 self.grants.iter().map(MemoryGrant::size).sum()
187 }
188
189 #[must_use]
191 pub fn len(&self) -> usize {
192 self.grants.len()
193 }
194
195 #[must_use]
197 pub fn is_empty(&self) -> bool {
198 self.grants.is_empty()
199 }
200
201 pub fn consume_all(self) -> usize {
203 self.grants.into_iter().map(MemoryGrant::consume).sum()
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use std::sync::atomic::AtomicUsize;
211
212 struct MockReleaser {
213 released: AtomicUsize,
214 allocated: AtomicUsize,
215 }
216
217 impl MockReleaser {
218 fn new() -> Arc<Self> {
219 Arc::new(Self {
220 released: AtomicUsize::new(0),
221 allocated: AtomicUsize::new(0),
222 })
223 }
224 }
225
226 impl GrantReleaser for MockReleaser {
227 fn release(&self, size: usize, _region: MemoryRegion) {
228 self.released.fetch_add(size, Ordering::Relaxed);
229 }
230
231 fn try_allocate_raw(&self, size: usize, _region: MemoryRegion) -> bool {
232 self.allocated.fetch_add(size, Ordering::Relaxed);
233 true
234 }
235 }
236
237 #[test]
238 fn test_grant_drop_releases_memory() {
239 let releaser = MockReleaser::new();
240
241 {
242 let _grant = MemoryGrant::new(
243 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
244 1024,
245 MemoryRegion::ExecutionBuffers,
246 );
247 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
248 }
249
250 assert_eq!(releaser.released.load(Ordering::Relaxed), 1024);
252 }
253
254 #[test]
255 fn test_grant_consume_no_release() {
256 let releaser = MockReleaser::new();
257
258 let grant = MemoryGrant::new(
259 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
260 1024,
261 MemoryRegion::ExecutionBuffers,
262 );
263
264 let size = grant.consume();
265 assert_eq!(size, 1024);
266
267 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
269 }
270
271 #[test]
272 fn test_grant_resize_grow() {
273 let releaser = MockReleaser::new();
274
275 let mut grant = MemoryGrant::new(
276 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
277 1024,
278 MemoryRegion::ExecutionBuffers,
279 );
280
281 assert!(grant.resize(2048));
282 assert_eq!(grant.size(), 2048);
283 assert_eq!(releaser.allocated.load(Ordering::Relaxed), 1024);
284 }
285
286 #[test]
287 fn test_grant_resize_shrink() {
288 let releaser = MockReleaser::new();
289
290 let mut grant = MemoryGrant::new(
291 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
292 1024,
293 MemoryRegion::ExecutionBuffers,
294 );
295
296 assert!(grant.resize(512));
297 assert_eq!(grant.size(), 512);
298 assert_eq!(releaser.released.load(Ordering::Relaxed), 512);
299 }
300
301 #[test]
302 fn test_grant_split() {
303 let releaser = MockReleaser::new();
304
305 let mut grant = MemoryGrant::new(
306 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
307 1000,
308 MemoryRegion::ExecutionBuffers,
309 );
310
311 let split = grant.split(400).unwrap();
312 assert_eq!(grant.size(), 600);
313 assert_eq!(split.size(), 400);
314
315 assert!(grant.split(1000).is_none());
317 }
318
319 #[test]
320 fn test_grant_merge() {
321 let releaser = MockReleaser::new();
322
323 let mut grant1 = MemoryGrant::new(
324 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
325 600,
326 MemoryRegion::ExecutionBuffers,
327 );
328
329 let grant2 = MemoryGrant::new(
330 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
331 400,
332 MemoryRegion::ExecutionBuffers,
333 );
334
335 grant1.merge(grant2);
336 assert_eq!(grant1.size(), 1000);
337
338 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
340 }
341
342 #[test]
343 fn test_composite_grant() {
344 let releaser = MockReleaser::new();
345
346 let mut composite = CompositeGrant::new();
347 assert!(composite.is_empty());
348
349 composite.add(MemoryGrant::new(
350 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
351 100,
352 MemoryRegion::ExecutionBuffers,
353 ));
354 composite.add(MemoryGrant::new(
355 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
356 200,
357 MemoryRegion::ExecutionBuffers,
358 ));
359
360 assert_eq!(composite.len(), 2);
361 assert_eq!(composite.total_size(), 300);
362
363 let total = composite.consume_all();
364 assert_eq!(total, 300);
365
366 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
368 }
369}