From 17d46992e1ae618b82ba4cefb7899091b2e76404 Mon Sep 17 00:00:00 2001 From: Matthew Aylward Date: Fri, 10 Jul 2026 13:27:05 +0200 Subject: [PATCH 1/2] use web-time for the resource-tracker clock so max_duration works on wasm32-unknown-unknown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `LimitedTracker` enforces `ResourceLimits::max_duration` via `std::time::Instant`. On `wasm32-unknown-unknown`, `Instant::now()` panics ("time not implemented on this platform"), so any run configured with a duration limit aborts. monty's wasm CI targets `wasm32-wasip1`, where `Instant` is implemented, so this path is green upstream and the gap is invisible there. Consumers that embed the `monty` crate directly as a `wasm32-unknown-unknown` wasm-bindgen module (in-process, browser-side Python) can't currently arm a duration limit without panicking — and `max_duration` is the only limit that stops a non-allocating runaway such as `while True: pass` (`max_allocations` / `max_memory` never fire on it). Swap the tracker clock to `web_time::Instant`, a drop-in that re-exports `std::time::Instant` verbatim on native and WASI and is backed by `performance.now()` on `wasm32-unknown-unknown`. No behavior change on any currently-supported target; all resource-limit tests pass unchanged and `wasm32-wasip1` still compiles. --- Cargo.lock | 1 + crates/monty/Cargo.toml | 6 ++++++ crates/monty/src/resource.rs | 15 +++++++++------ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb64d2996..0371137c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2022,6 +2022,7 @@ dependencies = [ "unicode-general-category", "unicode-normalization", "unicode_names2", + "web-time", ] [[package]] diff --git a/crates/monty/Cargo.toml b/crates/monty/Cargo.toml index b97cc9851..3d25f997e 100644 --- a/crates/monty/Cargo.toml +++ b/crates/monty/Cargo.toml @@ -42,6 +42,12 @@ speedate = "0.17.0" itertools = "0.14.0" jiter = { version = "0.15.0", features = ["num-bigint"] } monty-macros = { workspace = true } +# Portable monotonic clock: a drop-in for `std::time::Instant` that re-exports +# std unchanged on native and WASI, and is backed by `performance.now()` on +# `wasm32-unknown-unknown` (where `std::time::Instant::now()` panics). Keeps the +# `max_duration` resource limit usable when `monty` is embedded directly in a +# browser wasm module rather than run over WASI. +web-time = "1.1" [features] # ref-count-return changes behavior to return information on reference counts to check they're correct diff --git a/crates/monty/src/resource.rs b/crates/monty/src/resource.rs index b74640178..e9d330228 100644 --- a/crates/monty/src/resource.rs +++ b/crates/monty/src/resource.rs @@ -1,9 +1,12 @@ -use std::{ - cell::Cell, - error::Error, - fmt, - time::{Duration, Instant}, -}; +use std::{cell::Cell, error::Error, fmt, time::Duration}; + +// `web_time::Instant` is `std::time::Instant` on native and WASI targets, and a +// `performance.now()`-backed clock on `wasm32-unknown-unknown`, where the std +// `Instant::now()` panics ("time not implemented on this platform"). This keeps +// the `max_duration` time limit working when `monty` is embedded directly in a +// browser wasm module. Time behavior on all currently-supported targets is +// unchanged (it re-exports std verbatim there). +use web_time::Instant; use crate::{ ExcType, MontyException, From ba9ba9aedf3352566b10c81f2badffdbe26a3008 Mon Sep 17 00:00:00 2001 From: Matthew Aylward Date: Fri, 10 Jul 2026 14:34:15 +0200 Subject: [PATCH 2/2] make web-time a target-specific dependency (wasm32-unknown-unknown only) Per review: web_time::Instant is only needed where std::time::Instant panics, i.e. wasm32-unknown-unknown. Gate the dependency and the import on that target so every other target (native, WASI) uses std directly and never pulls web-time into its graph. Verified: web-time is absent from the native and wasm32-wasip1 dependency graphs and present only under wasm32-unknown-unknown; both compile and all resource-limit tests still pass. --- crates/monty/Cargo.toml | 11 ++++++----- crates/monty/src/resource.rs | 14 ++++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/monty/Cargo.toml b/crates/monty/Cargo.toml index 3d25f997e..c9866c8d5 100644 --- a/crates/monty/Cargo.toml +++ b/crates/monty/Cargo.toml @@ -42,11 +42,12 @@ speedate = "0.17.0" itertools = "0.14.0" jiter = { version = "0.15.0", features = ["num-bigint"] } monty-macros = { workspace = true } -# Portable monotonic clock: a drop-in for `std::time::Instant` that re-exports -# std unchanged on native and WASI, and is backed by `performance.now()` on -# `wasm32-unknown-unknown` (where `std::time::Instant::now()` panics). Keeps the -# `max_duration` resource limit usable when `monty` is embedded directly in a -# browser wasm module rather than run over WASI. + +# `std::time::Instant` panics on `wasm32-unknown-unknown`, so the resource +# tracker uses `web_time::Instant` (a `performance.now()`-backed drop-in) there +# to keep `max_duration` working when `monty` is embedded directly in a browser +# wasm module. Only that target needs it; every other target uses std directly. +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] web-time = "1.1" [features] diff --git a/crates/monty/src/resource.rs b/crates/monty/src/resource.rs index e9d330228..8c09222e1 100644 --- a/crates/monty/src/resource.rs +++ b/crates/monty/src/resource.rs @@ -1,11 +1,13 @@ +#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] +use std::time::Instant; use std::{cell::Cell, error::Error, fmt, time::Duration}; -// `web_time::Instant` is `std::time::Instant` on native and WASI targets, and a -// `performance.now()`-backed clock on `wasm32-unknown-unknown`, where the std -// `Instant::now()` panics ("time not implemented on this platform"). This keeps -// the `max_duration` time limit working when `monty` is embedded directly in a -// browser wasm module. Time behavior on all currently-supported targets is -// unchanged (it re-exports std verbatim there). +// `std::time::Instant::now()` panics ("time not implemented on this platform") +// on `wasm32-unknown-unknown`, so any `max_duration` limit aborts there. Swap in +// `web_time::Instant` (a `performance.now()`-backed drop-in) only for that +// target; every other target (native, WASI) keeps std, so the `web-time` +// dependency is pulled in only where it's needed (see Cargo.toml). +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] use web_time::Instant; use crate::{