deltachat/
timesmearing.rs1use std::cmp::{max, min};
18use std::sync::atomic::{AtomicI64, Ordering};
19
20pub(crate) const MAX_SECONDS_TO_LEND_FROM_FUTURE: i64 = 30;
21
22#[derive(Debug)]
24pub struct SmearedTimestamp {
25 smeared_timestamp: AtomicI64,
27}
28
29impl SmearedTimestamp {
30 pub fn new() -> Self {
32 Self {
33 smeared_timestamp: AtomicI64::new(0),
34 }
35 }
36
37 #[expect(clippy::arithmetic_side_effects)]
41 pub fn create_n(&self, now: i64, count: i64) -> i64 {
42 let mut prev = self.smeared_timestamp.load(Ordering::Relaxed);
43 loop {
44 let t = max(prev, now - count + 1);
47
48 let first = min(t, now + MAX_SECONDS_TO_LEND_FROM_FUTURE - count + 1);
53
54 let next = first + count;
56
57 if let Err(x) = self.smeared_timestamp.compare_exchange_weak(
58 prev,
59 next,
60 Ordering::Relaxed,
61 Ordering::Relaxed,
62 ) {
63 prev = x;
64 } else {
65 return first;
66 }
67 }
68 }
69
70 pub fn create(&self, now: i64) -> i64 {
72 self.create_n(now, 1)
73 }
74
75 pub fn current(&self) -> i64 {
77 self.smeared_timestamp.load(Ordering::Relaxed)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::test_utils::TestContext;
85 use crate::tools::{
86 SystemTime, create_smeared_timestamp, create_smeared_timestamps, smeared_time, time,
87 };
88
89 #[test]
90 fn test_smeared_timestamp() {
91 let smeared_timestamp = SmearedTimestamp::new();
92 let now = time();
93
94 assert_eq!(smeared_timestamp.current(), 0);
95
96 for i in 0..MAX_SECONDS_TO_LEND_FROM_FUTURE {
97 assert_eq!(smeared_timestamp.create(now), now + i);
98 }
99 assert_eq!(
100 smeared_timestamp.create(now),
101 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
102 );
103 assert_eq!(
104 smeared_timestamp.create(now),
105 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
106 );
107
108 let now = now - 1000;
110 assert_eq!(
111 smeared_timestamp.create(now),
112 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
113 );
114 assert_eq!(
115 smeared_timestamp.create(now),
116 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
117 );
118 assert_eq!(
119 smeared_timestamp.create(now + 1),
120 now + MAX_SECONDS_TO_LEND_FROM_FUTURE + 1
121 );
122 assert_eq!(smeared_timestamp.create(now + 100), now + 100);
123 assert_eq!(smeared_timestamp.create(now + 100), now + 101);
124 assert_eq!(smeared_timestamp.create(now + 100), now + 102);
125 }
126
127 #[test]
128 fn test_create_n_smeared_timestamps() {
129 let smeared_timestamp = SmearedTimestamp::new();
130 let now = time();
131
132 assert_eq!(smeared_timestamp.create(now), now);
134
135 let now = now + 60;
137
138 let forwarded_messages = 7;
140
141 assert_eq!(smeared_timestamp.create_n(now, forwarded_messages), now - 6);
144
145 assert_eq!(smeared_timestamp.current(), now + 1);
146
147 let now = now + 4;
150
151 assert_eq!(smeared_timestamp.current(), now - 3);
152
153 assert_eq!(smeared_timestamp.create_n(now, forwarded_messages), now - 3);
156
157 assert_eq!(smeared_timestamp.current(), now + 4);
160
161 assert_eq!(smeared_timestamp.create_n(now, 32), now - 1);
166 }
167
168 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
169 async fn test_create_smeared_timestamp() {
170 let t = TestContext::new().await;
171 assert_ne!(create_smeared_timestamp(&t), create_smeared_timestamp(&t));
172 assert!(
173 create_smeared_timestamp(&t)
174 >= SystemTime::now()
175 .duration_since(SystemTime::UNIX_EPOCH)
176 .unwrap()
177 .as_secs() as i64
178 );
179 }
180
181 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
182 async fn test_create_smeared_timestamps() {
183 let t = TestContext::new().await;
184 let count = MAX_SECONDS_TO_LEND_FROM_FUTURE - 1;
185 let start = create_smeared_timestamps(&t, count as usize);
186 let next = smeared_time(&t);
187 assert!((start + count - 1) < next);
188
189 let count = MAX_SECONDS_TO_LEND_FROM_FUTURE + 30;
190 let start = create_smeared_timestamps(&t, count as usize);
191 let next = smeared_time(&t);
192 assert!((start + count - 1) < next);
193 }
194}