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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
//! Rewind: periodic snapshots plus replay from the nearest one
//! (`ROADMAP.md` §4.5, and phase 9's third gate item).
//!
//! Rewind is not a separate mechanism. It is
//! [`Machine::load`](crate::machine::Machine::load) and
//! [`core::record`](crate::core::record) used together, and this module is the
//! bookkeeping that makes the pair usable: keep a snapshot every so often,
//! keep the input log, and to reach an earlier instant restore the newest
//! snapshot at or before it and replay forward.
//!
//! ```text
//! snapshots S0 S1 S2 S3
//! │ │ │ │
//! timeline ───●─────────●─────────●─────────●──────────► now
//! ▲
//! rewind_to(t) restores S1 and replays the
//! log from S1's instant up to t
//! ```
//!
//! # Cadence is a memory/latency trade and nothing else
//!
//! The snapshot interval decides two numbers and no others: how much memory a
//! session costs (one snapshot per interval), and how long a rewind takes (up
//! to one interval of re-execution). [`DEFAULT_CADENCE`] is one virtual second,
//! which on the boards in this tree is a handful of milliseconds of host time
//! to replay and a snapshot small enough to keep hundreds of. A machine with
//! gigabytes of RAM wants a different answer and will want §4.5's page-indexed
//! incremental encoding first — this module deliberately has no opinion about
//! what is inside a snapshot, only about when one is taken.
//!
//! A snapshot is taken **on a round boundary**, because that is where the
//! machine is a complete architectural state and where inputs are delivered.
//! Taking one mid-round would restore to an instant no input log entry can be
//! aligned against.
//!
//! # The replay cursor is derived, not saved
//!
//! The piece that makes a rewind land where it says it does, and it is
//! deliberately *not* in the snapshot.
//! [`Machine::load`](crate::machine::Machine::load) calls
//! [`Recorder::rewind_to`](crate::core::record::Recorder::rewind_to), which
//! seeks the log to the restored instant by binary search. A cursor kept in the
//! snapshot would be a second copy of a number the log already implies, and
//! `CLAUDE.md` is explicit that derived state is rebuilt rather than saved; a
//! cursor kept nowhere would restart the recording from the beginning and hand
//! the guest every keystroke of the run a second time. Seeking is the third
//! option, and it is the one that also works for a debugger loading a snapshot
//! with no timeline in sight.
//!
//! # What a rewind does to host state that cannot be rewound
//!
//! The interesting half, and it is not solvable — only decidable. Three kinds
//! of host state exist on the other side of the seam, and the timeline treats
//! them differently on purpose:
//!
//! * **Queued input the guest has not consumed yet.** Rewindable, and rewound:
//! [`Recorder::rewind_to`](crate::core::record::Recorder::rewind_to) calls
//! [`InputSink::on_rewind`](crate::core::record::InputSink::on_rewind) on
//! every channel so a port drops what it is holding, and the log re-delivers
//! the same bytes on the way forward. Without this the bytes arrive twice.
//!
//! * **Output the guest has already emitted.** *Not* rewindable, and the
//! timeline says so rather than pretending: characters printed to a terminal,
//! bytes written to a socket, samples already in a sound card's ring have
//! left. A rewound machine will emit them again, and the host sees them
//! twice. That is the correct behaviour for a debugger — the guest really did
//! do it twice — and the wrong behaviour for anything that treats the output
//! as a side effect on the world. A frontend that cares suppresses output
//! between the rewind target and the point it rewound from; it has the two
//! instants, and the machine does not have the frontend's policy.
//!
//! * **Host handles: an open file, a socket, a disk image.** Neither rewound
//! nor rewindable here. A machine snapshot taken while a write-back cache
//! holds dirty blocks and restored without a matching disk snapshot restores
//! to a corrupt guest filesystem — §4.5 states that as the *atomicity rule*
//! and settles it in favour of "storage is snapshotted with the machine or
//! not at all". Until a block backend can snapshot itself, [`Timeline`]
//! rewinds the machine and leaves the backing store where it was, which is
//! sound for a read-only image and unsound for a writable one. The honest
//! statement is that this is a limitation of the *storage* layer rather than
//! of rewind, and it moves when §7.1's cache-flush contract lands.
//!
//! # Example
//!
//! ```no_run
//! # use std::sync::Arc;
//! # use rsemu::core::clock::GlobalTime;
//! # use rsemu::core::record::Recorder;
//! # use rsemu::machine::{Machine, Timeline};
//! # fn demo(machine: &mut Machine) -> rsemu::Result<()> {
//! let recorder = Arc::new(Recorder::recording());
//! machine.set_recorder(Arc::clone(&recorder))?;
//!
//! let mut timeline = Timeline::new(recorder, GlobalTime::from_nanos(1_000_000));
//! timeline.run_for(machine, GlobalTime::from_nanos(50_000_000))?;
//!
//! let landed = timeline.rewind_to(machine, GlobalTime::from_nanos(20_000_000))?;
//! assert!(landed <= GlobalTime::from_nanos(20_000_000));
//! # Ok(())
//! # }
//! ```
use ToString;
use Arc;
use Vec;
use crateGlobalTime;
use crate;
use crateRecorder;
use crateMachine;
/// The default interval between snapshots: one virtual second.
///
/// See the module docs for what the number buys. It is a `GlobalTime` rather
/// than a count of rounds because a round is not a fixed length of virtual
/// time — a quantum interrupted by an event is shorter — and a cadence that
/// drifted with event density would make rewind latency unpredictable for no
/// reason.
pub const DEFAULT_CADENCE: GlobalTime = from_nanos;
/// One snapshot and the instant it was taken at.
/// A machine's history: periodic snapshots beside the input log.
///
/// Holds the recorder rather than the machine, so a caller keeps ownership of
/// the machine and can do anything else with it between calls. Every method
/// that needs the machine takes it, which also makes it impossible to point a
/// timeline at a machine other than the one whose recorder it holds without
/// noticing — the shape check on the snapshot catches it.