monoloop_loop/transaction/lifecycle/
capacity.rs1use monoloop_contracts::ChannelId;
7use std::collections::HashMap;
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::sync::Arc;
10
11#[derive(Debug)]
13pub struct ReservationPool {
14 max_global: usize,
15 max_ledger: usize,
16 global_active: AtomicUsize,
17 ledger_active: AtomicUsize,
18 per_channel: HashMap<ChannelId, ChannelSlot>,
19}
20
21#[derive(Debug)]
22struct ChannelSlot {
23 max: usize,
24 active: AtomicUsize,
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum ReservationPoolError {
30 ZeroGlobal,
32 ZeroChannel,
34}
35
36#[derive(Debug)]
38pub struct TransactionReservations {
39 pool: Arc<ReservationPool>,
40 channel_id: ChannelId,
41 global: Option<GlobalPermit>,
42 channel: Option<ChannelPermit>,
43 ledger: Option<LedgerPermit>,
44}
45
46#[derive(Debug)]
47struct GlobalPermit {
48 pool: Arc<ReservationPool>,
49}
50
51#[derive(Debug)]
52struct ChannelPermit {
53 pool: Arc<ReservationPool>,
54 channel_id: ChannelId,
55}
56
57#[derive(Debug)]
58struct LedgerPermit {
59 pool: Arc<ReservationPool>,
60}
61
62impl Drop for GlobalPermit {
63 fn drop(&mut self) {
64 self.pool.global_active.fetch_sub(1, Ordering::SeqCst);
65 }
66}
67
68impl Drop for ChannelPermit {
69 fn drop(&mut self) {
70 if let Some(ch) = self.pool.per_channel.get(&self.channel_id) {
71 ch.active.fetch_sub(1, Ordering::SeqCst);
72 }
73 }
74}
75
76impl Drop for LedgerPermit {
77 fn drop(&mut self) {
78 self.pool.ledger_active.fetch_sub(1, Ordering::SeqCst);
79 }
80}
81
82impl ReservationPool {
83 pub fn try_new(
87 max_global: usize,
88 channels: impl IntoIterator<Item = (ChannelId, usize)>,
89 ) -> Result<Arc<Self>, ReservationPoolError> {
90 if max_global == 0 {
91 return Err(ReservationPoolError::ZeroGlobal);
92 }
93 let mut per_channel = HashMap::new();
94 for (id, max) in channels {
95 if max == 0 {
96 return Err(ReservationPoolError::ZeroChannel);
97 }
98 per_channel.insert(
99 id,
100 ChannelSlot {
101 max,
102 active: AtomicUsize::new(0),
103 },
104 );
105 }
106 Ok(Arc::new(Self {
107 max_global,
108 max_ledger: max_global,
109 global_active: AtomicUsize::new(0),
110 ledger_active: AtomicUsize::new(0),
111 per_channel,
112 }))
113 }
114
115 pub fn global_active(&self) -> usize {
117 self.global_active.load(Ordering::SeqCst)
118 }
119
120 pub fn ledger_active(&self) -> usize {
122 self.ledger_active.load(Ordering::SeqCst)
123 }
124
125 pub fn channel_active(&self, id: &ChannelId) -> usize {
127 self.per_channel
128 .get(id)
129 .map(|ch| ch.active.load(Ordering::SeqCst))
130 .unwrap_or(0)
131 }
132
133 pub fn max_global(&self) -> usize {
135 self.max_global
136 }
137
138 pub fn try_reserve(self: &Arc<Self>, channel: &ChannelId) -> Option<TransactionReservations> {
140 let ch = self.per_channel.get(channel)?;
141
142 loop {
144 let g = self.global_active.load(Ordering::SeqCst);
145 if g >= self.max_global {
146 return None;
147 }
148 if self
149 .global_active
150 .compare_exchange(g, g + 1, Ordering::SeqCst, Ordering::SeqCst)
151 .is_ok()
152 {
153 break;
154 }
155 }
156 let global = GlobalPermit {
157 pool: Arc::clone(self),
158 };
159
160 loop {
162 let c = ch.active.load(Ordering::SeqCst);
163 if c >= ch.max {
164 drop(global);
165 return None;
166 }
167 if ch
168 .active
169 .compare_exchange(c, c + 1, Ordering::SeqCst, Ordering::SeqCst)
170 .is_ok()
171 {
172 break;
173 }
174 }
175 let channel_permit = ChannelPermit {
176 pool: Arc::clone(self),
177 channel_id: channel.clone(),
178 };
179
180 loop {
182 let l = self.ledger_active.load(Ordering::SeqCst);
183 if l >= self.max_ledger {
184 drop(channel_permit);
185 drop(global);
186 return None;
187 }
188 if self
189 .ledger_active
190 .compare_exchange(l, l + 1, Ordering::SeqCst, Ordering::SeqCst)
191 .is_ok()
192 {
193 break;
194 }
195 }
196 let ledger = LedgerPermit {
197 pool: Arc::clone(self),
198 };
199
200 Some(TransactionReservations {
201 pool: Arc::clone(self),
202 channel_id: channel.clone(),
203 global: Some(global),
204 channel: Some(channel_permit),
205 ledger: Some(ledger),
206 })
207 }
208}
209
210impl TransactionReservations {
211 pub fn channel_id(&self) -> &ChannelId {
213 &self.channel_id
214 }
215
216 pub fn release(self) {
218 drop(self);
219 }
220}
221
222impl Drop for TransactionReservations {
223 fn drop(&mut self) {
224 self.ledger.take();
225 self.channel.take();
226 self.global.take();
227 let _ = &self.pool;
228 }
229}