moonpool_explorer/
energy.rs1use std::io;
12use std::sync::atomic::{AtomicI64, Ordering};
13
14use crate::assertion_slots::MAX_ASSERTION_SLOTS;
15use crate::shared_mem;
16
17#[repr(C)]
21pub struct EnergyBudget {
22 pub global_remaining: AtomicI64,
24 pub per_mark: [AtomicI64; MAX_ASSERTION_SLOTS],
26 pub per_mark_initial: i64,
28 pub realloc_pool: AtomicI64,
30}
31
32pub fn init_energy_budget(
38 global_energy: i64,
39 per_mark_initial: i64,
40) -> Result<*mut EnergyBudget, io::Error> {
41 let ptr = shared_mem::alloc_shared(std::mem::size_of::<EnergyBudget>())?;
42 let budget = ptr as *mut EnergyBudget;
43 unsafe {
45 (*budget)
46 .global_remaining
47 .store(global_energy, Ordering::Relaxed);
48 (*budget).per_mark_initial = per_mark_initial;
49 }
52 Ok(budget)
53}
54
55pub unsafe fn init_mark_budget(budget: *mut EnergyBudget, slot_idx: usize) {
61 if slot_idx < MAX_ASSERTION_SLOTS {
62 let b = unsafe { &*budget };
63 b.per_mark[slot_idx].store(b.per_mark_initial, Ordering::Relaxed);
64 }
65}
66
67pub unsafe fn decrement_mark_energy(budget: *mut EnergyBudget, slot_idx: usize) -> bool {
76 let b = unsafe { &*budget };
77
78 if b.global_remaining.fetch_sub(1, Ordering::Relaxed) <= 0 {
80 b.global_remaining.fetch_add(1, Ordering::Relaxed);
81 return false;
82 }
83
84 if slot_idx < MAX_ASSERTION_SLOTS {
86 if b.per_mark[slot_idx].fetch_sub(1, Ordering::Relaxed) > 0 {
87 return true;
88 }
89 b.per_mark[slot_idx].fetch_add(1, Ordering::Relaxed);
91
92 if b.realloc_pool.fetch_sub(1, Ordering::Relaxed) > 0 {
93 return true;
94 }
95 b.realloc_pool.fetch_add(1, Ordering::Relaxed);
96 }
97
98 b.global_remaining.fetch_add(1, Ordering::Relaxed);
100 false
101}
102
103pub unsafe fn reset_energy_budget(budget: *mut EnergyBudget, new_global_energy: i64) {
112 let b = unsafe { &*budget };
113 b.global_remaining
114 .store(new_global_energy, Ordering::Relaxed);
115 b.realloc_pool.store(0, Ordering::Relaxed);
116 for slot in &b.per_mark {
117 slot.store(0, Ordering::Relaxed);
118 }
119}
120
121pub unsafe fn return_mark_energy_to_pool(budget: *mut EnergyBudget, slot_idx: usize) {
129 if slot_idx < MAX_ASSERTION_SLOTS {
130 let b = unsafe { &*budget };
131 let remaining = b.per_mark[slot_idx].swap(0, Ordering::Relaxed);
132 if remaining > 0 {
133 b.realloc_pool.fetch_add(remaining, Ordering::Relaxed);
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_mark_budget_decrement_and_return() {
144 let ptr = init_energy_budget(100, 10).expect("init failed");
145 unsafe {
146 init_mark_budget(ptr, 0);
147 assert!(decrement_mark_energy(ptr, 0));
149 assert!(decrement_mark_energy(ptr, 0));
150
151 return_mark_energy_to_pool(ptr, 0);
153 let b = &*ptr;
154 assert_eq!(b.per_mark[0].load(Ordering::Relaxed), 0);
155 assert_eq!(b.realloc_pool.load(Ordering::Relaxed), 8);
156
157 shared_mem::free_shared(ptr as *mut u8, std::mem::size_of::<EnergyBudget>());
158 }
159 }
160
161 #[test]
162 fn test_productive_mark_draws_from_realloc() {
163 let ptr = init_energy_budget(100, 5).expect("init failed");
164 unsafe {
165 init_mark_budget(ptr, 0);
167 decrement_mark_energy(ptr, 0); return_mark_energy_to_pool(ptr, 0); init_mark_budget(ptr, 1);
172 for _ in 0..5 {
173 assert!(decrement_mark_energy(ptr, 1));
174 }
175 assert!(decrement_mark_energy(ptr, 1));
177 let b = &*ptr;
178 assert_eq!(b.realloc_pool.load(Ordering::Relaxed), 3);
179
180 shared_mem::free_shared(ptr as *mut u8, std::mem::size_of::<EnergyBudget>());
181 }
182 }
183
184 #[test]
185 fn test_global_energy_exhaustion() {
186 let ptr = init_energy_budget(3, 100).expect("init failed");
187 unsafe {
188 init_mark_budget(ptr, 0);
189 assert!(decrement_mark_energy(ptr, 0));
190 assert!(decrement_mark_energy(ptr, 0));
191 assert!(decrement_mark_energy(ptr, 0));
192 assert!(!decrement_mark_energy(ptr, 0));
194 let b = &*ptr;
196 assert_eq!(b.global_remaining.load(Ordering::Relaxed), 0);
197
198 shared_mem::free_shared(ptr as *mut u8, std::mem::size_of::<EnergyBudget>());
199 }
200 }
201
202 #[test]
203 fn test_realloc_flow() {
204 let ptr = init_energy_budget(50, 5).expect("init failed");
205 unsafe {
206 init_mark_budget(ptr, 0);
208 decrement_mark_energy(ptr, 0);
209 return_mark_energy_to_pool(ptr, 0);
210
211 let b = &*ptr;
212 assert_eq!(b.realloc_pool.load(Ordering::Relaxed), 4);
213
214 init_mark_budget(ptr, 1);
216 for _ in 0..5 {
217 assert!(decrement_mark_energy(ptr, 1));
218 }
219 for _ in 0..3 {
220 assert!(decrement_mark_energy(ptr, 1));
221 }
222 assert_eq!(b.realloc_pool.load(Ordering::Relaxed), 1);
223
224 shared_mem::free_shared(ptr as *mut u8, std::mem::size_of::<EnergyBudget>());
225 }
226 }
227}