1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! **One frame's model duty** (§7.2): take the newest derivation, adopt what
//! changed under it, and settle the two off-frame hand-offs — the wire's read
//! path and, since bl-4841, its act path.
//!
//! Split out of [`app`](super)'s root at §12's budget. It is the whole of what
//! a frame asks the model to *do*: everything else the model offers is a read,
//! and everything it derives belongs to the worker.
use super::echo;
use crate::state::latest_snapshot;
use std::sync::Arc;
impl crate::AppModel {
/// One frame's whole model duty (§7.2): take the newest completed snapshot
/// if the worker published one, adopt an externally-changed `ui.json` from
/// it, and hold the §6 acknowledgement. Returns whether the render source
/// moved.
///
/// It never blocks on a derivation — the only wait is the pointer swap in
/// [`crate::state`] — and it never *starts* one. A frame that arrives
/// mid-pass renders the previous snapshot, which is the whole point.
pub fn refresh(&mut self) -> bool {
let latest = latest_snapshot(&self.cell);
let landed = !Arc::ptr_eq(&self.derived, &latest);
if landed {
self.derived = latest;
self.adopt_ui();
}
// A fired start focuses the conversation it started (§3.4), the first
// frame whose roster carries its root, and the pending echo it carries
// retires on the same predicate (§7.2). Over the roster, not the
// pointer swap — so it is asked every frame, and free with nothing
// pending.
self.adopt_started();
// The §3.4 raise claim retires on its own noun's predicate (bl-7407):
// the derivation enumerating the wall the start founded. Asked here,
// ahead of the fold, so the painted snapshot never carries the same
// workspace twice.
self.adopt_raised();
// Tell the follower which conversation is on screen and take whatever
// it has folded since the last frame (§7.2 live tail). Both are one
// lock and one compare, which is what a frame is allowed to cost.
crate::state::follow(&self.tail, self.followed_subject());
let followed = crate::state::taken_tail(&self.tail);
// The one fold of derivation + the non-derived facts (§7.2), run only
// when one of its inputs moved so the rendered `Arc` stays stable under
// `SnapMemo`.
if landed
|| self.started != self.folded
|| followed != self.followed
|| self.raised != self.folded_raise
{
self.followed = followed;
self.refold();
}
// The wire read path's one frame duty (REMOTE §1.2, bl-ae05): take the
// answers the asker landed and tell it what this window is standing on
// if that changed. Two channel drains and one set compare — no lock, no
// dial, and nothing here can wait on a socket.
self.wire.settle();
// The act path's own frame duty (REMOTE §9.8, bl-4841): take the
// receipts the poster landed and mark each act's root dirty now that
// the engine is done with it — the aftermath a dispatched verb used to
// run the instant it returned, run at the moment it actually happened.
self.settle_acts();
// The §6 ack is a state, not a gesture (bl-aa1f): re-stamp the focused
// agent's evidence every frame, so a signal that landed on the
// conversation the operator is reading is already seen. Free — §4.1
// elides a write whose bytes are unchanged.
self.ack_focused();
landed
}
/// **The §7.2 fold, run** (`echo::compose`) — with the three memos it keys
/// on brought up to date, so the rendered `Arc` stays stable while its
/// inputs are.
///
/// A method rather than the tail of [`refresh`](Self::refresh) because the
/// §3.4 raise claim needs it a second time: the claim is taken on the frame
/// the receipt lands ([`adopt_workspace`](Self::adopt_workspace)), and a
/// claim the rest of that same frame cannot see is a claim the composer it
/// exists for would miss.
pub(super) fn refold(&mut self) {
self.folded = self.started.clone();
self.folded_raise = self.raised.clone();
self.snap = echo::compose(
&self.derived,
self.started.as_ref(),
self.followed.as_deref(),
self.raised.as_deref(),
);
}
/// Adopt an external `ui.json` change the worker read for us (§4.1, I5):
/// unless it is our own echo (content-hash match), wholesale-adopt it — the
/// converging seen/pins path both instances share.
fn adopt_ui(&mut self) {
if let Some(bytes) = self.derived.ui_bytes.clone()
&& !self.ui.is_echo(&bytes)
{
self.ui.adopt(&bytes);
}
}
}