1use std::sync::{
10 Arc,
11 atomic::{AtomicUsize, Ordering},
12};
13
14#[derive(Debug)]
15pub struct MemoryBudget {
16 limit: usize,
17 used: AtomicUsize,
18 peak: AtomicUsize,
19}
20
21impl MemoryBudget {
22 pub fn new(limit: usize) -> Arc<Self> {
23 Arc::new(Self {
24 limit,
25 used: AtomicUsize::new(0),
26 peak: AtomicUsize::new(0),
27 })
28 }
29
30 pub fn unlimited() -> Arc<Self> {
32 Self::new(usize::MAX)
33 }
34
35 pub fn limit(&self) -> usize {
36 self.limit
37 }
38
39 pub fn used(&self) -> usize {
40 self.used.load(Ordering::Relaxed)
41 }
42
43 pub fn peak(&self) -> usize {
45 self.peak.load(Ordering::Relaxed)
46 }
47
48 pub fn try_reserve(&self, bytes: usize) -> bool {
50 let mut used = self.used.load(Ordering::Relaxed);
51 loop {
52 let Some(next) = used.checked_add(bytes).filter(|next| *next <= self.limit) else {
53 return false;
54 };
55 match self
56 .used
57 .compare_exchange_weak(used, next, Ordering::Relaxed, Ordering::Relaxed)
58 {
59 Ok(_) => {
60 self.peak.fetch_max(next, Ordering::Relaxed);
61 return true;
62 }
63 Err(current) => used = current,
64 }
65 }
66 }
67
68 pub fn force_reserve(&self, bytes: usize) {
71 let next = self
72 .used
73 .fetch_add(bytes, Ordering::Relaxed)
74 .saturating_add(bytes);
75 self.peak.fetch_max(next, Ordering::Relaxed);
76 }
77
78 pub fn release(&self, bytes: usize) {
79 self.used.fetch_sub(bytes, Ordering::Relaxed);
80 }
81}
82
83#[derive(Debug)]
85pub struct Reservation {
86 budget: Arc<MemoryBudget>,
87 bytes: usize,
88}
89
90impl Reservation {
91 pub fn empty(budget: &Arc<MemoryBudget>) -> Self {
92 Self {
93 budget: Arc::clone(budget),
94 bytes: 0,
95 }
96 }
97
98 pub fn forced(budget: &Arc<MemoryBudget>, bytes: usize) -> Self {
100 budget.force_reserve(bytes);
101 Self {
102 budget: Arc::clone(budget),
103 bytes,
104 }
105 }
106
107 pub fn bytes(&self) -> usize {
108 self.bytes
109 }
110
111 pub fn try_grow(&mut self, bytes: usize) -> bool {
112 let granted = self.budget.try_reserve(bytes);
113 if granted {
114 self.bytes += bytes;
115 }
116 granted
117 }
118
119 pub fn force_grow(&mut self, bytes: usize) {
120 self.budget.force_reserve(bytes);
121 self.bytes += bytes;
122 }
123
124 pub fn release_all(&mut self) {
125 self.budget.release(self.bytes);
126 self.bytes = 0;
127 }
128}
129
130impl Drop for Reservation {
131 fn drop(&mut self) {
132 self.release_all();
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn reservations_respect_the_limit_and_track_the_peak() {
142 let budget = MemoryBudget::new(100);
143 let mut first = Reservation::empty(&budget);
144 assert!(first.try_grow(60));
145 let mut second = Reservation::empty(&budget);
146 assert!(!second.try_grow(50));
147 assert!(second.try_grow(40));
148 assert_eq!(budget.used(), 100);
149 drop(first);
150 assert_eq!(budget.used(), 40);
151 second.force_grow(80);
152 assert_eq!(budget.used(), 120);
153 assert_eq!(budget.peak(), 120);
154 drop(second);
155 assert_eq!(budget.used(), 0);
156 }
157}