Skip to content

[EH] Make std::terminate() work with EH #16921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions system/lib/libcxx/include/exception
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@ unexpected_handler get_unexpected() noexcept;
typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler f ) noexcept;
terminate_handler get_terminate() noexcept;
#ifdef __USING_WASM_EXCEPTIONS__
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to add a comment here, or just point people to libcxxabi for an explanation. Unless that's obvious enough?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not sure if it would be OK to copy-paste the same comment four times. Did that anyway.

// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup
// // from which std::__terminate is called again, causing an infinite loop
[[noreturn]] void terminate();
#else
[[noreturn]] void terminate() noexcept;
#endif

bool uncaught_exception() noexcept;
int uncaught_exceptions() noexcept; // C++17
Expand Down Expand Up @@ -128,7 +134,13 @@ _LIBCPP_NORETURN _LIBCPP_FUNC_VIS void unexpected();
typedef void (*terminate_handler)();
_LIBCPP_FUNC_VIS terminate_handler set_terminate(terminate_handler) _NOEXCEPT;
_LIBCPP_FUNC_VIS terminate_handler get_terminate() _NOEXCEPT;
#ifdef __USING_WASM_EXCEPTIONS__
// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup
// // from which std::__terminate is called again, causing an infinite loop
_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void terminate();
#else
_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void terminate() _NOEXCEPT;
#endif

_LIBCPP_FUNC_VIS bool uncaught_exception() _NOEXCEPT;
_LIBCPP_FUNC_VIS _LIBCPP_AVAILABILITY_UNCAUGHT_EXCEPTIONS int uncaught_exceptions() _NOEXCEPT;
Expand Down
12 changes: 12 additions & 0 deletions system/lib/libcxxabi/src/cxa_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ get_terminate() noexcept
}

void
#ifdef __USING_WASM_EXCEPTIONS__
// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup
// from which std::__terminate is called again, causing an infinite loop
__terminate(terminate_handler func)
#else
__terminate(terminate_handler func) noexcept
#endif
{
#ifndef _LIBCXXABI_NO_EXCEPTIONS
try
Expand All @@ -71,7 +77,13 @@ __terminate(terminate_handler func) noexcept

__attribute__((noreturn))
void
#ifdef __USING_WASM_EXCEPTIONS__
// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup
// from which std::terminate is called again, causing an infinite loop
terminate()
#else
terminate() noexcept
#endif
{
#if !defined(_LIBCXXABI_NO_EXCEPTIONS) && !defined(__USING_EMSCRIPTEN_EXCEPTIONS__)
// If there might be an uncaught exception
Expand Down
6 changes: 6 additions & 0 deletions system/lib/libcxxabi/src/cxa_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ __unexpected(unexpected_handler func);

_LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN
void
#ifdef __USING_WASM_EXCEPTIONS__
// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup
// // from which std::__terminate is called again, causing an infinite loop
__terminate(terminate_handler func);
#else
__terminate(terminate_handler func) noexcept;
#endif

} // std

Expand Down
18 changes: 18 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,24 @@ class Polymorphic {virtual void member(){}};
}
''', 'exception caught: std::bad_typeid')

@with_both_eh_sjlj
def test_std_terminate(self):
# std::terminate eventually calls abort(), which is implemented with
# throwing a JS exception, which used to cause an infinite loop that
# exhausted the call stack. The reason is std::terminate is marked
# 'noexcept' in the upstream LLVM, which generates cleanuppads that call
# std::terminate in case of an unexpected second exception happens while
# aborting, and our abort() was considered as that second exception. We
# removed 'noexcept' from std::terminate signature when Wasm EH is enabled
# to avoid this issue.
err = self.do_run(r'''
#include <exception>
int main() {
std::terminate();
}
''', assert_returncode=NON_ZERO)
self.assertNotContained('Maximum call stack size exceeded', err)

def test_iostream_ctors(self):
# iostream stuff must be globally constructed before user global
# constructors, so iostream works in global constructors
Expand Down