diff --git a/src/internal/event_loop/event_loop.js.mbt b/src/internal/event_loop/event_loop.js.mbt index 407ed629..1e7d865d 100644 --- a/src/internal/event_loop/event_loop.js.mbt +++ b/src/internal/event_loop/event_loop.js.mbt @@ -28,7 +28,7 @@ pub fn reschedule() -> Unit { // Instead of looping blockingly until there is no ready task, // we only perform one round of scheduling here // (i.e., only those tasks already ready when `reschedule` is called are run). - // Remaining tasks are delayed untin the next js event loop, + // Remaining tasks are delayed until the next event loop iteration, // so that those blocking jobs got a chance to execute instead of starving. if @coroutine.has_immediately_ready_task() { ignore(set_timeout(0, reschedule)) diff --git a/src/internal/event_loop/moon.pkg b/src/internal/event_loop/moon.pkg index a3745c74..ce30c5f9 100644 --- a/src/internal/event_loop/moon.pkg +++ b/src/internal/event_loop/moon.pkg @@ -34,7 +34,7 @@ options( ], targets: { "cancel_before_submit_test.mbt": [ "native" ], - "event_loop.js.mbt": [ "js" ], + "event_loop.js.mbt": [ "js", "wasm-gc" ], "event_loop.mbt": [ "native" ], "fs.mbt": [ "native" ], "io.mbt": [ "native" ], @@ -49,8 +49,9 @@ options( "process_windows.mbt": [ "native" ], "stdio.mbt": [ "native" ], "thread_pool.mbt": [ "native" ], + "timer.js.mbt": [ "js", "wasm-gc" ], "timer.mbt": [ "native" ], - "unimplemented.mbt": [ "wasm", "wasm-gc" ], + "unimplemented.mbt": [ "wasm" ], "worker_wbtest.mbt": [ "native", "js" ], }, ) diff --git a/src/internal/event_loop/timer.js.mbt b/src/internal/event_loop/timer.js.mbt index 3d3d4fc3..a5e75a5d 100644 --- a/src/internal/event_loop/timer.js.mbt +++ b/src/internal/event_loop/timer.js.mbt @@ -17,9 +17,14 @@ type Timer ///| +#cfg(target="js") extern "js" fn set_timeout(duration : Int, f : () -> Unit) -> Timer = #| (duration, f) => setTimeout(f, duration) +///| +#cfg(target="wasm-gc") +fn set_timeout(duration : Int, f : () -> Unit) -> Timer = "moonbitlang_async_timer" "setTimeout" + ///| pub fn Timer::new(duration : Int, f : () -> Unit) -> Timer { set_timeout(duration, () => { @@ -29,5 +34,16 @@ pub fn Timer::new(duration : Int, f : () -> Unit) -> Timer { } ///| +#cfg(target="js") pub extern "js" fn Timer::cancel(self : Timer) = #| (timer) => clearTimeout(timer) + +///| +#cfg(target="wasm-gc") +fn clear_timeout(timer : Timer) = "moonbitlang_async_timer" "clearTimeout" + +///| +#cfg(target="wasm-gc") +pub fn Timer::cancel(self : Timer) -> Unit { + clear_timeout(self) +} diff --git a/src/internal/event_loop/wasm-gc-imports.js b/src/internal/event_loop/wasm-gc-imports.js new file mode 100644 index 00000000..7f70a582 --- /dev/null +++ b/src/internal/event_loop/wasm-gc-imports.js @@ -0,0 +1,15 @@ +// Import objects for moonbitlang/async wasm-gc timer, event loop, and time modules. +// When loading the compiled wasm-gc module, merge these into the import object: +// +// import { moonbitlang_async_timer, moonbitlang_async_time } from "./wasm-gc-imports.js"; +// const imports = { ...otherImports, moonbitlang_async_timer, moonbitlang_async_time }; +// const { instance } = await WebAssembly.instantiateStreaming(fetch("module.wasm"), imports); + +export const moonbitlang_async_timer = { + setTimeout: (duration, f) => setTimeout(f, duration), + clearTimeout: (timer) => clearTimeout(timer), +}; + +export const moonbitlang_async_time = { + date_now: () => Date.now(), +}; diff --git a/src/internal/time/time.mbt b/src/internal/time/time.mbt index 74a4d4da..652dd110 100644 --- a/src/internal/time/time.mbt +++ b/src/internal/time/time.mbt @@ -62,7 +62,34 @@ pub fn ms_since_epoch() -> Int64 { } ///| -#cfg(any(target="wasm", target="wasm-gc")) +#cfg(target="wasm-gc") +fn wasm_gc_date_now() -> Double = "moonbitlang_async_time" "date_now" + +///| +/// Get the current wall-clock time in milliseconds. +/// +/// This is the time source used by the async runtime. It is intended for +/// computing elapsed time by subtraction. The value can jump forwards or +/// backwards if the system clock is adjusted, so do not assume monotonicity. +/// +/// Platform notes: +/// - Unix/macOS: `gettimeofday()` (Unix epoch) +/// - Windows: `GetSystemTimeAsFileTime()` (FILETIME epoch, 1601) +/// - JavaScript/wasm-gc: `Date.now()` (Unix epoch) +/// +/// # Example +/// ```mbt check +/// test { +/// let _ : Int64 = ms_since_epoch() +/// } +/// ``` +#cfg(target="wasm-gc") +pub fn ms_since_epoch() -> Int64 { + wasm_gc_date_now().to_int64() +} + +///| +#cfg(target="wasm") pub fn ms_since_epoch() -> Int64 { abort("unimplemented") }