1use crate::types::{Budget, CancelReason, RegionId, TaskId, Time};
4use std::task::Waker;
5
6pub const MAX_MASK_DEPTH: u32 = 64;
14
15#[derive(Debug, Clone)]
24pub struct CheckpointState {
25 pub last_checkpoint: Option<Time>,
27 pub last_message: Option<String>,
29 pub checkpoint_count: u64,
31}
32
33impl Default for CheckpointState {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl CheckpointState {
40 #[inline]
42 #[must_use]
43 pub fn new() -> Self {
44 Self {
45 last_checkpoint: None,
46 last_message: None,
47 checkpoint_count: 0,
48 }
49 }
50
51 #[inline]
53 pub fn record(&mut self) {
54 self.record_at(crate::time::wall_now());
55 }
56
57 #[inline]
59 pub fn record_at(&mut self, at: Time) {
60 self.last_checkpoint = Some(at);
61 self.last_message = None;
62 self.checkpoint_count += 1;
63 }
64
65 #[inline]
67 pub fn record_with_message(&mut self, message: String) {
68 self.record_with_message_at(message, crate::time::wall_now());
69 }
70
71 #[inline]
73 pub fn record_with_message_at(&mut self, message: String, at: Time) {
74 self.last_checkpoint = Some(at);
75 self.last_message = Some(message);
76 self.checkpoint_count += 1;
77 }
78}
79
80#[derive(Debug)]
86pub struct CxInner {
87 pub region: RegionId,
89 pub task: TaskId,
91 pub task_type: Option<String>,
93 pub budget: Budget,
95 pub budget_baseline: Budget,
97 pub cancel_requested: bool,
99 pub cancel_reason: Option<CancelReason>,
101 pub cancel_acknowledged: bool,
103 pub cancel_waker: Option<Waker>,
105 pub mask_depth: u32,
107 pub checkpoint_state: CheckpointState,
109 pub fast_cancel: std::sync::Arc<std::sync::atomic::AtomicBool>,
111}
112
113impl CxInner {
114 #[must_use]
116 pub fn new(region: RegionId, task: TaskId, budget: Budget) -> Self {
117 Self {
118 region,
119 task,
120 task_type: None,
121 budget,
122 budget_baseline: budget,
123 cancel_requested: false,
124 cancel_reason: None,
125 cancel_acknowledged: false,
126 cancel_waker: None,
127 mask_depth: 0,
128 checkpoint_state: CheckpointState::new(),
129 fast_cancel: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 fn init_test(name: &str) {
139 crate::test_utils::init_test_logging();
140 crate::test_phase!(name);
141 }
142
143 #[test]
144 fn test_checkpoint_state_default() {
145 init_test("test_checkpoint_state_default");
146 let state = CheckpointState::new();
147 crate::assert_with_log!(
148 state.last_checkpoint.is_none(),
149 "last_checkpoint",
150 true,
151 state.last_checkpoint.is_none()
152 );
153 crate::assert_with_log!(
154 state.last_message.is_none(),
155 "last_message",
156 true,
157 state.last_message.is_none()
158 );
159 crate::assert_with_log!(
160 state.checkpoint_count == 0,
161 "checkpoint_count",
162 0,
163 state.checkpoint_count
164 );
165 crate::test_complete!("test_checkpoint_state_default");
166 }
167
168 #[test]
169 fn test_checkpoint_state_record() {
170 init_test("test_checkpoint_state_record");
171 let mut state = CheckpointState::new();
172 state.record();
173 crate::assert_with_log!(
174 state.last_checkpoint.is_some(),
175 "last_checkpoint",
176 true,
177 state.last_checkpoint.is_some()
178 );
179 crate::assert_with_log!(
180 state.last_message.is_none(),
181 "last_message",
182 true,
183 state.last_message.is_none()
184 );
185 crate::assert_with_log!(
186 state.checkpoint_count == 1,
187 "checkpoint_count",
188 1,
189 state.checkpoint_count
190 );
191 state.record();
192 crate::assert_with_log!(
193 state.checkpoint_count == 2,
194 "checkpoint_count 2",
195 2,
196 state.checkpoint_count
197 );
198 crate::test_complete!("test_checkpoint_state_record");
199 }
200
201 #[test]
202 fn test_checkpoint_state_record_at() {
203 init_test("test_checkpoint_state_record_at");
204 let mut state = CheckpointState::new();
205 let at = Time::from_nanos(123);
206
207 state.record_at(at);
208
209 crate::assert_with_log!(
210 state.last_checkpoint == Some(at),
211 "explicit checkpoint instant stored",
212 format!("{at:?}"),
213 format!("{:?}", state.last_checkpoint)
214 );
215 crate::assert_with_log!(
216 state.last_message.is_none(),
217 "record_at clears message",
218 true,
219 state.last_message.is_none()
220 );
221 crate::assert_with_log!(
222 state.checkpoint_count == 1,
223 "record_at increments count",
224 1,
225 state.checkpoint_count
226 );
227 crate::test_complete!("test_checkpoint_state_record_at");
228 }
229
230 #[test]
231 fn test_checkpoint_state_record_with_message() {
232 init_test("test_checkpoint_state_record_with_message");
233 let mut state = CheckpointState::new();
234 state.record_with_message("hello".to_string());
235 crate::assert_with_log!(
236 state.last_checkpoint.is_some(),
237 "last_checkpoint",
238 true,
239 state.last_checkpoint.is_some()
240 );
241 crate::assert_with_log!(
242 state.last_message.as_deref() == Some("hello"),
243 "last_message",
244 Some("hello"),
245 state.last_message.as_deref()
246 );
247 crate::assert_with_log!(
248 state.checkpoint_count == 1,
249 "checkpoint_count",
250 1,
251 state.checkpoint_count
252 );
253 state.record();
254 crate::assert_with_log!(
255 state.last_message.is_none(),
256 "last_message cleared",
257 true,
258 state.last_message.is_none()
259 );
260 crate::test_complete!("test_checkpoint_state_record_with_message");
261 }
262
263 #[test]
264 fn test_checkpoint_state_record_with_message_at() {
265 init_test("test_checkpoint_state_record_with_message_at");
266 let mut state = CheckpointState::new();
267 let at = Time::from_nanos(456);
268
269 state.record_with_message_at("hello".to_string(), at);
270
271 crate::assert_with_log!(
272 state.last_checkpoint == Some(at),
273 "explicit checkpoint instant stored",
274 format!("{at:?}"),
275 format!("{:?}", state.last_checkpoint)
276 );
277 crate::assert_with_log!(
278 state.last_message.as_deref() == Some("hello"),
279 "record_with_message_at stores message",
280 Some("hello"),
281 state.last_message.as_deref()
282 );
283 crate::assert_with_log!(
284 state.checkpoint_count == 1,
285 "record_with_message_at increments count",
286 1,
287 state.checkpoint_count
288 );
289 crate::test_complete!("test_checkpoint_state_record_with_message_at");
290 }
291
292 #[test]
293 fn test_checkpoint_state_message_overwrite() {
294 init_test("test_checkpoint_state_message_overwrite");
295 let mut state = CheckpointState::new();
296 state.record_with_message("first".to_string());
297 state.record_with_message("second".to_string());
298 crate::assert_with_log!(
299 state.last_message.as_deref() == Some("second"),
300 "last_message overwrite",
301 Some("second"),
302 state.last_message.as_deref()
303 );
304 crate::assert_with_log!(
305 state.checkpoint_count == 2,
306 "checkpoint_count",
307 2,
308 state.checkpoint_count
309 );
310 crate::test_complete!("test_checkpoint_state_message_overwrite");
311 }
312
313 #[test]
314 fn test_cx_inner_new() {
315 init_test("test_cx_inner_new");
316 let region = RegionId::testing_default();
317 let task = TaskId::testing_default();
318 let budget = Budget::new();
319 let cx = CxInner::new(region, task, budget);
320 crate::assert_with_log!(cx.region == region, "region", region, cx.region);
321 crate::assert_with_log!(cx.task == task, "task", task, cx.task);
322 crate::assert_with_log!(cx.budget == budget, "budget", budget, cx.budget);
323 crate::assert_with_log!(
324 cx.budget_baseline == budget,
325 "budget_baseline",
326 budget,
327 cx.budget_baseline
328 );
329 crate::assert_with_log!(
330 !cx.cancel_requested,
331 "cancel_requested",
332 false,
333 cx.cancel_requested
334 );
335 crate::assert_with_log!(
336 cx.cancel_reason.is_none(),
337 "cancel_reason",
338 true,
339 cx.cancel_reason.is_none()
340 );
341 crate::assert_with_log!(cx.mask_depth == 0, "mask_depth", 0, cx.mask_depth);
342 crate::test_complete!("test_cx_inner_new");
343 }
344
345 #[test]
350 fn checkpoint_state_debug_clone_default() {
351 let def = CheckpointState::default();
352 assert!(def.last_checkpoint.is_none());
353 assert!(def.last_message.is_none());
354 assert_eq!(def.checkpoint_count, 0);
355 let dbg = format!("{def:?}");
356 assert!(dbg.contains("CheckpointState"), "{dbg}");
357
358 let mut state = CheckpointState::new();
359 state.record_with_message("progress".into());
360 let cloned = state.clone();
361 assert_eq!(cloned.checkpoint_count, 1);
362 assert_eq!(cloned.last_message.as_deref(), Some("progress"));
363 }
364
365 #[test]
366 fn cx_inner_debug() {
367 let region = RegionId::testing_default();
368 let task = TaskId::testing_default();
369 let cx = CxInner::new(region, task, Budget::new());
370 let dbg = format!("{cx:?}");
371 assert!(dbg.contains("CxInner"), "{dbg}");
372 }
373}