Skip to content

Commit 28bcb86

Browse files
authored
[EH] Fix inconsistency of in/decrementing refcounts (#25988)
This fixes inconsistency of incrementing/decrementing refcounts between Wasm EH and Emscripten EH. Previously, in Emscripten EH, we incremented the refcount in `__cxa_begin_catch`, while Wasm EH incremented it in `__cxa_throw`. This PR moves the incrementing call from `__cxa_begin_catch` to `__cxa_throw` as well. This also increments the refcount in `__cxa_rethrow`. These incrementing calls are guarded with `+#if !DISABLE_EXCEPTION_CATCHING`, because without that, `std::terminate` will run: https://github.com/emscripten-core/emscripten/blob/d1251798144df813c52934768964a1223504c440/system/lib/libcxxabi/src/cxa_noexception.cpp#L25-L35 Fixes #17115.
1 parent 234c976 commit 28bcb86

File tree

8 files changed

+36
-41
lines changed

8 files changed

+36
-41
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.23 (in development)
2222
-----------------------
23+
- The inconsistency of incrementing / decrementing refcounts between Wasm EH and
24+
Emscripten EH has been fixed. See `test_EXPORT_EXCEPTION_HANDLING_HELPERS` in
25+
`test_core.py` to see the usage. (#25988)
2326
- The `select()` and `poll()` system calls can now block under certain
2427
circumstances. Specifically, if they are called from a background thread and
2528
file descriptors include pipes. (#25523, #25990)

site/source/docs/porting/exceptions.rst

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,10 @@ leaked.
155155

156156
.. note:: If you catch a Wasm exception and do not rethrow it, you need to free
157157
the storage associated with the exception in JS using
158-
``decrementExceptionRefcount`` method because the exception
159-
catching code in Wasm does not have a chance to free it. But currently due to
160-
an implementation issue that Wasm EH and Emscripten (JS-based) EH, you need
161-
to call incrementExceptionRefcount additionally in case of Emscripten EH. See
162-
https://github.com/emscripten-core/emscripten/issues/17115 for details and a
163-
code example.
164-
165-
.. todo:: Fix the above-mentinoed `inconsistency
166-
<https://github.com/emscripten-core/emscripten/issues/17115>`_ between Wasm
167-
EH and Emscripten EH, on the reference counting.
158+
``decrementExceptionRefcount`` method because the exception catching code in
159+
Wasm does not have a chance to free it. See
160+
``test_EXPORT_EXCEPTION_HANDLING_HELPERS`` in test/test_core.py for an
161+
example usage.
168162

169163

170164
Using Exceptions and setjmp-longjmp Together

site/source/docs/tools_reference/settings_reference.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,9 +1134,7 @@ the JS library:
11341134
Setting this option also adds refcount increasing and decreasing functions
11351135
('incrementExceptionRefcount' and 'decrementExceptionRefcount') in the JS
11361136
library because if you catch an exception from JS, you may need to manipulate
1137-
the refcount manually not to leak memory. What you need to do is different
1138-
depending on the kind of EH you use
1139-
(https://github.com/emscripten-core/emscripten/issues/17115).
1137+
the refcount manually not to leak memory.
11401138

11411139
See test_EXPORT_EXCEPTION_HANDLING_HELPERS in test/test_core.py for an
11421140
example usage.

src/lib/libexceptions.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,21 @@ var LibraryExceptions = {
8282

8383
// Here, we throw an exception after recording a couple of values that we need to remember
8484
// We also remember that it was the last exception thrown as we need to know that later.
85-
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount'],
85+
__cxa_throw__deps: ['$ExceptionInfo', '$exceptionLast', '$uncaughtExceptionCount'
86+
#if !DISABLE_EXCEPTION_CATCHING
87+
, '__cxa_increment_exception_refcount'
88+
#endif
89+
],
8690
__cxa_throw: (ptr, type, destructor) => {
8791
#if EXCEPTION_DEBUG
8892
dbg('__cxa_throw: ' + [ptrToString(ptr), type, ptrToString(destructor)]);
8993
#endif
9094
var info = new ExceptionInfo(ptr);
9195
// Initialize ExceptionInfo content after it was allocated in __cxa_allocate_exception.
9296
info.init(type, destructor);
97+
#if !DISABLE_EXCEPTION_CATCHING
98+
___cxa_increment_exception_refcount(ptr);
99+
#endif
93100
{{{ storeException('exceptionLast', 'ptr') }}}
94101
uncaughtExceptionCount++;
95102
{{{ makeThrow('exceptionLast') }}}
@@ -98,7 +105,11 @@ var LibraryExceptions = {
98105
// This exception will be caught twice, but while begin_catch runs twice,
99106
// we early-exit from end_catch when the exception has been rethrown, so
100107
// pop that here from the caught exceptions.
101-
__cxa_rethrow__deps: ['$exceptionCaught', '$exceptionLast', '$uncaughtExceptionCount'],
108+
__cxa_rethrow__deps: ['$exceptionCaught', '$exceptionLast', '$uncaughtExceptionCount'
109+
#if !DISABLE_EXCEPTION_CATCHING
110+
, '__cxa_increment_exception_refcount'
111+
#endif
112+
],
102113
__cxa_rethrow: () => {
103114
var info = exceptionCaught.pop();
104115
if (!info) {
@@ -112,6 +123,9 @@ var LibraryExceptions = {
112123
info.set_caught(false);
113124
uncaughtExceptionCount++;
114125
}
126+
#if !DISABLE_EXCEPTION_CATCHING
127+
___cxa_increment_exception_refcount(ptr);
128+
#endif
115129
#if EXCEPTION_DEBUG
116130
dbg('__cxa_rethrow, popped ' +
117131
[ptrToString(ptr), exceptionLast, 'stack', exceptionCaught]);
@@ -122,8 +136,7 @@ var LibraryExceptions = {
122136

123137
llvm_eh_typeid_for: (type) => type,
124138

125-
__cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_increment_exception_refcount',
126-
'__cxa_get_exception_ptr',
139+
__cxa_begin_catch__deps: ['$exceptionCaught', '__cxa_get_exception_ptr',
127140
'$uncaughtExceptionCount'],
128141
__cxa_begin_catch: (ptr) => {
129142
var info = new ExceptionInfo(ptr);
@@ -136,7 +149,6 @@ var LibraryExceptions = {
136149
#if EXCEPTION_DEBUG
137150
dbg('__cxa_begin_catch ' + [ptrToString(ptr), 'stack', exceptionCaught]);
138151
#endif
139-
___cxa_increment_exception_refcount(ptr);
140152
return ___cxa_get_exception_ptr(ptr);
141153
},
142154

src/settings.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -768,9 +768,7 @@ var DISABLE_EXCEPTION_THROWING = false;
768768
// Setting this option also adds refcount increasing and decreasing functions
769769
// ('incrementExceptionRefcount' and 'decrementExceptionRefcount') in the JS
770770
// library because if you catch an exception from JS, you may need to manipulate
771-
// the refcount manually not to leak memory. What you need to do is different
772-
// depending on the kind of EH you use
773-
// (https://github.com/emscripten-core/emscripten/issues/17115).
771+
// the refcount manually not to leak memory.
774772
//
775773
// See test_EXPORT_EXCEPTION_HANDLING_HELPERS in test/test_core.py for an
776774
// example usage.

test/codesize/test_codesize_cxx_except.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 23309,
3-
"a.out.js.gz": 9122,
2+
"a.out.js": 23315,
3+
"a.out.js.gz": 9125,
44
"a.out.nodebug.wasm": 171263,
55
"a.out.nodebug.wasm.gz": 57315,
6-
"total": 194572,
7-
"total_gz": 66437,
6+
"total": 194578,
7+
"total_gz": 66440,
88
"sent": [
99
"__cxa_begin_catch",
1010
"__cxa_end_catch",

test/codesize/test_codesize_cxx_mangle.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"a.out.js": 23359,
3-
"a.out.js.gz": 9142,
2+
"a.out.js": 23365,
3+
"a.out.js.gz": 9145,
44
"a.out.nodebug.wasm": 235290,
55
"a.out.nodebug.wasm.gz": 78917,
6-
"total": 258649,
7-
"total_gz": 88059,
6+
"total": 258655,
7+
"total_gz": 88062,
88
"sent": [
99
"__cxa_begin_catch",
1010
"__cxa_end_catch",

test/test_core.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,18 +1551,8 @@ class myexception : public exception {
15511551
} catch(p) {
15521552
// Because we are catching and handling the exception in JS, the normal
15531553
// exception catching C++ code doesn't kick in, so we need to make sure we free
1554-
// the exception, if necessary. By incrementing and decrementing the refcount
1555-
// we trigger the free'ing of the exception if its refcount was zero.
1556-
#ifdef __USING_EMSCRIPTEN_EXCEPTION__
1557-
// FIXME Currently Wasm EH and Emscripten EH increases
1558-
// refcounts in different places. Wasm EH sets the refcount to
1559-
// 1 when throwing, and decrease it in __cxa_end_catch.
1560-
// Emscripten EH sets the refcount to 0 when throwing, and
1561-
// increase it in __cxa_begin_catch, and decrease it in
1562-
// __cxa_end_catch. Fix this inconsistency later.
1563-
// https://github.com/emscripten-core/emscripten/issues/17115
1564-
incrementExceptionRefcount(p);
1565-
#endif
1554+
// the exception, if necessary. By decrementing the refcount we trigger the
1555+
// free'ing of the exception.
15661556
out(getExceptionMessage(p).toString());
15671557
decrementExceptionRefcount(p);
15681558
}

0 commit comments

Comments
 (0)