1use std::{
3 any::{Any, TypeId},
4 cell::{Cell, RefCell},
5 collections::{BTreeMap, BTreeSet},
6 fmt,
7 rc::Rc,
8 task::Poll,
9};
10
11#[cfg(test)]
12mod tests;
13
14use futures::{
15 future::{LocalBoxFuture, poll_fn},
16 task::AtomicWaker,
17};
18use lenso_kernel::RuntimeFailure;
19use serde_json::Value;
20
21pub trait NativeGenerationResource: fmt::Debug + 'static {
23 fn revoke(&self) -> LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
24}
25
26struct Entry {
27 type_id: TypeId,
28 configuration: Value,
29 value: Rc<dyn Any>,
30 revoke: Rc<dyn Fn() -> LocalBoxFuture<'static, Result<(), RuntimeFailure>>>,
31}
32
33#[derive(Default)]
34struct State {
35 closed: Cell<bool>,
36 leases: Cell<usize>,
37 wake: AtomicWaker,
38 entries: RefCell<BTreeMap<(String, String), Entry>>,
39 order: RefCell<Vec<(String, String)>>,
40 constructing: RefCell<BTreeSet<(String, String)>>,
41 retirement: RefCell<Option<LocalBoxFuture<'static, Result<(), RuntimeFailure>>>>,
42 outcome: RefCell<Option<Result<(), RuntimeFailure>>>,
43 retiring: Cell<bool>,
44}
45
46struct PollGuard(Rc<State>);
47impl Drop for PollGuard {
48 fn drop(&mut self) {
49 self.0.retiring.set(false);
50 }
51}
52
53struct Reservation {
54 state: Rc<State>,
55 key: (String, String),
56}
57impl Drop for Reservation {
58 fn drop(&mut self) {
59 self.state.constructing.borrow_mut().remove(&self.key);
60 self.state.wake.wake();
61 }
62}
63
64pub struct NativeGenerationScope {
66 state: Rc<State>,
67}
68
69impl fmt::Debug for NativeGenerationScope {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 f.debug_struct("NativeGenerationScope")
72 .field("closed", &self.state.closed.get())
73 .field("leases", &self.state.leases.get())
74 .finish_non_exhaustive()
75 }
76}
77
78impl Default for NativeGenerationScope {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl NativeGenerationScope {
85 pub fn new() -> Self {
86 Self {
87 state: Rc::default(),
88 }
89 }
90
91 pub fn context(&self) -> NativeGenerationContext {
93 NativeGenerationContext {
94 state: self.state.clone(),
95 }
96 }
97
98 pub fn close(&self) {
100 self.state.closed.set(true);
101 self.state.wake.wake();
102 }
103
104 pub async fn retire(&self) -> Result<(), RuntimeFailure> {
106 self.close();
107 if self.state.retiring.replace(true) {
108 return Err(invalid("generation retirement already being polled"));
109 }
110 let _guard = PollGuard(self.state.clone());
111 poll_fn(|cx| {
112 if let Some(result) = self.state.outcome.borrow().as_ref() {
113 return Poll::Ready(result.clone());
114 }
115 self.state.wake.register(cx.waker());
116 if self.state.leases.get() != 0 || !self.state.constructing.borrow().is_empty() {
117 return Poll::Pending;
118 }
119 if self.state.retirement.borrow().is_none() {
120 let resources: Vec<_> = self
121 .state
122 .order
123 .borrow()
124 .iter()
125 .map(|key| self.state.entries.borrow()[key].revoke.clone())
126 .collect();
127 *self.state.retirement.borrow_mut() = Some(Box::pin(async move {
128 let mut failure = None;
129 for resource in resources.into_iter().rev() {
130 if let Err(error) = resource().await
131 && failure.is_none()
132 {
133 failure = Some(error);
134 }
135 }
136 failure.map_or(Ok(()), Err)
137 }));
138 }
139 let result = self
140 .state
141 .retirement
142 .borrow_mut()
143 .as_mut()
144 .expect("retirement initialized")
145 .as_mut()
146 .poll(cx);
147 if let Poll::Ready(result) = result {
148 if result.is_ok() {
149 self.state.entries.borrow_mut().clear();
150 self.state.order.borrow_mut().clear();
151 }
152 *self.state.outcome.borrow_mut() = Some(result.clone());
153 Poll::Ready(result)
154 } else {
155 Poll::Pending
156 }
157 })
158 .await
159 }
160}
161
162impl Drop for NativeGenerationScope {
163 fn drop(&mut self) {
164 self.close();
165 if !self.state.entries.borrow().is_empty() || !self.state.constructing.borrow().is_empty() {
168 std::mem::forget(self.state.clone());
169 }
170 }
171}
172
173#[derive(Clone)]
175pub struct NativeGenerationContext {
176 state: Rc<State>,
177}
178
179impl fmt::Debug for NativeGenerationContext {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 f.debug_struct("NativeGenerationContext")
182 .finish_non_exhaustive()
183 }
184}
185
186impl NativeGenerationContext {
187 pub fn resolve<T: NativeGenerationResource>(
188 &self,
189 owner: &str,
190 reference: &str,
191 configuration: &Value,
192 create: impl FnOnce() -> Result<T, RuntimeFailure>,
193 ) -> Result<NativeGenerationHandle<T>, RuntimeFailure> {
194 if self.state.closed.get() || !valid_reference(owner) || !valid_reference(reference) {
195 return Err(invalid("invalid or closed generation authority reference"));
196 }
197 let key = (owner.to_owned(), reference.to_owned());
198 if let Some(entry) = self.state.entries.borrow().get(&key) {
199 if entry.type_id != TypeId::of::<T>() || &entry.configuration != configuration {
200 return Err(invalid(
201 "generation authority owner/reference has conflicting type or configuration",
202 ));
203 }
204 return Ok(NativeGenerationHandle {
205 value: entry
206 .value
207 .clone()
208 .downcast::<T>()
209 .map_err(|_| invalid("generation authority type mismatch"))?,
210 state: self.state.clone(),
211 });
212 }
213 if !self.state.constructing.borrow_mut().insert(key.clone()) {
214 return Err(invalid(
215 "generation authority constructor reentered its own reference",
216 ));
217 }
218 let _reservation = Reservation {
219 state: self.state.clone(),
220 key: key.clone(),
221 };
222 let value = Rc::new(create()?);
223 let revoke = value.clone();
224 self.state.order.borrow_mut().push(key.clone());
225 self.state.entries.borrow_mut().insert(
226 key,
227 Entry {
228 type_id: TypeId::of::<T>(),
229 configuration: configuration.clone(),
230 value: value.clone(),
231 revoke: Rc::new(move || revoke.revoke()),
232 },
233 );
234 if self.state.closed.get() {
235 return Err(invalid(
236 "generation authority closed during construction; rollback required",
237 ));
238 }
239 Ok(NativeGenerationHandle {
240 value,
241 state: self.state.clone(),
242 })
243 }
244}
245
246pub struct NativeGenerationHandle<T> {
248 value: Rc<T>,
249 state: Rc<State>,
250}
251
252impl<T> Clone for NativeGenerationHandle<T> {
253 fn clone(&self) -> Self {
254 Self {
255 value: self.value.clone(),
256 state: self.state.clone(),
257 }
258 }
259}
260impl<T> fmt::Debug for NativeGenerationHandle<T> {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 f.debug_struct("NativeGenerationHandle")
263 .finish_non_exhaustive()
264 }
265}
266
267impl<T> NativeGenerationHandle<T> {
268 pub fn acquire(&self) -> Result<NativeGenerationLease<T>, RuntimeFailure> {
270 if self.state.closed.get() {
271 return Err(invalid("generation authority admission is closed"));
272 }
273 self.state.leases.set(
274 self.state
275 .leases
276 .get()
277 .checked_add(1)
278 .ok_or_else(|| invalid("generation authority lease limit"))?,
279 );
280 Ok(NativeGenerationLease {
281 value: self.value.clone(),
282 state: self.state.clone(),
283 })
284 }
285}
286
287pub struct NativeGenerationLease<T> {
288 value: Rc<T>,
289 state: Rc<State>,
290}
291impl<T> fmt::Debug for NativeGenerationLease<T> {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 f.debug_struct("NativeGenerationLease")
294 .finish_non_exhaustive()
295 }
296}
297impl<T> std::ops::Deref for NativeGenerationLease<T> {
298 type Target = T;
299 fn deref(&self) -> &T {
300 &self.value
301 }
302}
303impl<T> Drop for NativeGenerationLease<T> {
304 fn drop(&mut self) {
305 self.state.leases.set(self.state.leases.get() - 1);
306 self.state.wake.wake();
307 }
308}
309
310fn invalid(detail: &str) -> RuntimeFailure {
311 RuntimeFailure::InvalidResolvedPlan {
312 detail: detail.into(),
313 }
314}
315
316fn valid_reference(reference: &str) -> bool {
317 !reference.is_empty()
318 && reference.len() <= 128
319 && reference
320 .bytes()
321 .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'.' | b'_' | b'-'))
322}