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

std::time::SystemTime::now() causes panic on wasm32-unknown-unknown. #48564

Closed
y-ich opened this issue Feb 26, 2018 · 18 comments
Closed

std::time::SystemTime::now() causes panic on wasm32-unknown-unknown. #48564

y-ich opened this issue Feb 26, 2018 · 18 comments
Labels
C-bug Category: This is a bug. O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@y-ich
Copy link

y-ich commented Feb 26, 2018

Hi.
Thank you guys for your great project.

I am trying wasm32-unknown-unknown and got an error(panic) in std::time::SystemTime::now().

Here is a reproduction code.

#[no_mangle]
pub fn test() -> u32 {
    use std::time;
    let now = time::SystemTime::now();
    return 1;
}
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script>
      fetch('wasm_time.wasm')
        .then((response) => response.arrayBuffer())
        .then((bytes) => WebAssembly.instantiate(bytes, {}))
        .then((results) => {
          const instance = results.instance;
          console.log(instance.exports.test());
        });
    </script>
  </head>
  <body>
  </body>
</html>

The error on Chrome on macOS. Similar error on Safari on macOS.

wasm-000873fa-78:2 Uncaught (in promise) RuntimeError: unreachable
    at __rust_start_panic (wasm-function[78]:1)
    at rust_panic (wasm-function[19]:40)
    at _ZN3std9panicking20rust_panic_with_hook17h4d23d2414abbb417E (wasm-function[14]:708)
    at _ZN3std9panicking11begin_panic17ha318dfc1360611dcE (wasm-function[11]:298)
    at _ZN3std3sys4wasm11TimeSysCall7perform17hfcfe4d7cbd5bdf2dE (wasm-function[69]:31)
    at _ZN3std3sys4wasm4time10SystemTime3now17h259757a3c249e0c6E (wasm-function[71]:20)
    at _ZN3std4time10SystemTime3now17hd79e83558f3b295bE (wasm-function[70]:1)
    at test (wasm-function[0]:38)
    at fetch.then.then.then (http://localhost:8000/:10:40)
    at <anonymous>
__rust_start_panic @ wasm-000873fa-78:2
rust_panic @ wasm-000873fa-19:21
_ZN3std9panicking20rust_panic_with_hook17h4d23d2414abbb417E @ wasm-000873fa-14:346
_ZN3std9panicking11begin_panic17ha318dfc1360611dcE @ wasm-000873fa-11:146
_ZN3std3sys4wasm11TimeSysCall7perform17hfcfe4d7cbd5bdf2dE @ wasm-000873fa-69:16
_ZN3std3sys4wasm4time10SystemTime3now17h259757a3c249e0c6E @ wasm-000873fa-71:12
_ZN3std4time10SystemTime3now17hd79e83558f3b295bE @ wasm-000873fa-70:2
test @ wasm-000873fa-0:20
fetch.then.then.then @ (index):10
Promise.then (async)
(anonymous) @ (index):8

My environment is,

OS : macOS 10.13.3
Rust: rustc 1.26.0-nightly (322d7f7 2018-02-25)
Chrome: 64.0.3282.167

Sorry, I have no idea to confirm the version of wasm32-unknown-unknown.

Cheers.

@abonander
Copy link
Contributor

It's weird that it seemingly panics with unreachable!() when, as far as I can tell, that macro was never used even when the time code was stubbed out with panics. Getting the time should actually work now unless the syscall interface is wrong.

@pietroalbini pietroalbini added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. O-wasm Target: WASM (WebAssembly), http://webassembly.org/ C-bug Category: This is a bug. labels Feb 27, 2018
@alexcrichton
Copy link
Member

Thanks for the report! This is unfortunately, for now, an intended implementation detail of the standard library. The unreachable is actually an "illegal instruction" for wasm which is what happens when Rust panics, and Rust currently panics for modules in libstd that don't actually map to wasm (like Instant)

@hodlbank
Copy link

@alexcrichton , isn't this commit from Jan 30th should have resolved this issue completely?

@alexcrichton
Copy link
Member

@hodlbank unfortunately that just added the possibility of another backend, but by default this still panics

@Kwarrtz
Copy link

Kwarrtz commented Jul 2, 2018

There is currently an implementation for SystemTime and Instant (which do not panic) in libstd/sys/wasm/time.rs. Shouldn't this suffice?

@MaulingMonkey
Copy link
Contributor

Still panics on nightly. Poking at this there's 3 issues, if I'm understanding right:

  1. The wasm_syscall feature is off by default (would require an imports object if enabled, which sounds like a breaking change...?)
  2. wasm32-shim.js isn't distributed with rust installs, nor anything else like wasm-bindgen (but versioning with the rust stdlib seems to make the most sense anyways...?)
  3. Most of the shim syscalls assume a node-like environment and will fail in the browser, although the time one should work.

@alexcrichton
Copy link
Member

@MaulingMonkey currently things are working as intended today. We're unlikely to ever stabilize the wasm_syscall feature and wasm32-shim.js is only intended for the test suite. Currently it's expected that the standard library's implementation of these primitives panics on the wasm32-unknown-unknown target since there's no way to implement them. For the wasm32-wasi target though they are implemented.

@MaulingMonkey
Copy link
Contributor

MaulingMonkey commented Jun 24, 2019

EDIT (2024-01-15): for anyone stumbling across this comment, consider using the instant crate instead of the below code.


Sweet, I'll try out WASI at some point. For anyone else who stumbles across this issue but isn't quite ready to switch away from wasm32-unknown-unknown yet, here's a quick, poorly tested drop-in replacement for std::time you can plop into your project:

#![allow(dead_code, unused_imports)]

use wasm_bindgen::prelude::*;
use std::convert::{TryInto};
use std::ops::{Add, Sub, AddAssign, SubAssign};

pub use std::time::*;

#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant(std::time::Instant);
#[cfg(not(target_arch = "wasm32"))] impl Instant {
    pub fn now() -> Self { Self(std::time::Instant::now()) }
    pub fn duration_since(&self, earlier: Instant) -> Duration { self.0.duration_since(earlier.0) }
    pub fn elapsed(&self) -> Duration { self.0.elapsed() }
    pub fn checked_add(&self, duration: Duration) -> Option<Self> { self.0.checked_add(duration).map(|i| Self(i)) }
    pub fn checked_sub(&self, duration: Duration) -> Option<Self> { self.0.checked_sub(duration).map(|i| Self(i)) }
}

#[cfg(target_arch = "wasm32")] #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = Date, js_name = now)] fn date_now() -> f64; }
#[cfg(target_arch = "wasm32")] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant(u64);
#[cfg(target_arch = "wasm32")] impl Instant {
    pub fn now() -> Self { Self(date_now() as u64) }
    pub fn duration_since(&self, earlier: Instant) -> Duration { Duration::from_millis(self.0 - earlier.0) }
    pub fn elapsed(&self) -> Duration { Self::now().duration_since(*self) }
    pub fn checked_add(&self, duration: Duration) -> Option<Self> {
        match duration.as_millis().try_into() {
            Ok(duration) => self.0.checked_add(duration).map(|i| Self(i)),
            Err(_) => None,
        }
    }
    pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
        match duration.as_millis().try_into() {
            Ok(duration) => self.0.checked_sub(duration).map(|i| Self(i)),
            Err(_) => None,
        }
    }
}

impl Add<Duration> for Instant { type Output = Instant; fn add(self, other: Duration) -> Instant { self.checked_add(other).unwrap() } }
impl Sub<Duration> for Instant { type Output = Instant; fn sub(self, other: Duration) -> Instant { self.checked_sub(other).unwrap() } }
impl Sub<Instant>  for Instant { type Output = Duration; fn sub(self, other: Instant) -> Duration { self.duration_since(other) } }
impl AddAssign<Duration> for Instant { fn add_assign(&mut self, other: Duration) { *self = *self + other; } }
impl SubAssign<Duration> for Instant { fn sub_assign(&mut self, other: Duration) { *self = *self - other; } }

@cheako
Copy link

cheako commented Jun 12, 2020

So it compiles, but for sure doesn't run... and I stress again Rust compiles it.

Edit: This is a misunderstanding on my part, compiled rust code is not garnered to be bug free. You have to deal with panics, throw renamed, and there are a few good ways to achieve this.

@Tails
Copy link

Tails commented Jul 11, 2020

I just spent days debugging coming to this. What is the workaround? Is it not possible to get the current timestamp in WASM?

@cheako
Copy link

cheako commented Jul 14, 2020

@Tails You have to use the code from #48564 (comment) though I wonder why it's not a crate...

I can see where getting the time differs from wasm runtime to runtime, but I also see the target being used is wasm32-unknown-unknown, where something like wasm32-unknown-html5, wasm32-unknown-node could be used to enable getting the time on those 'systems'.

@crawlingChaos
Copy link

Why does std::time::SystemTime::now() not fail when the target is wasm32-unknown-emscripten, but it does fail when the target is wasm32-unknown-unknown ?

@cheako
Copy link

cheako commented Sep 19, 2020

@crawlingChaos I think it's because the system type is emscripten and as such the API is known... emscripten has a libc/POSIX API, right? Where as with an unknown system designation the API is literally unknown.

@kupiakos
Copy link
Contributor

kupiakos commented Sep 25, 2020

The code in #48564 (comment) is great, but missing an important detail. Date.now() breaks the requirement that std::time::Instant be monotonic. Right now, it's more like std::time::SystemTime. A better replacement can use performance.now().

#48564 (comment) rewritten to use performance.now:

#![allow(dead_code, unused_imports)]

use wasm_bindgen::prelude::*;
use std::convert::{TryInto};
use std::ops::{Add, Sub, AddAssign, SubAssign};

pub use std::time::*;

#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(std::time::Instant);

#[cfg(not(target_arch = "wasm32"))]
impl Instant {
    pub fn now() -> Self { Self(std::time::Instant::now()) }
    pub fn duration_since(&self, earlier: Instant) -> Duration { self.0.duration_since(earlier.0) }
    pub fn elapsed(&self) -> Duration { self.0.elapsed() }
    pub fn checked_add(&self, duration: Duration) -> Option<Self> { self.0.checked_add(duration).map(|i| Self(i)) }
    pub fn checked_sub(&self, duration: Duration) -> Option<Self> { self.0.checked_sub(duration).map(|i| Self(i)) }
}

#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(inline_js = r#"
export function performance_now() {
  return performance.now();
}"#)]
extern "C" {
    fn performance_now() -> f64;
}

#[cfg(target_arch = "wasm32")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(u64);

#[cfg(target_arch = "wasm32")]
impl Instant {
    pub fn now() -> Self { Self((performance_now() * 1000.0) as u64) }
    pub fn duration_since(&self, earlier: Instant) -> Duration { Duration::from_micros(self.0 - earlier.0) }
    pub fn elapsed(&self) -> Duration { Self::now().duration_since(*self) }
    pub fn checked_add(&self, duration: Duration) -> Option<Self> {
        match duration.as_micros().try_into() {
            Ok(duration) => self.0.checked_add(duration).map(|i| Self(i)),
            Err(_) => None,
        }
    }
    pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
        match duration.as_micros().try_into() {
            Ok(duration) => self.0.checked_sub(duration).map(|i| Self(i)),
            Err(_) => None,
        }
    }
}

impl Add<Duration> for Instant { type Output = Instant; fn add(self, other: Duration) -> Instant { self.checked_add(other).unwrap() } }
impl Sub<Duration> for Instant { type Output = Instant; fn sub(self, other: Duration) -> Instant { self.checked_sub(other).unwrap() } }
impl Sub<Instant>  for Instant { type Output = Duration; fn sub(self, other: Instant) -> Duration { self.duration_since(other) } }
impl AddAssign<Duration> for Instant { fn add_assign(&mut self, other: Duration) { *self = *self + other; } }
impl SubAssign<Duration> for Instant { fn sub_assign(&mut self, other: Duration) { *self = *self - other; } }

Note: If you're targeting Node, it needs to be at least v8.5.0.

armansito added a commit to armansito/vello that referenced this issue Mar 12, 2023
Fixed the WASM crashes by reverting to wgpu = 0.14. This surfaced two
more bugs in the repo for which I included fixes. A big issue is
wasm32-unknown-unknown does not support std::time::Instant, so I
implemented a polyfill based on
rust-lang/rust#48564 (comment).
The animated examples, SVG loader, and the frame statistics monitor
won't work in WASM without this. Once there is progress on the wgpu end,
I'll turn that into a proper PR.
armansito added a commit to armansito/vello that referenced this issue Mar 12, 2023
Fixed the WASM crashes by reverting to wgpu = 0.14. This surfaced two
more bugs in the repo for which I included fixes. A big issue is
wasm32-unknown-unknown does not support std::time::Instant, so I
implemented a polyfill based on
rust-lang/rust#48564 (comment).
The animated examples, SVG loader, and the frame statistics monitor
won't work in WASM without this. Once there is progress on the wgpu end,
I'll turn that into a proper PR.

Other issues:
* The WGSL won't compile on native since this version of wgpu/naga
  doesn't support `const`. Chrome Canary in WASM works though.
* There are serious visual artifacts in the examples when run in the
  browser.
armansito added a commit to armansito/vello that referenced this issue Mar 12, 2023
Fixed the WASM crashes by reverting to wgpu = 0.14. This surfaced two
more bugs in the repo for which I included fixes. A big issue is
wasm32-unknown-unknown does not support std::time::Instant, so I
implemented a polyfill based on
rust-lang/rust#48564 (comment).
The animated examples, SVG loader, and the frame statistics monitor
won't work in WASM without this. Once there is progress on the wgpu end,
I'll turn that into a proper PR.

Other issues:
* The WGSL won't compile on native since this version of wgpu/naga
  doesn't support `const`. Chrome Canary in WASM works though.
* There are serious visual artifacts in the examples when run in the
  browser.
armansito added a commit to armansito/vello that referenced this issue Mar 12, 2023
Fixed the WASM crashes by reverting to wgpu = 0.14. This surfaced two
more bugs in the repo for which I included fixes. A big issue is
wasm32-unknown-unknown does not support std::time::Instant, so I
implemented a polyfill based on
rust-lang/rust#48564 (comment).
The animated examples, SVG loader, and the frame statistics monitor
won't work in WASM without this. Once there is progress on the wgpu end,
I'll turn that into a proper PR.

Also currently need to run the example with
`cargo run_wasm --package with_winit --bin with_winit_bin`

Other issues:
* The WGSL won't compile on native since this version of wgpu/naga
  doesn't support `const`. Chrome Canary in WASM works though.
* There are serious visual artifacts in the examples when run in the
  browser.
FaceFTW added a commit to FaceFTW/rust-pipes that referenced this issue Dec 28, 2023
@vivekvpandya
Copy link

So it compiles, but for sure doesn't run... and I stress again Rust compiles it.

@cheako I am also facing same issue, adding the code above compiles fine but at runtime I am still getting "time not implemented" assert failed. how to fix it?

@cheako
Copy link

cheako commented Jan 23, 2024

@vivekvpandya This is a misunderstanding on my part, compiled rust code is not garnered to be bug free. You have to deal with panics, throw renamed, and there are a few good ways to achieve this.

@woelper
Copy link

woelper commented Mar 10, 2024

I've found https://crates.io/crates/web-time to be a good solution - it is a drop-in replacement for Instant and SystemTime - from their readme:

use web_time::{Instant, SystemTime};

let now = Instant::now();
let time = SystemTime::now();

@gilescope
Copy link
Contributor

Is it time we had a wasm32-browser-browser triple so that we can assume a better minimum level of host support... the www foundation has standardised a lot over the last 30 years.
This feels unresolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. O-wasm Target: WASM (WebAssembly), http://webassembly.org/ T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests