Skip to content

[cppjit] Do not throw out of callback wrappers on Apple ARM64 - #23741

Draft
guitargeek wants to merge 2 commits into
root-project:masterfrom
guitargeek:cppjit-callback-error-no-throw-apple
Draft

guitargeek wants to merge 2 commits into
root-project:masterfrom
guitargeek:cppjit-callback-error-no-throw-apple

Conversation

@guitargeek

Copy link
Copy Markdown
Contributor

When a Python callable that was passed to C++ as a function pointer, a std::function or a virtual override fails, the JIT-compiled wrapper around it throws a cpyrt::PyException. That exception unwinds through the C++ frames in between, which are JIT-compiled or come from a library built by a different compiler, up to the try block in CPPMethod::ExecuteFast. On Windows this unwind hangs and the wrapper instead leaves the Python error set, to be picked up after the C++ call returns.

Exceptions through JIT-compiled frames do not work on Apple ARM64 either: the test suite skips the tests that catch a PyException in C++ there ("JIT exceptions can not be caught in JITed code on Mac ARM"), and test27/test28 of test_datatypes, which drive this path many times, are marked as crashing on macOS. test26_function_pointers drives it exactly once, and it is the test during which test_datatypes crashes sporadically in the garbage collector on the mac-beta ARM64 CI runner (and nowhere else). This is a guess at that crash: use the Windows mechanism on Apple ARM64 as well.

The platform choice moves into one macro, CPYRT_CALLBACK_NOTHROW, used by both throw sites in the generated wrapper code (the argument conversion failure path used to throw on Windows too) and by the post-call check in ExecuteFast. That check now also sets the kPyException flag, so the error counts as the callee's for overload resolution and keeps its type instead of being wrapped in a TypeError about failed overloads.

Verified on Linux with the no-throw path forced on: 24 of the 26 cppjit test files pass, including test_datatypes with its callable tests; the two that fail, test_crossinheritance test08 and test_concurrent test04, are the ones where C++ catches the PyException itself, which is exactly what this mode gives up and what the suite already skips on Mac ARM. With the intended platform condition the full cppjit suite passes on Linux.

🤖 Done with the help of AI

When a Python callable that was passed to C++ as a function pointer, a
std::function or a virtual override fails, the JIT-compiled wrapper around
it throws a cpyrt::PyException. That exception unwinds through the C++
frames in between, which are JIT-compiled or come from a library built by a
different compiler, up to the try block in CPPMethod::ExecuteFast. On
Windows this unwind hangs and the wrapper instead leaves the Python error
set, to be picked up after the C++ call returns.

Exceptions through JIT-compiled frames do not work on Apple ARM64 either:
the test suite skips the tests that catch a PyException in C++ there
("JIT exceptions can not be caught in JITed code on Mac ARM"), and
test27/test28 of test_datatypes, which drive this path many times, are
marked as crashing on macOS. test26_function_pointers drives it exactly
once, and it is the test during which test_datatypes crashes sporadically
in the garbage collector on the mac-beta ARM64 CI runner (and nowhere
else). This is a guess at that crash: use the Windows mechanism on Apple
ARM64 as well.

The platform choice moves into one macro, CPYRT_CALLBACK_NOTHROW, used by
both throw sites in the generated wrapper code (the argument conversion
failure path used to throw on Windows too) and by the post-call check in
ExecuteFast. That check now also sets the kPyException flag, so the error
counts as the callee's for overload resolution and keeps its type instead
of being wrapped in a TypeError about failed overloads.

Verified on Linux with the no-throw path forced on: 24 of the 26 cppjit
test files pass, including test_datatypes with its callable tests; the two
that fail, test_crossinheritance test08 and test_concurrent test04, are the
ones where C++ catches the PyException itself, which is exactly what this
mode gives up and what the suite already skips on Mac ARM. With the
intended platform condition the full cppjit suite passes on Linux.

🤖 Done with the help of AI
Measurement commit for the sporadic crash of test_datatypes on the mac-beta
ARM64 runner: disable the Windows and Linux jobs, drop the other macOS
platforms from the matrix, and restrict ctest on mac-beta to the cppjit
tests with --repeat until-fail:20. At the observed rate of roughly one
crash in six runs, 20 repetitions of test_datatypes fail with near
certainty without the fix and pass with it.

Not to be merged.

🤖 Done with the help of AI
@github-actions

Copy link
Copy Markdown

Test Results

26 tests   26 ✅  2m 42s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit 1dc38fb.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant