Skip to content
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

Add release_gil_before_calling_cpp_dtor annotation for class_ #30088

Merged
merged 12 commits into from
Jan 18, 2024

Conversation

rwgk
Copy link
Contributor

@rwgk rwgk commented Jan 16, 2024

Description

The primary motivation for this PR is to support PyCLIF-pybind11: this PR fixes about 20 failures when testing PyCLIF-pybind11 Google-globally.

However, more generally, it also solves pybind/pybind11#1446.

Background:

  • PyCLIF-C-API releases the GIL when calling the destructor of a wrapped C++ object (this cannot be disabled by the user).

  • pybind11 does not.

Relevant code:

An attempt was made to remove the GIL release code from the PyCLIF-C-API gen.py code linked above, but Google-global testing made it clear that this is infeasible. There are too many use cases that critically rely on the current PyCLIF behavior. Reviewing the use cases made it obvious that pushing the GIL-release code into user code is impractical.

In contrast, adding the GIL-release feature as an option into pybind11 turned out to be relatively straightforward:

  • The core change (in pybind11.h class_::dealloc()) is to add ReleaseGIL-try-catch-ReaquireGIL-rethrow logic around the existing code calling the C++ destructor of wrapped objects.

  • All the rest is more-or-less boilerplate code to add the py:: release_gil_before_calling_cpp_dtor annotation, then use it to enable the new feature if that annotation is provided by the user.

PyCLIF-pybind11 can then simply use the annotation to replicate the established behavior of PyCLIF-C-API.

Suggested changelog entry:

@rwgk rwgk marked this pull request as ready for review January 17, 2024 22:15
} else {
detail::call_operator_delete(
v_h.value_ptr<type>(), v_h.type->type_size, v_h.type->type_align);
PyThreadState *py_ts = release_gil_before_calling_cpp_dtor ? PyEval_SaveThread() : nullptr;

Choose a reason for hiding this comment

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

What about py::gil_scoped_acquire?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the nudge! This got me into some useful back and forth experimenting. The end result I settled on is functionally equivalent to what you saw before, but the code organization is better I think (avoids the bool release_gil_before_calling_cpp_dtor). I added comments to explain why I'm not using gil_scoped_release, and that the catch (...) isn't expected to be reachable.

// std::terminate().
error_scope scope;
// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
// We could be deallocating because we are cleaning up after a Python exception.

Choose a reason for hiding this comment

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

We no longer clear the error scope in this function. Consider move the comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I rewrote the comment, so that this is first:

// NOTE: The Python error indicator needs to cleared BEFORE this function is called.

static void dealloc_release_gil_before_calling_cpp_dtor(detail::value_and_holder &v_h) {
error_scope scope;
// Intentionally not using `gil_scoped_release` because the non-simple
// version unconditionally calls `get_internals()`.

Choose a reason for hiding this comment

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

Thanks for the research!

@rwgk
Copy link
Contributor Author

rwgk commented Jan 18, 2024

NOT waiting for GitHub Actions to finish: 1. The only change to before is a rewrite of a source code comment. 2. The only prior CI failure is a frequent flake.

@rwgk rwgk merged commit ce00785 into google:main Jan 18, 2024
147 checks passed
@rwgk rwgk deleted the dtorgil_pywrapcc branch January 18, 2024 23:02
rwgk added a commit to google/clif that referenced this pull request Jan 22, 2024
For easy direct access:

* google/pybind11clif#30034

* google/pybind11clif#30087

* google/pybind11clif#30088

Note regarding the change in std_containers_copy_move_test.py:

When the test was added (cl/570839777), the undocumented expectation was that it is hyper-sensitive, but that it will be adjusted or made more permissive (similar to e.g. google3/third_party/clif/testing/python/return_value_policy_test.py;l=25;rcl=534200687) as needed.

#MIGRATION_3P_PYBIND11__DEFAULT

```
  - 24a3b1c3729401ca661efacfbbd13a83117e1bae Add `case return_value_policy::_clif_automatic` in type_c... by Ralf W. Grosse-Kunstleve <rwgk@google.com>
  - 015834b13a99d233c15406c36a3a44d27ebfc846 Change `array_caster` in pybind11/stl.h to support value ... by Ralf W. Grosse-Kunstleve <rwgk@google.com>
  - ce00785ef877f0bb1dba10115f00366111583b3c Add `release_gil_before_calling_cpp_dtor` annotation for ... by Ralf W. Grosse-Kunstleve <rwgk@google.com>
```

PiperOrigin-RevId: 599883612
rwgk added a commit to google/clif that referenced this pull request Jan 22, 2024
This one-line production code change builds on google/pybind11clif#30088 (imported via cl/599883612).

PyCLIF-pybind11 TGP failures fixed: b/287289622#comment51 (21 Passing, 20 unique)

GitHub testing: #87

PiperOrigin-RevId: 600509355
@rwgk rwgk added the could be backported Could be backported to pybind11 master label Jan 25, 2024
rwgk added a commit to rwgk/pybind11 that referenced this pull request Feb 15, 2025
…if#30092 (minor fixes).

Note for completeness:

These are identical to the current versions on the pybind11clif main branch (@ commit 4841661df5daf26ecdedaace54e64d0782e63f64):

* test_class_release_gil_before_calling_cpp_dtor.cpp
* test_class_release_gil_before_calling_cpp_dtor.py
rwgk added a commit to pybind/pybind11 that referenced this pull request Feb 16, 2025
)

* Backport of google/pybind11clif#30088 (main PR) and google/pybind11clif#30092 (minor fixes).

Note for completeness:

These are identical to the current versions on the pybind11clif main branch (@ commit 4841661df5daf26ecdedaace54e64d0782e63f64):

* test_class_release_gil_before_calling_cpp_dtor.cpp
* test_class_release_gil_before_calling_cpp_dtor.py

* Fix potential data race in test_class_release_gil_before_calling_cpp_dtor.cpp

The original intent was to let the singleton leak, but making that tread-safe is slightly more involved than this solution. It's totally fine in this case if the RegistryType destructor runs on process teardown.

See also: #5522 (comment)

---------

Co-authored-by: Ralf W. Grosse-Kunstleve <rwgk@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
could be backported Could be backported to pybind11 master
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants