Skip to content

Commit 10bf849

Browse files
authored
Embind: add explicit resource management support (#23818)
Adds experimental support for the [explicit resource management proposal](https://github.com/tc39/proposal-explicit-resource-management) to Embind classes. It's currently only natively supported in latest Chrome (unflagged) and Node.js (flagged), but users can already enjoy it via TypeScript and Babel transpilation. It's designed specifically for RAII objects that need to be freed when they go out of scope, which is a perfect fit for Embind (much better than FinalizationRegistry that we used as a loose fallback for forgotten deletes so far). Before: ```js var foo = new Module.MyClass(); foo.doSomething(); // somewhere later: foo.delete(); ``` With this support: ```js using foo = new Module.MyClass(); foo.doSomething(); ```
1 parent c2c03cb commit 10bf849

File tree

4 files changed

+68
-6
lines changed

4 files changed

+68
-6
lines changed

site/source/docs/porting/connecting_cpp_and_javascript/embind.rst

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,27 @@ a C++ object is no longer needed and can be deleted:
243243
Automatic memory management
244244
---------------------------
245245

246-
JavaScript only gained support for `finalizers`_ in ECMAScript 2021, or ECMA-262
247-
Edition 12. The new API is called `FinalizationRegistry`_ and it still does not
248-
offer any guarantees that the provided finalization callback will be called.
249-
Embind uses this for cleanup if available, but only for smart pointers,
250-
and only as a last resort.
246+
Embind integrates with the `Explicit Resource Management`_ proposal.
247+
248+
It allows to automatically delete short-lived C++ objects at the end of the
249+
scope when they're declared with a `using` keyword:
250+
251+
.. code:: javascript
252+
253+
using x = new Module.MyClass;
254+
x.method();
255+
256+
At the moment of writing, this proposal is natively supported in
257+
Chromium-based browsers as well as Babel and TypeScript via transpilation.
258+
259+
Embind also supports `finalizers`_, which were added in ECMAScript 2021 under a
260+
`FinalizationRegistry`_ API. Unlike the `using` keyword, finalizers are not
261+
guaranteed to be called, and even if they are, there are no guarantees about
262+
their timing or order of execution, which makes them unsuitable for general
263+
RAII-style resource management.
264+
265+
Embind uses it for cleanup if available, but only for smart pointers, and only
266+
as a last resort.
251267

252268
.. warning:: It is strongly recommended that JavaScript code explicitly deletes
253269
any C++ object handles it has received.
@@ -1245,3 +1261,4 @@ real-world applications has proved to be more than acceptable.
12451261
.. _Making sine, square, sawtooth and triangle waves: http://stuartmemo.com/making-sine-square-sawtooth-and-triangle-waves/
12461262
.. _embind_tsgen.cpp: https://github.com/emscripten-core/emscripten/blob/main/test/other/embind_tsgen.cpp
12471263
.. _embind_tsgen.d.ts: https://github.com/emscripten-core/emscripten/blob/main/test/other/embind_tsgen.d.ts
1264+
.. _Explicit Resource Management: https://tc39.es/proposal-explicit-resource-management/

src/closure-externs/closure-externs.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,9 @@ var moduleRtn;
261261
*/
262262
Navigator.prototype.webkitGetUserMedia = function(
263263
constraints, successCallback, errorCallback) {};
264+
265+
/**
266+
* A symbol from the explicit resource management proposal that isn't yet part of Closure.
267+
* @type {symbol}
268+
*/
269+
Symbol.dispose;

src/lib/libembind.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,9 @@ var LibraryEmbind = {
15581558
'$delayFunction',
15591559
],
15601560
$init_ClassHandle: () => {
1561-
Object.assign(ClassHandle.prototype, {
1561+
let proto = ClassHandle.prototype;
1562+
1563+
Object.assign(proto, {
15621564
"isAliasOf"(other) {
15631565
if (!(this instanceof ClassHandle)) {
15641566
return false;
@@ -1644,6 +1646,12 @@ var LibraryEmbind = {
16441646
return this;
16451647
},
16461648
});
1649+
1650+
// Support `using ...` from https://github.com/tc39/proposal-explicit-resource-management.
1651+
const symbolDispose = Symbol.dispose;
1652+
if (symbolDispose) {
1653+
proto[symbolDispose] = proto["delete"];
1654+
}
16471655
},
16481656

16491657
$ClassHandle__docs: '/** @constructor */',

test/test_other.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3379,6 +3379,37 @@ def test_embind_long_long(self, args):
33793379
self.do_runf('embind/test_embind_long_long.cpp', '1000000000000n\n-1000000000000n',
33803380
emcc_args=['-lembind', '-sWASM_BIGINT'] + args)
33813381

3382+
@requires_node_canary
3383+
def test_embind_resource_management(self):
3384+
self.node_args.append('--js-explicit-resource-management')
3385+
3386+
create_file('main.cpp', r'''
3387+
#include <emscripten.h>
3388+
#include <emscripten/bind.h>
3389+
3390+
using namespace emscripten;
3391+
3392+
class MyClass {};
3393+
EMSCRIPTEN_BINDINGS(my_module) {
3394+
class_<MyClass>("MyClass")
3395+
.constructor();
3396+
}
3397+
3398+
int main() {
3399+
EM_ASM({
3400+
let instanceRef;
3401+
{
3402+
using instance = new Module.MyClass();
3403+
assert(!instance.isDeleted());
3404+
// "Leak" a reference to the instance just to check that it's deleted.
3405+
instanceRef = instance;
3406+
}
3407+
assert(instanceRef.isDeleted());
3408+
});
3409+
}
3410+
''')
3411+
self.do_runf('main.cpp', emcc_args=['-lembind'])
3412+
33823413
@requires_jspi
33833414
@parameterized({
33843415
'': [['-sJSPI_EXPORTS=async*']],

0 commit comments

Comments
 (0)