Skip to main content

rsynko_memory/
session.rs

1use rsynko_session::*;
2use std::cell::Cell;
3use std::rc::Rc;
4use std::time::Duration;
5
6/// Attends to runs that say what they were authored to say, one thing per pass.
7///
8/// Nothing here happens elsewhere: a run is a count of things left to say, and time passes only
9/// when a scenario says it does, so what attendance means can be read without waiting for it.
10#[derive(Debug, Default)]
11pub struct ReferenceSession {
12    requests: Vec<ReferenceRequest>,
13    clock: Duration,
14}
15
16/// Holds one authored request, and everything it has been told about its run.
17#[derive(Debug)]
18struct ReferenceRequest {
19    id: usize,
20    wanted: Wanted,
21    says: usize,
22    beginnable: bool,
23    attended: bool,
24    told: Vec<Telling>,
25    abandonments: Rc<Cell<usize>>,
26}
27
28/// Carries one authored run: the work it has left, what it has said, and whether it was stopped.
29#[derive(Debug)]
30pub struct ReferenceRun {
31    work: usize,
32    said: Vec<usize>,
33    held: bool,
34    abandoning: bool,
35    abandonments: Rc<Cell<usize>>,
36}
37
38impl ReferenceSession {
39    /// Observes one authored request.
40    fn request(&self, id: usize) -> &ReferenceRequest {
41        self.requests.iter().find(|request| request.id == id).expect("an authored request")
42    }
43
44    /// Observes one authored request to tell it something.
45    fn request_mut(&mut self, id: usize) -> &mut ReferenceRequest {
46        self.requests.iter_mut().find(|request| request.id == id).expect("an authored request")
47    }
48
49    /// Authors one request, and names it.
50    fn author(&mut self, says: usize, beginnable: bool) -> usize {
51        let id = self.requests.len();
52        self.requests.push(ReferenceRequest {
53            id,
54            wanted: Wanted::Running,
55            says,
56            beginnable,
57            attended: false,
58            told: Vec::new(),
59            abandonments: Rc::new(Cell::new(0)),
60        });
61        id
62    }
63}
64
65impl SessionSorts for ReferenceSession {
66    type Id = usize;
67    type Run = ReferenceRun;
68    type Report = usize;
69    type Ending = ();
70    type Refusal = String;
71}
72
73impl UndertakingAlg for ReferenceSession {
74    fn unattended(&self) -> Vec<usize> {
75        self.requests.iter().filter(|request| !request.attended).map(|request| request.id).collect()
76    }
77
78    fn begin(&self, id: &usize) -> Result<ReferenceRun, String> {
79        let request = self.request(*id);
80        if !request.beginnable {
81            return Err("the work refuses to begin".to_owned());
82        }
83        Ok(ReferenceRun {
84            work: request.says,
85            said: Vec::new(),
86            held: false,
87            abandoning: false,
88            abandonments: Rc::clone(&request.abandonments),
89        })
90    }
91}
92
93impl RunReadAlg for ReferenceSession {
94    fn run_is_over(&self, run: &ReferenceRun) -> bool {
95        run.work == 0
96    }
97
98    fn read_run(&self, run: &mut ReferenceRun) -> Vec<usize> {
99        if run.abandoning {
100            // Being told to stop is honored where the run next looks, which is here.
101            run.work = 0;
102            run.said.clear();
103            return Vec::new();
104        }
105        if run.held {
106            return Vec::new();
107        }
108        // What a run says reaches a reader after it was said, so the last thing it says is still
109        // waiting to be read once the run itself is over.
110        let said = std::mem::take(&mut run.said);
111        if run.work > 0 {
112            run.work -= 1;
113            run.said.push(run.work);
114        }
115        said
116    }
117
118    fn end_run(&self, _run: ReferenceRun) -> Result<(), String> {
119        Ok(())
120    }
121}
122
123impl RunHoldAlg for ReferenceSession {
124    fn holding_is_possible(&self) -> bool {
125        true
126    }
127
128    fn hold_run(&self, run: &mut ReferenceRun, held: bool) {
129        run.held = held;
130    }
131
132    fn abandon_run(&self, run: &mut ReferenceRun) {
133        run.abandoning = true;
134        run.abandonments.set(run.abandonments.get() + 1);
135    }
136}
137
138impl AttentionAlg for ReferenceSession {
139    fn begun(&mut self, id: &usize, _holdable: bool) {
140        let request = self.request_mut(*id);
141        request.attended = true;
142        request.told.push(Telling::Begun);
143    }
144
145    fn heard(&mut self, id: &usize, _report: usize) {
146        self.request_mut(*id).told.push(Telling::Heard);
147    }
148
149    fn ran_for(&mut self, id: &usize, elapsed: Duration) {
150        self.request_mut(*id).told.push(Telling::RanFor(elapsed));
151    }
152
153    fn ended(&mut self, id: &usize, ending: Result<(), String>) {
154        let request = self.request_mut(*id);
155        // Work that refused to begin is over the moment it refused, and is not begun again.
156        request.attended = true;
157        request.told.push(match ending {
158            Ok(()) => Telling::Ended,
159            Err(_) => Telling::Refused,
160        });
161    }
162
163    fn wanted(&self, id: &usize) -> Wanted {
164        self.request(*id).wanted
165    }
166}
167
168impl ClockAlg for ReferenceSession {
169    type Moment = Duration;
170
171    fn now(&self) -> Duration {
172        self.clock
173    }
174
175    fn since(&self, moment: &Duration) -> Duration {
176        self.clock.saturating_sub(*moment)
177    }
178}
179
180impl SessionLawFixture for ReferenceSession {
181    fn law_wanting(&mut self, says: usize) -> usize {
182        self.author(says, true)
183    }
184
185    fn law_unbeginnable(&mut self) -> usize {
186        self.author(0, false)
187    }
188
189    fn law_wants(&mut self, id: &usize, wanted: Wanted) {
190        self.request_mut(*id).wanted = wanted;
191    }
192
193    fn law_passes(&mut self, elapsed: Duration) {
194        self.clock = self.clock.saturating_add(elapsed);
195    }
196
197    fn law_told(&self, id: &usize) -> Vec<Telling> {
198        self.request(*id).told.clone()
199    }
200
201    fn law_abandonments(&self, id: &usize) -> usize {
202        self.request(*id).abandonments.get()
203    }
204}