Use Asyncify to process ties in web UI
This commit is contained in:
parent
a64110b6a1
commit
a5a61731b5
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
/html/opentally.js
|
/html/opentally.js
|
||||||
/html/opentally_bg.wasm
|
/html/opentally_*.wasm
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
PATH=$PATH:$HOME/.cargo/bin
|
||||||
|
|
||||||
|
# Build cargo
|
||||||
PROFILE=${1:-release}
|
PROFILE=${1:-release}
|
||||||
cargo build --lib --target wasm32-unknown-unknown --$PROFILE && /home/runassudo/.cargo/bin/wasm-bindgen --target no-modules target/wasm32-unknown-unknown/$PROFILE/opentally.wasm --out-dir html --no-typescript
|
if [ $PROFILE == 'debug' ]; then
|
||||||
|
cargo build --lib --target wasm32-unknown-unknown
|
||||||
|
else
|
||||||
|
cargo build --lib --target wasm32-unknown-unknown --$PROFILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apply wasm-bindgen
|
||||||
|
wasm-bindgen --target no-modules target/wasm32-unknown-unknown/$PROFILE/opentally.wasm --out-dir html --no-typescript
|
||||||
|
|
||||||
|
# Apply Asyncify
|
||||||
|
MANGLED=$(wasm-dis html/opentally_bg.wasm | grep '(import "wbg" "__wbg_getuserinput_' | awk '{print $3;}' | tr -d '"')
|
||||||
|
wasm-opt -O2 --asyncify --pass-arg asyncify-imports@wbg.$MANGLED html/opentally_bg.wasm -o html/opentally_async.wasm
|
||||||
|
|
||||||
|
rm html/opentally_bg.wasm
|
||||||
|
@ -295,7 +295,6 @@
|
|||||||
<div id="printWarning">Printing directly from this page is not supported. Use the ‘Print result’ button to generate a printer-friendly report.</div>
|
<div id="printWarning">Printing directly from this page is not supported. Use the ‘Print result’ button to generate a printer-friendly report.</div>
|
||||||
|
|
||||||
<script src="vendor/vanilla-js-dropdown.min.js"></script>
|
<script src="vendor/vanilla-js-dropdown.min.js"></script>
|
||||||
<script src="opentally.js?v=GITVERSION"></script>
|
|
||||||
<script src="index.js?v=GITVERSION"></script>
|
<script src="index.js?v=GITVERSION"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
importScripts('opentally.js');
|
importScripts('opentally.js');
|
||||||
|
|
||||||
var wasm = wasm_bindgen;
|
var wasm = wasm_bindgen;
|
||||||
|
var wasmRaw;
|
||||||
|
|
||||||
|
// For asyncify
|
||||||
|
const DATA_ADDR = 16;
|
||||||
|
const DATA_START = DATA_ADDR + 8;
|
||||||
|
const DATA_END = 50 * 1024; // Needs to be increased compared with Asyncify default
|
||||||
|
|
||||||
async function initWasm() {
|
async function initWasm() {
|
||||||
await wasm_bindgen('opentally_bg.wasm');
|
wasmRaw = await wasm_bindgen('opentally_async.wasm');
|
||||||
|
|
||||||
|
new Int32Array(wasmRaw.memory.buffer, DATA_ADDR).set([DATA_START, DATA_END]);
|
||||||
|
|
||||||
postMessage({'type': 'init', 'version': wasm.version()});
|
postMessage({'type': 'init', 'version': wasm.version()});
|
||||||
}
|
}
|
||||||
initWasm();
|
initWasm();
|
||||||
@ -59,28 +68,31 @@ onmessage = function(evt) {
|
|||||||
|
|
||||||
stageNum = 2;
|
stageNum = 2;
|
||||||
|
|
||||||
resume_count();
|
resumeCount();
|
||||||
|
|
||||||
} else if (evt.data.type == 'userInput') {
|
} else if (evt.data.type == 'userInput') {
|
||||||
user_input_buffer = evt.data.response;
|
userInputBuffer = evt.data.response;
|
||||||
resume_count();
|
|
||||||
|
// Rewind the stack
|
||||||
|
// Asyncify will retrace the function calls in the stack until again reaching get_user_input
|
||||||
|
wasmRaw.asyncify_start_rewind(DATA_ADDR);
|
||||||
|
resumeCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resume_count() {
|
function resumeCount() {
|
||||||
for (;; stageNum++) {
|
for (;; stageNum++) {
|
||||||
try {
|
|
||||||
let isDone = wasm['count_one_stage_' + numbers](state, opts);
|
let isDone = wasm['count_one_stage_' + numbers](state, opts);
|
||||||
|
|
||||||
|
if (unwindingStack) {
|
||||||
|
// This stage caused a stack unwind in get_user_input so ignore the result
|
||||||
|
// We will resume execution when a userInput message is received
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
|
||||||
if (ex === "RequireInput") {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](stageNum, state, opts)});
|
postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](stageNum, state, opts)});
|
||||||
postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
|
postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
|
||||||
@ -90,15 +102,27 @@ function resume_count() {
|
|||||||
postMessage({'type': 'finalResultSummary', 'summary': wasm['final_result_summary_' + numbers](state, opts)});
|
postMessage({'type': 'finalResultSummary', 'summary': wasm['final_result_summary_' + numbers](state, opts)});
|
||||||
}
|
}
|
||||||
|
|
||||||
var user_input_buffer = null;
|
var unwindingStack = false;
|
||||||
|
var userInputBuffer = null;
|
||||||
|
|
||||||
function read_user_input_buffer(message) {
|
function get_user_input(message) {
|
||||||
if (user_input_buffer === null) {
|
if (userInputBuffer === null) {
|
||||||
postMessage({'type': 'requireInput', 'message': message});
|
postMessage({'type': 'requireInput', 'message': message});
|
||||||
|
|
||||||
|
// Record the current state of the stack
|
||||||
|
wasmRaw.asyncify_start_unwind(DATA_ADDR);
|
||||||
|
unwindingStack = true;
|
||||||
|
|
||||||
|
// No further WebAssembly will be executed and control will return to resumeCount
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
let user_input = user_input_buffer;
|
// We have reached the point the stack was originally unwound, so resume normal execution
|
||||||
user_input_buffer = null;
|
unwindingStack = false;
|
||||||
return user_input;
|
wasmRaw.asyncify_stop_rewind();
|
||||||
|
|
||||||
|
// Return the correct result to WebAssembly
|
||||||
|
let userInput = userInputBuffer;
|
||||||
|
userInputBuffer = null;
|
||||||
|
return userInput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
src/ties.rs
20
src/ties.rs
@ -198,7 +198,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prompt the candidate for input, depending on CLI or WebAssembly target
|
/// Prompt the candidate for input, depending on CLI or WebAssembly target
|
||||||
// FIXME: This may have unexpected behaviour if the tie occurs in the middle of a stage
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn prompt<'c>(candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> {
|
fn prompt<'c>(candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> {
|
||||||
println!("Multiple tied candidates:");
|
println!("Multiple tied candidates:");
|
||||||
@ -231,7 +230,7 @@ fn prompt<'c>(candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn read_user_input_buffer(s: &str) -> Option<String>;
|
fn get_user_input(s: &str) -> Option<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -242,25 +241,30 @@ fn prompt<'c>(candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError
|
|||||||
}
|
}
|
||||||
message.push_str(&format!("Which candidate to select? [1-{}] ", candidates.len()));
|
message.push_str(&format!("Which candidate to select? [1-{}] ", candidates.len()));
|
||||||
|
|
||||||
match read_user_input_buffer(&message) {
|
loop {
|
||||||
|
let response = get_user_input(&message);
|
||||||
|
|
||||||
|
match response {
|
||||||
Some(response) => {
|
Some(response) => {
|
||||||
match response.trim().parse::<usize>() {
|
match response.trim().parse::<usize>() {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
if val >= 1 && val <= candidates.len() {
|
if val >= 1 && val <= candidates.len() {
|
||||||
return Ok(candidates[val - 1]);
|
return Ok(candidates[val - 1]);
|
||||||
} else {
|
} else {
|
||||||
let _ = read_user_input_buffer(&message);
|
// Invalid selection
|
||||||
return Err(STVError::RequireInput);
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let _ = read_user_input_buffer(&message);
|
// Invalid selection
|
||||||
return Err(STVError::RequireInput);
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(STVError::RequireInput);
|
// No available user input in buffer - stack will be unwound
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user