Web: fix ControlFlow::WaitUntil to never wake up **before** the given time (#3133)

This commit is contained in:
daxpedda 2023-10-09 12:31:02 +02:00 committed by GitHub
parent 0363be4776
commit fac6110cb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 2 deletions

View file

@ -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

View file

@ -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! {
@ -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())
@ -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();