xet_runtime/utils/
rw_task_lock.rs1use std::future::Future;
2use std::mem::replace;
3use std::ops::Deref;
4
5use thiserror::Error;
6use tokio::sync::{RwLock, RwLockReadGuard};
7use tokio::task::{JoinError, JoinHandle};
8#[cfg(target_family = "wasm")]
9use tokio_with_wasm::alias as tokio;
10
11#[derive(Debug, Error)]
12#[non_exhaustive]
13pub enum RwTaskLockError {
14 #[error(transparent)]
15 JoinError(#[from] JoinError),
16
17 #[error("Attempting to access value not available due to a previously reported error.")]
18 CalledAfterError,
19}
20
21enum RwTaskLockState<T, E> {
22 Pending(JoinHandle<Result<T, E>>),
23 Ready(T),
24 Error,
25}
26
27pub struct RwTaskLockReadGuard<'a, T, E> {
29 guard: RwLockReadGuard<'a, RwTaskLockState<T, E>>,
30}
31
32impl<T, E> Deref for RwTaskLockReadGuard<'_, T, E> {
33 type Target = T;
34 fn deref(&self) -> &T {
35 match &*self.guard {
36 RwTaskLockState::Ready(val) => val,
37 _ => unreachable!("Read guard is only constructed for Ready state"),
38 }
39 }
40}
41
42pub struct RwTaskLock<T, E>
67where
68 T: Send + Sync + 'static,
69 E: Send + Sync + 'static + From<RwTaskLockError>,
70{
71 state: RwLock<RwTaskLockState<T, E>>,
72}
73
74impl<T, E> RwTaskLock<T, E>
75where
76 T: Send + Sync + 'static,
77 E: Send + Sync + 'static + From<RwTaskLockError>,
78{
79 pub fn from_value(val: T) -> Self {
81 Self {
82 state: RwLock::new(RwTaskLockState::Ready(val)),
83 }
84 }
85
86 pub fn from_task<Fut>(fut: Fut) -> Self
88 where
89 Fut: Future<Output = Result<T, E>> + Send + 'static,
90 {
91 let task = tokio::spawn(fut);
92
93 Self {
94 state: RwLock::new(RwTaskLockState::Pending(task)),
95 }
96 }
97
98 pub async fn read(&self) -> Result<RwTaskLockReadGuard<'_, T, E>, E> {
100 {
102 let state = self.state.read().await;
103 match &*state {
104 RwTaskLockState::Ready(_) => {
105 return Ok(RwTaskLockReadGuard { guard: state });
106 },
107 RwTaskLockState::Error => return Err(E::from(RwTaskLockError::CalledAfterError)),
108 RwTaskLockState::Pending(_) => {},
109 }
110 }
111 let mut state = self.state.write().await;
113
114 match replace(&mut *state, RwTaskLockState::Error) {
115 RwTaskLockState::Ready(v) => {
116 *state = RwTaskLockState::Ready(v);
117 },
118 RwTaskLockState::Error => {
119 return Err(E::from(RwTaskLockError::CalledAfterError));
120 },
121 RwTaskLockState::Pending(jh) => {
122 match jh.await.map_err(RwTaskLockError::JoinError)? {
123 Ok(v) => {
124 *state = RwTaskLockState::Ready(v);
125 },
126 Err(e) => {
127 *state = RwTaskLockState::Error;
128 return Err(e);
129 },
130 };
131 },
132 };
133
134 Ok(RwTaskLockReadGuard {
135 guard: state.downgrade(),
136 })
137 }
138
139 pub async fn update<Fut, Updater>(&self, updater: Updater) -> Result<(), RwTaskLockError>
186 where
187 Updater: FnOnce(T) -> Fut + Send + 'static,
188 Fut: Future<Output = Result<T, E>> + Send + 'static,
189 {
190 use RwTaskLockState::*;
191
192 let mut state_lg = self.state.write().await;
193
194 let state = replace(&mut *state_lg, RwTaskLockState::Error);
195
196 match state {
197 Pending(jh) => {
198 let new_task = tokio::spawn(async move {
200 let current = jh.await.map_err(RwTaskLockError::JoinError)??;
201 updater(current).await
202 });
203 *state_lg = Pending(new_task);
204 Ok(())
205 },
206 Ready(v) => {
207 *state_lg = Pending(tokio::spawn(updater(v)));
209 Ok(())
210 },
211 Error => {
212 *state_lg = Error;
214 Err(RwTaskLockError::CalledAfterError)
215 },
216 }
217 }
218}
219
220#[cfg(test)]
221mod tests {
222
223 use super::*;
224
225 #[tokio::test]
226 async fn test_from_value() {
227 let lock: RwTaskLock<_, RwTaskLockError> = RwTaskLock::from_value(7);
228 let guard = lock.read().await.unwrap();
229 assert_eq!(*guard, 7);
230 let guard2 = lock.read().await.unwrap();
231 assert_eq!(*guard2, 7);
232 }
233
234 #[tokio::test]
235 async fn test_from_future_success() {
236 let lock = RwTaskLock::from_task(async {
237 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
238 Ok::<_, RwTaskLockError>(999)
239 });
240 let guard = lock.read().await.unwrap();
241 assert_eq!(*guard, 999);
242 let guard2 = lock.read().await.unwrap();
243 assert_eq!(*guard2, 999);
244 }
245
246 #[tokio::test]
247 async fn test_from_future_error() {
248 let lock = RwTaskLock::<u8, RwTaskLockError>::from_task(async { Err(RwTaskLockError::CalledAfterError) });
249 let result = lock.read().await;
250 assert!(matches!(result, Err(RwTaskLockError::CalledAfterError)));
251 let result2 = lock.read().await;
252 assert!(matches!(result2, Err(RwTaskLockError::CalledAfterError)));
253 }
254
255 #[tokio::test]
256 async fn test_concurrent_read() {
257 use std::sync::Arc;
258 let lock = Arc::new(RwTaskLock::from_task(async {
259 tokio::time::sleep(std::time::Duration::from_millis(30)).await;
260 Ok::<_, RwTaskLockError>("concurrent".to_string())
261 }));
262 let lock1 = lock.clone();
263 let lock2 = lock.clone();
264 let (a, b) = tokio::join!(lock1.read(), lock2.read());
265 assert_eq!(*a.unwrap(), "concurrent");
266 assert_eq!(*b.unwrap(), "concurrent");
267 }
268
269 #[tokio::test]
270 async fn test_error_then_retrieval() {
271 let lock = RwTaskLock::<u8, RwTaskLockError>::from_task(async { Err(RwTaskLockError::CalledAfterError) });
272 let _ = lock.read().await;
273 let result = lock.read().await;
274 assert!(matches!(result, Err(RwTaskLockError::CalledAfterError)));
275 }
276
277 #[tokio::test]
278 async fn test_update_from_ready() {
279 let lock = RwTaskLock::from_value(100);
280 lock.update(|v| async move { Ok::<_, RwTaskLockError>(v + 1) }).await.unwrap();
281 let guard = lock.read().await.unwrap();
282 assert_eq!(*guard, 101);
283 }
284
285 #[tokio::test]
286 async fn test_update_chained_pending() {
287 use std::sync::Arc;
288 let lock = Arc::new(RwTaskLock::from_task(async {
289 tokio::time::sleep(std::time::Duration::from_millis(20)).await;
290 Ok::<_, RwTaskLockError>(5)
291 }));
292 let lock2 = lock.clone();
293 lock2.update(|v| async move { Ok::<_, RwTaskLockError>(v * 3) }).await.unwrap();
295 let guard = lock.read().await.unwrap();
296 assert_eq!(*guard, 15);
297 }
298
299 #[tokio::test]
300 async fn test_update_error_state() {
301 let lock = RwTaskLock::<i32, RwTaskLockError>::from_task(async { Err(RwTaskLockError::CalledAfterError) });
302 let _ = lock.read().await;
303 let result = lock.update(|v| async move { Ok::<_, RwTaskLockError>(v + 1) }).await;
304 assert!(matches!(result, Err(RwTaskLockError::CalledAfterError)));
305 }
306
307 #[tokio::test]
308 async fn test_update_to_error() {
309 let lock = RwTaskLock::from_value(123);
310 lock.update(|_v| async move { Err(RwTaskLockError::CalledAfterError) })
312 .await
313 .unwrap();
314 let result = lock.read().await;
315 assert!(matches!(result, Err(RwTaskLockError::CalledAfterError)));
316 }
317
318 #[tokio::test]
319 async fn test_multiple_updates() {
320 let lock = RwTaskLock::from_value(1);
321 lock.update(|v| async move { Ok::<_, RwTaskLockError>(v + 10) }).await.unwrap();
322 lock.update(|v| async move { Ok::<_, RwTaskLockError>(v * 2) }).await.unwrap();
323 let guard = lock.read().await.unwrap();
324 assert_eq!(*guard, 22);
325 }
326}