Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ cocoa=ON
davix=OFF
minuit2_omp=OFF
x11=OFF
ROOT_CTEST_CUSTOM_FLAGS=-R ^pyunittests-bindings-cppjit-
21 changes: 4 additions & 17 deletions .github/workflows/root-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,10 @@
matrix:
# Specify platform + optional build option overrides
# Build options: https://root.cern/install/build_from_source/#all-build-options
platform: [mac26, mac27, mac-beta]
# TEMP: only mac-beta, to measure the sporadic cppjit test_datatypes crash
platform: [mac-beta]
arch: [ARM64]
include:
- platform: mac15
arch: X86
- platform: mac-beta
is_special: true

Expand All @@ -132,7 +131,7 @@
- self-hosted
- ${{ matrix.platform }}

name: |

Check failure on line 134 in .github/workflows/root-ci.yml

View workflow job for this annotation

GitHub Actions / lint-action-files

property "overrides" is not defined in object type {arch: string; is_special: bool; platform: string}
${{ matrix.platform }} ${{ matrix.arch }}
${{ (github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' && join( matrix.overrides, ', ' )) || '' }}

Expand All @@ -151,8 +150,8 @@
if: github.event_name == 'pull_request'
env:
GITHUB_PR_ORIGIN: ${{ github.event.pull_request.head.repo.clone_url }}
OVERRIDES: ${{ join( matrix.overrides, ' ') }}

Check failure on line 153 in .github/workflows/root-ci.yml

View workflow job for this annotation

GitHub Actions / lint-action-files

property "overrides" is not defined in object type {arch: string; is_special: bool; platform: string}
run: |

Check failure on line 154 in .github/workflows/root-ci.yml

View workflow job for this annotation

GitHub Actions / lint-action-files

"github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
[ -d "${VIRTUAL_ENV_DIR}" ] && source ${VIRTUAL_ENV_DIR}/bin/activate
echo "Python is now $(which python3) $(python3 --version)"
src/.github/workflows/root-ci-config/build_root.py \
Expand Down Expand Up @@ -184,7 +183,7 @@
- name: Nightlies and push to branch
if: github.event_name == 'schedule' || github.event_name == 'push'
env:
OVERRIDES: ${{ github.event_name == 'push' && format('{0} {1} {2}', '--overrides', env.GLOBAL_OVERRIDES, join( matrix.overrides, ' ')) || '' }}

Check failure on line 186 in .github/workflows/root-ci.yml

View workflow job for this annotation

GitHub Actions / lint-action-files

property "overrides" is not defined in object type {arch: string; is_special: bool; platform: string}
BASE_REF: ${{ github.event_name == 'push' && github.ref_name || inputs.ref_name }}
# For pushes, build exactly the pushed commit, not wherever the
# branch happens to be when the job starts (issue #14061).
Expand Down Expand Up @@ -246,13 +245,7 @@
build-windows:
# For any event that is not a PR, the CI will always run. In PRs, the CI
# can be skipped if the tag [skip-ci] or [skip ci] is written in the title.
if: |
(github.repository_owner == 'root-project' && github.event_name != 'pull_request') ||
(github.event_name == 'pull_request' && !(
contains(github.event.pull_request.title, '[skip-ci]') ||
contains(github.event.pull_request.title, '[skip ci]') ||
contains(github.event.pull_request.labels.*.name, 'skip ci')
))
if: false # TEMP: only mac-beta runs, see build-macos

permissions:
contents: read
Expand Down Expand Up @@ -372,13 +365,7 @@
build-linux:
# For any event that is not a PR, the CI will always run. In PRs, the CI
# can be skipped if the tag [skip-ci] or [skip ci] is written in the title.
if: |
(github.repository_owner == 'root-project' && github.event_name != 'pull_request') ||
(github.event_name == 'pull_request' && !(
contains(github.event.pull_request.title, '[skip-ci]') ||
contains(github.event.pull_request.title, '[skip ci]') ||
contains(github.event.pull_request.labels.*.name, 'skip ci')
))
if: false # TEMP: only mac-beta runs, see build-macos

permissions:
contents: read
Expand Down
8 changes: 5 additions & 3 deletions bindings/pyroot/cppjit/src/cpyrt/CPPMethod.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,12 @@ inline PyObject* cpyrt::CPPMethod::ExecuteFast(void* self, ptrdiff_t offset,
result = nullptr;
}

// TODO: covers the PyException throw case, which does not seem to work on
// Windows, so instead leaves the error be
#ifdef _WIN32
#if CPYRT_CALLBACK_NOTHROW
// the callback wrappers leave the Python error set instead of throwing a
// PyException (see CommonDefs.h), so pick it up here, as the catch above
// would have, and flag it as the callee's error for overload resolution
if (PyErr_Occurred()) {
ctxt->fFlags |= CallContext::kPyException;
if (ResultIsPyObject())
Py_XDECREF(result);
result = nullptr;
Expand Down
19 changes: 19 additions & 0 deletions bindings/pyroot/cppjit/src/cpyrt/CommonDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,23 @@

#endif

// How JIT-compiled callback wrappers (function pointers, std::function, and
// virtual overrides implemented in Python) report a failed Python call. The
// default is to throw a cpyrt::PyException from the wrapper, which unwinds
// through the C++ frames in between (JIT-compiled, or built by another
// compiler) up to the try block in CPPMethod::ExecuteFast. That unwind hangs
// on Windows, and on Apple ARM64 exceptions through JIT-compiled frames are
// known not to work (the test suite skips the tests that catch a PyException
// in C++ there) and are the prime suspect for the sporadic crash of
// test_datatypes on the macOS beta CI runner. On those platforms the wrapper
// therefore leaves the Python error set and returns a default value instead;
// the C++ code keeps running and ExecuteFast turns the pending error into a
// failed call once the C++ call returns. The price is that C++ code can not
// catch the Python error as a PyException.
#if defined(_WIN32) || (defined(__APPLE__) && defined(__aarch64__))
#define CPYRT_CALLBACK_NOTHROW 1
#else
#define CPYRT_CALLBACK_NOTHROW 0
#endif

#endif // !CPYRT_COMMONDEFS_H
11 changes: 8 additions & 3 deletions bindings/pyroot/cppjit/src/cpyrt/Utility.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,12 @@ void cpyrt::Utility::ConstructCallbackPreamble(
}
code << " } catch(int) {\n"
<< " for (auto pyarg : pyargs) Py_XDECREF(pyarg);\n"
#if CPYRT_CALLBACK_NOTHROW
// error is set; see CPYRT_CALLBACK_NOTHROW in CommonDefs.h
<< (isVoid ? " return;\n" : " return ret;\n")
#else
<< " cppjit::cpyrt::PyException pyexc; throw pyexc;\n"
#endif
<< " }\n";
}
}
Expand Down Expand Up @@ -1095,9 +1100,9 @@ void cpyrt::Utility::ConstructCallbackReturn(const std::string& retType,
if (isPtr)
code << " }\n";
code << " if (!cOk) {" // assume error set when converter failed
// TODO: On Windows, throwing a C++ exception here makes the code hang; leave
// the error be which allows at least one layer of propagation
#ifdef _WIN32
// see CPYRT_CALLBACK_NOTHROW in CommonDefs.h: leave the error be, which allows
// at least one layer of propagation
#if CPYRT_CALLBACK_NOTHROW
" /* do nothing */ }\n"
#else
" cppjit::cpyrt::PyException pyexc; throw pyexc; }\n"
Expand Down
11 changes: 9 additions & 2 deletions bindings/pyroot/cppjit/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ endif()

function(CPPJIT_ADD_PYTEST name)
cmake_parse_arguments(ARG
"DROP_ROOTMAP" "" "DICTS;FIXTURES_SETUP;FIXTURES_REQUIRED" ${ARGN})
"DROP_ROOTMAP" "SUFFIX" "DICTS;FIXTURES_SETUP;FIXTURES_REQUIRED" ${ARGN})

set(extra)
if(ARG_DICTS)
Expand All @@ -60,7 +60,7 @@ function(CPPJIT_ADD_PYTEST name)
endif()

string(REGEX REPLACE "[_]" "-" good_name "${name}")
set(test_name pyunittests-bindings-cppjit-${good_name})
set(test_name pyunittests-bindings-cppjit-${good_name}${ARG_SUFFIX})

ROOT_ADD_TEST(${test_name}
# Upstream marks platform xfails without explicit strict=; the in-tree
Expand Down Expand Up @@ -115,6 +115,13 @@ CPPJIT_ADD_PYTEST(test_basic_api
FIXTURES_REQUIRED cppjit_example01_dict_fixture)
CPPJIT_ADD_PYTEST(test_datatypes
FIXTURES_REQUIRED cppjit_datatypes_dict_fixture)
# TEMP: 300 more copies of test_datatypes, run by ctest in parallel in this
# directory like the real suite does, to measure its sporadic crash on the
# mac-beta CI runner (about 3% of runs).
foreach(i RANGE 1 300)
CPPJIT_ADD_PYTEST(test_datatypes SUFFIX -rep${i}
FIXTURES_REQUIRED cppjit_datatypes_dict_fixture)
endforeach()

CPPJIT_ADD_PYTEST(test_api)
CPPJIT_ADD_PYTEST(test_boost)
Expand Down
Loading