GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-102381: don't call watcher callback with dead object by carljm · Pull Request #102382 · python/cpython

Conversation

@carljm

@carljm carljm commented Mar 2, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Fixes #102381, #102351 and #102385.

Ensure that func_dealloc, code_dealloc, and dict_dealloc temporarily incref the function/code object/dict before calling the watcher callback, so we don't pass around dead objects.

A watcher callback could take a reference to the object; check for this and allow it to be resurrected in that case, rather than going ahead and freeing it (making the taken reference a pointer to freed memory.)

Also, don't pass the object itself to PyErr_WriteUnraisable if the callback raises, allowing an unraisablehook to take a reference to the object and resurrect it and, in the worst case, causing an infinite loop. Safer to construct an informative debug string and pass that as context to PyErr_WriteUnraisable. This actually provides more debug information than before, since the new message clarifies that the error occurred in a watcher callback for the mentioned object.

  • Issue: raising an exception in a dict/code/function watcher callback will segfault on a DEALLOCATED event #102381

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

itamaro, arhadthedev, and corona10 reacted with thumbs up emoji
All reactions
  • 👍 3 reactions
@bedevere-bot bedevere-bot mentioned this pull request Mar 2, 2023
raising an exception in a dict/code/function watcher callback will segfault on a DEALLOCATED event #102381
Closed
@bedevere-bot bedevere-bot added the awaiting core review label Mar 2, 2023
@carljm carljm changed the title gh-102381: don't call dict watcher callback with dead dict gh-102381: don't call watcher callback with dead object Mar 3, 2023
sobolevn added a commit to sobolevn/cpython that referenced this pull request Mar 3, 2023
@sobolevn
pythongh-102382: [docs] Arguments of PyObject_CopyData are PyObjects
0b7ee2c
@carljm
pythongh-102381: don't call watcher callback with dead object
6d0b789
@carljm
carljm force-pushed the nodeaddict branch from ecbd4a5 to 6d0b789 Compare March 3, 2023 23:25
@carljm carljm added the skip news label Mar 3, 2023
@carljm

carljm commented Mar 3, 2023

Copy link
Copy Markdown
Member Author

Skipping news since this is just fixing a bug in an unreleased new feature.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@carljm carljm added the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Mar 3, 2023
@bedevere-bot

bedevere-bot commented Mar 3, 2023

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @carljm for commit 6d0b789 🤖

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Mar 3, 2023
@carljm
carljm marked this pull request as ready for review March 3, 2023 23:40
@carljm
carljm requested review from markshannon and methane as code owners March 3, 2023 23:40
@carljm
carljm requested a review from Yhg1s March 3, 2023 23:40
@carljm
Windows formats pointer values differently
1ccec46
JelleZijlstra
JelleZijlstra reviewed Mar 4, 2023
View reviewed changes
Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

carljm added 3 commits March 3, 2023 16:23
@carljm
Update watcher docs with notes on deallocation events
8e8843e
@carljm
Try to handle errors in context string building
b5256e7
@carljm
Update code-object watcher docs on return value also
9b938c7
JelleZijlstra
JelleZijlstra approved these changes Mar 4, 2023
View reviewed changes

@JelleZijlstra JelleZijlstra left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Looks good, thanks for fixing this! I'd like other reviewers to also have a look though.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

carljm reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@bedevere-bot bedevere-bot added awaiting merge and removed awaiting core review labels Mar 4, 2023
@carljm carljm added the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Mar 4, 2023
@bedevere-bot

bedevere-bot commented Mar 4, 2023

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @carljm for commit 9b938c7 🤖

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Mar 4, 2023
Yhg1s
Yhg1s reviewed Mar 4, 2023
View reviewed changes
Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/dictobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/dictobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/dictobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/funcobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/c-api/function.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/c-api/function.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/dictobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/funcobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

carljm and others added 3 commits March 3, 2023 22:40
@carljm @Yhg1s
Apply suggestions from code review
6b323fa
Co-authored-by: T. Wouters <[email protected]>
@carljm
Merge branch 'main' into nodeaddict
a31af32
* main:
  pythongh-102021 : Allow multiple input files for interpreter loop generator (python#102022)
  Add import of `unittest.mock.Mock` in documentation (python#102346)
  pythongh-102383: [docs] Arguments of `PyObject_CopyData` are `PyObject *` (python#102390)
  pythongh-101754: Document that Windows converts keys in `os.environ` to uppercase (pythonGH-101840)
  pythongh-102324: Improve tests of `typing.override` (python#102325)
@carljm
review comments
b893187
@carljm

carljm commented Mar 4, 2023

Copy link
Copy Markdown
Member Author

Thanks @Yhg1s for the review! I've updated per all comments.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Yhg1s
Yhg1s reviewed Mar 6, 2023
View reviewed changes
Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/dictobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

carljm added 2 commits March 6, 2023 14:16
@carljm
add event name to watcher callback error message
4894127
@carljm
Merge branch 'main' into nodeaddict
33dda2a
* main: (21 commits)
  pythongh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives in sub interpreters module (python#102472)
  pythongh-95672: Fix versionadded indentation of get_pagesize in test.rst (pythongh-102455)
  pythongh-102416: Do not memoize incorrectly loop rules in the parser (python#102467)
  pythonGH-101362: Optimise PurePath(PurePath(...)) (pythonGH-101667)
  pythonGH-101362: Check pathlib.Path flavour compatibility at import time (pythonGH-101664)
  pythonGH-101362: Call join() only when >1 argument supplied to pathlib.PurePath() (python#101665)
  pythongh-102444: Fix minor bugs in `test_typing` highlighted by pyflakes (python#102445)
  pythonGH-102341: Improve the test function for pow (python#102342)
  Fix unused classes in a typing test (pythonGH-102437)
  pythongh-101979: argparse: fix a bug where parentheses in metavar argument of add_argument() were dropped (python#102318)
  pythongh-102356: Add thrashcan macros to filter object dealloc (python#102426)
  Move around example in to_bytes() to avoid confusion (python#101595)
  pythonGH-97546: fix flaky asyncio `test_wait_for_race_condition` test (python#102421)
  pythongh-96821: Add config option `--with-strict-overflow` (python#96823)
  pythongh-101992: update pstlib module documentation (python#102133)
  pythongh-63301: Set exit code when tabnanny CLI exits on error (python#7699)
  pythongh-101863: Fix wrong comments in EUC-KR codec (pythongh-102417)
  pythongh-102302 Micro-optimize `inspect.Parameter.__hash__` (python#102303)
  pythongh-102179: Fix `os.dup2` error reporting for negative fds (python#102180)
  pythongh-101892: Fix `SystemError` when a callable iterator call exhausts the iterator (python#101896)
  ...
Yhg1s
Yhg1s reviewed Mar 6, 2023
View reviewed changes
Comment thread Include/cpython/dictobject.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@carljm
use PY_ prefix, add Py_UNREACHABLE() to switches
239981c
@carljm

carljm commented Mar 6, 2023

Copy link
Copy Markdown
Member Author

@Yhg1s I pushed the explicit exhaustiveness check, and one possible way of addressing your comments about the x-macros. I really don't feel strongly about how to handle the event type logging, so if you have a clearly preferred approach, just let me know and I'll do that.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Yhg1s
Yhg1s approved these changes Mar 7, 2023
View reviewed changes
Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/codeobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

JelleZijlstra
JelleZijlstra reviewed Mar 7, 2023
View reviewed changes

@JelleZijlstra JelleZijlstra left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Sorry, noticed one more thing. I think this can affect users who include the header in C code that independently defines a DEF_EVENT macro.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

carljm reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Include/cpython/code.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Include/cpython/dictobject.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Include/cpython/funcobject.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

@carljm
Merge branch 'main' into nodeaddict
e391218
* main:
  pythongh-102493: fix normalization in PyErr_SetObject (python#102502)
  pythongh-87092: compiler's CFG construction moved to after codegen stage (python#102320)
  pythongh-95913: Consolidate build requirements changes in 3.11 WhatsNew (pythonGH-98781)
  Remove redundant `_ensure_future` in favor of `ensure_future` in `asyncio` (python#102398)
  pythongh-95913: Edit Faster CPython section in 3.11 WhatsNew (pythonGH-98429)
  pythongh-90110: Fix the c-analyzer Tool (python#102483)
  pythongh-101759: Update macOS installer SQLite 3.40.1 checksum (pythongh-102485)
  Remove unused import of `warnings` from `unittest.loader` (python#102479)
  Add gettext support to tools/extensions/c_annotations.py (python#101989)
@JelleZijlstra

JelleZijlstra commented Mar 7, 2023

Copy link
Copy Markdown
Member

And as discussed offline, let's run the buildbots again to be safe; after that feel free to merge.

carljm reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@carljm
use PY_ prefix for temp macros in headers too
05e2ce0
@carljm carljm added the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Mar 7, 2023
@bedevere-bot

bedevere-bot commented Mar 7, 2023

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @carljm for commit 05e2ce0 🤖

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Mar 7, 2023
@carljm
carljm merged commit 1e703a4 into python:main Mar 8, 2023
@bedevere-bot bedevere-bot removed the awaiting merge label Mar 8, 2023
@carljm
carljm deleted the nodeaddict branch March 8, 2023 00:11
This was referenced Mar 8, 2023
dict/func/code watchers may call dealloc callback with exception set #102385
Closed
s390x RHEL7 got crash on the refleak test #102351
Closed
kumaraditya303
kumaraditya303 reviewed Mar 8, 2023
View reviewed changes
Comment thread Objects/funcobject.c
Py_SET_REFCNT(op, Py_REFCNT(op) - 1);
return;
}
Py_SET_REFCNT(op, 0);

@kumaraditya303 kumaraditya303 Mar 8, 2023

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Is this Py_SET_REFCNT necessary? IIUC this is redundant here otherwise it isn't obvious to me and a comment would be nice.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@carljm carljm Mar 8, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

In the normal case, assuming the watcher has not taken any new reference to the object, the refcount will be 1 at this point (because we set it to 1 above.) We should set it to 0 before continuing with deallocation, because objects being deallocated should have a refcount of zero.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@markshannon markshannon Mar 8, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

The watcher must take a reference to any object it is watching, otherwise the refcount is incorrect and we crash and burn.

The GC, or an optimizer, can legitimately assume that any object with a reference count of zero and no weakrefs is to be freed and re-use the memory. That's not going to end well if the watcher still thinks that memory is still a dictionary.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@carljm carljm Mar 8, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

@markshannon I think your comment is more relevant to the discussion below (about why to have dealloc watchers at all) than it is to this thread, so I'll reply below.

The answer to @kumaraditya303's question here is that this Py_SET_REFCNT(op, 0) is the decref that has to be paired with the incref Py_SET_REFCNT(op, 1); that occurs on line 799. We use explicit Py_SET_REFCNT instead of Py_INCREF and Py_DECREF because Py_DECREF would cause recursive deallocation, and if we use Py_SET_REFCNT for the decref we have to use it for the incref also, otherwise the total refcount tracking will be off.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@markshannon

markshannon commented Mar 8, 2023

Copy link
Copy Markdown
Member

I can't help feeling that we would be better off without these watchers.
They appear to be bug magnets. Handling the "zombie" objects is tricky and error prone.
How is the watcher interacting with the freed object if there are no references to it?

This PR adds quite a lot of code to dict_dealloc which is called for every dictionary created, which has performance implications.

I think we should just remove these watchers. What are they for?
I've been going through possible optimizations for CPython and these watchers aren't needed for any of them.
I don't see why Cinder would need them either.

(As an aside: we won't need the creation events either, but I can see why Cinder wants those).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@carljm

carljm commented Mar 8, 2023

Copy link
Copy Markdown
Member Author

This PR adds quite a lot of code to dict_dealloc which is called for every dictionary created, which has performance implications.

I was also initially concerned about adding an incref and decref in this path, but on a pyperformance run (with --enable-optimizations build, on a bare-metal AWS host) this PR was 1.00x faster than the base commit in main, so it doesn't seem to have a detectable impact?

I don't see why Cinder would need them either.

In general, in Cinder they are used for de-registering objects from JIT registries consisting of borrowed references, to avoid having dangling pointers in these registries. E.g. for each watched dict, we remember what key(s) we care about in that dict, and for each key we may also watch a different builtins (and maybe also globals) dict that that key might actually have been found in (or might still need to be found in if the key goes away in the main dict). If the main dict is deallocated we unwatch the additional globals/builtins dicts, and erase the whole entry from the registry. Similarly, we have a registry of created functions/code-objects that are pending compilation (in some modes we don't compile eagerly but do a multi-threaded compilation of everything pending compilation, right before fork), so if a function or code object is deallocated we remove it from that registry.

In every case the deallocated handler doesn't do anything with the zombie PyObject itself, it just needs the pointer value to erase any registry entry for that pointer value.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@carljm

carljm commented Mar 8, 2023

Copy link
Copy Markdown
Member Author

The watcher must take a reference to any object it is watching, otherwise the refcount is incorrect and we crash and burn.

Cinder JIT currently doesn't take references to objects it is watching, so as not to keep them alive if they would otherwise die. There is no "crash and burn" precisely because of the dealloc events; if the object is deallocated we know about it and can erase any pointer to it we were keeping. Effectively it's like we are keeping a weakref, but without the allocation and indirection overhead of an actual weakref object. Perhaps we should be keeping an actual weakref instead; it would take some experimentation to check the performance impact of that.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

carljm added a commit to carljm/cpython that referenced this pull request Mar 8, 2023
@carljm
Merge branch 'main' into inlinecomp2
24a9d9f
* main:
  pythongh-102381: don't call watcher callback with dead object (python#102382)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@JelleZijlstra JelleZijlstra JelleZijlstra approved these changes
@Yhg1s Yhg1s Yhg1s approved these changes
@kumaraditya303 kumaraditya303 kumaraditya303 left review comments
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner
@methane methane Awaiting requested review from methane methane is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

skip news

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

raising an exception in a dict/code/function watcher callback will segfault on a DEALLOCATED event

Uh oh!

There was an error while loading. Please reload this page.

6 participants

@carljm @bedevere-bot @JelleZijlstra @markshannon @Yhg1s @kumaraditya303
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.