fedimint_core/util/update_merge.rs
1use futures::Future;
2use tokio::sync::Mutex;
3
4#[derive(Debug)]
5pub struct UpdateMerge {
6 last_failed: Mutex<bool>,
7}
8
9impl Default for UpdateMerge {
10 fn default() -> Self {
11 Self {
12 last_failed: Mutex::new(false),
13 }
14 }
15}
16impl UpdateMerge {
17 /// Merges concurrent futures execution.
18 ///
19 /// If two `merge` are called concurrently, the calls are merged.
20 /// But if the first call fails, the second call is still run again.
21 ///
22 /// The future `fut` is never executed concurrently.
23 pub async fn merge<E>(&self, fut: impl Future<Output = Result<(), E>>) -> Result<(), E> {
24 let mut guard = if let Ok(guard) = self.last_failed.try_lock() {
25 // not running => run now
26 guard
27 } else {
28 // already running concurrently
29 // wait for other call to return
30 let guard = self.last_failed.lock().await;
31 if *guard {
32 // Last call failed. Run again.
33 guard
34 } else {
35 // Last call completed successfully. Merge the call.
36 return Ok(());
37 }
38 };
39 // future may panic, use mark as failed initially
40 *guard = true;
41 // run the future and save last call status
42 let result = fut.await;
43 *guard = result.is_err();
44 result
45 }
46}
47
48#[cfg(test)]
49mod tests;