graphos_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 if new_size > current {
65 let diff = new_size - current;
67 if self.releaser.try_allocate_raw(diff, self.region) {
68 self.size.store(new_size, Ordering::Relaxed);
69 true
70 } else {
71 false
72 }
73 } else if new_size < current {
74 let diff = current - new_size;
76 self.releaser.release(diff, self.region);
77 self.size.store(new_size, Ordering::Relaxed);
78 true
79 } else {
80 true
82 }
83 }
84
85 pub fn split(&mut self, amount: usize) -> Option<MemoryGrant> {
89 let current = self.size.load(Ordering::Relaxed);
90 if amount > current {
91 return None;
92 }
93
94 self.size.store(current - amount, Ordering::Relaxed);
95 Some(MemoryGrant {
96 releaser: Arc::clone(&self.releaser),
97 size: AtomicUsize::new(amount),
98 region: self.region,
99 consumed: false,
100 })
101 }
102
103 pub fn merge(&mut self, other: MemoryGrant) {
111 assert_eq!(
112 self.region, other.region,
113 "Cannot merge grants from different regions"
114 );
115
116 let other_size = other.consume();
117 let current = self.size.load(Ordering::Relaxed);
118 self.size.store(current + other_size, Ordering::Relaxed);
119 }
120
121 pub fn consume(mut self) -> usize {
126 self.consumed = true;
127 self.size.load(Ordering::Relaxed)
128 }
129
130 #[must_use]
132 pub fn is_consumed(&self) -> bool {
133 self.consumed
134 }
135
136 #[must_use]
138 pub fn is_empty(&self) -> bool {
139 self.size.load(Ordering::Relaxed) == 0
140 }
141}
142
143impl Drop for MemoryGrant {
144 fn drop(&mut self) {
145 if !self.consumed {
146 let size = self.size.load(Ordering::Relaxed);
147 if size > 0 {
148 self.releaser.release(size, self.region);
149 }
150 }
151 }
152}
153
154impl std::fmt::Debug for MemoryGrant {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 f.debug_struct("MemoryGrant")
157 .field("size", &self.size.load(Ordering::Relaxed))
158 .field("region", &self.region)
159 .field("consumed", &self.consumed)
160 .finish()
161 }
162}
163
164#[derive(Default)]
166pub struct CompositeGrant {
167 grants: Vec<MemoryGrant>,
168}
169
170impl CompositeGrant {
171 #[must_use]
173 pub fn new() -> Self {
174 Self { grants: Vec::new() }
175 }
176
177 pub fn add(&mut self, grant: MemoryGrant) {
179 self.grants.push(grant);
180 }
181
182 #[must_use]
184 pub fn total_size(&self) -> usize {
185 self.grants.iter().map(MemoryGrant::size).sum()
186 }
187
188 #[must_use]
190 pub fn len(&self) -> usize {
191 self.grants.len()
192 }
193
194 #[must_use]
196 pub fn is_empty(&self) -> bool {
197 self.grants.is_empty()
198 }
199
200 pub fn consume_all(self) -> usize {
202 self.grants.into_iter().map(MemoryGrant::consume).sum()
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use std::sync::atomic::AtomicUsize;
210
211 struct MockReleaser {
212 released: AtomicUsize,
213 allocated: AtomicUsize,
214 }
215
216 impl MockReleaser {
217 fn new() -> Arc<Self> {
218 Arc::new(Self {
219 released: AtomicUsize::new(0),
220 allocated: AtomicUsize::new(0),
221 })
222 }
223 }
224
225 impl GrantReleaser for MockReleaser {
226 fn release(&self, size: usize, _region: MemoryRegion) {
227 self.released.fetch_add(size, Ordering::Relaxed);
228 }
229
230 fn try_allocate_raw(&self, size: usize, _region: MemoryRegion) -> bool {
231 self.allocated.fetch_add(size, Ordering::Relaxed);
232 true
233 }
234 }
235
236 #[test]
237 fn test_grant_drop_releases_memory() {
238 let releaser = MockReleaser::new();
239
240 {
241 let _grant = MemoryGrant::new(
242 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
243 1024,
244 MemoryRegion::ExecutionBuffers,
245 );
246 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
247 }
248
249 assert_eq!(releaser.released.load(Ordering::Relaxed), 1024);
251 }
252
253 #[test]
254 fn test_grant_consume_no_release() {
255 let releaser = MockReleaser::new();
256
257 let grant = MemoryGrant::new(
258 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
259 1024,
260 MemoryRegion::ExecutionBuffers,
261 );
262
263 let size = grant.consume();
264 assert_eq!(size, 1024);
265
266 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
268 }
269
270 #[test]
271 fn test_grant_resize_grow() {
272 let releaser = MockReleaser::new();
273
274 let mut grant = MemoryGrant::new(
275 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
276 1024,
277 MemoryRegion::ExecutionBuffers,
278 );
279
280 assert!(grant.resize(2048));
281 assert_eq!(grant.size(), 2048);
282 assert_eq!(releaser.allocated.load(Ordering::Relaxed), 1024);
283 }
284
285 #[test]
286 fn test_grant_resize_shrink() {
287 let releaser = MockReleaser::new();
288
289 let mut grant = MemoryGrant::new(
290 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
291 1024,
292 MemoryRegion::ExecutionBuffers,
293 );
294
295 assert!(grant.resize(512));
296 assert_eq!(grant.size(), 512);
297 assert_eq!(releaser.released.load(Ordering::Relaxed), 512);
298 }
299
300 #[test]
301 fn test_grant_split() {
302 let releaser = MockReleaser::new();
303
304 let mut grant = MemoryGrant::new(
305 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
306 1000,
307 MemoryRegion::ExecutionBuffers,
308 );
309
310 let split = grant.split(400).unwrap();
311 assert_eq!(grant.size(), 600);
312 assert_eq!(split.size(), 400);
313
314 assert!(grant.split(1000).is_none());
316 }
317
318 #[test]
319 fn test_grant_merge() {
320 let releaser = MockReleaser::new();
321
322 let mut grant1 = MemoryGrant::new(
323 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
324 600,
325 MemoryRegion::ExecutionBuffers,
326 );
327
328 let grant2 = MemoryGrant::new(
329 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
330 400,
331 MemoryRegion::ExecutionBuffers,
332 );
333
334 grant1.merge(grant2);
335 assert_eq!(grant1.size(), 1000);
336
337 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
339 }
340
341 #[test]
342 fn test_composite_grant() {
343 let releaser = MockReleaser::new();
344
345 let mut composite = CompositeGrant::new();
346 assert!(composite.is_empty());
347
348 composite.add(MemoryGrant::new(
349 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
350 100,
351 MemoryRegion::ExecutionBuffers,
352 ));
353 composite.add(MemoryGrant::new(
354 Arc::clone(&releaser) as Arc<dyn GrantReleaser>,
355 200,
356 MemoryRegion::ExecutionBuffers,
357 ));
358
359 assert_eq!(composite.len(), 2);
360 assert_eq!(composite.total_size(), 300);
361
362 let total = composite.consume_all();
363 assert_eq!(total, 300);
364
365 assert_eq!(releaser.released.load(Ordering::Relaxed), 0);
367 }
368}