feat(tiles): initial WASM integration
The first steps towards WASM module integration
This commit is contained in:
commit
1586a705d2
5 changed files with 1098 additions and 17 deletions
973
Cargo.lock
generated
973
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -19,6 +19,9 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
|
serde_json = "1.0"
|
||||||
|
wasmer = { git = "https://github.com/wasmerio/wasmer.git" }
|
||||||
|
wasmer-wasi = { git = "https://github.com/wasmerio/wasmer.git" }
|
||||||
|
|
||||||
[dependencies.async-std]
|
[dependencies.async-std]
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
|
||||||
108
src/main.rs
108
src/main.rs
|
|
@ -11,15 +11,19 @@ mod screen;
|
||||||
mod terminal_pane;
|
mod terminal_pane;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use wasmer::{Exports, Function, Instance, Module, Store, Value};
|
||||||
|
use wasmer_wasi::WasiState;
|
||||||
|
|
||||||
use crate::command_is_executing::CommandIsExecuting;
|
use crate::command_is_executing::CommandIsExecuting;
|
||||||
use crate::input::input_loop;
|
use crate::input::input_loop;
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
|
|
@ -253,6 +257,108 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Here be dragons! This is very much a work in progress, and isn't quite functional
|
||||||
|
// yet. It's being left out of the tests because is slows them down massively (by
|
||||||
|
// recompiling a WASM module for every single test). Stay tuned for more updates!
|
||||||
|
#[cfg(not(test))]
|
||||||
|
active_threads.push(
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("wasm".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
// TODO: Clone shared state here
|
||||||
|
move || -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let store = Store::default();
|
||||||
|
|
||||||
|
println!("Compiling module...");
|
||||||
|
// FIXME: Switch to a higher performance compiler (`Store::default()`) and cache this on disk
|
||||||
|
// I could use `(de)serialize_to_file()` for that
|
||||||
|
let module = if let Ok(m) = Module::from_file(&store, "strider.wasm") {
|
||||||
|
m
|
||||||
|
} else {
|
||||||
|
return Ok(()); // Just abort this thread quietly if the WASM isn't found
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: Upstream the `Pipe` struct
|
||||||
|
//let output = fluff::Pipe::new();
|
||||||
|
//let input = fluff::Pipe::new();
|
||||||
|
let mut wasi_env = WasiState::new("mosaic")
|
||||||
|
.env("CLICOLOR_FORCE", "1")
|
||||||
|
.preopen(|p| {
|
||||||
|
p.directory(".") // TODO: Change this to a more meaningful dir
|
||||||
|
.alias(".")
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
})?
|
||||||
|
//.stdin(Box::new(input))
|
||||||
|
//.stdout(Box::new(output))
|
||||||
|
.finalize()?;
|
||||||
|
|
||||||
|
let mut import_object = wasi_env.import_object(&module)?;
|
||||||
|
// FIXME: Upstream an `ImportObject` merge method
|
||||||
|
let mut host_exports = Exports::new();
|
||||||
|
/* host_exports.insert(
|
||||||
|
"host_open_file",
|
||||||
|
Function::new_native_with_env(&store, Arc::clone(&wasi_env.state), host_open_file),
|
||||||
|
); */
|
||||||
|
fn noop() {}
|
||||||
|
host_exports.insert("host_open_file", Function::new_native(&store, noop));
|
||||||
|
import_object.register("mosaic", host_exports);
|
||||||
|
let instance = Instance::new(&module, &import_object)?;
|
||||||
|
|
||||||
|
// WASI requires to explicitly set the memory for the `WasiEnv`
|
||||||
|
wasi_env.set_memory(instance.exports.get_memory("memory")?.clone());
|
||||||
|
|
||||||
|
let start = instance.exports.get_function("_start")?;
|
||||||
|
let handle_key = instance.exports.get_function("handle_key")?;
|
||||||
|
let draw = instance.exports.get_function("draw")?;
|
||||||
|
|
||||||
|
// This eventually calls the `.init()` method
|
||||||
|
start.call(&[])?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
break;
|
||||||
|
//let (cols, rows) = terminal::size()?;
|
||||||
|
//draw.call(&[Value::I32(rows as i32), Value::I32(cols as i32)])?;
|
||||||
|
|
||||||
|
// FIXME: This downcasting mess needs to be abstracted away
|
||||||
|
/* let mut state = wasi_env.state();
|
||||||
|
let wasi_file = state.fs.stdout_mut()?.as_mut().unwrap();
|
||||||
|
let output: &mut fluff::Pipe = wasi_file.downcast_mut().unwrap();
|
||||||
|
// Needed because raw mode doesn't implicitly return to the start of the line
|
||||||
|
write!(
|
||||||
|
io::stdout(),
|
||||||
|
"{}\n\r",
|
||||||
|
output.to_string().lines().collect::<Vec<_>>().join("\n\r")
|
||||||
|
)?;
|
||||||
|
output.clear();
|
||||||
|
|
||||||
|
let wasi_file = state.fs.stdin_mut()?.as_mut().unwrap();
|
||||||
|
let input: &mut fluff::Pipe = wasi_file.downcast_mut().unwrap();
|
||||||
|
input.clear(); */
|
||||||
|
|
||||||
|
/* match event::read()? {
|
||||||
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('q'),
|
||||||
|
..
|
||||||
|
}) => break,
|
||||||
|
Event::Key(e) => {
|
||||||
|
writeln!(input, "{}\r", serde_json::to_string(&e)?)?;
|
||||||
|
drop(state);
|
||||||
|
// Need to release the implicit `state` mutex or I deadlock!
|
||||||
|
handle_key.call(&[])?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
debug_log_to_file("WASM module loaded and exited cleanly :)".to_string())?;
|
||||||
|
Ok(())
|
||||||
|
}()
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: currently we don't push this into active_threads
|
// TODO: currently we don't push this into active_threads
|
||||||
// because otherwise the app will hang. Need to fix this so it both
|
// because otherwise the app will hang. Need to fix this so it both
|
||||||
// listens to the ipc-bus and is able to quit cleanly
|
// listens to the ipc-bus and is able to quit cleanly
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use ::std::collections::HashMap;
|
||||||
use ::std::io::{Read, Write};
|
use ::std::io::{Read, Write};
|
||||||
use ::std::os::unix::io::RawFd;
|
use ::std::os::unix::io::RawFd;
|
||||||
use ::std::path::PathBuf;
|
use ::std::path::PathBuf;
|
||||||
|
use ::std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use ::std::sync::{Arc, Mutex};
|
use ::std::sync::{Arc, Mutex};
|
||||||
use ::std::time::{Duration, Instant};
|
use ::std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
|
@ -24,14 +25,20 @@ pub struct FakeStdinReader {
|
||||||
pub input_chars: Vec<[u8; 10]>,
|
pub input_chars: Vec<[u8; 10]>,
|
||||||
pub read_position: usize,
|
pub read_position: usize,
|
||||||
last_snapshot_time: Arc<Mutex<Instant>>,
|
last_snapshot_time: Arc<Mutex<Instant>>,
|
||||||
|
started_reading_from_pty: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeStdinReader {
|
impl FakeStdinReader {
|
||||||
pub fn new(input_chars: Vec<[u8; 10]>, last_snapshot_time: Arc<Mutex<Instant>>) -> Self {
|
pub fn new(
|
||||||
|
input_chars: Vec<[u8; 10]>,
|
||||||
|
last_snapshot_time: Arc<Mutex<Instant>>,
|
||||||
|
started_reading_from_pty: Arc<AtomicBool>,
|
||||||
|
) -> Self {
|
||||||
FakeStdinReader {
|
FakeStdinReader {
|
||||||
input_chars,
|
input_chars,
|
||||||
read_position: 0,
|
read_position: 0,
|
||||||
last_snapshot_time,
|
last_snapshot_time,
|
||||||
|
started_reading_from_pty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +46,9 @@ impl FakeStdinReader {
|
||||||
impl Read for FakeStdinReader {
|
impl Read for FakeStdinReader {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
||||||
loop {
|
loop {
|
||||||
|
if self.started_reading_from_pty.load(Ordering::Acquire) == false {
|
||||||
|
::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS);
|
||||||
|
} else {
|
||||||
let last_snapshot_time = { *self.last_snapshot_time.lock().unwrap() };
|
let last_snapshot_time = { *self.last_snapshot_time.lock().unwrap() };
|
||||||
if last_snapshot_time.elapsed() > MIN_TIME_BETWEEN_SNAPSHOTS {
|
if last_snapshot_time.elapsed() > MIN_TIME_BETWEEN_SNAPSHOTS {
|
||||||
break;
|
break;
|
||||||
|
|
@ -46,6 +56,7 @@ impl Read for FakeStdinReader {
|
||||||
::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS - last_snapshot_time.elapsed());
|
::std::thread::sleep(MIN_TIME_BETWEEN_SNAPSHOTS - last_snapshot_time.elapsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let read_position = self.read_position;
|
let read_position = self.read_position;
|
||||||
match self.input_chars.get(read_position) {
|
match self.input_chars.get(read_position) {
|
||||||
Some(bytes_to_read) => {
|
Some(bytes_to_read) => {
|
||||||
|
|
@ -120,6 +131,7 @@ pub struct FakeInputOutput {
|
||||||
win_sizes: Arc<Mutex<HashMap<RawFd, PositionAndSize>>>,
|
win_sizes: Arc<Mutex<HashMap<RawFd, PositionAndSize>>>,
|
||||||
possible_tty_inputs: HashMap<u16, Bytes>,
|
possible_tty_inputs: HashMap<u16, Bytes>,
|
||||||
last_snapshot_time: Arc<Mutex<Instant>>,
|
last_snapshot_time: Arc<Mutex<Instant>>,
|
||||||
|
started_reading_from_pty: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeInputOutput {
|
impl FakeInputOutput {
|
||||||
|
|
@ -137,6 +149,7 @@ impl FakeInputOutput {
|
||||||
io_events: Arc::new(Mutex::new(vec![])),
|
io_events: Arc::new(Mutex::new(vec![])),
|
||||||
win_sizes: Arc::new(Mutex::new(win_sizes)),
|
win_sizes: Arc::new(Mutex::new(win_sizes)),
|
||||||
possible_tty_inputs: get_possible_tty_inputs(),
|
possible_tty_inputs: get_possible_tty_inputs(),
|
||||||
|
started_reading_from_pty: Arc::new(AtomicBool::new(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_tty_inputs(mut self, tty_inputs: HashMap<u16, Bytes>) -> Self {
|
pub fn with_tty_inputs(mut self, tty_inputs: HashMap<u16, Bytes>) -> Self {
|
||||||
|
|
@ -200,6 +213,7 @@ impl OsApi for FakeInputOutput {
|
||||||
// them fail
|
// them fail
|
||||||
::std::thread::sleep(::std::time::Duration::from_millis(25));
|
::std::thread::sleep(::std::time::Duration::from_millis(25));
|
||||||
} else if attempts_left == 0 {
|
} else if attempts_left == 0 {
|
||||||
|
self.started_reading_from_pty.store(true, Ordering::Release);
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
let mut read_buffers = self.read_buffers.lock().unwrap();
|
let mut read_buffers = self.read_buffers.lock().unwrap();
|
||||||
|
|
@ -213,6 +227,7 @@ impl OsApi for FakeInputOutput {
|
||||||
if bytes_read > bytes.read_position {
|
if bytes_read > bytes.read_position {
|
||||||
bytes.set_read_position(bytes_read);
|
bytes.set_read_position(bytes_read);
|
||||||
}
|
}
|
||||||
|
self.started_reading_from_pty.store(true, Ordering::Release);
|
||||||
return Ok(bytes_read);
|
return Ok(bytes_read);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -245,7 +260,11 @@ impl OsApi for FakeInputOutput {
|
||||||
input_chars.push(*bytes);
|
input_chars.push(*bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let reader = FakeStdinReader::new(input_chars, self.last_snapshot_time.clone());
|
let reader = FakeStdinReader::new(
|
||||||
|
input_chars,
|
||||||
|
self.last_snapshot_time.clone(),
|
||||||
|
self.started_reading_from_pty.clone(),
|
||||||
|
);
|
||||||
Box::new(reader)
|
Box::new(reader)
|
||||||
}
|
}
|
||||||
fn get_stdout_writer(&self) -> Box<dyn Write> {
|
fn get_stdout_writer(&self) -> Box<dyn Write> {
|
||||||
|
|
|
||||||
BIN
strider.wasm
Executable file
BIN
strider.wasm
Executable file
Binary file not shown.
Loading…
Add table
Reference in a new issue