Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check upstream for changes in CI #40

Merged
merged 1 commit into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/upstream-sources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{ "owner": "rust-lang", "repo": "libm", "tag": "libm-v0.2.11", "path": "src/math/trunc.rs" },
{ "owner": "rust-lang", "repo": "libm", "tag": "libm-v0.2.11", "path": "src/math/copysign.rs" },
{ "owner": "rust-lang", "repo": "libm", "tag": "libm-v0.2.11", "path": "src/math/round.rs" },
{ "owner": "rust-lang", "repo": "rust", "tag": "1.83.0", "path": "library/core/src/time.rs" },
{
"owner": "rust-lang",
"repo": "rust",
"tag": "1.83.0",
"path": "library/std/src/sys/alloc/wasm.rs"
},
{
"owner": "serde-rs",
"repo": "serde",
"tag": "v1.0.215",
"path": "serde/src/de/impls.rs"
}
]
57 changes: 57 additions & 0 deletions .github/workflows/upstream.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Upstream check

on:
push:
branches: ["main"]
pull_request:
schedule:
- cron: "0 7 * * *"

concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true

jobs:
check:
name: Check upstream for changes

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Test
uses: actions/github-script@v7
with:
script: |
const upstreamSources = require('./.github/upstream-sources.json')
for (const source of upstreamSources) {
Promise.all([
github.rest.repos.getContent({
owner: source.owner,
repo: source.repo,
path: source.path,
ref: source.tag,
}),
github.rest.repos
.getLatestRelease({
owner: source.owner,
repo: source.repo,
})
.then((tags) => {
return github.rest.repos.getContent({
owner: source.owner,
repo: source.repo,
path: source.path,
ref: tags.data.tag_name,
})
}),
]).then(([current, latest]) => {
if (current.data.sha != latest.data.sha) {
core.setFailed(`
<${current.data.html_url}> has been updated to <${latest.data.html_url}>.
Check if the relevant code needs to be updated and then update the entry in \`.github/upstream-sources.json\`.
`)
}
})
}
46 changes: 30 additions & 16 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,26 @@ pub fn main() {
Duration::from_secs_f64(time_stamp) / 1000
});
benchmark("`Duration::from_secs_f64()` with rounding", |time_stamp| {
// See <https://doc.rust-lang.org/1.73.0/src/core/time.rs.html#657-668>.
// Inspired by <https://github.com/rust-lang/rust/blob/1.83.0/library/core/src/time.rs#L822-L833>.
/// Number of nanoseconds in a second.
const NANOS_PER_SEC: u64 = 1_000_000_000;
let rhs: u32 = 1000;
let time_stamp = Duration::from_secs_f64(time_stamp);
let secs = time_stamp.as_secs() / 1000;
let carry = time_stamp.as_secs() - secs * 1000;
let extra_nanos = (carry * 1_000_000_000 / 1000) as u32;
let nanos = time_stamp.subsec_micros()
+ u32::from(time_stamp.subsec_nanos() % 1000 > 499)
+ extra_nanos;
let (secs, extra_secs) = (
time_stamp.as_secs() / u64::from(rhs),
time_stamp.as_secs() % u64::from(rhs),
);
let (mut nanos, extra_nanos) = (
time_stamp.subsec_nanos() / rhs,
time_stamp.subsec_nanos() % rhs,
);
// CHANGED: Extracted part of the calculation into variable.
let extra = extra_secs * NANOS_PER_SEC + u64::from(extra_nanos);
nanos += u32::try_from(extra / u64::from(rhs)).unwrap();
// CHANGED: Added rounding.
nanos += u32::from(extra % u64::from(rhs) >= u64::from(rhs / 2));
// CHANGED: Removed check that would fail because of the additional time added
// by rounding.
Duration::new(secs, nanos)
});
}
Expand Down Expand Up @@ -186,19 +198,17 @@ impl F64 {
}

/// Adjusted [`Duration::from_secs_f64()`].
///
/// See <https://doc.rust-lang.org/1.73.0/src/core/time.rs.html#1262-1340>.
#[expect(warnings, reason = "code is copied")]
fn adjusted_std(time_stamp: f64) -> Duration {
// CHANGED: 1G to 1M.
const NANOS_PER_MILLI: u32 = 1_000_000;

// See <https://doc.rust-lang.org/1.73.0/src/core/time.rs.html#1477-1484>.
// See <https://github.com/rust-lang/rust/blob/1.83.0/library/core/src/time.rs#L1694-L1703>.
const MANT_BITS: i16 = 52;
const EXP_BITS: i16 = 11;
const OFFSET: i16 = 44;

// See <https://doc.rust-lang.org/1.73.0/src/core/time.rs.html#1262-1340>.
// See <https://github.com/rust-lang/rust/blob/1.83.0/library/core/src/time.rs#L1480-L1558>.
const MIN_EXP: i16 = 1 - (1_i16 << EXP_BITS) / 2;
const MANT_MASK: u64 = (1 << MANT_BITS) - 1;
const EXP_MASK: u64 = (1 << 1_i16 << EXP_BITS) - 1;
Expand Down Expand Up @@ -277,13 +287,17 @@ fn adjusted_std(time_stamp: f64) -> Duration {
panic!("can not convert float seconds to Duration: value is either too big or NaN")
};

let secs = millis / 1000;
let carry = millis - secs * 1000;
let extra_nanos = carry * u64::from(NANOS_PER_MILLI);
nanos += extra_nanos as u32;
// Inspired by <https://github.com/rust-lang/rust/blob/1.83.0/library/core/src/time.rs#L822-L833>.
/// Number of nanoseconds in a second.
const NANOS_PER_SEC: u64 = 1_000_000_000;
let rhs: u32 = 1000;

let (secs, extra_secs) = (millis / u64::from(rhs), millis % u64::from(rhs));
// CHANGED: Nanos were already calculated during the conversion.
nanos += u32::try_from((extra_secs * NANOS_PER_SEC) / u64::from(rhs)).unwrap();

debug_assert!(
nanos < 1_000_000_000,
u64::from(nanos) < NANOS_PER_SEC,
"impossible amount of nanoseconds found"
);

Expand Down
28 changes: 18 additions & 10 deletions src/time/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,19 +282,27 @@ mod test {
struct ControlDuration(Duration);

impl ControlDuration {
/// Implements conversion from `DOMHighResTimeStamp` to [`Duration`] by
/// using [`Duration::checked_div()`].
/// Implements perfect but expensive conversion from
/// `DOMHighResTimeStamp` to [`Duration`].
fn new(time_stamp: f64) -> Self {
// See <https://doc.rust-lang.org/1.73.0/src/core/time.rs.html#657-668>.
// Inspired by <https://github.com/rust-lang/rust/blob/1.83.0/library/core/src/time.rs#L822-L833>.
/// Number of nanoseconds in a second.
const NANOS_PER_SEC: u64 = 1_000_000_000;
let rhs: u32 = 1000;
let time_stamp = Duration::from_secs_f64(time_stamp);
let secs = time_stamp.as_secs() / 1000;
let carry = time_stamp.as_secs() - secs * 1000;
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
let extra_nanos = (carry * 1_000_000_000 / 1000) as u32;
let (secs, extra_secs) = (
time_stamp.as_secs() / u64::from(rhs),
time_stamp.as_secs() % u64::from(rhs),
);
let (mut nanos, extra_nanos) = (
time_stamp.subsec_nanos() / rhs,
time_stamp.subsec_nanos() % rhs,
);
// CHANGED: Extracted part of the calculation into variable.
let extra = extra_secs * NANOS_PER_SEC + u64::from(extra_nanos);
nanos += u32::try_from(extra / u64::from(rhs)).unwrap();
// CHANGED: Added rounding.
let nanos = time_stamp.subsec_micros()
+ u32::from(time_stamp.subsec_nanos() % 1000 > 499)
+ extra_nanos;
nanos += u32::from(extra % u64::from(rhs) >= u64::from(rhs / 2));
// CHANGED: Removed check that would fail because of the additional time added
// by rounding.
Self(Duration::new(secs, nanos))
Expand Down
4 changes: 2 additions & 2 deletions src/time/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//! compatible with Serde's implementation for [`std::time::SystemTime`].
//!
//! This implementation was copied from Serde's
//! [`Deserialize`](https://github.com/serde-rs/serde/blob/5fa711d75d91173aafc6019e03cf8af6ac9ba7b2/serde/src/de/impls.rs#L2168-L2314),
//! [`Deserialize`](https://github.com/serde-rs/serde/blob/v1.0.215/serde/src/de/impls.rs#L2282-L2426),
//! and
//! [`Serialize`](https://github.com/serde-rs/serde/blob/5fa711d75d91173aafc6019e03cf8af6ac9ba7b2/serde/src/ser/impls.rs#L730-L747)
//! [`Serialize`](https://github.com/serde-rs/serde/blob/v1.0.215/serde/src/ser/impls.rs#L753-L768)
//! implementation.

#![allow(warnings)]
Expand Down
63 changes: 45 additions & 18 deletions tests-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ mod allocator {
//! See <https://github.com/rust-lang/rust/blob/1.82.0/library/std/src/sys/alloc/wasm.rs>.

use core::alloc::{GlobalAlloc, Layout};
use core::sync::atomic::{AtomicBool, Ordering};

use dlmalloc::Dlmalloc;

/// The allocator.
static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();
/// The lock flag.
static LOCKED: AtomicBool = AtomicBool::new(false);
static mut DLMALLOC: Dlmalloc = Dlmalloc::new();
/// Global allocator.
#[global_allocator]
static ALLOC: System = System;
Expand All @@ -37,7 +36,7 @@ mod allocator {
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let _lock = lock();
let _lock = lock::lock();
// SAFETY: `DLMALLOC` access is guaranteed to be safe because the lock gives us
// unique and non-reentrant access. Calling `malloc()` is safe because
// preconditions on this function match the trait method preconditions.
Expand All @@ -46,7 +45,7 @@ mod allocator {

#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let _lock = lock();
let _lock = lock::lock();
// SAFETY: `DLMALLOC` access is guaranteed to be safe because the lock gives us
// unique and non-reentrant access. Calling `calloc()` is safe because
// preconditions on this function match the trait method preconditions.
Expand All @@ -55,7 +54,7 @@ mod allocator {

#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let _lock = lock();
let _lock = lock::lock();
// SAFETY: `DLMALLOC` access is guaranteed to be safe because the lock gives us
// unique and non-reentrant access. Calling `free()` is safe because
// preconditions on this function match the trait method preconditions.
Expand All @@ -64,7 +63,7 @@ mod allocator {

#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let _lock = lock();
let _lock = lock::lock();
// SAFETY: `DLMALLOC` access is guaranteed to be safe because the lock gives us
// unique and non-reentrant access. Calling `realloc()` is safe because
// preconditions on this function match the trait method preconditions.
Expand All @@ -80,21 +79,49 @@ mod allocator {
}

/// The lock guard.
// CHANGED: Add `#[must_used]` for safety.
#[must_use = "if unused it will immediately unlock"]
struct DropLock;

/// Create a [`DropLock`].
fn lock() -> DropLock {
while LOCKED
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{}
/// Lock implementation.
#[cfg(target_feature = "atomics")]
mod lock {
use core::sync::atomic::{AtomicBool, Ordering};

use super::DropLock;

/// The lock flag.
// CHANGED: using an `AtomicBool` instead of an `AtomicU32`.
static LOCKED: AtomicBool = AtomicBool::new(false);

DropLock
/// Locks the thread until available.
pub(super) fn lock() -> DropLock {
loop {
if !LOCKED.swap(true, Ordering::Acquire) {
return DropLock;
}
}
}

impl Drop for DropLock {
fn drop(&mut self) {
LOCKED.swap(false, Ordering::Release);
}
}
}

impl Drop for DropLock {
fn drop(&mut self) {
LOCKED.swap(false, Ordering::Release);
/// Empty lock implementation when threads are not available.
#[cfg(not(target_feature = "atomics"))]
mod lock {
use super::DropLock;

/// Locks the thread until available.
#[expect(
clippy::missing_const_for_fn,
reason = "compatibility with non-atomic lock"
)]
pub(super) fn lock() -> DropLock {
DropLock
}
}
}
Loading