clock_bound_vmclock/
lib.rs1use std::ffi::CString;
2use tracing::debug;
3
4use crate::shm_reader::VMClockShmReader;
5use clock_bound_shm::{ClockStatus, ShmError, ShmReader};
6use nix::sys::time::TimeSpec;
7
8pub mod shm;
9pub mod shm_reader;
10pub mod shm_writer;
11
12pub struct VMClock {
17 clockbound_shm_reader: ShmReader,
18 vmclock_shm_path: String,
19 vmclock_shm_reader: Option<VMClockShmReader>,
20}
21
22impl VMClock {
23 pub fn new(clockbound_shm_path: &str, vmclock_shm_path: &str) -> Result<VMClock, ShmError> {
29 let clockbound_shm_path = CString::new(clockbound_shm_path).expect("CString::new failed");
30 let mut clockbound_shm_reader = ShmReader::new(clockbound_shm_path.as_c_str())?;
31 let clockbound_snapshot = clockbound_shm_reader.snapshot()?;
32
33 let mut vmclock_shm_reader: Option<VMClockShmReader> = None;
34 if clockbound_snapshot.clock_disruption_support_enabled {
35 vmclock_shm_reader = Some(VMClockShmReader::new(vmclock_shm_path)?);
36 }
37
38 Ok(VMClock {
39 clockbound_shm_reader,
40 vmclock_shm_path: String::from(vmclock_shm_path),
41 vmclock_shm_reader,
42 })
43 }
44
45 pub fn now(&mut self) -> Result<(TimeSpec, TimeSpec, ClockStatus), ShmError> {
53 let clockbound_snapshot = self.clockbound_shm_reader.snapshot()?;
55
56 if self.vmclock_shm_reader.is_none() && clockbound_snapshot.clock_disruption_support_enabled
57 {
58 self.vmclock_shm_reader = Some(VMClockShmReader::new(self.vmclock_shm_path.as_str())?);
59 }
60
61 let (earliest, latest, clock_status) = clockbound_snapshot.now()?;
62
63 if clockbound_snapshot.clock_disruption_support_enabled {
64 if let Some(ref mut vmclock_shm_reader) = self.vmclock_shm_reader {
65 let vmclock_snapshot = vmclock_shm_reader.snapshot()?;
67
68 debug!("clock_status: {:?}, vmclock_snapshot.disruption_marker: {:?}, clockbound_snapshot.disruption_marker: {:?}",
72 clock_status, vmclock_snapshot.disruption_marker,
73 clockbound_snapshot.disruption_marker);
74
75 if vmclock_snapshot.disruption_marker == clockbound_snapshot.disruption_marker {
76 return Ok((earliest, latest, clock_status));
79 } else {
80 return Ok((earliest, latest, ClockStatus::Unknown));
87 }
88 }
89 }
90
91 debug!("clock_status: {:?}", clock_status);
92 Ok((earliest, latest, clock_status))
93 }
94}
95
96#[cfg(test)]
97mod t_lib {
98 use super::*;
99
100 use clock_bound_shm::{ClockErrorBound, ShmWrite, ShmWriter};
101 use std::path::Path;
102
103 use crate::shm::{VMClockClockStatus, VMClockShmBody};
104 use crate::shm_writer::{VMClockShmWrite, VMClockShmWriter};
105 use tempfile::NamedTempFile;
109
110 macro_rules! vmclockshmbody {
111 () => {
112 VMClockShmBody {
113 disruption_marker: 10,
114 flags: 0_u64,
115 _padding: [0x00, 0x00],
116 clock_status: VMClockClockStatus::Unknown,
117 leap_second_smearing_hint: 0,
118 tai_offset_sec: 37_i16,
119 leap_indicator: 0,
120 counter_period_shift: 0,
121 counter_value: 0,
122 counter_period_frac_sec: 0,
123 counter_period_esterror_rate_frac_sec: 0,
124 counter_period_maxerror_rate_frac_sec: 0,
125 time_sec: 0,
126 time_frac_sec: 0,
127 time_esterror_nanosec: 0,
128 time_maxerror_nanosec: 0,
129 }
130 };
131 }
132
133 fn remove_file_or_directory(path: &str) {
135 let p = Path::new(&path);
137 while p.exists() {
138 if p.is_dir() {
139 std::fs::remove_dir_all(&path).expect("failed to remove file");
140 } else {
141 std::fs::remove_file(&path).expect("failed to remove file");
142 }
143 }
144 }
145
146 #[test]
149 fn test_vmclock_now_with_clock_disruption_support_enabled_success() {
150 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
151 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
152 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
153 remove_file_or_directory(&clockbound_shm_path);
154 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
155 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
156 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
157 remove_file_or_directory(&vmclock_shm_path);
158
159 let ceb = ClockErrorBound::new(
161 TimeSpec::new(1, 2), TimeSpec::new(3, 4), 123, 10, 100, ClockStatus::Synchronized, true, );
169
170 let mut clockbound_shm_writer =
171 ShmWriter::new(Path::new(&clockbound_shm_path)).expect("Failed to create a ShmWriter");
172 clockbound_shm_writer.write(&ceb);
173
174 let vmclock_shm_body = vmclockshmbody!();
176 let mut vmclock_shm_writer = VMClockShmWriter::new(Path::new(&vmclock_shm_path))
177 .expect("Failed to create a VMClockShmWriter");
178 vmclock_shm_writer.write(&vmclock_shm_body);
179
180 let vmclock_new_result = VMClock::new(&clockbound_shm_path, &vmclock_shm_path);
182 match vmclock_new_result {
183 Ok(mut vmclock) => {
184 let now_result = vmclock.now();
186 assert!(now_result.is_ok());
187 }
188 Err(_) => {
189 assert!(false);
190 }
191 }
192 }
193
194 #[test]
197 fn test_vmclock_now_with_clock_disruption_support_enabled_failure() {
198 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
199 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
200 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
201 remove_file_or_directory(&clockbound_shm_path);
202 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
203 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
204 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
205 remove_file_or_directory(&vmclock_shm_path);
206
207 let ceb = ClockErrorBound::new(
209 TimeSpec::new(1, 2), TimeSpec::new(3, 4), 123, 10, 100, ClockStatus::Synchronized, true, );
217
218 let mut clockbound_shm_writer =
219 ShmWriter::new(Path::new(&clockbound_shm_path)).expect("Failed to create a ShmWriter");
220 clockbound_shm_writer.write(&ceb);
221
222 let vmclock_new_result = VMClock::new(&clockbound_shm_path, &vmclock_shm_path);
224 assert!(vmclock_new_result.is_err());
225 }
226
227 #[test]
230 fn test_vmclock_now_with_clock_disruption_support_not_enabled() {
231 let clockbound_shm_tempfile = NamedTempFile::new().expect("create clockbound file failed");
232 let clockbound_shm_temppath = clockbound_shm_tempfile.into_temp_path();
233 let clockbound_shm_path = clockbound_shm_temppath.to_str().unwrap();
234 remove_file_or_directory(&clockbound_shm_path);
235 let vmclock_shm_tempfile = NamedTempFile::new().expect("create vmclock file failed");
236 let vmclock_shm_temppath = vmclock_shm_tempfile.into_temp_path();
237 let vmclock_shm_path = vmclock_shm_temppath.to_str().unwrap();
238 remove_file_or_directory(&vmclock_shm_path);
239
240 let ceb = ClockErrorBound::new(
242 TimeSpec::new(1, 2), TimeSpec::new(3, 4), 123, 10, 100, ClockStatus::Synchronized, false, );
250
251 let mut clockbound_shm_writer =
252 ShmWriter::new(Path::new(&clockbound_shm_path)).expect("Failed to create a ShmWriter");
253 clockbound_shm_writer.write(&ceb);
254
255 let vmclock_new_result = VMClock::new(&clockbound_shm_path, &vmclock_shm_path);
258 match vmclock_new_result {
259 Ok(mut vmclock) => {
260 let now_result = vmclock.now();
262 assert!(now_result.is_ok());
263 }
264 Err(_) => {
265 assert!(false);
266 }
267 }
268 }
269}