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

[smart_holder] Unique ptr deleter roundtrip tests and fix #4921

Merged
merged 13 commits into from
Nov 8, 2023

Conversation

iwanders
Copy link
Contributor

@iwanders iwanders commented Nov 5, 2023

Description

@rwgk , as promised; followup to #4850, this PR ensures custom deleters are propagated correctly and keep their state.

I only saw the note about conventional commits when I filed this PR, so their descriptions aren't that good, the work is mostly split out though in individual commits;

  • 7cc1e1f a failing unit test was introduced showing the deleter got lost.
  • 5b00921 subsequently fixes that.
  • ad7eaa4 adds a unit test for std::unique_ptr<atyp const, custom_deleter>, similar how the other default delete unit tests had that.
  • a49a7ce Fixes that new unit test for const atyp.
  • eb05f97 Removes the assignment in case the 'use_del_fun' is false, I don't think this is necessary as the code in smart_holder_poc guarantees that the del_fun is used in case there's anything but the default deleter, and for the default deleters no special handling is necessary.

Unit tests make check all pass, I ran clang format 13 on the files I touched, but didn't see a make format or anything along those lines, so it wouldn't surprise me if some linters still fail.

Happy to iterate, but feel free to push commits to this branch as you see fit. Filing as draft first before involving other code owners.

Somewhat related observation

One thing that stood out to me was this line;

delete static_cast<T *>(raw_ptr);

I feel that shouldn't be doing a delete, but instead do:

   std::default_delete<T>{}(raw_ptr);

Because the trait system that std::default_delete provides can be considered an extension point of the standard library, and calling delete would bypass any specializations that are provided.

Suggested changelog entry:

Custom deleters in unique_ptrs are stored and extracted correctly from the smart holder.

Feels like there's still a gap around the raw pointer flavour, but this at least
makes the unit test of the previous commit succeed.
Currently failing, custom deleter is lost.
Unit test from the previous commit passes.
At the construction of the smart holder, it is either a del_fun, or a default constructed deleter, so this complexity is unnecessary.
@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch 2 times, most recently from 1bc9821 to 711b158 Compare November 5, 2023 17:58
Clang 3.6 requires the extra constructors in the custom_deleter.
@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch from 711b158 to 8800800 Compare November 5, 2023 19:07
include/pybind11/detail/smart_holder_type_casters.h Outdated Show resolved Hide resolved
include/pybind11/detail/smart_holder_type_casters.h Outdated Show resolved Hide resolved
include/pybind11/detail/smart_holder_type_casters.h Outdated Show resolved Hide resolved
tests/test_class_sh_basic.cpp Outdated Show resolved Hide resolved
tests/test_class_sh_basic.cpp Outdated Show resolved Hide resolved
@rwgk
Copy link
Collaborator

rwgk commented Nov 6, 2023

@iwanders wrote (in the PR description):


One thing that stood out to me was this line;

delete static_cast<T *>(raw_ptr);

I feel that shouldn't be doing a delete, but instead do:

   std::default_delete<T>{}(raw_ptr);

I agree. I didn't think deeply at all about that line.

Do you want to fix that in a separate small PR on the side?

@iwanders
Copy link
Contributor Author

iwanders commented Nov 6, 2023

Oh, oops, I forgot std::make_unique is c++14 and not 11 😬 let me fix that.

Edit, should be fixed with 25c2e40

@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch 2 times, most recently from 6753d03 to 25c2e40 Compare November 6, 2023 23:48
@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch from 25c2e40 to 2c4fbda Compare November 7, 2023 00:05
@iwanders
Copy link
Contributor Author

iwanders commented Nov 7, 2023

Previous CI failed on c++20, the explicit string constructor is a requirement if we remove the default constructor, otherwise we can't instantiate the unique pointer in the unit test. Quick minimal example with compile error.

And force pushed again to fix clang tidy with f2f87f0, 🤞 should be in the clear now...

@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch from 2c4fbda to f2f87f0 Compare November 7, 2023 00:19
Copy link
Collaborator

@rwgk rwgk left a comment

Choose a reason for hiding this comment

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

I'll run some internal testing with this PR as is. My suggestions don't change anything substantial. Thanks!

include/pybind11/detail/smart_holder_type_casters.h Outdated Show resolved Hide resolved
include/pybind11/detail/smart_holder_type_casters.h Outdated Show resolved Hide resolved
tests/test_class_sh_basic.cpp Outdated Show resolved Hide resolved
@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch from 08a5015 to 5c062eb Compare November 7, 2023 13:07
@rwgk
Copy link
Collaborator

rwgk commented Nov 7, 2023

The latest commits all look good to me, thanks!

I'm not surprised by the test failures, most likely they're very easy to fix, like this:

assert re.match("passenger(_MvCtor)*_MvCtor", m.get_mtxt(recycled))

@rwgk
Copy link
Collaborator

rwgk commented Nov 7, 2023

I'll run some internal testing with this PR as is. My suggestions don't change anything substantial. Thanks!

Unit testing with all sanitizers in the Google toolchain passes. (as expected)

Global testing just changing pybind11 passes. (as expected; there are only a handful of smart_holder uses this way)

None of the remaining PyCLIF-pybind11 global test failures is fixed by this PR. (I had some wishful thinking some may be fixed.) — This was a "rerun" of global testing, after adding in this PR.

Fresh global testing — with this PR included from the start — with PyCLIF-pybind11 exercises the smart_holder functionality in vastly more situations, but it is very expensive. I'll do that next weekend at the latest. (It might need a little bit of trickery to determine for certain if this PR fixed latent bugs.)

@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch from c8214f5 to 21bbcd0 Compare November 7, 2023 23:38
@iwanders
Copy link
Contributor Author

iwanders commented Nov 7, 2023

I'm not surprised by the test failures, most likely they're very easy to fix, like this:

Awesome, thanks for that suggestion, I was surprised tbh, I would've expected the amount of move constructors to be equal on between the compilers, but guess there's still some variation in how they implement the language. Pipeline is running with that suggested fix now 🤞

None of the remaining PyCLIF-pybind11 global test failures is fixed by this PR. (I had some wishful thinking some may be fixed.) — This was a "rerun" of global testing, after adding in this PR.

Heh, well, I don't think too many people use a unique pointer with a custom deleter, so not too surprised. Regardless, it's a good improvement and I had fun doing some C++ for a good cause :)

@iwanders
Copy link
Contributor Author

iwanders commented Nov 8, 2023

This looks like a flakey test, at least, we didn't see this error before; https://github.com/pybind/pybind11/actions/runs/6791647051/job/18463525701?pr=4921

         FAILED test_iostream.py::test_flush - AssertionError: assert '### TestFact...(not flushed)' == '(not flushed)'
           + ### TestFactory2 @ 0x227bbf9d0a0 destroyed
             (not flushed)
         = 1 failed, 760 passed, 50 skipped, 26 xfailed, 7 xpassed in 200.07s (0:03:20) =

Going to incorporate the clang tidy noexcept suggestion, but thought I'd point it out in case it doesn't occur again.

@iwanders iwanders force-pushed the unique_ptr_destructor_roundtrip branch from 21bbcd0 to 8a75f12 Compare November 8, 2023 00:08
@rwgk
Copy link
Collaborator

rwgk commented Nov 8, 2023 via email

@rwgk
Copy link
Collaborator

rwgk commented Nov 8, 2023

Looks like it's working now (121 successful already, no failures).

Could you do me a small favor and click update branch here, or git merge smart_holder, git push interactively? (If I click the button here it'll mark me a collaborator just for that. I think it's better if it's clear that it's all your work.)

@iwanders iwanders marked this pull request as ready for review November 8, 2023 00:43
@iwanders
Copy link
Contributor Author

iwanders commented Nov 8, 2023

Done, I've also removed the 'draft' label, good to go in from my side, but if you want to wait until your big weekend run of tests that's fine by me of course, no rush.

@rwgk
Copy link
Collaborator

rwgk commented Nov 8, 2023

Done, I've also removed the 'draft' label, good to go in from my side, but if you want to wait until your big weekend run of tests that's fine by me of course, no rush.

Thanks for the great work!

I'll wait for the GHA to finish, then merge. For the weekend run I'd want to include this PR from the start anyway. Big maybe: if I see a reason to back it out for a rerun (to see if that resolves any failures), I can do that easily with a local revert. In the worst case (very much unexpected) we can revert here, or fix forward.

@rwgk rwgk merged commit e5ce963 into pybind:smart_holder Nov 8, 2023
152 checks passed
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Nov 8, 2023
@rwgk rwgk removed the needs changelog Possibly needs a changelog entry label Nov 8, 2023
@iwanders
Copy link
Contributor Author

iwanders commented Nov 8, 2023

Please do let me know if any of the test runs uncover any issues, happy to help tackle them if related to this piece of work.

@rwgk
Copy link
Collaborator

rwgk commented Nov 13, 2023

For the weekend run I'd want to include this PR from the start anyway.

Done. No surprises.

Big maybe: if I see a reason to back it out for a rerun (to see if that resolves any failures), I can do that easily with a local revert.

I didn't really see a reason, but for completeness, and because it was easy, I tested with the local revert anyway. Very clearly: this PR did not break anything.

In summary (see #4921 (comment)): This PR did not fix anything in the wild, but also did not break anything.

I'm very thankful though that this bug was fixed, so that we no longer have it hanging over our heads as an uncertainty.

@iwanders
Copy link
Contributor Author

I didn't really see a reason, but for completeness, and because it was easy, I tested with the local revert anyway. Very clearly: this PR did not break anything.

Thanks for letting me know, glad we didn't discover any new bugs.

I'm very thankful though that this bug was fixed, so that we no longer have it hanging over our heads as an uncertainty.

Agreed, this is one of those 'would lead to an annoying bug in production' type of things.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants