Skip to content

Commit

Permalink
WIP: wasm with query example
Browse files Browse the repository at this point in the history
  • Loading branch information
tailhook committed Jun 27, 2018
1 parent c745aa6 commit 5c58081
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 2 deletions.
2 changes: 2 additions & 0 deletions example-configs/wasm-with-query/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Cargo.lock
/target
9 changes: 9 additions & 0 deletions example-configs/wasm-with-query/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "verwalter-minimal-wasm-scheduler"
description = """
Demo scheduler in rust+webassembly for verwalter
"""
version = "0.1.0"

[dependencies]
serde_json = "1.0.0"
6 changes: 6 additions & 0 deletions example-configs/wasm-with-query/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---------------
Minimal Example
---------------

The purpose of this example is to show absolute minimum files to start a
working verwalter using Rust compiled to Webassembly as a scheduler.
1 change: 1 addition & 0 deletions example-configs/wasm-with-query/frontend/common
Empty file.
1 change: 1 addition & 0 deletions example-configs/wasm-with-query/scheduler/v1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.wasm
86 changes: 86 additions & 0 deletions example-configs/wasm-with-query/src/bin/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#[macro_use] extern crate serde_json;

use std::mem;
use std::slice;
use std::panic::set_hook;
use std::os::raw::{c_void};

use serde_json::{Value, from_slice, to_vec};

extern {
fn log_panic(payload_ptr: *const u8, payload_len: usize,
file_ptr: *const u8, file_len: usize, line: u32);
}

fn main() {
set_hook(Box::new(|panic_info| {
let payload = panic_info.payload();
let (ptr, len) = if let Some(s) = payload.downcast_ref::<&str>() {
(s.as_bytes().as_ptr(), s.len())
} else if let Some(s) = payload.downcast_ref::<String>() {
(s.as_bytes().as_ptr(), s.len())
} else {
(0 as *const u8, 0)
};
let (file_ptr, file_len, line) = match panic_info.location() {
Some(loc) => {
let file = loc.file().as_bytes();
(file.as_ptr(), file.len(), loc.line())
}
None => (0 as *const u8, 0, 0),
};
unsafe {
log_panic(ptr, len, file_ptr, file_len, line);
}
}));
}

#[no_mangle]
pub extern "C" fn init(ptr: *const u8, len: usize) -> *mut c_void {
let input = unsafe { slice::from_raw_parts(ptr, len) };
let mut out = _init_wrapper(input);
let out_ptr = out.as_mut_ptr();
mem::forget(out);
return out_ptr as *mut c_void;
}

fn _init_wrapper(data: &[u8]) -> Vec<u8> {
let input = match from_slice(data) {
Ok(inp) => inp,
Err(e) => {
return to_vec(&json!({
"Err": format!("Error deserializing input: {}", e),
})).expect("should serialize standard json");
}
};
let result = _init_inner(input);
match to_vec(&result) {
Ok(result) => result,
Err(e) => {
// TODO(pc) log error
return to_vec(&json!({
"Err": format!("Error serializing output: {}", e),
})).expect("should serialize standard json");
}
}
}

fn _init_inner(_input: Value) -> Result<Value, String> {
return Ok(json!(null))
}

// In order to work with the memory we expose (de)allocation methods
#[no_mangle]
pub extern "C" fn alloc(size: usize) -> *mut c_void {
let mut buf = Vec::with_capacity(size);
let ptr = buf.as_mut_ptr();
mem::forget(buf);
return ptr as *mut c_void;
}

#[no_mangle]
pub extern "C" fn dealloc(ptr: *mut c_void) {
unsafe {
let _buf = Vec::from_raw_parts(ptr, 0, 1);
}
}
87 changes: 87 additions & 0 deletions example-configs/wasm-with-query/src/bin/scheduler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#[macro_use] extern crate serde_json;

use std::mem;
use std::slice;
use std::panic::set_hook;
use std::fmt::Write;
use std::os::raw::{c_void};

use serde_json::{Value, from_slice, to_vec};

extern {
fn log_panic(payload_ptr: *const u8, payload_len: usize,
file_ptr: *const u8, file_len: usize, line: u32);
}

fn main() {
set_hook(Box::new(|panic_info| {
let payload = panic_info.payload();
let (ptr, len) = if let Some(s) = payload.downcast_ref::<&str>() {
(s.as_bytes().as_ptr(), s.len())
} else if let Some(s) = payload.downcast_ref::<String>() {
(s.as_bytes().as_ptr(), s.len())
} else {
(0 as *const u8, 0)
};
let (file_ptr, file_len, line) = match panic_info.location() {
Some(loc) => {
let file = loc.file().as_bytes();
(file.as_ptr(), file.len(), loc.line())
}
None => (0 as *const u8, 0, 0),
};
unsafe {
log_panic(ptr, len, file_ptr, file_len, line);
}
}));
}

#[no_mangle]
pub extern "C" fn scheduler(ptr: *const u8, len: usize) -> *mut c_void {
let input = unsafe { slice::from_raw_parts(ptr, len) };
let mut out = _scheduler_wrapper(input);
let out_ptr = out.as_mut_ptr();
mem::forget(out);
return out_ptr as *mut c_void;
}

fn _scheduler_wrapper(data: &[u8]) -> Vec<u8> {
let input = match from_slice(data) {
Ok(inp) => inp,
Err(e) => {
return to_vec(
&json!([{}, format!("Error deserialing input: {}", e)])
).expect("can serialize error")
}
};
let (schedule, mut debug) = _scheduler_inner(input);
match to_vec(&json!({"schedule": schedule, "log": debug})) {
Ok(result) => result,
Err(e) => {
writeln!(&mut debug, "\nError serializing input: {}", e).ok();
return to_vec(
&json!({"schedule": Value::Null, "log": debug})
).expect("can serialize error")
}
}
}

fn _scheduler_inner(_input: Value) -> (Value, String) {
return (json!({}), "Scheduler works!".into())
}

// In order to work with the memory we expose (de)allocation methods
#[no_mangle]
pub extern "C" fn alloc(size: usize) -> *mut c_void {
let mut buf = Vec::with_capacity(size);
let ptr = buf.as_mut_ptr();
mem::forget(buf);
return ptr as *mut c_void;
}

#[no_mangle]
pub extern "C" fn dealloc(ptr: *mut c_void) {
unsafe {
let _buf = Vec::from_raw_parts(ptr, 0, 1);
}
}
5 changes: 3 additions & 2 deletions src/daemon/query/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use std::sync::Arc;
use std::path::Path;

use failure::{Error};
use failure::{Error, err_msg};
use query::Settings;
use serde_json::{Value as Json};

Expand All @@ -27,10 +27,11 @@ impl Responder {
-> Result<Responder, Error>
{
let mut wasm = Program::read(file)?;
let _ = wasm.json_call("init", &QueryInit {
let init_res: Result<(), String> = wasm.json_call("init", &QueryInit {
schedule: &*schedule,
hostname: &settings.hostname,
})?;
init_res.map_err(|e| err_msg(e))?;
Ok(Responder {
schedule: schedule.clone(),
wasm,
Expand Down
30 changes: 30 additions & 0 deletions vagga.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,36 @@ commands:
- --debug-force-leader
cantal: *fake_cantal

_make-wasm-query: !Command
description: Compile wasm-with-query example
container: wasm
work-dir: /work/example-configs/wasm-with-query
run: |
cargo build --target=wasm32-unknown-unknown --release
cp \
target/wasm32-unknown-unknown/release/scheduler.wasm \
scheduler/v1/scheduler.wasm
cp \
target/wasm32-unknown-unknown/release/query.wasm \
scheduler/v1/query.wasm
run-example-wasm-query: !Supervise
description: "Same as example-wasm-minimal but with query interface"
prerequisites: [make-js, make, _make-wasm-query]
children:
verw: !Command
container: xenial
environ: *fake_rust_log
run:
- ./target/debug/verwalter
- --config-dir=/work/example-configs/wasm-with-query
- --storage-dir=tmp/storage3
- --log-dir=/tmp/logs
- --override-machine-id=77985419c732412ea38b94db00000000
- --hostname=alpha
- --debug-force-leader
cantal: *fake_cantal


containers:

Expand Down

0 comments on commit 5c58081

Please sign in to comment.