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}