summaryrefslogtreecommitdiffhomepage
path: root/talpid-time
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-04-08 10:01:31 +0200
committerDavid Lönnhager <david.l@mullvad.net>2022-04-11 13:09:58 +0200
commitbdf327cfa3a482b4b8dd336f61fdc644f55d3f21 (patch)
treeed7e1bd727d39bf12aa2460817dd5eebaaf427c3 /talpid-time
parent744fb04aa9e7180f37c00d63cd8d068fb04882b1 (diff)
downloadmullvadvpn-bdf327cfa3a482b4b8dd336f61fdc644f55d3f21.tar.xz
mullvadvpn-bdf327cfa3a482b4b8dd336f61fdc644f55d3f21.zip
Add talpid-time crate
Diffstat (limited to 'talpid-time')
-rw-r--r--talpid-time/Cargo.toml12
-rw-r--r--talpid-time/src/lib.rs53
-rw-r--r--talpid-time/src/unix.rs58
3 files changed, 123 insertions, 0 deletions
diff --git a/talpid-time/Cargo.toml b/talpid-time/Cargo.toml
new file mode 100644
index 0000000000..754007e84a
--- /dev/null
+++ b/talpid-time/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "talpid-time"
+version = "0.1.0"
+authors = ["Mullvad VPN"]
+description = "Time functions"
+license = "GPL-3.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+tokio = { version = "1.8", features = ["time"] }
+libc = "0.2"
diff --git a/talpid-time/src/lib.rs b/talpid-time/src/lib.rs
new file mode 100644
index 0000000000..3b536eda54
--- /dev/null
+++ b/talpid-time/src/lib.rs
@@ -0,0 +1,53 @@
+use std::time::Duration;
+
+#[cfg(target_os = "windows")]
+mod inner {
+ pub use std::time::Instant;
+}
+
+#[cfg(unix)]
+#[path = "unix.rs"]
+mod inner;
+
+const MAX_SLEEP_INTERVAL: Duration = Duration::from_secs(60);
+
+/// Represents a measurement of a monotonic clock.
+/// Unlike [std::time::Instant], the difference between two
+/// instances is guaranteed to include time spent in system
+/// sleep.
+#[derive(Clone, Copy)]
+pub struct Instant {
+ t: inner::Instant,
+}
+
+impl Instant {
+ pub fn now() -> Self {
+ Self {
+ t: inner::Instant::now(),
+ }
+ }
+
+ pub fn duration_since(&self, earlier: Instant) -> Duration {
+ self.t.duration_since(earlier.t)
+ }
+}
+
+/// Waits for the specified interval while taking into account system sleep or suspension.
+/// The accuracy is to within about one minute.
+pub async fn sleep(duration: Duration) {
+ let started = Instant::now();
+
+ loop {
+ let elapsed = Instant::now().duration_since(started);
+
+ if elapsed >= duration {
+ return;
+ }
+
+ tokio::time::sleep(std::cmp::min(
+ MAX_SLEEP_INTERVAL,
+ duration.saturating_sub(elapsed),
+ ))
+ .await;
+ }
+}
diff --git a/talpid-time/src/unix.rs b/talpid-time/src/unix.rs
new file mode 100644
index 0000000000..b9721dd27a
--- /dev/null
+++ b/talpid-time/src/unix.rs
@@ -0,0 +1,58 @@
+use libc::{clock_gettime, clockid_t, timespec};
+use std::{mem::MaybeUninit, time::Duration};
+
+const NSEC_PER_SEC: i64 = 1000000000;
+
+#[cfg(target_os = "macos")]
+const CLOCK_ID: clockid_t = libc::CLOCK_MONOTONIC;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+const CLOCK_ID: clockid_t = libc::CLOCK_BOOTTIME;
+
+#[derive(Clone, Copy)]
+pub struct Instant {
+ t: timespec,
+}
+
+impl Instant {
+ pub fn now() -> Self {
+ Self { t: now() }
+ }
+
+ fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
+ // Assumptions:
+ // * `tv_sec >= 0`
+ // * `tv_nsec < NSEC_PER_SEC`
+
+ let (tv_sec, tv_nsec) = if self.t.tv_nsec < earlier.t.tv_nsec {
+ (
+ self.t.tv_sec - earlier.t.tv_sec - 1,
+ NSEC_PER_SEC - earlier.t.tv_nsec + self.t.tv_nsec,
+ )
+ } else {
+ (
+ self.t.tv_sec - earlier.t.tv_sec,
+ self.t.tv_nsec - earlier.t.tv_nsec,
+ )
+ };
+
+ if tv_sec < 0 {
+ return None;
+ }
+
+ Some(Duration::new(tv_sec as _, tv_nsec as _))
+ }
+
+ pub fn duration_since(&self, earlier: Instant) -> Duration {
+ self.checked_duration_since(earlier)
+ .unwrap_or(Duration::ZERO)
+ }
+}
+
+fn now() -> timespec {
+ let mut t = MaybeUninit::zeroed();
+ unsafe {
+ clock_gettime(CLOCK_ID, t.as_mut_ptr());
+ t.assume_init()
+ }
+}