File size: 2,632 Bytes
1d3f990
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
//! WORM Checkpointing — Append-only rollback snapshots

use crate::memory::{Memory, Address};
use anyhow::{Result, Context};
use std::sync::Arc;
use parking_lot::RwLock;
use uuid::Uuid;

#[derive(Debug, Clone)]
pub struct Checkpoint {
    pub id: String,
    pub ip: Address,
    pub step_count: u64,
    pub mutation_log_len: usize,
    pub memory_snapshot: Vec<i64>,
    pub timestamp: u64,
}

impl Checkpoint {
    pub fn id(&self) -> &str {
        &self.id
    }
}

pub struct CheckpointManager {
    memory: Arc<RwLock<Memory>>,
    checkpoints: Vec<Checkpoint>,
}

impl CheckpointManager {
    pub fn new(memory: Arc<RwLock<Memory>>) -> Self {
        Self {
            memory,
            checkpoints: Vec::new(),
        }
    }

    pub fn create(&mut self, ip: Address, step_count: u64, mutation_log_len: usize) -> Checkpoint {
        use std::time::{SystemTime, UNIX_EPOCH};

        let mem = self.memory.read();
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let checkpoint = Checkpoint {
            id: Uuid::new_v4().to_string(),
            ip,
            step_count,
            mutation_log_len,
            memory_snapshot: mem.as_slice().to_vec(),
            timestamp,
        };

        self.checkpoints.push(checkpoint.clone());
        checkpoint
    }

    pub fn restore(&self, checkpoint: &Checkpoint, memory: &mut Memory) -> Result<()> {
        if checkpoint.memory_snapshot.len() != memory.size() {
            return Err(anyhow::anyhow!("Checkpoint memory size mismatch"));
        }

        for (addr, &word) in checkpoint.memory_snapshot.iter().enumerate() {
            memory.write(addr, word)?;
        }

        Ok(())
    }

    pub fn list_checkpoints(&self) -> &[Checkpoint] {
        &self.checkpoints
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_checkpoint_create_restore() {
        let mut mem = Memory::new(1024);
        mem.write(0, 42).unwrap();
        mem.write(1, 100).unwrap();

        let mem = Arc::new(RwLock::new(mem));
        let mut mgr = CheckpointManager::new(mem.clone());

        let cp = mgr.create(0, 0, 0);
        assert_eq!(cp.step_count, 0);

        {
            let mut m = mem.write();
            m.write(0, 999).unwrap();
        }

        {
            let mut m = mem.write();
            mgr.restore(&cp, &mut m).unwrap();
        }

        let m = mem.read();
        assert_eq!(m.read(0), Some(42));
    }
}