emscripten-core / emscripten-core/emscripten

Using embind + pthreads - deallocating shared_ptr from other thread

Open
#19,311 4 comments 0 reactions 0 assignees View on GitHub
embind
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:
Screenshot 2023-05-05 at 12 11 31

## 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

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.