perspective_viewer/utils/debounce.rs
1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors. ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use std::cell::Cell;
14use std::future::Future;
15use std::rc::Rc;
16
17use async_lock::Mutex;
18use perspective_js::utils::ApiResult;
19
20#[derive(Default)]
21struct DebounceMutexData {
22 id: Cell<u64>,
23 mutex: Mutex<u64>,
24}
25
26/// An async `Mutex` type specialized for Perspective's rendering, which
27/// debounces calls in addition to providing exclusivity. Calling `debounce`
28/// with a _cancellable_ [`Future`] will resolve only after at least one
29/// _complete_ evaluation of a call awaiting the lock.
30#[derive(Clone, Default)]
31pub struct DebounceMutex(Rc<DebounceMutexData>);
32
33impl DebounceMutex {
34 /// Lock like a normal `Mutex`.
35 pub async fn lock<T>(&self, f: impl Future<Output = T>) -> T {
36 let mut last = self.0.mutex.lock().await;
37 let next = self.0.id.get();
38 let result = f.await;
39 *last = next;
40 result
41 }
42
43 /// Lock and also debounce `f`, which should be cancellable.
44 pub async fn debounce(&self, f: impl Future<Output = ApiResult<()>>) -> ApiResult<()> {
45 let next = self.0.id.get() + 1;
46 let mut last = self.0.mutex.lock().await;
47 if *last < next {
48 let next = self.0.id.get() + 1;
49 self.0.id.set(next);
50 let result = f.await;
51 if result.is_ok() {
52 *last = next;
53 }
54
55 result
56 } else {
57 Ok(())
58 }
59 }
60}