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

gh-95348: Split clear_weakref into two functions #95449

Closed
wants to merge 6 commits into from

Conversation

PurityLake
Copy link
Contributor

@PurityLake PurityLake commented Jul 29, 2022

Fixes #95348

Moves the removal of the passed in reference from a list to a function called remove_weakref adding a parameter called list that is a list of weak references. Then to abstract the need to get this list a function remove_weakref_from_referent has been added to automatically pass the list to the remove_weakref.

This is added to make the process more transparent as to what it is doing because as the issue mentions that where clear_weakref is used it isn't clear what it is doing.

Functions added:

  • remove_weakref
  • remove_weakref_from_referent

remove_weakref shouldn't be called by itself as remove_weakref_from_referent will call it and pass the list of references.

Also remove_weakref_from_referent should always be called after `clear_weakref, otherwise a segmentation fault will occur.

3 tests were found to have failed but seem to occur even without changes to code:

  • test_strftime
  • test_strptime
  • test_time

@ghost
Copy link

ghost commented Jul 29, 2022

All commit authors signed the Contributor License Agreement.
CLA signed

@bedevere-bot
Copy link

Most changes to Python require a NEWS entry.

Please add it using the blurb_it web app or the blurb command-line tool.

Copy link
Member

@ericsnowcurrently ericsnowcurrently left a comment

Choose a reason for hiding this comment

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

I've left some comments.

Most importantly, the weakref should be removed from the weaklist before clear_weakref() is called, not after.

then the weakref list itself (and thus the value of *list) will
end up being set to NULL. */
*list = self->wr_next;
self->wr_object = Py_None;
Copy link
Member

Choose a reason for hiding this comment

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

Clearing wr_object should still happen in clear_weakref(), not remove_weakref().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The thing is, the program segfaults when I reverse the order of remove before clear

weaklist_remove_weakref(list, *list);
clear_weakref(*list);

causes a segfault for me.

if (callback != NULL) {
Py_DECREF(callback);
self->wr_callback = NULL;
}
}

/* This function removes the pass in reference from the list of weak
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/* This function removes the pass in reference from the list of weak
/* This function removes the passed-in reference from the list of weak

Comment on lines 49 to 51
/* This function is responsible for clearing the callback slot. When using this
* function it must be called before calling remove_weakref_from_referent,
* otherwise a segmentation fault will occur at build time.
Copy link
Member

Choose a reason for hiding this comment

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

The function should be clearing the weakref object. So:

Suggested change
/* This function is responsible for clearing the callback slot. When using this
* function it must be called before calling remove_weakref_from_referent,
* otherwise a segmentation fault will occur at build time.
/* This function clears the passed-in reference. It must have been removed
* from the weaklist already. (See weaklist_remove_weakref().)

Comment on lines 65 to 67
* references for the referent. This should be called from
* remove_weakref_fromt_referent as it will pass the required list
* argument.
Copy link
Member

Choose a reason for hiding this comment

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

There is no need for that last sentence.

Suggested change
* references for the referent. This should be called from
* remove_weakref_fromt_referent as it will pass the required list
* argument.
* references for the referent.

self->wr_next = NULL;
}

/* This function removes the pass in reference from the list of weak references
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/* This function removes the pass in reference from the list of weak references
/* This function removes the passed-in reference from the list of weak references

end up being set to NULL. */
*list = self->wr_next;
}
self->wr_object = Py_None;
Copy link
Member

Choose a reason for hiding this comment

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

This belongs in clear_weakref().

Suggested change
self->wr_object = Py_None;

Comment on lines 1 to 4
Split ``clear_weakref`` into two functions: ``clear_weakref``
and ``remove_weakref``. Abstracts ``remove_weakref`` with
``remove_weakref_from_referent`` in weakrefobject.c.
Patch by Robert O'Shea.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure that a NEWS entry is warranted. If this PR changes any behavior then something has gone wrong, so there isn't anything to communicate to users. This change is strictly about slightly improving the clarity of the Objects/weakrefobject.c code. That sort of minor maintainability improvement typically does not get a NEWS entry.

* argument.
*/
static void
remove_weakref(PyWeakReference *self, PyWeakReference **list)
Copy link
Member

Choose a reason for hiding this comment

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

This function is an operation on a weaklist, rather than on a single weakref, so let's change the name and signature accordingly:

Suggested change
remove_weakref(PyWeakReference *self, PyWeakReference **list)
weaklist_remove_ref(PyWeakReference **list, PyWeakReference *ref)

Comment on lines 90 to 91
* for the referent. This should be called after clear_weakref, otherwise a
* segmentation fault will occur at build time.
Copy link
Member

Choose a reason for hiding this comment

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

We don't need to mention segfaulting.

Suggested change
* for the referent. This should be called after clear_weakref, otherwise a
* segmentation fault will occur at build time.
* for the referent. This should always be called before clear_weakref().

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@@ -958,8 +983,11 @@ PyObject_ClearWeakRefs(PyObject *object)
/* Remove the callback-less basic and proxy references */
if (*list != NULL && (*list)->wr_callback == NULL) {
clear_weakref(*list);
if (*list != NULL && (*list)->wr_callback == NULL)
remove_weakref_from_referent(*list);
Copy link
Member

Choose a reason for hiding this comment

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

Here we already have the list, so we should use weaklist_remove_weakref() directly:

Suggested change
remove_weakref_from_referent(*list);
weaklist_remove_weakref(list, *list);

clear_weakref(*list);
remove_weakref_from_referent(*list);
Copy link
Member

Choose a reason for hiding this comment

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

Likewise with weaklist_remove_weakref().

@@ -972,6 +1000,7 @@ PyObject_ClearWeakRefs(PyObject *object)

current->wr_callback = NULL;
clear_weakref(current);
remove_weakref_from_referent(current);
Copy link
Member

Choose a reason for hiding this comment

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

Likewise with weaklist_remove_weakref().

@@ -1002,6 +1031,7 @@ PyObject_ClearWeakRefs(PyObject *object)
}
current->wr_callback = NULL;
clear_weakref(current);
remove_weakref_from_referent(current);
Copy link
Member

Choose a reason for hiding this comment

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

Likewise with weaklist_remove_weakref().

@@ -1036,5 +1066,6 @@ _PyStaticType_ClearWeakRefs(PyTypeObject *type)
weaklist before clearing its wr_object and wr_callback.
That is how we're able to loop over the list. */
clear_weakref((PyWeakReference *)*list);
remove_weakref_from_referent((PyWeakReference *)*list);
Copy link
Member

Choose a reason for hiding this comment

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

Likewise with weaklist_remove_weakref().

@PurityLake
Copy link
Contributor Author

@ericsnowcurrently

The issue with calling clear first is that wr_object is set to Py_None. Making it impossible to get the list after the fact.

@ericsnowcurrently
Copy link
Member

(This advice applies to everyone, not just you. 😄)

FYI, there are downsides to force-pushing once other people have started interacting with a PR:

  • it causes the reviewed commits to go away, making it harder to re-review
  • if you make edits to earlier commits, It makes it much harder to see what changes were made in response to a review
  • earlier CI runs mostly become inaccessible
  • it's a slight inconvenience for anyone that pulls down your branch

So I recommend that, as soon as others are interacting with your PR (e.g. reviewing), you should avoid force-pushing. If you need to update the base branch (e.g. main) then use "merge" instead.

All that said, force-pushing is usually fine up to the point that other people start looking at the PR. Personally, I use force-push extensively, but try to stick to the above advice (once others start participating).

@ericsnowcurrently
Copy link
Member

The issue with calling clear first is that wr_object is set to Py_None. Making it impossible to get the list after the fact.

That only matters if you use remove_weakref_from_referent(). Instead, use weaklist_remove_weakref().

@PurityLake
Copy link
Contributor Author

I have made the requested changes; please review again

This should be correct now.

Apologies for the issue with the force push.

@bedevere-bot
Copy link

Thanks for making the requested changes!

@ericsnowcurrently: please review the changes made to this pull request.

@PurityLake PurityLake closed this Dec 25, 2022
@PurityLake PurityLake deleted the fix-issue-95348 branch December 25, 2022 02:16
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.

clear_weakref() in Objects/weakrefobject.c is Confusing
4 participants