emscripten-core / emscripten-core/emscripten
WebIDL-wrapped C++ objects are never garbage collected
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
Please include the following in your bug report:
**Version of emscripten/emsdk:**
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.39 (36f871819b566281d160470a1ec4515f379e9f77)
clang version 17.0.0 (https://github.com/llvm/llvm-project c672c3fe05adbb590abc99da39143b55ad510538)
Target: wasm32-unknown-emscripten
Thread model: posix
**Repro**
This shell script generates bindings using WebIDL, starts a simple http server and creates a webpage to open:
```
cat > index.html <
Testcase
Destroy
Do not Destroy
Run
Stop
Reclaim
let MyModule;
let allocateTimerId;
let interval = 1000;
function createVector() {
return new MyModule.MyVector(1, 1, 1);
}
function createVectors() {
for (let ii = 0; ii < 10; ++ii)
{
let mat = createVector();
if (document.getElementById('doDestroy').checked)
MyModule.destroy(mat);
}
}
function initModule(isStopped)
{
if (allocateTimerId !== undefined) {
clearInterval(allocateTimerId);
allocateTimerId = undefined;
}
delete MyModule;
MyModule = null;
Module().then(instance => {
MyModule = instance;
if (!isStopped)
{
allocateTimerId = setInterval(() => {
createVectors();
}, interval);
}
});
}
initModule(false);
let handleStateToggle = event => {
if (document.getElementById('doRun').checked)
{
allocateTimerId = setInterval(() => {
createVectors();
}, interval);
} else {
clearInterval(allocateTimerId);
allocateTimerId = undefined;
}
};
document.getElementById('doRun').addEventListener('change', handleStateToggle);
document.getElementById('doStop').addEventListener('change', handleStateToggle);
document.getElementById('reclaimbutton').addEventListener('click', event => {
initModule(allocateTimerId === undefined);
});
EOF
cat > myBindings.idl < myBindings.cpp < JavaScript will automatically garbage collect any of the wrapped C++ objects when there are no more references. If the C++ object doesn’t require specific clean up (i.e. it doesn’t have a destructor) then no other action needs to be taken.
This test case shows that that is not the case. The wrapped objects are never garbage collected. The wrapped type does not have a destructor which needs to be invoked.
The documentation also notes:
> You will usually need to destroy the objects which you create, but this depends on the library being ported.
This is confusing and contradictory.
If the radio button is selected to destroy objects after creating them, then `destroy` is called and memory stops growing. This is not a reasonable workaround as the code becomes unmaintainable very quickly.
If this is not fixed, at least the documentation should be updated to indicate that all wrapped objects must be explicitly destroyed.
Contributor guide
Assessment
This issue has not been assessed yet.