1use std::sync::{Arc, Weak};
2
3use atomic_refcell::AtomicRefCell;
4use log::trace;
5use shadow_shim_helper_rs::emulated_time::EmulatedTime;
6use shadow_shim_helper_rs::simulation_time::SimulationTime;
7
8use super::host::Host;
9use crate::core::work::task::TaskRef;
10use crate::core::worker::Worker;
11use crate::utility::{Magic, ObjectCounter};
12
13pub struct Timer {
14    magic: Magic<Self>,
15    _counter: ObjectCounter,
16    internal: Arc<AtomicRefCell<TimerInternal>>,
22}
23
24struct TimerInternal {
25    next_expire_time: Option<EmulatedTime>,
26    expire_interval: Option<SimulationTime>,
27    expiration_count: u64,
28    next_expire_id: u64,
29    min_valid_expire_id: u64,
30    on_expire: Box<dyn Fn(&Host) + Send + Sync>,
31}
32
33impl TimerInternal {
34    fn reset(
35        &mut self,
36        next_expire_time: Option<EmulatedTime>,
37        expire_interval: Option<SimulationTime>,
38    ) {
39        self.min_valid_expire_id = self.next_expire_id;
40        self.expiration_count = 0;
41        self.next_expire_time = next_expire_time;
42        self.expire_interval = expire_interval;
43    }
44}
45
46impl Timer {
47    pub fn new<F: 'static + Fn(&Host) + Send + Sync>(on_expire: F) -> Self {
52        Self {
53            magic: Magic::new(),
54            _counter: ObjectCounter::new("Timer"),
55            internal: Arc::new(AtomicRefCell::new(TimerInternal {
56                next_expire_time: None,
57                expire_interval: None,
58                expiration_count: 0,
59                next_expire_id: 0,
60                min_valid_expire_id: 0,
61                on_expire: Box::new(on_expire),
62            })),
63        }
64    }
65
66    pub fn expiration_count(&self) -> u64 {
69        self.magic.debug_check();
70        self.internal.borrow().expiration_count
71    }
72
73    pub fn expire_interval(&self) -> Option<SimulationTime> {
76        self.magic.debug_check();
77        self.internal.borrow().expire_interval
78    }
79
80    pub fn consume_expiration_count(&mut self) -> u64 {
83        self.magic.debug_check();
84        let mut internal = self.internal.borrow_mut();
85        let e = internal.expiration_count;
86        internal.expiration_count = 0;
87        e
88    }
89
90    pub fn remaining_time(&self) -> Option<SimulationTime> {
93        self.magic.debug_check();
94        let t = self.internal.borrow().next_expire_time?;
95        let now = Worker::current_time().unwrap();
96        Some(t.saturating_duration_since(&now))
97    }
98
99    pub fn disarm(&mut self) {
101        self.magic.debug_check();
102        let mut internal = self.internal.borrow_mut();
103        internal.reset(None, None);
104    }
105
106    fn timer_expire(
107        internal_weak: &Weak<AtomicRefCell<TimerInternal>>,
108        host: &Host,
109        expire_id: u64,
110    ) {
111        let Some(internal) = Weak::upgrade(internal_weak) else {
112            trace!("Expired Timer no longer exists.");
113            return;
114        };
115
116        let mut internal_brw = internal.borrow_mut();
117        trace!(
118            "timer expire check; expireID={expire_id} minValidExpireID={}",
119            internal_brw.min_valid_expire_id
120        );
121
122        if expire_id < internal_brw.min_valid_expire_id {
124            return;
126        }
127
128        let next_expire_time = internal_brw.next_expire_time.unwrap();
129        if next_expire_time > Worker::current_time().unwrap() {
130            Self::schedule_new_expire_event(&mut internal_brw, internal_weak.clone(), host);
132            return;
133        }
134
135        internal_brw.expiration_count += 1;
137
138        if let Some(interval) = internal_brw.expire_interval {
140            debug_assert!(interval.is_positive());
142            internal_brw.next_expire_time = Some(next_expire_time + interval);
143            Self::schedule_new_expire_event(&mut internal_brw, internal_weak.clone(), host);
144        } else {
145            internal_brw.next_expire_time = None;
150        }
151
152        drop(internal_brw);
154        let internal_brw = internal.borrow();
155        (internal_brw.on_expire)(host);
156    }
157
158    fn schedule_new_expire_event(
159        internal_ref: &mut TimerInternal,
160        internal_ptr: Weak<AtomicRefCell<TimerInternal>>,
161        host: &Host,
162    ) {
163        let now = Worker::current_time().unwrap();
164
165        let since_start = now.duration_since(&EmulatedTime::SIMULATION_START);
169        let early_expire_time_since_start =
170            SimulationTime::from_secs(since_start.as_secs()) + SimulationTime::SECOND * 2;
171
172        let time = std::cmp::min(
173            internal_ref.next_expire_time.unwrap(),
174            EmulatedTime::SIMULATION_START + early_expire_time_since_start,
175        );
176        let expire_id = internal_ref.next_expire_id;
177        internal_ref.next_expire_id += 1;
178        let task = TaskRef::new(move |host| Self::timer_expire(&internal_ptr, host, expire_id));
179        host.schedule_task_at_emulated_time(task, time);
180    }
181
182    pub fn arm(
192        &mut self,
193        host: &Host,
194        expire_time: EmulatedTime,
195        expire_interval: Option<SimulationTime>,
196    ) {
197        self.magic.debug_check();
198        debug_assert!(expire_time >= Worker::current_time().unwrap());
199
200        if let Some(interval) = expire_interval {
202            debug_assert!(interval.is_positive());
203        }
204
205        let mut internal = self.internal.borrow_mut();
206        internal.reset(Some(expire_time), expire_interval);
207        Self::schedule_new_expire_event(&mut internal, Arc::downgrade(&self.internal), host);
208    }
209}
210
211pub mod export {
212    use shadow_shim_helper_rs::emulated_time::CEmulatedTime;
213    use shadow_shim_helper_rs::simulation_time::CSimulationTime;
214
215    use super::*;
216
217    #[unsafe(no_mangle)]
225    pub unsafe extern "C-unwind" fn timer_new(task: *const TaskRef) -> *mut Timer {
226        let task = unsafe { task.as_ref() }.unwrap().clone();
227        let timer = Timer::new(move |host| task.execute(host));
228        Box::into_raw(Box::new(timer))
229    }
230
231    #[unsafe(no_mangle)]
235    pub unsafe extern "C-unwind" fn timer_drop(timer: *mut Timer) {
236        drop(unsafe { Box::from_raw(timer) });
237    }
238
239    #[unsafe(no_mangle)]
243    #[allow(non_snake_case)]
244    pub unsafe extern "C-unwind" fn timer_arm(
245        timer: *mut Timer,
246        host: *const Host,
247        nextExpireTime: CEmulatedTime,
248        expireInterval: CSimulationTime,
249    ) {
250        let timer = unsafe { timer.as_mut() }.unwrap();
251        let host = unsafe { host.as_ref().unwrap() };
252        let nextExpireTime = EmulatedTime::from_c_emutime(nextExpireTime).unwrap();
253        let expireInterval = SimulationTime::from_c_simtime(expireInterval).unwrap();
254        timer.arm(
255            host,
256            nextExpireTime,
257            expireInterval.is_positive().then_some(expireInterval),
258        )
259    }
260
261    #[unsafe(no_mangle)]
265    pub unsafe extern "C-unwind" fn timer_disarm(timer: *mut Timer) {
266        let timer = unsafe { timer.as_mut() }.unwrap();
267        timer.disarm()
268    }
269}