Skip to content

Commit

Permalink
Web: fix ControlFlow::WaitUntil to never wake up **before** the giv…
Browse files Browse the repository at this point in the history
…en time (rust-windowing#3133)
  • Loading branch information
daxpedda authored Oct 9, 2023
1 parent 0363be4 commit fac6110
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- **Breaking:** remove `DeviceEvent::Text`.
- On Android, fix `DeviceId` to contain device id's.
- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now.
- On Web, fix `ControlFlow::WaitUntil` to never wake up **before** the given time.

# 0.29.1-beta

Expand Down
41 changes: 39 additions & 2 deletions src/platform_impl/web/web_sys/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ impl Schedule {
options.signal(&controller.signal());

if let Some(duration) = duration {
options.delay(duration.as_millis() as f64);
// `Duration::as_millis()` always rounds down (because of truncation), we want to round
// up instead. This makes sure that the we never wake up **before** the given time.
let duration = duration
.as_secs()
.checked_mul(1000)
.and_then(|secs| secs.checked_add(duration_millis_ceil(duration).into()))
.unwrap_or(u64::MAX);

options.delay(duration as f64);
}

thread_local! {
Expand Down Expand Up @@ -121,9 +129,24 @@ impl Schedule {
.expect("Failed to send message")
});
let handle = if let Some(duration) = duration {
// `Duration::as_millis()` always rounds down (because of truncation), we want to round
// up instead. This makes sure that the we never wake up **before** the given time.
let duration = duration
.as_secs()
.try_into()
.ok()
.and_then(|secs: i32| secs.checked_mul(1000))
.and_then(|secs: i32| {
let millis: i32 = duration_millis_ceil(duration)
.try_into()
.expect("millis are somehow bigger then 1K");
secs.checked_add(millis)
})
.unwrap_or(i32::MAX);

window.set_timeout_with_callback_and_timeout_and_arguments_0(
timeout_closure.as_ref().unchecked_ref(),
duration.as_millis() as i32,
duration,
)
} else {
window.set_timeout_with_callback(timeout_closure.as_ref().unchecked_ref())
Expand Down Expand Up @@ -160,6 +183,20 @@ impl Drop for Schedule {
}
}

// TODO: Replace with `u32::div_ceil()` when we hit Rust v1.73.
fn duration_millis_ceil(duration: Duration) -> u32 {
let micros = duration.subsec_micros();

// From <https://doc.rust-lang.org/1.73.0/src/core/num/uint_macros.rs.html#2086-2094>.
let d = micros / 1000;
let r = micros % 1000;
if r > 0 && 1000 > 0 {
d + 1
} else {
d
}
}

fn has_scheduler_support(window: &web_sys::Window) -> bool {
thread_local! {
static SCHEDULER_SUPPORT: OnceCell<bool> = OnceCell::new();
Expand Down

0 comments on commit fac6110

Please sign in to comment.