emscripten-core / emscripten-core/emscripten

WebIDL-wrapped C++ objects are never garbage collected

Open
#19,438 4 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.