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 pub fn create_n(&self, now: i64, count: i64) -> i64 {
41 let mut prev = self.smeared_timestamp.load(Ordering::Relaxed);
42 loop {
43 let t = max(prev, now - count + 1);
46
47 let first = min(t, now + MAX_SECONDS_TO_LEND_FROM_FUTURE - count + 1);
52
53 let next = first + count;
55
56 if let Err(x) = self.smeared_timestamp.compare_exchange_weak(
57 prev,
58 next,
59 Ordering::Relaxed,
60 Ordering::Relaxed,
61 ) {
62 prev = x;
63 } else {
64 return first;
65 }
66 }
67 }
68
69 pub fn create(&self, now: i64) -> i64 {
71 self.create_n(now, 1)
72 }
73
74 pub fn current(&self) -> i64 {
76 self.smeared_timestamp.load(Ordering::Relaxed)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::test_utils::TestContext;
84 use crate::tools::{
85 create_smeared_timestamp, create_smeared_timestamps, smeared_time, time, SystemTime,
86 };
87
88 #[test]
89 fn test_smeared_timestamp() {
90 let smeared_timestamp = SmearedTimestamp::new();
91 let now = time();
92
93 assert_eq!(smeared_timestamp.current(), 0);
94
95 for i in 0..MAX_SECONDS_TO_LEND_FROM_FUTURE {
96 assert_eq!(smeared_timestamp.create(now), now + i);
97 }
98 assert_eq!(
99 smeared_timestamp.create(now),
100 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
101 );
102 assert_eq!(
103 smeared_timestamp.create(now),
104 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
105 );
106
107 let now = now - 1000;
109 assert_eq!(
110 smeared_timestamp.create(now),
111 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
112 );
113 assert_eq!(
114 smeared_timestamp.create(now),
115 now + MAX_SECONDS_TO_LEND_FROM_FUTURE
116 );
117 assert_eq!(
118 smeared_timestamp.create(now + 1),
119 now + MAX_SECONDS_TO_LEND_FROM_FUTURE + 1
120 );
121 assert_eq!(smeared_timestamp.create(now + 100), now + 100);
122 assert_eq!(smeared_timestamp.create(now + 100), now + 101);
123 assert_eq!(smeared_timestamp.create(now + 100), now + 102);
124 }
125
126 #[test]
127 fn test_create_n_smeared_timestamps() {
128 let smeared_timestamp = SmearedTimestamp::new();
129 let now = time();
130
131 assert_eq!(smeared_timestamp.create(now), now);
133
134 let now = now + 60;
136
137 let forwarded_messages = 7;
139
140 assert_eq!(smeared_timestamp.create_n(now, forwarded_messages), now - 6);
143
144 assert_eq!(smeared_timestamp.current(), now + 1);
145
146 let now = now + 4;
149
150 assert_eq!(smeared_timestamp.current(), now - 3);
151
152 assert_eq!(smeared_timestamp.create_n(now, forwarded_messages), now - 3);
155
156 assert_eq!(smeared_timestamp.current(), now + 4);
159
160 assert_eq!(smeared_timestamp.create_n(now, 32), now - 1);
165 }
166
167 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
168 async fn test_create_smeared_timestamp() {
169 let t = TestContext::new().await;
170 assert_ne!(create_smeared_timestamp(&t), create_smeared_timestamp(&t));
171 assert!(
172 create_smeared_timestamp(&t)
173 >= SystemTime::now()
174 .duration_since(SystemTime::UNIX_EPOCH)
175 .unwrap()
176 .as_secs() as i64
177 );
178 }
179
180 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
181 async fn test_create_smeared_timestamps() {
182 let t = TestContext::new().await;
183 let count = MAX_SECONDS_TO_LEND_FROM_FUTURE - 1;
184 let start = create_smeared_timestamps(&t, count as usize);
185 let next = smeared_time(&t);
186 assert!((start + count - 1) < next);
187
188 let count = MAX_SECONDS_TO_LEND_FROM_FUTURE + 30;
189 let start = create_smeared_timestamps(&t, count as usize);
190 let next = smeared_time(&t);
191 assert!((start + count - 1) < next);
192 }
193}