Loading WASM, then exporting its functions?


#1

Hi all. Bit of a generic question, but figured enough of us are on the bleeding edge that some might be able to answer or give a good example.

I’ve compiled our internal lib to WASM, and can load and run it, call various functions etc just fine. No issues there. But… I need to be able to call these function outside of the actual WASM module load - if I do so as is then the functions and many other things end up undefined since the WASM hadn’t finished loading.

I figure it needs async/await or similar on the JS side of it, but JS isn’t my strong suite.

Ideas or tips?


#2

Hi Luke,

Have you played around with Alex Crichton’s wasm-bindgen?

Here’s the associated article: https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/


#3

Yeah there’s a few issues I have with it:

  1. Have to add more feature flags to enable/disable this in existing codebase,
  2. Or, have to write yet another crate that duplicates functionality
  3. Type support is limited (though this is good for JS)

Basically it’s a whole lot more work required when I should be able to use the existing C style exports. I just need to work out async loading. Otherwise, wasm-bindgen is looking pretty exciting!


#4

Async and await should not be too hard!

I found an example template a while back. It uses a Webpack loader to include Rust via require:

Also came across this example which I believe uses the wasm-bindgen you’re talking about:

Both examples call then, but this can easily be turned into await.


#5

Thanks for the examples. I have no problems with calling any functions using .then() or variations of this. Works well.

But I can’t use those functions outside of the .then call, which is actually my main problem here :slight_smile:


#6

Ah, then I misunderstood. Is the following something you’re after?

import { hello_world, wasmBooted } from './lib.rs'

async function myFunction() {
    await wasmBooted;

    hello_world();
}

myFunction().then(() => {
    console.log('done');
});

#7

Hmm, that actually gives me a decent idea…

Initially I was trying to do something like:

export var Module = {
  wasmBinaryFile: "test.wasm",
  onRuntimeInitialized: obj => {
    var wrapped = {
      test_func: Module.cwrap(test_func',
        'number', []),

And export the wrapped var or Module. Except I can’t access wrapped, or wasm doesn’t load quickly enough for cwrap to be available if done other ways. I’ve tried all sorts of variations on this based on how I do node.js ffi.

But, that last example of yours - I could use the onRuntimeInitialized to set a global, and wrap the WASM functions in async functions that check the global before either calling or doing a cwrap.


#8

For anyone curious - I’m using emscriptem

var wasmLoaded = false;

var Module = {
  wasmBinaryFile: "./test.wasm",
  onRuntimeInitialized: function() {
    wasmLoaded = true;
  }
};

async function wasmLoadWait() {
  // Early exit
  if (wasmLoaded === true) {
    return true
  }
  let check = function() {
    return new Promise( load => {
      setTimeout(() => {
        load(wasmLoaded);
      }, 10);
    });
  };
  // Loop until WASM is loaded
  // Require an async check because the function captures the global
  // as it is when the function is called - meaning infinite loops if not released
  while (!await check()) {}
  // will not exit until check() is true
}

So with this, you can wrap a WASM function and include a call to await wasmLoadWait(); at teh start of the function to ensure that the function doesn’t return unless the WASM is loaded, thereby eliminating undefined. Eg:

async function life() {
  await wasmLoadWait();
  return Module.cwrap('meaning_of', 'number', [])();
}

This is probably similar to a spinlock.


#9

This topic was automatically closed after 60 days. New replies are no longer allowed.