From 68221580f023031128aac76a5ef8ced784374cf9 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 6 Aug 2024 20:37:40 -0600 Subject: [PATCH] web: Updates for futures rework --- Cargo.lock | 1 + cli/src/main.rs | 2 +- web/emulator.ts | 4 +--- web/glue/Cargo.toml | 1 + web/glue/src/lib.rs | 58 ++++++++++++++++++++++++++++++++++++++------- x86/src/x86.rs | 2 +- 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4c3c71d..24f20cdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,6 +273,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", + "futures", "js-sys", "log", "serde", diff --git a/cli/src/main.rs b/cli/src/main.rs index 591b371f..bcc16509 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -247,7 +247,7 @@ fn main() -> anyhow::Result { #[cfg(feature = "x86-emu")] { - let millis = start.elapsed().as_millis() as u64; + let millis = start.elapsed().as_millis() as usize; if millis > 0 { eprintln!( "{} instrs in {} ms: {}m/s", diff --git a/web/emulator.ts b/web/emulator.ts index 0490bb1b..7e90e072 100644 --- a/web/emulator.ts +++ b/web/emulator.ts @@ -70,7 +70,7 @@ export class Emulator extends JsHost { const endTime = performance.now(); const endSteps = this.emu.instr_count; - const steps = Number(endSteps - startSteps); + const steps = endSteps - startSteps; const deltaTime = endTime - startTime; if (steps > 1000 && deltaTime >= 1) { // only update if we ran enough instructions to get a good measurement const instrPerMs = steps / deltaTime; @@ -106,8 +106,6 @@ export class Emulator extends JsHost { return false; } case wasm.CPUState.Blocked: - // Return control to the browser event loop. - return false; case wasm.CPUState.Error: case wasm.CPUState.Exit: this.emuHost.onStopped(); diff --git a/web/glue/Cargo.toml b/web/glue/Cargo.toml index 023c540f..c3a4891d 100644 --- a/web/glue/Cargo.toml +++ b/web/glue/Cargo.toml @@ -13,6 +13,7 @@ x86 = { workspace = true, features = ["wasm"] } anyhow = "1.0" chrono = "0.4.38" +futures = "0.3.30" js-sys = "0.3.69" serde = "1.0" serde_json = "1.0" diff --git a/web/glue/src/lib.rs b/web/glue/src/lib.rs index 758e07d8..3a379aee 100644 --- a/web/glue/src/lib.rs +++ b/web/glue/src/lib.rs @@ -3,6 +3,7 @@ mod host; mod log; use crate::host::JsHost; +use std::task::{Context, Poll}; use wasm_bindgen::prelude::*; pub type JsResult = Result; @@ -10,9 +11,16 @@ fn err_from_anyhow(err: anyhow::Error) -> JsError { JsError::new(&err.to_string()) } +struct AsyncShimCall { + shim: &'static win32::shims::Shim, + future: win32::shims::BoxFuture, +} + #[wasm_bindgen] pub struct Emulator { machine: win32::Machine, + shim_calls: Vec, + ctx: Context<'static>, } #[wasm_bindgen] @@ -62,7 +70,7 @@ impl Emulator { } #[wasm_bindgen(getter)] - pub fn instr_count(&self) -> u64 { + pub fn instr_count(&self) -> usize { self.machine.emu.x86.instr_count } @@ -80,10 +88,23 @@ impl Emulator { if count == 1 { return match self.machine.run(1) { win32::StopReason::None => Ok(CPUState::Running), - win32::StopReason::Blocked => Ok(CPUState::Blocked), + win32::StopReason::Blocked => { + // Poll the last future. + let shim_call = self.shim_calls.last_mut().unwrap(); + match shim_call.future.as_mut().poll(&mut self.ctx) { + Poll::Ready(ret) => { + self.machine.finish_shim_call(shim_call.shim, ret); + self.shim_calls.pop(); + } + Poll::Pending => {} + } + Ok(CPUState::Running) + } win32::StopReason::Breakpoint { .. } => Ok(CPUState::DebugBreak), win32::StopReason::ShimCall(shim) => { - self.machine.call_shim(shim); + if let Some(future) = self.machine.call_shim(shim) { + self.shim_calls.push(AsyncShimCall { shim, future }); + } Ok(CPUState::Running) } win32::StopReason::Error { message, .. } => Err(JsError::new(&message)), @@ -93,14 +114,31 @@ impl Emulator { // Note that instr_count overflows at 4b, but we don't expect to run // 4b instructions in a single run() invocation. let start = self.machine.emu.x86.instr_count; - while self.machine.emu.x86.instr_count.wrapping_sub(start) < count as u64 { + while self.machine.emu.x86.instr_count.wrapping_sub(start) < count { match self.machine.run(0) { win32::StopReason::None => {} - win32::StopReason::Blocked => break, + win32::StopReason::Blocked => { + // Poll the last future. + let shim_call = self.shim_calls.last_mut().unwrap(); + match shim_call.future.as_mut().poll(&mut self.ctx) { + Poll::Ready(ret) => { + self.machine.finish_shim_call(shim_call.shim, ret); + self.shim_calls.pop(); + // Continue running after the shim call completes. + } + Poll::Pending => { + // Return control to the event loop to allow the future to progress. + break; + } + } + } win32::StopReason::Breakpoint { .. } => return Ok(CPUState::DebugBreak), win32::StopReason::ShimCall(shim) => { - self.machine.call_shim(shim); - break; + if let Some(future) = self.machine.call_shim(shim) { + self.shim_calls.push(AsyncShimCall { shim, future }); + // Return control to the event loop to allow the future to progress. + break; + } } win32::StopReason::Error { message, .. } => return Err(JsError::new(&message)), win32::StopReason::Exit { .. } => return Ok(CPUState::Exit), @@ -140,5 +178,9 @@ impl Emulator { pub fn new_emulator(host: JsHost, cmdline: String) -> Emulator { log::init(log::JsLogger::unchecked_from_js(host.clone())); let machine = win32::Machine::new(Box::new(host), cmdline); - Emulator { machine } + Emulator { + machine, + shim_calls: Default::default(), + ctx: Context::from_waker(futures::task::noop_waker_ref()), + } } diff --git a/x86/src/x86.rs b/x86/src/x86.rs index b42be9c5..85cbcd30 100644 --- a/x86/src/x86.rs +++ b/x86/src/x86.rs @@ -147,7 +147,7 @@ pub struct X86 { pub cur_cpu: usize, /// Total number of instructions executed. - pub instr_count: u64, + pub instr_count: usize, pub icache: InstrCache, }