emscripten-core / emscripten-core/emscripten
Using embind + pthreads - deallocating shared_ptr from other thread
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
### Discussed in https://github.com/emscripten-core/emscripten/discussions/19299
Originally posted by **ricardperez** May 5, 2023
Hello everybody,
I am facing a problem when allocating a C++ object from JS, which is handled by a `std::shared_ptr` and it is being deallocated from a `std::thread` (reaching ref count 0).
Let me introduce you with the most basic example to reproduce such problem:
_lib.cpp_
```c++
#include
#include
#include
using namespace emscripten;
class Base {};
class Derived : public Base {};
EMSCRIPTEN_KEEPALIVE void launch(std::shared_ptr x) {
static std::thread t;
t = std::thread([x = std::move(x)]{
// note how x ownership is being transferred to the lambda
// which when deallocated will decrease its ref count, in this
// case triggering the destructor
});
}
EMSCRIPTEN_BINDINGS(my_module) {
class_("Base")
.smart_ptr>("std::shared_ptr");
class_>("Derived")
.smart_ptr_constructor("std::shared_ptr", &std::make_shared);
function("launch", &launch);
}
int main() {
return 0;
}
```
_worker.js_
```js
importScripts('lib.js');
function doLaunch() {
var x = new Module.Derived();
Module.launch(x);
x.delete();
}
self.addEventListener('message', (event) => {
if (event.data === 'launch') {
doLaunch();
}
});
```
_index.html_
```html
Hello World
Launch
const worker = new Worker('worker.js');
const button = document.querySelector('button');
button.addEventListener('click', () => {
worker.postMessage('launch');
});
```
And I would be building and serving the app with the commands:
```sh
em++ lib.cpp -lembind -o lib.js -s USE_PTHREADS=1 -s PROXY_TO_PTHREAD=1 -s ALLOW_MEMORY_GROWTH=1 -s EXIT_RUNTIME=0 -O0 -g3
emrun index.html
```
## Demo
This is the error when clicking the button and logic is triggered:

## What I've tried so far
### Using the derived type directly
I've noticed that if my C++ function consumes a `std::shared_ptr` instead of a `std::shared_ptr` then everything goes fine. The destructor is being called and there's no error.
Somehow, the problem happens when the shared_ptr reference count reaches 0 and it needs to invoke the destructor of its base class, I guess.
And by the way, I can't change my application functions to receive the derived types.
### Explicitly decreasing the ref count from the main thread
I tried to call `.reset()` to the shared pointer from inside a function passed to `emscripten_sync_run_in_main_runtime_thread`. This is forcing the deallocation to be triggered from the main runtime thread, which has no problem in calling the destructor properly.
But again this is something I would like to avoid since I could not be sure of how many places in my application I'd need to modify.
Contributor guide
Assessment
This issue has not been assessed yet.