1use std::thread::sleep;
2use std::time::{Duration, Instant};
3
4use tracing::{Level, event};
5
6use super::clock::{BasicClock, Clock};
7
8#[derive(Debug)]
9struct SignedDuration {
10 negative: bool,
11 magnitude: Duration,
12}
13
14impl SignedDuration {
15 pub const ZERO: SignedDuration = SignedDuration {
16 negative: false,
17 magnitude: Duration::ZERO,
18 };
19}
20
21impl From<Duration> for SignedDuration {
22 fn from(magnitude: Duration) -> Self {
23 Self {
24 negative: false,
25 magnitude,
26 }
27 }
28}
29
30impl SignedDuration {
31 fn checked_sub(&self, d: Duration) -> Option<SignedDuration> {
32 if self.negative {
33 self.magnitude.checked_add(d).map(|result| SignedDuration {
34 negative: true,
35 magnitude: result,
36 })
37 } else {
38 match self.magnitude.checked_sub(d) {
39 Some(diff) => Some(SignedDuration {
40 negative: false,
41 magnitude: diff,
42 }),
43 None => match d.checked_sub(self.magnitude) {
44 Some(diff) => Some(SignedDuration {
45 negative: true,
46 magnitude: diff,
47 }),
48 None => {
49 panic!(
50 "checked_sub inconsistent for {:?} - {:?}",
51 self.magnitude, d
52 );
53 }
54 },
55 }
56 }
57 }
58
59 fn checked_add(&self, d: Duration) -> Option<SignedDuration> {
60 if self.negative {
61 match d.checked_sub(self.magnitude) {
62 Some(diff) => Some(SignedDuration {
63 negative: false,
64 magnitude: diff,
65 }),
66 None => match self.magnitude.checked_sub(d) {
67 Some(diff) => Some(SignedDuration {
68 negative: true,
69 magnitude: diff,
70 }),
71 None => {
72 panic!(
73 "checked_add inconsistent for {:?} - {:?}",
74 self.magnitude, d
75 );
76 }
77 },
78 }
79 } else {
80 self.magnitude.checked_add(d).map(|tot| SignedDuration {
81 negative: false,
82 magnitude: tot,
83 })
84 }
85 }
86}
87
88#[derive(Debug)]
102pub struct MinimalSleeper {
103 min_sleep: Duration,
105
106 sleep_owed: SignedDuration,
107
108 total_cumulative_sleep: Duration,
109}
110
111impl MinimalSleeper {
112 pub fn new(min_sleep: Duration) -> MinimalSleeper {
113 MinimalSleeper {
114 min_sleep,
115 sleep_owed: SignedDuration::ZERO,
116 total_cumulative_sleep: Duration::ZERO,
117 }
118 }
119
120 fn really_sleep(&mut self) {
121 match self.sleep_owed {
122 SignedDuration {
123 negative: false,
124 magnitude,
125 } => {
126 let then = Instant::now();
127 event!(Level::DEBUG, "Sleeping for {:?}...", self.sleep_owed);
128 sleep(magnitude);
129 self.total_cumulative_sleep += magnitude;
130 let now = Instant::now();
131 let slept_for = now - then;
132 event!(
133 Level::TRACE,
134 "MinimalSleeper: owed sleep is {:?}, actually slept for {:?}",
135 self.sleep_owed,
136 slept_for
137 );
138 match self.sleep_owed.checked_sub(slept_for) {
139 Some(remainder) => {
140 self.sleep_owed = remainder;
141 }
142 None => {
143 panic!("MinimalSleeper::really_sleep: overflow in sleep_owed");
144 }
145 }
146 }
147 _ => unreachable!(), }
149 event!(
150 Level::DEBUG,
151 "MinimalSleeper: updated sleep debt is {:?}...",
152 self.sleep_owed
153 );
154 }
155
156 pub fn sleep(&mut self, duration: &Duration) {
157 match self.sleep_owed.checked_add(*duration) {
158 Some(more) => {
159 self.sleep_owed = more;
160 match self.sleep_owed {
161 SignedDuration {
162 negative: false,
163 magnitude,
164 } if magnitude > self.min_sleep => self.really_sleep(),
165 _ => {
166 }
170 }
171 }
172 None => {
173 panic!("MinimalSleeper::really_sleep: overflow in sleep_owed");
174 }
175 }
176 }
177}
178
179impl Drop for MinimalSleeper {
180 fn drop(&mut self) {
181 event!(
182 Level::INFO,
183 "MinimalSleeper: drop: total cumulative sleep is {:?}",
184 self.total_cumulative_sleep
185 );
186 }
187}
188
189pub fn time_passes(
190 clk: &mut BasicClock,
191 sleeper: &mut MinimalSleeper,
192 t: &Duration,
193 multiplier: Option<f64>,
194) {
195 clk.consume(t);
196 if let Some(m) = multiplier {
197 sleeper.sleep(&t.mul_f64(m));
198 }
199}