Skip to content

[df] Release GIL around parallel event loop - #24148

Open
SamdaniSayam wants to merge 1 commit into
root-project:masterfrom
SamdaniSayam:fix/24140-deadlock
Open

SamdaniSayam wants to merge 1 commit into
root-project:masterfrom
SamdaniSayam:fix/24140-deadlock

Conversation

@SamdaniSayam

Copy link
Copy Markdown

In RDataFrame, when Python callables are used inside Define/Filter and ROOT.EnableImplicitMT() is active, the main Python thread holds the GIL while waiting in TThreadExecutor::ParallelFor. TBB worker threads therefore block forever in PyGILState_Ensure when invoking the Python callback. The event loop never completes, as reported in #24140.

This PR releases the GIL before entering the parallel loop (RunEmptySourceMT, ROOT::Internal::RDF::ProcessMT) and reacquires it afterwards. RDF::RDF::RunGraphs is adjusted the same way for the case where multiple unique computation graphs are triggered in parallel from Python. In a pure C++ context the release is a no-op (the current thread does not hold the GIL).

Validation

  • Syntax-checked all modified translation units against the current build configuration; the change compiles cleanly.
  • I was not able to run the issue's reproducer end-to-end here, so CI will be the first full verification.

AI disclosure
This fix was drafted with LLM assistance and has been thoroughly reviewed and understood by me. I take full responsibility for the change, as required by CONTRIBUTING.md.

Fixes #24140

Pure-Python callables passed to Define/Filter deadlock when
EnableImplicitMT() is used: the Python thread holds the GIL while
waiting in TThreadExecutor, and worker threads block in
PyGILState_Ensure when they call the Python callback.

Release the GIL before entering the parallel loop (and reacquire it
afterwards) so that worker threads can execute Python callables.

Fixes root-project#24140

@vepadulano vepadulano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Dear @SamdaniSayam ,

Thanks for taking interest in the project.

I was not able to run the issue's reproducer end-to-end here, so CI will be the first full verification.

Please, try to be more mindful when submitting PRs. Do not hope for the CI to be some sort of magic barrier.

See also comments inline

// Python.h must come before any other headers, it must be included before any standard header.
#if defined(R__USE_IMT) && defined(R__HAS_PYTHON_GIL)
#include <Python.h>
#endif

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is absolutely a no-go, without even looking further, it would mean that the RDataFrame library has a build-time dependency on the Python system headers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok I looked until the end of the code changes and I can see what I feared in CMakeLists.txt

Comment on lines +31 to +37
#ifdef R__HAS_PYTHON_GIL
// The calling Python thread holds the GIL while waiting in TThreadExecutor.
// Release it so that worker threads can invoke Python callables without deadlock.
PyThreadState *gilSave = nullptr;
bool const hasGIL = Py_IsInitialized() && PyGILState_Check();
if (hasGIL)
gilSave = PyEval_SaveThread();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

None of this can go in RDataFrame, at all. Usually these type of changes are delegated to the Python bindings, although in this particular case I question if they could even go there.


#include "RConfigure.h" // R__USE_IMT

// Python.h must come before any other headers, it must be included before any standard header.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why? This sentence given without context underlines a lack of understanding of the problem.

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.

[Python][RDF] Pure Python callables in Define/Filter deadlock with EnableImplicitMT

2 participants