Search code examples
javascriptarrayswebassembly

How to handle array size created in webassembly


I have a code instantiating a webassembly module containing c++ code creating an array. The _Z9createArrayi function returns me a pointer on an array it has created. I could read it easily by casting it with Int32Array(), but the problem is that I don't know how I can handle its size. In the case below, It gives me a gigantic array of the size of the memory buffer. In this case, the size is 12, so I could limit the size of the int32Array to 12. But I will have cases where I will not know the size of the created array, and I want to know how I can handle them at best.

javascript code:

async function main() {
  // Read the wasm file.
  let response = await fetch(wasmPath);
  let res = await WebAssembly.instantiateStreaming(response, info);
  const { memory, _Z9createArrayi} = res.instance.exports;
  memoryManager.memory = memory;

  const mult = 2;
  let ptr = _Z9createArrayi(mult);
  let result = new Int32Array(memory.buffer, ptr);
  console.log(result);
}

c++ code:

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int *createArray(int multiplicator)
{
    int *result = new int[12];

    for (int i = 0; i < 12; ++i)
    {
        result[i] = i * multiplicator;
    }

    return result;
}

Solution

  • The value returned by _Z9createArrayi(mult); is the address where the new array was just allocated (with the assumption that there is enough free memory to be allocated in the first place).

    Then new Int32Array(memory.buffer, ptr); will create a view of the buffer, starting from the offset ptr, till the end of the buffer, because no length argument is given.

    Keep in mind that when you create views to the WebAssembly.Memory buffer, you attach this view to the current memory buffer. In case the memory grows later, your view will point to the old buffer, and because the memory in JavaScript is garbage collected, it might happen that you not only use the old buffer, but also keep it referenced, so that it cannot be collected.

    C++ does not give you a direct access to the length of the dynamically created arrays. That in turn transfers to the compiled WebAssembly. To have the length you will have to store it explicitly in a variable.

    Additionally, to be sure that you read the actual bytes, you must first ensure that the modules memory buffer is the same as your view's buffer, and if it is not, you have to create a new view to the current memory buffer.

    If you have a multi-threaded access to the same WebAssembly.Memory, then you are having even bigger problem, because the memory buffer might change from another thread (i.e. Worker). One solution is to do your work on the C++ side, because the WebAssembly will ensure that each memory access is synchronized across threads, per instruction. By "access" here, I mean that you cannot read from an old module memory. You might still need critical sections/semaphores (all based on the atomic instructions) to properly do multi-threading access to the memory.

    All that said, if your memory is fully allocated (length=maximum), then you are safe to allocate views and access them at any time.