Skip to main content

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}