fix: Patch coarsetime for MIPS AtomicU64 support

- Vendor coarsetime crate with portable-atomic fallback
- Use portable-atomic on targets without 64-bit atomics
- Patch crates.io to use local coarsetime
- Fixes MIPS build failure (AtomicU64 missing)
This commit is contained in:
spinline
2026-02-06 00:20:51 +03:00
parent 25e2b6bec9
commit e8af1a1812
19 changed files with 1225 additions and 2 deletions

9
Cargo.lock generated
View File

@@ -477,10 +477,9 @@ checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "coarsetime"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2"
dependencies = [
"libc",
"portable-atomic",
"wasix",
"wasm-bindgen",
]
@@ -2278,6 +2277,12 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "potential_utf"
version = "0.1.4"

View File

@@ -9,3 +9,6 @@ lto = true
codegen-units = 1
panic = "abort"
strip = true
[patch.crates-io]
coarsetime = { path = "third_party/coarsetime" }

1
third_party/coarsetime/.cargo-ok vendored Normal file
View File

@@ -0,0 +1 @@
{"v":1}

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "831c97016aa3d8f7851999aa1deea8407e7cbd42"
},
"path_in_vcs": ""
}

View File

@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10

View File

@@ -0,0 +1,17 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
repo-token: ${{ secrets.GITHUB_TOKEN }}

4
third_party/coarsetime/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target
Cargo.lock
.vscode
zig-cache

82
third_party/coarsetime/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,82 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "coarsetime"
version = "0.1.37"
authors = ["Frank Denis <github@pureftpd.org>"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Time and duration crate optimized for speed"
homepage = "https://github.com/jedisct1/rust-coarsetime"
readme = "README.md"
keywords = [
"time",
"date",
"duration",
]
categories = [
"concurrency",
"date-and-time",
"os",
]
license = "BSD-2-Clause"
repository = "https://github.com/jedisct1/rust-coarsetime"
[features]
wasi-abi2 = ["dep:wasi-abi2"]
[lib]
name = "coarsetime"
path = "src/lib.rs"
[[bench]]
name = "benchmark"
path = "benches/benchmark.rs"
harness = false
[dev-dependencies.benchmark-simple]
version = "0.1.10"
[dependencies.portable-atomic]
version = "1.6"
[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies.wasm-bindgen]
version = "0.2"
[target.'cfg(any(target_os = "wasix", target_os = "wasi"))'.dependencies.wasix]
version = "0.13"
[target.'cfg(not(any(target_os = "wasix", target_os = "wasi")))'.dependencies.libc]
version = "0.2"
[target.'cfg(target_os = "wasi")'.dependencies.wasi-abi2]
version = "0.14.7"
optional = true
package = "wasi"
[profile.bench]
codegen-units = 1
[profile.dev]
overflow-checks = true
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
incremental = false

47
third_party/coarsetime/Cargo.toml.orig generated vendored Normal file
View File

@@ -0,0 +1,47 @@
[package]
name = "coarsetime"
version = "0.1.37"
description = "Time and duration crate optimized for speed"
authors = ["Frank Denis <github@pureftpd.org>"]
keywords = ["time", "date", "duration"]
readme = "README.md"
license = "BSD-2-Clause"
homepage = "https://github.com/jedisct1/rust-coarsetime"
repository = "https://github.com/jedisct1/rust-coarsetime"
categories = ["concurrency", "date-and-time", "os"]
edition = "2018"
[features]
wasi-abi2 = ["dep:wasi-abi2"]
[target.'cfg(not(any(target_os = "wasix", target_os = "wasi")))'.dependencies]
libc = "0.2"
[target.'cfg(target_os = "wasi")'.dependencies]
wasi-abi2 = { package = "wasi", version = "0.14.7", optional = true }
[target.'cfg(any(target_os = "wasix", target_os = "wasi"))'.dependencies]
wasix = "0.13"
[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies]
wasm-bindgen = "0.2"
[dev-dependencies]
benchmark-simple = "0.1.10"
[profile.bench]
codegen-units = 1
[[bench]]
name = "benchmark"
harness = false
[profile.release]
lto = true
panic = "abort"
opt-level = 3
codegen-units = 1
incremental = false
[profile.dev]
overflow-checks=true

25
third_party/coarsetime/LICENSE vendored Normal file
View File

@@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2016-2026, Frank Denis
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

90
third_party/coarsetime/README.md vendored Normal file
View File

@@ -0,0 +1,90 @@
[![Documentation](https://docs.rs/coarsetime/badge.svg)](https://docs.rs/coarsetime)
[![Windows build status](https://ci.appveyor.com/api/projects/status/xlbhk9850dvl5ylh?svg=true)](https://ci.appveyor.com/project/jedisct1/rust-coarsetime)
# coarsetime
A Rust crate to make time measurements, that focuses on speed, API stability and portability.
This crate is a partial replacement for the `Time` and `Duration` structures
from the standard library, with the following differences:
* Speed is privileged over accuracy. In particular, `CLOCK_MONOTONIC_COARSE` is
used to retrieve the clock value on Linux systems, and transformations avoid
operations that can be slow on non-Intel systems.
* The number of system calls can be kept to a minimum. The "most recent
timestamp" is always kept in memory. It can be read with just a load operation,
and can be updated only as frequently as necessary.
* The API is stable, and the same for all platforms. Unlike the standard library, it doesn't silently compile functions that do nothing but panic at runtime on some platforms.
# Installation
`coarsetime` is available on [crates.io](https://crates.io/crates/coarsetime)
and works on Rust stable, beta, and nightly.
Windows and Unix-like systems are supported.
Available feature:
* `wasi-abi2`: when targeting WASI, use the second preview of the ABI. Default is to use the regular WASI-core ABI.
# Documentation
[API documentation](https://docs.rs/coarsetime)
# Example
```rust
extern crate coarsetime;
use coarsetime::{Duration, Instant, Updater};
// Get the current instant. This may require a system call, but it may also
// be faster than the stdlib equivalent.
let now = Instant::now();
// Get the latest known instant. This operation is super fast.
// In this case, the value will be identical to `now`, because we haven't
// updated the latest known instant yet.
let ts1 = Instant::recent();
// Update the latest known instant. This may require a system call.
// Note that a call to `Instant::now()` also updates the stored instant.
Instant::update();
// Now, we may get a different instant. This call is also super fast.
let ts2 = Instant::recent();
// Compute the time elapsed between ts2 and ts1.
let elapsed_ts2_ts1 = ts2.duration_since(ts1);
// Operations such as `+` and `-` between `Instant` and `Duration` are also
// available.
let elapsed_ts2_ts1 = ts2 - ts1;
// Returns the time elapsed since ts1.
// This retrieves the actual current time, and may require a system call.
let elapsed_since_ts1 = ts1.elapsed();
// Returns the approximate time elapsed since ts1.
// This uses the latest known instant, and is super fast.
let elapsed_since_recent = ts1.elapsed_since_recent();
// Instant::update() should be called periodically, for example using an
// event loop. Alternatively, the crate provides an easy way to spawn a
// background task that will periodically update the latest known instant.
// Here, the update will happen every 250ms.
let updater = Updater::new(250).start().unwrap();
// From now on, Instant::recent() will always return an approximation of the
// current instant.
let ts3 = Instant::recent();
// Stop the task.
updater.stop().unwrap();
// Returns the elapsed time since the UNIX epoch
let unix_timestamp = Clock::now_since_epoch();
// Returns an approximation of the elapsed time since the UNIX epoch, based on
// the latest time update
let unix_timestamp_approx = Clock::recent_since_epoch();
```

View File

@@ -0,0 +1,61 @@
use benchmark_simple::*;
use coarsetime::*;
use std::time;
fn main() {
let options = &Options {
iterations: 250_000,
warmup_iterations: 25_000,
min_samples: 5,
max_samples: 10,
max_rsd: 1.0,
..Default::default()
};
bench_coarsetime_now(options);
bench_coarsetime_recent(options);
bench_coarsetime_elapsed(options);
bench_coarsetime_elapsed_since_recent(options);
bench_stdlib_now(options);
bench_stdlib_elapsed(options);
}
fn bench_coarsetime_now(options: &Options) {
let b = Bench::new();
Instant::update();
let res = b.run(options, Instant::now);
println!("coarsetime_now(): {}", res.throughput(1));
}
fn bench_coarsetime_recent(options: &Options) {
let b = Bench::new();
Instant::update();
let res = b.run(options, Instant::recent);
println!("coarsetime_recent(): {}", res.throughput(1));
}
fn bench_coarsetime_elapsed(options: &Options) {
let b = Bench::new();
let ts = Instant::now();
let res = b.run(options, || ts.elapsed());
println!("coarsetime_elapsed(): {}", res.throughput(1));
}
fn bench_coarsetime_elapsed_since_recent(options: &Options) {
let b = Bench::new();
let ts = Instant::now();
let res = b.run(options, || ts.elapsed_since_recent());
println!("coarsetime_since_recent(): {}", res.throughput(1));
}
fn bench_stdlib_now(options: &Options) {
let b = Bench::new();
let res = b.run(options, time::Instant::now);
println!("stdlib_now(): {}", res.throughput(1));
}
fn bench_stdlib_elapsed(options: &Options) {
let b = Bench::new();
let ts = time::Instant::now();
let res = b.run(options, || ts.elapsed());
println!("stdlib_elapsed(): {}", res.throughput(1));
}

93
third_party/coarsetime/src/clock.rs vendored Normal file
View File

@@ -0,0 +1,93 @@
#[cfg(not(all(
any(target_arch = "wasm32", target_arch = "wasm64"),
target_os = "unknown"
)))]
use std::time;
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(not(target_has_atomic = "64"))]
use portable_atomic::{AtomicU64, Ordering};
use super::Duration;
static RECENT: AtomicU64 = AtomicU64::new(0);
#[cfg(all(
any(target_arch = "wasm32", target_arch = "wasm64"),
target_os = "unknown"
))]
mod js_imports {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
pub type Date;
#[wasm_bindgen(static_method_of = Date)]
pub fn now() -> f64;
}
}
/// System time
#[derive(Debug)]
pub struct Clock;
/// Alias for `Duration`.
pub type UnixTimeStamp = Duration;
impl Clock {
/// Returns the elapsed time since the UNIX epoch
#[inline]
pub fn now_since_epoch() -> UnixTimeStamp {
Duration::from_u64(unix_ts())
}
/// Returns the elapsed time since the UNIX epoch, based on the latest
/// explicit time update
#[inline]
pub fn recent_since_epoch() -> UnixTimeStamp {
Duration::from_u64(RECENT.load(Ordering::Relaxed))
}
/// Updates the cached system time.
///
/// This function should be called frequently, for example in an event loop
/// or using an `Updater` task.
#[inline]
pub fn update() {
let now = unix_ts();
RECENT.store(now, Ordering::Relaxed)
}
/// Sets the cached system time to the specified timestamp.
/// This function is intended for testing purposes only.
/// It should not be used in production code.
pub fn set_recent_since_epoch(recent: UnixTimeStamp) {
RECENT.store(recent.as_u64(), Ordering::Relaxed)
}
}
#[cfg(all(
any(target_arch = "wasm32", target_arch = "wasm64"),
target_os = "unknown"
))]
#[inline]
fn unix_ts() -> u64 {
let unix_ts_now_sys = (js_imports::Date::now() / 1000.0).round() as u64;
let unix_ts_now = Duration::from_secs(unix_ts_now_sys);
unix_ts_now.as_u64()
}
#[cfg(not(all(
any(target_arch = "wasm32", target_arch = "wasm64"),
target_os = "unknown"
)))]
#[inline]
fn unix_ts() -> u64 {
let unix_ts_now_sys = time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.expect("The system clock is not properly set");
let unix_ts_now = Duration::from(unix_ts_now_sys);
unix_ts_now.as_u64()
}

270
third_party/coarsetime/src/duration.rs vendored Normal file
View File

@@ -0,0 +1,270 @@
use std::convert::From;
use std::ops::*;
use std::time;
use super::helpers::*;
/// A duration type to represent an approximate span of time
#[derive(Copy, Clone, Debug, Hash, Ord, Eq, PartialOrd, PartialEq, Default)]
pub struct Duration(u64);
impl Duration {
/// Creates a new `Duration` from the specified number of seconds and
/// additional nanosecond precision
#[inline]
pub const fn new(sec: u64, nanos: u32) -> Duration {
Duration(_timespec_to_u64(sec, nanos))
}
/// Creates a new Duration from the specified number of days
#[inline]
pub const fn from_days(days: u64) -> Duration {
Duration(_sec_to_u64(days * 86400))
}
/// Creates a new Duration from the specified number of hours
#[inline]
pub const fn from_hours(hours: u64) -> Duration {
Duration(_sec_to_u64(hours * 3600))
}
/// Creates a new Duration from the specified number of minutes
#[inline]
pub const fn from_mins(mins: u64) -> Duration {
Duration(_sec_to_u64(mins * 60))
}
/// Creates a new Duration from the specified number of seconds
#[inline]
pub const fn from_secs(secs: u64) -> Duration {
Duration(_sec_to_u64(secs))
}
/// Creates a new Duration from the specified number of milliseconds
#[inline]
pub const fn from_millis(millis: u64) -> Duration {
Duration(_millis_to_u64(millis))
}
/// Returns the number of days represented by this duration
#[inline]
pub const fn as_days(&self) -> u64 {
self.as_secs() / 86400
}
/// Returns the number of hours represented by this duration
#[inline]
pub const fn as_hours(&self) -> u64 {
self.as_secs() / 3600
}
/// Returns the number of minutes represented by this duration
#[inline]
pub const fn as_mins(&self) -> u64 {
self.as_secs() / 60
}
/// Returns the number of whole seconds represented by this duration
#[inline]
pub const fn as_secs(&self) -> u64 {
self.0 >> 32
}
/// Returns the number of whole milliseconds represented by this duration
#[inline]
pub const fn as_millis(&self) -> u64 {
((self.0 as u128 * 125) >> 29) as u64
}
/// Returns the number of whole microseconds represented by this duration
#[inline]
pub const fn as_micros(&self) -> u64 {
((self.0 as u128 * 125_000) >> 29) as u64
}
/// Returns the number of whole nanoseconds represented by this duration
#[inline]
pub const fn as_nanos(&self) -> u64 {
((self.0 as u128 * 125_000_000) >> 29) as u64
}
/// Returns the nanosecond precision represented by this duration
#[inline]
pub const fn subsec_nanos(&self) -> u32 {
((self.0 as u32 as u64 * 125_000_000) >> 29) as u32
}
/// Return this duration as a number of "ticks".
///
/// Note that length of a 'tick' is not guaranteed to represent
/// the same amount of time across different platforms, or from
/// one version of `coarsetime` to another.
#[inline]
pub const fn as_ticks(&self) -> u64 {
self.as_u64()
}
/// Creates a new Duration from the specified number of "ticks".
///
/// Note that length of a 'tick' is not guaranteed to represent
/// the same amount of time across different platforms, or from
/// one version of `coarsetime` to another.
#[inline]
pub const fn from_ticks(ticks: u64) -> Duration {
Self::from_u64(ticks)
}
#[doc(hidden)]
#[inline]
pub const fn as_u64(&self) -> u64 {
self.0
}
#[doc(hidden)]
#[inline]
pub const fn from_u64(ts: u64) -> Duration {
Duration(ts)
}
/// Returns the duration as a floating point number, representing the number
/// of seconds
#[inline]
pub fn as_f64(&self) -> f64 {
(self.0 as f64) / ((1u64 << 32) as f64)
}
/// Returns the absolute difference between two `Duration`s
#[inline]
pub const fn abs_diff(&self, other: Duration) -> Duration {
Duration(self.0.abs_diff(other.0))
}
/// Add two durations, saturating on overflow
#[inline]
pub const fn saturating_add(self, rhs: Duration) -> Duration {
Duration(self.0.saturating_add(rhs.0))
}
/// Add two durations, returning `None` on overflow
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
self.0.checked_add(rhs.0).map(Duration)
}
/// Subtract two durations, saturating on underflow/overflow
#[inline]
pub const fn saturating_sub(self, rhs: Duration) -> Duration {
Duration(self.0.saturating_sub(rhs.0))
}
/// Subtract two durations, returning `None` on underflow/overflow
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
self.0.checked_sub(rhs.0).map(Duration)
}
/// Multiply a duration by a scalar, saturating on overflow
#[inline]
pub const fn saturating_mul(self, rhs: u32) -> Duration {
Duration(self.0.saturating_mul(rhs as u64))
}
/// Multiply a duration by a scalar, returning `None` on overflow
#[inline]
pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
self.0.checked_mul(rhs as u64).map(Duration)
}
/// Divide a duration by a scalar, returning `None` for division by zero
#[inline]
pub fn checked_div(self, rhs: u32) -> Option<Duration> {
self.0.checked_div(rhs as u64).map(Duration)
}
}
#[doc(hidden)]
impl From<u64> for Duration {
#[doc(hidden)]
#[inline]
fn from(ts: u64) -> Duration {
Duration::from_u64(ts)
}
}
impl Add for Duration {
type Output = Duration;
#[inline]
fn add(self, rhs: Duration) -> Duration {
Duration(self.0 + rhs.0)
}
}
impl AddAssign for Duration {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl Sub for Duration {
type Output = Duration;
#[inline]
fn sub(self, rhs: Duration) -> Duration {
Duration(self.0 - rhs.0)
}
}
impl SubAssign for Duration {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
impl Mul<u32> for Duration {
type Output = Duration;
#[inline]
fn mul(self, rhs: u32) -> Duration {
Duration(self.0 * rhs as u64)
}
}
impl MulAssign<u32> for Duration {
#[inline]
fn mul_assign(&mut self, rhs: u32) {
*self = *self * rhs;
}
}
impl Div<u32> for Duration {
type Output = Duration;
#[inline]
fn div(self, rhs: u32) -> Duration {
Duration(self.0 / rhs as u64)
}
}
impl DivAssign<u32> for Duration {
#[inline]
fn div_assign(&mut self, rhs: u32) {
*self = *self / rhs;
}
}
impl From<Duration> for time::Duration {
#[inline]
fn from(duration: Duration) -> time::Duration {
time::Duration::new(duration.as_secs(), duration.subsec_nanos())
}
}
impl From<time::Duration> for Duration {
#[inline]
fn from(duration_sys: time::Duration) -> Duration {
Duration::new(duration_sys.as_secs(), duration_sys.subsec_nanos())
}
}

26
third_party/coarsetime/src/helpers.rs vendored Normal file
View File

@@ -0,0 +1,26 @@
#[inline]
pub const fn _sec_to_u64(sec: u64) -> u64 {
sec.saturating_mul(1 << 32)
}
#[inline]
pub const fn _millis_to_u64(millis: u64) -> u64 {
let secs = millis / 1_000;
secs.saturating_mul(1 << 32) | ((millis - secs * 1_000) << 22)
}
#[inline]
pub const fn _nsecs_to_u64(nsecs: u64) -> u64 {
let secs = nsecs / 1_000_000_000;
_timespec_to_u64(secs, (nsecs - secs * 1_000_000_000) as u32)
}
#[inline]
pub const fn _timespec_to_u64(tp_sec: u64, tp_nsec: u32) -> u64 {
tp_sec.saturating_mul(1 << 32) | ((tp_nsec as u64 * 9_223_372_037) >> 31)
}
#[inline]
pub const fn _timeval_to_u64(tv_sec: u64, tv_usec: u32) -> u64 {
tv_sec.saturating_mul(1 << 32) | ((tv_usec as u64 * 9_223_372_036_855) >> 31)
}

335
third_party/coarsetime/src/instant.rs vendored Normal file
View File

@@ -0,0 +1,335 @@
#[allow(unused_imports)]
use std::mem::MaybeUninit;
use std::ops::*;
#[allow(unused_imports)]
use std::ptr::*;
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(not(target_has_atomic = "64"))]
use portable_atomic::{AtomicU64, Ordering};
use super::duration::*;
#[allow(unused_imports)]
use super::helpers::*;
/// A measurement of a *monotonically* increasing clock.
/// Opaque and useful only with `Duration`.
///
/// Resulting durations are actual durations; they do not get affected by
/// clock adjustments, leap seconds, or similar.
/// In order to get a measurement of the *wall clock*, use `Date` instead.
#[derive(Copy, Clone, Debug, Hash, Ord, Eq, PartialOrd, PartialEq)]
pub struct Instant(u64);
static RECENT: AtomicU64 = AtomicU64::new(0);
#[cfg(windows)]
extern "system" {
pub fn GetTickCount64() -> libc::c_ulonglong;
}
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
#[allow(non_camel_case_types)]
type clockid_t = libc::c_int;
#[cfg(target_os = "macos")]
const CLOCK_MONOTONIC_RAW_APPROX: clockid_t = 5;
#[cfg(target_os = "macos")]
extern "system" {
fn clock_gettime_nsec_np(clk_id: clockid_t) -> u64;
}
#[cfg(target_os = "freebsd")]
const CLOCK_MONOTONIC_FAST: clockid_t = 12;
#[cfg(all(
any(target_arch = "wasm32", target_arch = "wasm64"),
target_os = "unknown"
))]
mod js_imports {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[allow(non_camel_case_types)]
pub type performance;
#[wasm_bindgen(static_method_of = performance)]
pub fn now() -> f64;
}
}
impl Instant {
/// Returns an instant corresponding to "now"
///
/// This function also updates the stored instant.
pub fn now() -> Instant {
let now = Self::_now();
Self::_update(now);
Instant(now)
}
/// Returns an instant corresponding to "now" without updating the cached value.
/// After this, `recent()` will still return the old instant.
///
/// `now()` is generally preferred over this function.
pub fn now_without_cache_update() -> Instant {
let now = Self::_now();
Instant(now)
}
/// Returns an instant corresponding to the latest update
pub fn recent() -> Instant {
match Self::_recent() {
0 => Instant::now(),
recent => Instant(recent),
}
}
/// Update the stored instant
///
/// This function should be called frequently, for example in an event loop
/// or using an `Updater` task.
pub fn update() {
let now = Self::_now();
Self::_update(now);
}
/// Returns the amount of time elapsed from another instant to this one
#[inline]
pub fn duration_since(&self, earlier: Instant) -> Duration {
*self - earlier
}
/// Returns the amount of time elapsed between the this instant was created
/// and the latest update
#[inline]
pub fn elapsed_since_recent(&self) -> Duration {
Self::recent() - *self
}
/// Returns the amount of time elapsed since this instant was created
///
/// This function also updates the stored instant.
#[inline]
pub fn elapsed(&self) -> Duration {
Self::now() - *self
}
/// Return a representation of this instant as a number of "ticks".
///
/// Note that length of a 'tick' is not guaranteed to represent
/// the same amount of time across different platforms, or from
/// one version of `coarsetime` to another.
///
/// Note also that the instant represented by "0" ticks is
/// unspecified. It is not guaranteed to be the same time across
/// different platforms, or from one version of `coarsetime` to
/// another.
///
/// This API is mainly intended for applications that need to
/// store the value of an `Instant` in an
/// [`AtomicU64`](std::sync::atomic::AtomicU64).
#[inline]
pub const fn as_ticks(&self) -> u64 {
self.as_u64()
}
/// Create an `Instant` from a number of "ticks".
///
/// Note that length of a 'tick' is not guaranteed to represent
/// the same amount of time across different platforms, or from
/// one version of `coarsetime` to another.
///
/// Note also that the instant represented by "0" ticks is
/// unspecified. It is not guaranteed to be the same time across
/// different platforms, or from one version of `coarsetime` to
/// another.
#[inline]
pub const fn from_ticks(ticks: u64) -> Instant {
Self::from_u64(ticks)
}
#[doc(hidden)]
#[inline]
pub const fn as_u64(&self) -> u64 {
self.0
}
#[doc(hidden)]
#[inline]
pub const fn from_u64(ts: u64) -> Instant {
Instant(ts)
}
/// Calculate an `Instant` that is a `Duration` later, saturating on overflow
#[inline]
pub const fn saturating_add(self, rhs: Duration) -> Instant {
Instant(self.0.saturating_add(rhs.as_u64()))
}
/// Calculate an `Instant` that is a `Duration` later, returning `None` on overflow
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<Instant> {
self.0.checked_add(rhs.as_u64()).map(Instant)
}
/// Calculate an `Instant` that is a `Duration` earlier, saturating on underflow
#[inline]
pub const fn saturating_sub(self, rhs: Duration) -> Instant {
Instant(self.0.saturating_sub(rhs.as_u64()))
}
/// Calculate an `Instant` that is a `Duration` earlier, returning `None` on underflow
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<Instant> {
self.0.checked_sub(rhs.as_u64()).map(Instant)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn _now() -> u64 {
let mut tp = MaybeUninit::<libc::timespec>::uninit();
let tp = unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr());
tp.assume_init()
};
_timespec_to_u64(tp.tv_sec as u64, tp.tv_nsec as u32)
}
#[cfg(target_os = "macos")]
fn _now() -> u64 {
let nsec = unsafe { clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX) };
_nsecs_to_u64(nsec)
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn _now() -> u64 {
let mut tp = MaybeUninit::<libc::timespec>::uninit();
let tp = unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC_FAST, tp.as_mut_ptr());
tp.assume_init()
};
_timespec_to_u64(tp.tv_sec as u64, tp.tv_nsec as u32)
}
#[cfg(all(
unix,
not(any(
target_os = "macos",
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly"
))
))]
fn _now() -> u64 {
let mut tv = MaybeUninit::<libc::timeval>::uninit();
let tv = unsafe {
libc::gettimeofday(tv.as_mut_ptr(), null_mut());
tv.assume_init()
};
_timeval_to_u64(tv.tv_sec as u64, tv.tv_usec as u32)
}
#[cfg(windows)]
fn _now() -> u64 {
let tc = unsafe { GetTickCount64() } as u64;
_millis_to_u64(tc)
}
#[cfg(all(target_os = "wasi", not(feature = "wasi-abi2")))]
fn _now() -> u64 {
use wasix::{clock_time_get, CLOCKID_MONOTONIC, CLOCKID_REALTIME};
let nsec = unsafe { clock_time_get(CLOCKID_MONOTONIC, 1_000_000) }
.or_else(|_| unsafe { clock_time_get(CLOCKID_REALTIME, 1_000_000) })
.expect("Clock not available");
_nsecs_to_u64(nsec)
}
#[cfg(all(target_os = "wasi", feature = "wasi-abi2"))]
fn _now() -> u64 {
let nsec = wasi_abi2::clocks::monotonic_clock::now();
_nsecs_to_u64(nsec)
}
#[cfg(all(
any(target_arch = "wasm32", target_arch = "wasm64"),
target_os = "unknown"
))]
fn _now() -> u64 {
_millis_to_u64(js_imports::performance::now() as u64)
}
#[cfg(all(target_arch = "x86_64", target_env = "sgx", target_vendor = "fortanix"))]
fn _now() -> u64 {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap();
timestamp.as_secs() * 1_000_000_000 + (timestamp.subsec_nanos() as u64)
}
#[inline]
fn _update(now: u64) {
RECENT.store(now, Ordering::Relaxed)
}
#[inline]
fn _recent() -> u64 {
let recent = RECENT.load(Ordering::Relaxed);
if recent != 0 {
recent
} else {
let now = Self::_now();
Self::_update(now);
Self::_recent()
}
}
}
impl Default for Instant {
fn default() -> Instant {
Self::now()
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
#[inline]
fn sub(self, other: Instant) -> Duration {
Duration::from_u64(self.0.saturating_sub(other.0))
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
#[inline]
fn sub(self, rhs: Duration) -> Instant {
Instant(self.0 - rhs.as_u64())
}
}
impl SubAssign<Duration> for Instant {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
impl Add<Duration> for Instant {
type Output = Instant;
#[inline]
fn add(self, rhs: Duration) -> Instant {
Instant(self.0 + rhs.as_u64())
}
}
impl AddAssign<Duration> for Instant {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}

37
third_party/coarsetime/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,37 @@
//! A crate to make time measurements that focuses on speed.
//!
//! This crate is a partial replacement for the `Time` and `Duration` structures
//! from the standard library, with the following differences:
//!
//! * Speed is privileged over accuracy. In particular, `CLOCK_MONOTONIC_COARSE`
//! is used to retrieve the clock value on Linux systems, and transformations avoid
//! operations that can be slow on non-Intel systems.
//! * The number of system calls can be kept to a minimum. The "most recent
//! timestamp" is always kept in memory.
//! It can be read with just a load operation, and can be
//! updated only as frequently as necessary.
//!
//! # Installation
//!
//! `coarsetime` is available on [crates.io](https://crates.io/crates/coarsetime) and works on
//! Rust stable, beta, and nightly.
//!
//! Windows and Unix-like systems are supported.
#![allow(clippy::trivially_copy_pass_by_ref)]
mod clock;
mod duration;
mod helpers;
mod instant;
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
mod updater;
#[cfg(test)]
mod tests;
pub use self::clock::*;
pub use self::duration::*;
pub use self::instant::*;
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
pub use self::updater::*;

55
third_party/coarsetime/src/tests.rs vendored Normal file
View File

@@ -0,0 +1,55 @@
use std::thread::sleep;
use std::time;
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use super::Updater;
use super::{Clock, Duration, Instant};
#[test]
fn tests() {
let ts = Instant::now();
let d = Duration::from_secs(2);
sleep(time::Duration::new(3, 0));
let elapsed = ts.elapsed().as_secs();
println!("Elapsed: {elapsed} secs");
assert!(elapsed >= 2);
assert!(elapsed < 100);
assert!(ts.elapsed_since_recent() > d);
let ts = Instant::now();
sleep(time::Duration::new(1, 0));
assert_eq!(Instant::recent(), ts);
Instant::update();
assert!(Instant::recent() > ts);
let clock_now = Clock::recent_since_epoch();
sleep(time::Duration::new(1, 0));
assert_eq!(Clock::recent_since_epoch(), clock_now);
assert!(Clock::now_since_epoch() > clock_now);
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
tests_updater();
}
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[test]
fn tests_updater() {
let updater = Updater::new(250)
.start()
.expect("Unable to start a background updater");
let ts = Instant::recent();
let clock_recent = Clock::recent_since_epoch();
sleep(time::Duration::new(2, 0));
assert!(Clock::recent_since_epoch() > clock_recent);
assert!(Instant::recent() != ts);
updater.stop().unwrap();
let clock_recent = Clock::recent_since_epoch();
sleep(time::Duration::new(1, 0));
assert_eq!(Clock::recent_since_epoch(), clock_recent);
}
#[test]
fn tests_duration() {
let duration = Duration::from_days(1000);
assert_eq!(duration.as_days(), 1000);
}

58
third_party/coarsetime/src/updater.rs vendored Normal file
View File

@@ -0,0 +1,58 @@
use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time;
use super::clock::*;
use super::instant::*;
/// A service to periodically call `Instant::update()`
#[derive(Debug)]
pub struct Updater {
period: time::Duration,
running: Arc<AtomicBool>,
th: Option<thread::JoinHandle<()>>,
}
impl Updater {
/// Spawns a background task to call `Instant::update()` periodically
pub fn start(mut self) -> Result<Self, io::Error> {
let period = self.period;
let running = self.running.clone();
running.store(true, Ordering::Relaxed);
let th: thread::JoinHandle<()> = thread::Builder::new()
.name("coarsetime".to_string())
.spawn(move || {
while running.load(Ordering::Relaxed) {
thread::sleep(period);
Instant::update();
Clock::update();
}
})?;
self.th = Some(th);
Instant::update();
Clock::update();
Ok(self)
}
/// Stops the periodic updates
pub fn stop(mut self) -> Result<(), io::Error> {
self.running.store(false, Ordering::Relaxed);
self.th
.take()
.expect("updater is not running")
.join()
.map_err(|_| io::Error::other("failed to properly stop the updater"))
}
/// Creates a new `Updater` with the specified update period, in
/// milliseconds.
pub fn new(period_millis: u64) -> Updater {
Updater {
period: time::Duration::from_millis(period_millis),
running: Arc::new(AtomicBool::new(false)),
th: None,
}
}
}