perspective_viewer/utils/pending_effects.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/// The element-scoped ledger of in-flight EFFECTS: every effectful public
19/// method (and the internal flows they schedule, e.g. the `before-resize`
20/// presize) registers an [`EffectGuard`] for the duration of its async
21/// work, whether or not the caller awaited it. `flush()` resolves only
22/// after the ledger drains, so "called before `flush`" implies "applied
23/// before `flush` resolves".
24#[derive(Clone, Default)]
25pub struct EffectLedger(Rc<EffectLedgerData>);
26
27#[derive(Default)]
28struct EffectLedgerData {
29 count: Cell<u32>,
30 on_drain: PubSub<()>,
31}
32
33pub struct EffectGuard(EffectLedger);
34
35impl EffectLedger {
36 pub fn guard(&self) -> EffectGuard {
37 self.0.count.set(self.0.count.get() + 1);
38 EffectGuard(self.clone())
39 }
40
41 pub fn is_empty(&self) -> bool {
42 self.0.count.get() == 0
43 }
44
45 pub async fn settle(&self) {
46 while self.0.count.get() > 0 {
47 if self.0.on_drain.read_next().await.is_err() {
48 break;
49 }
50 }
51 }
52}
53
54impl Drop for EffectGuard {
55 fn drop(&mut self) {
56 let data = &self.0.0;
57 data.count.set(data.count.get() - 1);
58 if data.count.get() == 0 {
59 data.on_drain.emit(());
60 }
61 }
62}