The read operation isn't a read-only operation: it can trigger
read-repair in the case where a hole is discovered. The PULSE
model needs to be aware of this kind of thing.
Imagine that we have a 3-way race, between an append to LPN 1,
a read of LPN 1, and a trim of LPN 1. There is a single chain
of length 3. The FLUs in the chain are annotated below with
"F1", "F2", and "F3". Note also the indentation levels, with
F1's indented is smaller than F2's << F3's.
2,{call,<0.8748.3>,{append,<<0>>,will_be,1}}},
4,{call,<0.8746.3>,{read,1}}},
6,{call,<0.8747.3>,{trim,1,will_fail,error_unwritten}}},
6, Read has contacted tail of chain, it is unwritten. Time for repair.
6,{read_repair,1,[<0.8741.3>,<0.8742.3>,<0.8743.3>]}},
6, F1:{flu,write,<0.8741.3>,1,ok}},
7, F1:{flu,trim,<0.8741.3>,1,ok}}, % by repair
9,{read_repair,1,fill,<0.8742.3>}},
9, F2:{flu,trim,<0.8742.3>,1,error_unwritten}},
9,{read_repair,1,<0.8741.3>,trimmed}},
10,{result,<0.8747.3>,error_unwritten}},
Trim operation from time=6 stops here
10, F2:{flu,write,<0.8742.3>,1,ok}},
11, F2:{flu,fill,<0.8742.3>,1,error_overwritten}},
12, F3:{flu,write,<0.8743.3>,1,ok}},
12,{read_repair,1,fill,<0.8742.3>,overwritten,try_trim}},
13,{result,<0.8748.3>,{ok,1}}}, % append/write to LPN 1
13, F2:{flu,trim,<0.8742.3>,1,ok}},
14,{read_repair,1,fill,<0.8743.3>}},
15, F3:{flu,fill,<0.8743.3>,1,error_overwritten}},
16,{read_repair,1,fill,<0.8743.3>,overwritten,try_to_trim}},
17, F3:{flu,trim,<0.8743.3>,1,ok}},
18,{result,<0.8746.3>,error_trimmed}}]
Also, for peace of mind (I hope), I've added this -ifndef to introduce
a bug that should cause the new exactly-once append_page() check to fail.
This should make it easier to change the model and *TEST* the changes,
to avoid breaking the model without ever knowing it.