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
//! **One frame's model duty** (§7.2): take the newest derivation, adopt what
//! changed under it, and settle the off-frame hand-offs — the wire's read path,
//! its act path (bl-4841) and the follow lane (bl-73e7).
//!
//! 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();
// 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 || self.raised != self.folded_raise {
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 follow lane's own frame duty (REMOTE §3, bl-73e7): take the folds
// the lane landed and tell it which conversation is on screen if that
// changed. One channel drain and one compare, on the lane that never
// waits on the standing set — which is the whole reason it is a second
// one.
self.lane.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.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);
}
}
}