1use crate::{CancellationSource, RequestContext};
4use fusor::{
5 OwnerHandle, Registration,
6 coherence::{Attempt, BoundaryLifetime, ReadLease},
7 versions::Versions,
8};
9use futures_util::future::{AbortHandle, Abortable, LocalBoxFuture};
10use std::{
11 cell::{Cell, RefCell},
12 future::Future,
13 rc::{Rc, Weak},
14};
15
16thread_local! { static NEXT: Cell<u64> = const { Cell::new(0) }; }
17
18pub enum AsyncRead<T> {
21 Pending,
22 Ready(Rc<T>),
23}
24
25enum State<T, E> {
26 Idle,
27 Pending,
28 Ready(Rc<T>),
29 Error(Rc<E>),
30}
31struct Work {
32 abort: AbortHandle,
33 cancellation: Option<CancellationSource>,
34}
35impl Drop for Work {
36 fn drop(&mut self) {
37 self.abort.abort();
38 }
39}
40type Load<K, T, E> = dyn Fn(K, RequestContext) -> LocalBoxFuture<'static, Result<T, E>>;
41
42struct Inner<K, T, E> {
43 id: u64,
44 owner: OwnerHandle,
45 boundary: RefCell<Option<(u64, BoundaryLifetime)>>,
46 key: Box<dyn Fn() -> K>,
47 selected: RefCell<Option<(K, Versions)>>,
48 state: RefCell<State<T, E>>,
49 generation: Cell<u64>,
50 retry: Cell<u64>,
51 request: RefCell<Option<Work>>,
52 cleanup: RefCell<Option<Registration>>,
53 load: Rc<Load<K, T, E>>,
54 spawn: Box<dyn Fn(LocalBoxFuture<'static, ()>)>,
55}
56
57impl<K, T, E> Inner<K, T, E> {
58 fn cancel_work(&self) {
59 self.generation.set(
60 self.generation
61 .get()
62 .checked_add(1)
63 .expect("read generation overflow"),
64 );
65 let work = self.request.take();
66 if matches!(*self.state.borrow(), State::Pending) {
67 *self.state.borrow_mut() = State::Idle;
68 }
69 drop(work);
70 }
71}
72impl<K, T, E> Drop for Inner<K, T, E> {
73 fn drop(&mut self) {
74 self.request.get_mut().take();
75 }
76}
77
78struct Lease<K, T, E>(Weak<Inner<K, T, E>>);
79impl<K, T, E> ReadLease for Lease<K, T, E> {
80 fn cancel(&self) {
81 if let Some(inner) = self.0.upgrade() {
82 inner.cancel_work();
83 }
84 }
85}
86
87pub struct AsyncValue<K, T, E>(Rc<Inner<K, T, E>>);
91impl<K, T, E> Clone for AsyncValue<K, T, E> {
92 fn clone(&self) -> Self {
93 Self(self.0.clone())
94 }
95}
96
97impl<K: Clone + PartialEq + 'static, T: 'static, E: std::fmt::Display + 'static>
98 AsyncValue<K, T, E>
99{
100 pub fn new<F: Future<Output = Result<T, E>> + 'static>(
101 owner: &OwnerHandle,
102 key: impl Fn() -> K + 'static,
103 load: impl Fn(K, RequestContext) -> F + 'static,
104 spawn: impl Fn(LocalBoxFuture<'static, ()>) + 'static,
105 ) -> Self {
106 let id = NEXT.with(|next| {
107 let id = next.get().checked_add(1).expect("read id overflow");
108 next.set(id);
109 id
110 });
111 let inner = Rc::new(Inner {
112 id,
113 owner: owner.clone(),
114 boundary: RefCell::new(None),
115 key: Box::new(key),
116 selected: RefCell::new(None),
117 state: RefCell::new(State::Idle),
118 generation: Cell::new(0),
119 retry: Cell::new(0),
120 request: RefCell::new(None),
121 cleanup: RefCell::new(None),
122 load: Rc::new(move |key, context| Box::pin(load(key, context))),
123 spawn: Box::new(spawn),
124 });
125 let weak = Rc::downgrade(&inner);
126 let cleanup = owner.on_cleanup(move || {
127 if let Some(inner) = weak.upgrade() {
128 inner.cancel_work();
129 let old = inner.state.replace(State::Idle);
130 drop(old);
131 }
132 });
133 *inner.cleanup.borrow_mut() = Some(cleanup);
134 Self(inner)
135 }
136
137 #[doc(hidden)]
139 pub fn read(&self, attempt: &Attempt) -> Result<AsyncRead<T>, String> {
140 let inner = &self.0;
141 if inner.owner.is_disposed() {
142 return Err("async read owner was disposed".into());
143 }
144 let boundary = attempt.boundary_id();
145 let (changed, live) = inner
146 .boundary
147 .borrow()
148 .as_ref()
149 .map_or((true, false), |(id, lifetime)| {
150 (*id != boundary, lifetime.is_live())
151 });
152 if changed && live {
153 return Err(
154 "one AsyncValue cannot participate in different live async boundaries".into(),
155 );
156 }
157 if changed {
158 inner.cancel_work();
162 let old = inner.state.replace(State::Idle);
163 inner.selected.take();
164 inner
165 .boundary
166 .replace(Some((boundary, attempt.boundary_lifetime())));
167 drop(old);
168 }
169 let (key, versions) = Versions::capture(|| (inner.key)());
170 let selected = inner.selected.borrow().clone();
171 let compatible = selected
172 .as_ref()
173 .is_some_and(|(old, inputs)| old == &key && inputs.same(&versions));
174 if !compatible {
175 inner.cancel_work();
176 let old = inner.state.replace(State::Idle);
177 inner.selected.replace(Some((key.clone(), versions)));
178 drop(old);
179 }
180 if inner.retry.get() != attempt.retry_generation() {
181 inner.retry.set(attempt.retry_generation());
182 if matches!(*inner.state.borrow(), State::Error(_)) {
183 let old = inner.state.replace(State::Idle);
184 drop(old);
185 }
186 }
187 attempt.register(inner.id, Rc::new(Lease(Rc::downgrade(inner))));
188 if matches!(*inner.state.borrow(), State::Idle) {
189 let generation = inner.generation.get();
190 let cancellation = CancellationSource::default();
191 let context = cancellation.context();
192 let (abort, registration) = AbortHandle::new_pair();
193 *inner.request.borrow_mut() = Some(Work {
194 abort,
195 cancellation: Some(cancellation),
196 });
197 *inner.state.borrow_mut() = State::Pending;
198 let weak = Rc::downgrade(inner);
199 let load = inner.load.clone();
200 let notify = attempt.notifier();
201 (inner.spawn)(Box::pin(async move {
202 let work = async move {
203 let result = load(key, context).await;
204 if let Some(inner) = weak.upgrade().filter(|inner| {
205 !inner.owner.is_disposed() && inner.generation.get() == generation
206 }) {
207 if let Some(mut work) = inner.request.take() {
208 work.cancellation
209 .take()
210 .expect("pending request")
211 .complete();
212 }
213 let old = inner.state.replace(match result {
214 Ok(value) => State::Ready(Rc::new(value)),
215 Err(error) => State::Error(Rc::new(error)),
216 });
217 drop(old);
218 notify();
219 }
220 };
221 let _ = Abortable::new(work, registration).await;
222 }));
223 }
224 let error = match &*inner.state.borrow() {
225 State::Ready(value) => return Ok(AsyncRead::Ready(value.clone())),
226 State::Error(error) => Some(error.clone()),
227 State::Idle | State::Pending => {
228 attempt.pending();
229 return Ok(AsyncRead::Pending);
230 }
231 };
232 Err(error.expect("error state").to_string())
233 }
234}