Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory error with repeated calls #109

Open
aekobear opened this issue Mar 5, 2024 · 5 comments
Open

Memory error with repeated calls #109

aekobear opened this issue Mar 5, 2024 · 5 comments

Comments

@aekobear
Copy link

aekobear commented Mar 5, 2024

I wrote a small script to benchmark wasmoon's runtime overhead with multiple calls. However calling doString() more than a few dozen times causes a memory error

const { LuaFactory } = require('wasmoon')

async function init() {
  const factory = new LuaFactory();
  const lua = await factory.createEngine();
  
  const length = 1000;
  for (let i = 0; i < length; i++) {
    const a = Math.floor(Math.random() * 100);
    const b = Math.floor(Math.random() * 100);
    try {
      const result = await lua.doString(`return ${a} + ${b};`);
      console.log(result);
    } catch (error) {
      console.log(error);
    }
  }

  lua.global.close()
}

init();

This script should print 1000 random numbers then return. It always works for roughly the first 60, the crashes with:

RuntimeError: memory access out of bounds
    at <anonymous> (wasm://wasm/00109376:1:77112)
    at <anonymous> (wasm://wasm/00109376:1:71192)
    at <anonymous> (wasm://wasm/00109376:1:79616)
    at <anonymous> (wasm://wasm/00109376:1:94618)
    at <anonymous> (wasm://wasm/00109376:1:87842)
    at <anonymous> (wasm://wasm/00109376:1:63545)
    at nc (file:///home/eikobear/code/goodsheet/tests/node_modules/.deno/[email protected]/node_modules/wasmoon/dist/index.js:1375:366)
    at <anonymous> (wasm://wasm/00109376:1:55544)
    at <anonymous> (wasm://wasm/00109376:1:19806)
    at <anonymous> (wasm://wasm/00109376:1:30689)
RuntimeError: memory access out of bounds
    at <anonymous> (wasm://wasm/00109376:1:77112)
    at <anonymous> (wasm://wasm/00109376:1:73026)
    at <anonymous> (wasm://wasm/00109376:1:71202)
    at <anonymous> (wasm://wasm/00109376:1:79616)
    at <anonymous> (wasm://wasm/00109376:1:136610)
    at file:///home/eikobear/code/goodsheet/tests/node_modules/.deno/[email protected]/node_modules/wasmoon/dist/index.js:1268:365
    at Object.e.ccall (file:///home/eikobear/code/goodsheet/tests/node_modules/.deno/[email protected]/node_modules/wasmoon/dist/index.js:1376:346)
    at LuaWasm.pointersToBeFreed [as lua_newthread] (file:///home/eikobear/code/goodsheet/tests/node_modules/.deno/[email protected]/node_modules/wasmoon/dist/index.js:1626:49)
    at Global.newThread (file:///home/eikobear/code/goodsheet/tests/node_modules/.deno/[email protected]/node_modules/wasmoon/dist/index.js:92:38)
    at LuaEngine.callByteCode (file:///home/eikobear/code/goodsheet/tests/node_modules/.deno/[email protected]/node_modules/wasmoon/dist/index.js:1224:40)

I tested on [email protected] and [email protected] and got the same result

@gudzpoz
Copy link
Contributor

gudzpoz commented Mar 23, 2024

According to the Lua manual:

When you interact with the Lua API, you are responsible for ensuring consistency. In particular, you are responsible for controlling stack overflow. You can use the function lua_checkstack to ensure that the stack has extra slots when pushing new elements.

Running return statements pushes values onto the stack. Without lua.global.pop() / lua.global.lua.lua_checkstack(lua.global.address), the stack grows and eventually overflows.

I am not sure whether this library should call lua_checkstack for the user though. But remembering to pop values off the stack should free you from random overflows.

@aekobear
Copy link
Author

According to the Lua manual:

When you interact with the Lua API, you are responsible for ensuring consistency. In particular, you are responsible for controlling stack overflow. You can use the function lua_checkstack to ensure that the stack has extra slots when pushing new elements.

Running return statements pushes values onto the stack. Without lua.global.pop() / lua.global.lua.lua_checkstack(lua.global.address), the stack grows and eventually overflows.

I am not sure whether this library should call lua_checkstack for the user though. But remembering to pop values off the stack should free you from random overflows.

This helps me move forward, thank you!

I do think this is something to fix. When using the API directly, it makes sense to manage the stack because that's how you communicate with lua:

lua_call(L, 1, 1) // call a function
result = lua_pop(L, 1) // get its result

However in wasmoon, not only does doString already copy the value out of the stack, but the wasmoon wrapper for popping the stack doesn't actually return a value (which may also be a bug):

// i already have my result, so no incentive to access stack
const result = await lua.doString(`return ${a} + ${b};`);

// stackResult is always undefined. This is just busy-work
const stackResult = lua.global.pop();

@ceifa
Copy link
Owner

ceifa commented Apr 1, 2024

@aekobear thanks for the suggestion, I think it does make sense for wasmoon to pop the values returned, I can't think on a use case for returning the value and it continue on the stack

@ceifa
Copy link
Owner

ceifa commented Apr 5, 2024

@tims-bsquare do you have any opinion on this?

@tims-bsquare
Copy link
Contributor

I think the lua_xmove is the memory leak in callByteCode. I can't remember why it's that way though. I have a suspicion something about returning a function from doString and calling it later but in theory a reference to that should continue to exist through lua_ref. Could try and remove it and see if things break?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants