Skip to main content

perspective_viewer/utils/
in_flight.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::rc::Rc;
15
16use super::pubsub::PubSub;
17
18/// A refcounted account of concurrent in-flight work, moved only by
19/// [`InFlightGuard`]'s `Drop` so no exit path can strand the count.
20#[derive(Clone, Default)]
21pub struct InFlight(Rc<InFlightData>);
22
23#[derive(Default)]
24struct InFlightData {
25    count: Cell<u32>,
26    changed: PubSub<u32>,
27}
28
29/// One unit of in-flight work, moved INTO the future it accounts for.
30#[must_use]
31pub struct InFlightGuard(InFlight);
32
33impl InFlight {
34    pub fn guard(&self) -> InFlightGuard {
35        let count = self.0.count.get() + 1;
36        self.0.count.set(count);
37        self.0.changed.emit(count);
38        InFlightGuard(self.clone())
39    }
40
41    pub fn count(&self) -> u32 {
42        self.0.count.get()
43    }
44
45    pub fn is_empty(&self) -> bool {
46        self.0.count.get() == 0
47    }
48
49    /// Fires with the ABSOLUTE [`Self::count`] on both edges, so subscribers
50    /// assign rather than accumulate.
51    pub fn changed(&self) -> &PubSub<u32> {
52        &self.0.changed
53    }
54
55    /// Resolve once every unit in flight has settled.
56    pub async fn settle(&self) {
57        while self.0.count.get() > 0 {
58            if self.0.changed.read_next().await.is_err() {
59                break;
60            }
61        }
62    }
63}
64
65impl Drop for InFlightGuard {
66    fn drop(&mut self) {
67        let data = &self.0.0;
68        let count = data.count.get();
69        debug_assert!(count > 0, "InFlight underflow");
70        let count = count.saturating_sub(1);
71        data.count.set(count);
72        data.changed.emit(count);
73    }
74}