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}