mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 11:16:42 +01:00
add watchdog to interupt hanging update
This commit is contained in:
@@ -35,6 +35,7 @@ Runs <file> which can be a binary WebAssembly module, an `.uw8` cart, a wat (Web
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
-t, --timeout FRAMES : Sets the timeout in frames (1/60s)
|
||||||
-w, --watch : Reloads the given file every time it changes on disk.
|
-w, --watch : Reloads the given file every time it changes on disk.
|
||||||
-p, --pack : Pack the file into an .uw8 cart before running it and print the resulting size.
|
-p, --pack : Pack the file into an .uw8 cart before running it and print the resulting size.
|
||||||
-u, --uncompressed : Use the uncompressed uw8 format for packing.
|
-u, --uncompressed : Use the uncompressed uw8 format for packing.
|
||||||
|
|||||||
@@ -284,6 +284,8 @@ Runs `<file>` which can be a binary WebAssembly module, an `.uw8` cart, a wat (W
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
* `-t FRAMES`, `--timeout FRAMES` : Sets the timeout in frames (1/60s). If the start or update function runs longer than this it is forcibly interupted
|
||||||
|
and execution of the cart is stopped. Defaults to 30 (0.5s)
|
||||||
* `-w`, `--watch`: Reloads the given file every time it changes on disk.
|
* `-w`, `--watch`: Reloads the given file every time it changes on disk.
|
||||||
* `-p`, `--pack`: Pack the file into an `.uw8` cart before running it and print the resulting size.
|
* `-p`, `--pack`: Pack the file into an `.uw8` cart before running it and print the resulting size.
|
||||||
* `-u`, `--uncompressed`: Use the uncompressed `uw8` format for packing.
|
* `-u`, `--uncompressed`: Use the uncompressed `uw8` format for packing.
|
||||||
|
|||||||
91
src/lib.rs
91
src/lib.rs
@@ -1,6 +1,8 @@
|
|||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fs::File, time::Instant};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{fs::File, thread, time::Instant};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use minifb::{Key, Window, WindowOptions};
|
use minifb::{Key, Window, WindowOptions};
|
||||||
@@ -8,7 +10,16 @@ use wasmtime::{
|
|||||||
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
|
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
|
||||||
};
|
};
|
||||||
|
|
||||||
static GAMEPAD_KEYS: &'static [Key] = &[Key::Up, Key::Down, Key::Left, Key::Right, Key::Z, Key::X, Key::A, Key::S];
|
static GAMEPAD_KEYS: &'static [Key] = &[
|
||||||
|
Key::Up,
|
||||||
|
Key::Down,
|
||||||
|
Key::Left,
|
||||||
|
Key::Right,
|
||||||
|
Key::Z,
|
||||||
|
Key::X,
|
||||||
|
Key::A,
|
||||||
|
Key::S,
|
||||||
|
];
|
||||||
|
|
||||||
pub struct MicroW8 {
|
pub struct MicroW8 {
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
@@ -16,6 +27,7 @@ pub struct MicroW8 {
|
|||||||
window: Window,
|
window: Window,
|
||||||
window_buffer: Vec<u32>,
|
window_buffer: Vec<u32>,
|
||||||
instance: Option<UW8Instance>,
|
instance: Option<UW8Instance>,
|
||||||
|
timeout: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UW8Instance {
|
struct UW8Instance {
|
||||||
@@ -24,12 +36,27 @@ struct UW8Instance {
|
|||||||
end_frame: TypedFunc<(), ()>,
|
end_frame: TypedFunc<(), ()>,
|
||||||
update: TypedFunc<(), ()>,
|
update: TypedFunc<(), ()>,
|
||||||
start_time: Instant,
|
start_time: Instant,
|
||||||
module: Vec<u8>
|
module: Vec<u8>,
|
||||||
|
watchdog: Arc<Mutex<UW8WatchDog>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UW8Instance {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Ok(mut watchdog) = self.watchdog.lock() {
|
||||||
|
watchdog.stop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UW8WatchDog {
|
||||||
|
interupt: wasmtime::InterruptHandle,
|
||||||
|
timeout: u32,
|
||||||
|
stop: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MicroW8 {
|
impl MicroW8 {
|
||||||
pub fn new() -> Result<MicroW8> {
|
pub fn new() -> Result<MicroW8> {
|
||||||
let engine = wasmtime::Engine::default();
|
let engine = wasmtime::Engine::new(wasmtime::Config::new().interruptable(true))?;
|
||||||
|
|
||||||
let loader_module =
|
let loader_module =
|
||||||
wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?;
|
wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?;
|
||||||
@@ -47,6 +74,7 @@ impl MicroW8 {
|
|||||||
window,
|
window,
|
||||||
window_buffer: vec![0u32; 320 * 240],
|
window_buffer: vec![0u32; 320 * 240],
|
||||||
instance: None,
|
instance: None,
|
||||||
|
timeout: 30,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +82,10 @@ impl MicroW8 {
|
|||||||
self.window.is_open() && !self.window.is_key_down(Key::Escape)
|
self.window.is_open() && !self.window.is_key_down(Key::Escape)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_timeout(&mut self, timeout: u32) {
|
||||||
|
self.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.instance = None;
|
self.instance = None;
|
||||||
for v in &mut self.window_buffer {
|
for v in &mut self.window_buffer {
|
||||||
@@ -130,7 +162,36 @@ impl MicroW8 {
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let watchdog = Arc::new(Mutex::new(UW8WatchDog {
|
||||||
|
interupt: store.interrupt_handle()?,
|
||||||
|
timeout: self.timeout,
|
||||||
|
stop: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
{
|
||||||
|
let watchdog = watchdog.clone();
|
||||||
|
thread::spawn(move || loop {
|
||||||
|
thread::sleep(Duration::from_millis(17));
|
||||||
|
if let Ok(mut watchdog) = watchdog.lock() {
|
||||||
|
if watchdog.stop {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if watchdog.timeout > 0 {
|
||||||
|
watchdog.timeout -= 1;
|
||||||
|
if watchdog.timeout == 0 {
|
||||||
|
watchdog.interupt.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let instance = linker.instantiate(&mut store, &module)?;
|
let instance = linker.instantiate(&mut store, &module)?;
|
||||||
|
if let Ok(mut watchdog) = watchdog.lock() {
|
||||||
|
watchdog.timeout = 0;
|
||||||
|
}
|
||||||
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
|
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
|
||||||
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd")?;
|
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd")?;
|
||||||
|
|
||||||
@@ -140,19 +201,26 @@ impl MicroW8 {
|
|||||||
end_frame,
|
end_frame,
|
||||||
update,
|
update,
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
module: module_data.into()
|
module: module_data.into(),
|
||||||
|
watchdog,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_frame(&mut self) -> Result<()> {
|
pub fn run_frame(&mut self) -> Result<()> {
|
||||||
|
let mut result = Ok(());
|
||||||
if let Some(mut instance) = self.instance.take() {
|
if let Some(mut instance) = self.instance.take() {
|
||||||
{
|
{
|
||||||
let time = instance.start_time.elapsed().as_millis() as i32;
|
let time = instance.start_time.elapsed().as_millis() as i32;
|
||||||
let mut gamepad: u32 = 0;
|
let mut gamepad: u32 = 0;
|
||||||
for key in self.window.get_keys() {
|
for key in self.window.get_keys() {
|
||||||
if let Some(index) = GAMEPAD_KEYS.iter().enumerate().find(|(_, &k)| k == key).map(|(i, _)| i) {
|
if let Some(index) = GAMEPAD_KEYS
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, &k)| k == key)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
{
|
||||||
gamepad |= 1 << index;
|
gamepad |= 1 << index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +230,13 @@ impl MicroW8 {
|
|||||||
mem[68..72].copy_from_slice(&gamepad.to_le_bytes());
|
mem[68..72].copy_from_slice(&gamepad.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.update.call(&mut instance.store, ())?;
|
if let Ok(mut watchdog) = instance.watchdog.lock() {
|
||||||
|
watchdog.timeout = self.timeout;
|
||||||
|
}
|
||||||
|
result = instance.update.call(&mut instance.store, ());
|
||||||
|
if let Ok(mut watchdog) = instance.watchdog.lock() {
|
||||||
|
watchdog.timeout = 0;
|
||||||
|
}
|
||||||
instance.end_frame.call(&mut instance.store, ())?;
|
instance.end_frame.call(&mut instance.store, ())?;
|
||||||
|
|
||||||
let memory = instance.memory.data(&instance.store);
|
let memory = instance.memory.data(&instance.store);
|
||||||
@@ -178,7 +252,7 @@ impl MicroW8 {
|
|||||||
|
|
||||||
if self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No) {
|
if self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No) {
|
||||||
self.load_from_memory(&instance.module)?;
|
self.load_from_memory(&instance.module)?;
|
||||||
} else {
|
} else if result.is_ok() {
|
||||||
self.instance = Some(instance);
|
self.instance = Some(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,6 +260,7 @@ impl MicroW8 {
|
|||||||
self.window
|
self.window
|
||||||
.update_with_buffer(&self.window_buffer, 320, 240)?;
|
.update_with_buffer(&self.window_buffer, 320, 240)?;
|
||||||
|
|
||||||
|
result?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ fn main() -> Result<()> {
|
|||||||
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
|
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
|
||||||
println!();
|
println!();
|
||||||
println!("Usage:");
|
println!("Usage:");
|
||||||
println!(" uw8 run [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output <out-file>] <file>");
|
println!(" uw8 run [-t/--timeout <frames>] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output <out-file>] <file>");
|
||||||
println!(" uw8 pack [-u/--uncompressed] [-l/--level] <in-file> <out-file>");
|
println!(" uw8 pack [-u/--uncompressed] [-l/--level] <in-file> <out-file>");
|
||||||
println!(" uw8 filter-exports <in-wasm> <out-wasm>");
|
println!(" uw8 filter-exports <in-wasm> <out-wasm>");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -42,6 +42,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
fn run(mut args: Arguments) -> Result<()> {
|
fn run(mut args: Arguments) -> Result<()> {
|
||||||
let watch_mode = args.contains(["-w", "--watch"]);
|
let watch_mode = args.contains(["-w", "--watch"]);
|
||||||
|
let timeout: Option<u32> = args.opt_value_from_str(["-t", "--timeout"])?;
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
if args.contains(["-p", "--pack"]) {
|
if args.contains(["-p", "--pack"]) {
|
||||||
@@ -67,6 +68,10 @@ fn run(mut args: Arguments) -> Result<()> {
|
|||||||
|
|
||||||
let mut uw8 = MicroW8::new()?;
|
let mut uw8 = MicroW8::new()?;
|
||||||
|
|
||||||
|
if let Some(timeout) = timeout {
|
||||||
|
uw8.set_timeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let mut watcher = notify::watcher(tx, Duration::from_millis(100))?;
|
let mut watcher = notify::watcher(tx, Duration::from_millis(100))?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user