Skip to content

Commit a630f26

Browse files
committed
fix: Use SystemTime instead of Instant everywhere
If a time value doesn't need to be sent to another host, saved to the db or otherwise used across program restarts, a monotonically nondecreasing clock (`Instant`) should be used. But as `Instant` may use `libc::clock_gettime(CLOCK_MONOTONIC)`, e.g. on Android, and does not advance while being in deep sleep mode, get rid of `Instant` in favor of using `SystemTime`, but add `tools::Time` as an alias for it with the appropriate comment so that it's clear why `Instant` isn't used in those places and to protect from unwanted usages of `Instant` in the future. Also this can help to switch to another clock impl if we find any.
1 parent 91c3a39 commit a630f26

File tree

10 files changed

+54
-43
lines changed

10 files changed

+54
-43
lines changed

src/constants.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ pub(crate) const DC_FOLDERS_CONFIGURED_VERSION: i32 = 4;
215215
pub(crate) const DEFAULT_MAX_SMTP_RCPT_TO: usize = 50;
216216

217217
/// How far the last quota check needs to be in the past to be checked by the background function (in seconds).
218-
pub(crate) const DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT: i64 = 12 * 60 * 60; // 12 hours
218+
pub(crate) const DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT: u64 = 12 * 60 * 60; // 12 hours
219219

220220
#[cfg(test)]
221221
mod tests {

src/context.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::ops::Deref;
66
use std::path::{Path, PathBuf};
77
use std::sync::atomic::{AtomicBool, Ordering};
88
use std::sync::Arc;
9-
use std::time::{Duration, Instant, SystemTime};
9+
use std::time::Duration;
1010

1111
use anyhow::{bail, ensure, Context as _, Result};
1212
use async_channel::{self as channel, Receiver, Sender};
@@ -28,7 +28,7 @@ use crate::scheduler::{convert_folder_meaning, SchedulerState};
2828
use crate::sql::Sql;
2929
use crate::stock_str::StockStrings;
3030
use crate::timesmearing::SmearedTimestamp;
31-
use crate::tools::{duration_to_str, time};
31+
use crate::tools::{self, duration_to_str, time, time_elapsed};
3232

3333
/// Builder for the [`Context`].
3434
///
@@ -228,15 +228,15 @@ pub struct InnerContext {
228228
/// IMAP METADATA.
229229
pub(crate) metadata: RwLock<Option<ServerMetadata>>,
230230

231-
pub(crate) last_full_folder_scan: Mutex<Option<Instant>>,
231+
pub(crate) last_full_folder_scan: Mutex<Option<tools::Time>>,
232232

233233
/// ID for this `Context` in the current process.
234234
///
235235
/// This allows for multiple `Context`s open in a single process where each context can
236236
/// be identified by this ID.
237237
pub(crate) id: u32,
238238

239-
creation_time: SystemTime,
239+
creation_time: tools::Time,
240240

241241
/// The text of the last error logged and emitted as an event.
242242
/// If the ui wants to display an error after a failure,
@@ -257,7 +257,7 @@ enum RunningState {
257257
Running { cancel_sender: Sender<()> },
258258

259259
/// Cancel signal has been sent, waiting for ongoing process to be freed.
260-
ShallStop { request: Instant },
260+
ShallStop { request: tools::Time },
261261

262262
/// There is no ongoing process, a new one can be allocated.
263263
Stopped,
@@ -389,7 +389,7 @@ impl Context {
389389
new_msgs_notify,
390390
server_id: RwLock::new(None),
391391
metadata: RwLock::new(None),
392-
creation_time: std::time::SystemTime::now(),
392+
creation_time: tools::Time::now(),
393393
last_full_folder_scan: Mutex::new(None),
394394
last_error: std::sync::RwLock::new("".to_string()),
395395
debug_logging: std::sync::RwLock::new(None),
@@ -471,7 +471,10 @@ impl Context {
471471
let quota = self.quota.read().await;
472472
quota
473473
.as_ref()
474-
.filter(|quota| quota.modified + DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT > time())
474+
.filter(|quota| {
475+
time_elapsed(&quota.modified)
476+
> Duration::from_secs(DC_BACKGROUND_FETCH_QUOTA_CHECK_RATELIMIT)
477+
})
475478
.is_none()
476479
};
477480

@@ -586,7 +589,7 @@ impl Context {
586589
pub(crate) async fn free_ongoing(&self) {
587590
let mut s = self.running_state.write().await;
588591
if let RunningState::ShallStop { request } = *s {
589-
info!(self, "Ongoing stopped in {:?}", request.elapsed());
592+
info!(self, "Ongoing stopped in {:?}", time_elapsed(&request));
590593
}
591594
*s = RunningState::Stopped;
592595
}
@@ -601,7 +604,7 @@ impl Context {
601604
}
602605
info!(self, "Signaling the ongoing process to stop ASAP.",);
603606
*s = RunningState::ShallStop {
604-
request: Instant::now(),
607+
request: tools::Time::now(),
605608
};
606609
}
607610
RunningState::ShallStop { .. } | RunningState::Stopped => {
@@ -853,8 +856,8 @@ impl Context {
853856
.to_string(),
854857
);
855858

856-
let elapsed = self.creation_time.elapsed();
857-
res.insert("uptime", duration_to_str(elapsed.unwrap_or_default()));
859+
let elapsed = time_elapsed(&self.creation_time);
860+
res.insert("uptime", duration_to_str(elapsed));
858861

859862
Ok(res)
860863
}
@@ -1115,7 +1118,7 @@ pub fn get_version_str() -> &'static str {
11151118

11161119
#[cfg(test)]
11171120
mod tests {
1118-
use std::time::Duration;
1121+
use std::time::{Duration, SystemTime};
11191122

11201123
use anyhow::Context as _;
11211124
use strum::IntoEnumIterator;

src/imap/idle.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::time::{Duration, SystemTime};
1+
use std::time::Duration;
22

33
use anyhow::{bail, Context as _, Result};
44
use async_channel::Receiver;
@@ -11,6 +11,7 @@ use crate::config::Config;
1111
use crate::context::Context;
1212
use crate::imap::{client::IMAP_TIMEOUT, FolderMeaning};
1313
use crate::log::LogExt;
14+
use crate::tools::{self, time_elapsed};
1415

1516
/// Timeout after which IDLE is finished
1617
/// if there are no responses from the server.
@@ -106,7 +107,7 @@ impl Imap {
106107
// Idle using polling. This is also needed if we're not yet configured -
107108
// in this case, we're waiting for a configure job (and an interrupt).
108109

109-
let fake_idle_start_time = SystemTime::now();
110+
let fake_idle_start_time = tools::Time::now();
110111

111112
// Do not poll, just wait for an interrupt when no folder is passed in.
112113
let watch_folder = if let Some(watch_folder) = watch_folder {
@@ -196,11 +197,7 @@ impl Imap {
196197
info!(
197198
context,
198199
"IMAP-fake-IDLE done after {:.4}s",
199-
SystemTime::now()
200-
.duration_since(fake_idle_start_time)
201-
.unwrap_or_default()
202-
.as_millis() as f64
203-
/ 1000.,
200+
time_elapsed(&fake_idle_start_time).as_millis() as f64 / 1000.,
204201
);
205202
}
206203
}

src/imap/scan_folders.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::BTreeMap, time::Instant};
1+
use std::collections::BTreeMap;
22

33
use anyhow::{Context as _, Result};
44
use futures::TryStreamExt;
@@ -7,6 +7,7 @@ use super::{get_folder_meaning_by_attrs, get_folder_meaning_by_name};
77
use crate::config::Config;
88
use crate::imap::Imap;
99
use crate::log::LogExt;
10+
use crate::tools::{self, time_elapsed};
1011
use crate::{context::Context, imap::FolderMeaning};
1112

1213
impl Imap {
@@ -15,7 +16,7 @@ impl Imap {
1516
// First of all, debounce to once per minute:
1617
let mut last_scan = context.last_full_folder_scan.lock().await;
1718
if let Some(last_scan) = *last_scan {
18-
let elapsed_secs = last_scan.elapsed().as_secs();
19+
let elapsed_secs = time_elapsed(&last_scan).as_secs();
1920
let debounce_secs = context
2021
.get_config_u64(Config::ScanAllFoldersDebounceSecs)
2122
.await?;
@@ -93,7 +94,7 @@ impl Imap {
9394
.await?;
9495
}
9596

96-
last_scan.replace(Instant::now());
97+
last_scan.replace(tools::Time::now());
9798
Ok(true)
9899
}
99100

src/key.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::constants::KeyGenType;
1818
use crate::context::Context;
1919
use crate::log::LogExt;
2020
use crate::pgp::KeyPair;
21-
use crate::tools::EmailAddress;
21+
use crate::tools::{self, time_elapsed, EmailAddress};
2222

2323
/// Convenience trait for working with keys.
2424
///
@@ -204,7 +204,7 @@ async fn generate_keypair(context: &Context) -> Result<KeyPair> {
204204
match load_keypair(context, &addr).await? {
205205
Some(key_pair) => Ok(key_pair),
206206
None => {
207-
let start = std::time::SystemTime::now();
207+
let start = tools::Time::now();
208208
let keytype = KeyGenType::from_i32(context.get_config_int(Config::KeyGenType).await?)
209209
.unwrap_or_default();
210210
info!(context, "Generating keypair with type {}", keytype);
@@ -216,7 +216,7 @@ async fn generate_keypair(context: &Context) -> Result<KeyPair> {
216216
info!(
217217
context,
218218
"Keypair generated in {:.3}s.",
219-
start.elapsed().unwrap_or_default().as_secs()
219+
time_elapsed(&start).as_secs(),
220220
);
221221
Ok(keypair)
222222
}

src/quota.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::imap::scan_folders::get_watched_folders;
1212
use crate::imap::session::Session as ImapSession;
1313
use crate::imap::Imap;
1414
use crate::message::{Message, Viewtype};
15-
use crate::tools::time;
15+
use crate::tools;
1616
use crate::{stock_str, EventType};
1717

1818
/// warn about a nearly full mailbox after this usage percentage is reached.
@@ -40,8 +40,8 @@ pub struct QuotaInfo {
4040
/// set to `Ok()` for valid quota information.
4141
pub(crate) recent: Result<BTreeMap<String, Vec<QuotaResource>>>,
4242

43-
/// Timestamp when structure was modified.
44-
pub(crate) modified: i64,
43+
/// When the structure was modified.
44+
pub(crate) modified: tools::Time,
4545
}
4646

4747
async fn get_unique_quota_roots_and_usage(
@@ -147,7 +147,7 @@ impl Context {
147147

148148
*self.quota.write().await = Some(QuotaInfo {
149149
recent: quota,
150-
modified: time(),
150+
modified: tools::Time::now(),
151151
});
152152

153153
self.emit_event(EventType::ConnectivityChanged);

src/scheduler.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::cmp;
22
use std::iter::{self, once};
33
use std::num::NonZeroUsize;
44
use std::sync::atomic::Ordering;
5+
use std::time::Duration;
56

67
use anyhow::{bail, Context as _, Error, Result};
78
use async_channel::{self as channel, Receiver, Sender};
@@ -24,7 +25,7 @@ use crate::log::LogExt;
2425
use crate::message::MsgId;
2526
use crate::smtp::{send_smtp_messages, Smtp};
2627
use crate::sql;
27-
use crate::tools::{duration_to_str, maybe_add_time_based_warnings, time};
28+
use crate::tools::{self, duration_to_str, maybe_add_time_based_warnings, time, time_elapsed};
2829

2930
pub(crate) mod connectivity;
3031

@@ -398,7 +399,7 @@ async fn inbox_loop(
398399
let quota = ctx.quota.read().await;
399400
quota
400401
.as_ref()
401-
.filter(|quota| quota.modified + 60 > time())
402+
.filter(|quota| time_elapsed(&quota.modified) > Duration::from_secs(60))
402403
.is_none()
403404
};
404405

@@ -767,7 +768,7 @@ async fn smtp_loop(
767768
// again, we increase the timeout exponentially, in order not to do lots of
768769
// unnecessary retries.
769770
if let Some(t) = timeout {
770-
let now = tokio::time::Instant::now();
771+
let now = tools::Time::now();
771772
info!(
772773
ctx,
773774
"smtp has messages to retry, planning to retry {} seconds later", t,
@@ -778,7 +779,7 @@ async fn smtp_loop(
778779
})
779780
.await
780781
.unwrap_or_default();
781-
let slept = (tokio::time::Instant::now() - now).as_secs();
782+
let slept = time_elapsed(&now).as_secs();
782783
timeout = Some(cmp::max(
783784
t,
784785
slept.saturating_add(rand::thread_rng().gen_range((slept / 2)..=slept)),

src/smtp.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
pub mod send;
44

5-
use std::time::{Duration, SystemTime};
5+
use std::time::Duration;
66

77
use anyhow::{bail, format_err, Context as _, Error, Result};
88
use async_smtp::response::{Category, Code, Detail};
@@ -28,6 +28,7 @@ use crate::scheduler::connectivity::ConnectivityStore;
2828
use crate::socks::Socks5Config;
2929
use crate::sql;
3030
use crate::stock_str::unencrypted_email;
31+
use crate::tools::{self, time_elapsed};
3132

3233
/// SMTP connection, write and read timeout.
3334
const SMTP_TIMEOUT: Duration = Duration::from_secs(60);
@@ -43,7 +44,7 @@ pub(crate) struct Smtp {
4344
/// Timestamp of last successful send/receive network interaction
4445
/// (eg connect or send succeeded). On initialization and disconnect
4546
/// it is set to None.
46-
last_success: Option<SystemTime>,
47+
last_success: Option<tools::Time>,
4748

4849
pub(crate) connectivity: ConnectivityStore,
4950

@@ -72,11 +73,7 @@ impl Smtp {
7273
/// have been successfully used the last 60 seconds
7374
pub fn has_maybe_stale_connection(&self) -> bool {
7475
if let Some(last_success) = self.last_success {
75-
SystemTime::now()
76-
.duration_since(last_success)
77-
.unwrap_or_default()
78-
.as_secs()
79-
> 60
76+
time_elapsed(&last_success).as_secs() > 60
8077
} else {
8178
false
8279
}
@@ -336,7 +333,7 @@ impl Smtp {
336333
}
337334

338335
self.transport = Some(transport);
339-
self.last_success = Some(SystemTime::now());
336+
self.last_success = Some(tools::Time::now());
340337

341338
context.emit_event(EventType::SmtpConnected(format!(
342339
"SMTP-LOGIN as {} ok",

src/smtp/send.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use super::Smtp;
66
use crate::config::Config;
77
use crate::context::Context;
88
use crate::events::EventType;
9+
use crate::tools;
910

1011
pub type Result<T> = std::result::Result<T, Error>;
1112

@@ -55,7 +56,7 @@ impl Smtp {
5556
format!("Message len={message_len_bytes} was SMTP-sent to {recipients_display}");
5657
info!(context, "{info_msg}.");
5758
context.emit_event(EventType::SmtpMessageSent(info_msg));
58-
self.last_success = Some(std::time::SystemTime::now());
59+
self.last_success = Some(tools::Time::now());
5960
} else {
6061
warn!(
6162
context,

src/tools.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ use std::io::{Cursor, Write};
99
use std::mem;
1010
use std::path::{Path, PathBuf};
1111
use std::str::from_utf8;
12+
// If a time value doesn't need to be sent to another host, saved to the db or otherwise used across
13+
// program restarts, a monotonically nondecreasing clock (`Instant`) should be used. But as
14+
// `Instant` may use `libc::clock_gettime(CLOCK_MONOTONIC)`, e.g. on Android, and does not advance
15+
// while being in deep sleep mode, we use `SystemTime` instead, but add an alias for it to document
16+
// why `Instant` isn't used in those places. Also this can help to switch to another clock impl if
17+
// we find any.
18+
pub use std::time::SystemTime as Time;
1219
use std::time::{Duration, SystemTime};
1320

1421
use anyhow::{bail, Context as _, Result};
@@ -482,6 +489,10 @@ pub(crate) fn time() -> i64 {
482489
.as_secs() as i64
483490
}
484491

492+
pub(crate) fn time_elapsed(time: &Time) -> Duration {
493+
time.elapsed().unwrap_or_default()
494+
}
495+
485496
/// Struct containing all mailto information
486497
#[derive(Debug, Default, Eq, PartialEq)]
487498
pub struct MailTo {

0 commit comments

Comments
 (0)