Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Share WebAssembly.Memory between module instances

I want to instantiate a WebAssembly module using the WebAssembly.Memory object created by a previous Module instance (all this using emscripten's glue code), is that feasible?

like image 977
Lucas Avatar asked Apr 30 '19 13:04

Lucas


1 Answers

It's a bit easier to create a memory object yourself and then inject it during instantiation of one or more WASM modules. I managed to do this with Emscripten by overriding instantiateWasm.

Start by creating a memory that will be shared by the WASM instances:

var TOTAL_MEMORY = 16777216;
var WASM_PAGE_SIZE = 65536;
var wasmMemory = new WebAssembly.Memory({ 'initial': TOTAL_MEMORY / WASM_PAGE_SIZE, 'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE });
var buffer = wasmMemory.buffer;

Then implement a custom instantiator that injects this memory into the import object:

function wasmInstantiator(wasmBinary) {
  return (info, receiveInstance) => {
    var importObject = Object.assign({}, info);
    importObject['env']['memory'] = wasmMemory;
    WebAssembly.instantiateStreaming(fetch(wasmBinary, { credentials: 'same-origin' }), importObject)
    .then((output) => { receiveInstance(output['instance']); },
          (err) => { console.error('wasm streaming compile failed: ' + err);});
     return {};
  };
};

Now you can instantiate the two modules using the same memory. Here I recommend to use Modularize (see documentation and settings.js, which means adding -s Modularize=1 -s EXPORT_NAME='MY_MODULE_NAME_1' to emcc command-line. Emscripten will create a function called MY_MODULE_NAME_1 that accepts a Module object where you can override certain elements such as the instantiator and the memory.

var createInstance1 = MY_MODULE_NAME_1( { instantiateWasm: wasmInstantiator('module1.wasm') , TOTAL_MEMORY, buffer } );
var createInstance2 = MY_MODULE_NAME_2( { instantiateWasm: wasmInstantiator('module2.wasm') , TOTAL_MEMORY, buffer } );

With these promise-like WASM instances, you can now interact with the two modules:

createInstance1.then( (instance1) => {
   createInstance2.then( (instance2) => {
       useWasm(instance1, instance2);
   });
});

But - there is a serious caveat here: if both modules try to allocate dynamic memory in the same memory, they override each-other's allocated buffers! So far I haven't found how to resolve this issue :-(.

See working example in This repo.

like image 121
Adi Levin Avatar answered Oct 11 '22 20:10

Adi Levin