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

Refleak tests: test_doctest and test_gc are failing #45881

Closed
tiran opened this issue Dec 2, 2007 · 17 comments
Closed

Refleak tests: test_doctest and test_gc are failing #45881

tiran opened this issue Dec 2, 2007 · 17 comments
Assignees
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) OS-windows tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error

Comments

@tiran
Copy link
Member

tiran commented Dec 2, 2007

BPO 1540
Nosy @gvanrossum, @tim-one, @amauryfa, @pitrou, @tiran, @ezio-melotti, @briancurtin, @jeremyhylton
Files
  • gc_bug.py
  • gcmodule.c.patch: Restructure GC to look for resurrected objects
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/tim-one'
    closed_at = <Date 2013-10-23.14:40:02.090>
    created_at = <Date 2007-12-02.14:41:56.124>
    labels = ['interpreter-core', 'type-bug', 'tests', 'OS-windows']
    title = 'Refleak tests: test_doctest and test_gc are failing'
    updated_at = <Date 2013-10-23.14:40:02.090>
    user = 'https://github.com/tiran'

    bugs.python.org fields:

    activity = <Date 2013-10-23.14:40:02.090>
    actor = 'pitrou'
    assignee = 'tim.peters'
    closed = True
    closed_date = <Date 2013-10-23.14:40:02.090>
    closer = 'pitrou'
    components = ['Interpreter Core', 'Tests', 'Windows']
    creation = <Date 2007-12-02.14:41:56.124>
    creator = 'christian.heimes'
    dependencies = []
    files = ['8880', '16259']
    hgrepos = []
    issue_num = 1540
    keywords = ['patch']
    message_count = 17.0
    messages = ['58089', '58205', '58218', '58221', '99522', '99523', '99555', '99557', '99565', '99590', '99593', '116797', '200765', '200775', '200776', '201030', '201031']
    nosy_count = 9.0
    nosy_names = ['gvanrossum', 'tim.peters', 'amaury.forgeotdarc', 'pitrou', 'christian.heimes', 'ezio.melotti', 'brian.curtin', 'Jeremy.Hylton', 'BreamoreBoy']
    pr_nums = []
    priority = 'normal'
    resolution = 'out of date'
    stage = 'patch review'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue1540'
    versions = ['Python 3.3']

    @tiran
    Copy link
    Member Author

    tiran commented Dec 2, 2007

    I've seen the problem on Windows only. test_doctest fails and the
    problem also causes test_gc to fail when it is run after test_doctest.
    W/o a prior run of test_doctest test_gc doesn't fail.

    File "c:\dev\python\py3k\lib\test\test_doctest.py", line 1570, in
    test.test_doct
    est.test_debug
    Failed example:
        try: doctest.debug_src(s)
        finally: sys.stdin = real_stdin
    Expected:
        > <string>(1)<module>()
        (Pdb) next
        12
        --Return--
        > <string>(1)<module>()->None
        (Pdb) print(x)
        12
        (Pdb) continue
    Got:
        > c:\dev\python\py3k\lib\io.py(281)__del__()
        -> try:
        (Pdb) next
        > c:\dev\python\py3k\lib\io.py(282)__del__()
        -> self.close()
        (Pdb) print(x)
        *** NameError: NameError("name 'x' is not defined",)
        (Pdb) continue
        12
    **********************************************************************
    1 items had failures:
       1 of   4 in test.test_doctest.test_debug
    ***Test Failed*** 1 failures.
    test test_doctest failed -- 1 of 418 doctests failed
    test_gc
    test test_gc failed -- Traceback (most recent call last):
      File "c:\dev\python\py3k\lib\test\test_gc.py", line 193, in test_saveall
        self.assertEqual(gc.garbage, [])
    AssertionError: [<io.BytesIO object at 0x01237968>] != []

    2 tests failed:
    test_doctest test_gc

    @tiran tiran added tests Tests in the Lib/test dir OS-windows labels Dec 2, 2007
    @amauryfa
    Copy link
    Member

    amauryfa commented Dec 5, 2007

    After some hard debugging:

    • doctest.debug_src() is unlucky enough to trigger a garbage collection
      just when compiling the given code.
    • gc collects unreachable objects, among them is an instance of the
      class doctest._SpoofOut, which derives from io.StringIO.
    • The debugger steps into io.IOBase.__del__

    Some possible directions:

    • Change the gc thresholds. A very temporary workaround to make the test
      pass.
    • Find the cycle involving the SpoofOut object, and try to break it in
      doctest.py.
    • Find a way to disable pdb tracing when the gc is running finalizers.
      (this is what happens in 2.5: pdb does not step into a C function)
    • Forget everything, and wait for the io.py object to be rewritten in C.

    @amauryfa
    Copy link
    Member

    amauryfa commented Dec 5, 2007

    Finally I found a potential problem with the garbage collector in a
    specific case:

    • Some object C participates in a reference cycle, and contains a
      reference to another object X.
    • X.__del__ 'resurrect' the object, by saving its 'self' somewhere else.
    • X contains a reference to Y, which has a __del__ method.
      When collecting all this, gc.garbage == [Y] !
      This is not true garbage: if you clean gc.garbage, then the next
      gc.collect() clears everything.

    Now, try to follow my explanation (if I correctly understand the gc
    internals):

    • When the cycle is garbage collected, X and Y are detected as
      'unreachable with finalizers', and put in a specific 'finalizers' list.
    • the cycle is broken, C is deallocated.
    • This correctly triggers X.__del__. X is removed from the 'finalizers'
      list.
    • when X is resurrected, it comes back to the normal gc tracking list.
    • At the end, 'finalizers' contains 3 objects: X.__dict__, Y and Y.__dict__.
    • Y is then considered as garbage.

    I join a script which reproduces the behaviour. Note that 2.4 and 2.5
    are affected too.
    In py3k, the 'resurrect' seems to be caused by a (caught) exception in
    TextIOWrapper.close(): the exception contains a reference to the frame,
    which references the self variable.

    @amauryfa amauryfa added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Dec 5, 2007
    @gvanrossum
    Copy link
    Member

    Hoping to draw Tim into this... He's the only one I know who truly
    understands these issues...

    @jeremyhylton
    Copy link
    Contributor

    I'm trying to figure out the attached script. If I run Python 3.0, the script doesn't run because of the undefined gc.DEBUG_OBJECTS. If I just remove that, the script runs without error. Does that mean the problem is fixed? Or is running without an error an example of the problem?

    If I add gc.DEBUG_SAVEALL, it fails--but that seems obvious because DEBUG_SAVEALL adds all objects with finalizers to gc.garbage.

    @amauryfa
    Copy link
    Member

    The attached script does not "fail". The weird thing is that after gc.collect(), gc.garbage is not empty.
    This happens even when gc.set_debug() is not called.

    @jeremyhylton
    Copy link
    Contributor

    I spent some time to understand the example script today. The specific issue is that a set of objects get put into the list of unreachable objects with finalizers (both Immutable and Finalizer instances). When Cycle's __dict__ is cleared, it also decrefs Immutable which resurrects it and Finalizer. The garbage collector is not prepared for an unreachable finalizer object to become reachable again. More generally, it's hard to assume anything about the state of the finalizers after unreachable trash is collected. I'll think more about what to do, but I don't see any easy solutions.

    @jeremyhylton
    Copy link
    Contributor

    One last thought on this bug. The problem is that after we try to delete garbage, we really can't know much about the state of the objects in the finalizers list. If any of the objects that are cleared end up causing a finalizer to run, then any of the objects in the finalizers list may be reachable again. One possibility is to do nothing with the objects in the finalizers list if there was any garbage to delete. That means objects with finalizers would be harder to get to gc.collect()--for example, you'd need to call gc.collect() twice in a row. The first time to clear garbage, the second time to handle unreachable objects with finalizers. Or the GC could run a second time if garbage was cleared and finalizers was non-empty.

    A more complicated possibility would be to track some object state about when a finalizer was run. If any of the objects in finalizers had a finalizer that ran while garbage was cleared, we could skip the finalizers list. I don't know how to implement this, since an arbitrary C type could run Python code in tp_dealloc without notifying GC.

    @amauryfa
    Copy link
    Member

    What if we simply run another collection on the finalizers list?
    after move_finalizers(), set young=finalizers, and start again at update_refs().
    This is the "garbage" list anyway, so it's empty most of the time and should not cause any slowdown.

    @jeremyhylton
    Copy link
    Contributor

    Amaury-- I think that will work. I put together a small patch that seems to pass all the tests, but it too messy. We need some care to make sure we don't spin forever if there's some degenerate case where we never escape GC.

    @jeremyhylton
    Copy link
    Contributor

    The code is still in no shape to submit. It has lots of debugging prints in it, etc. but the basic structure might work. Do you want to let me know if it makes sense?

    @BreamoreBoy
    Copy link
    Mannequin

    BreamoreBoy mannequin commented Sep 18, 2010

    Can we have an update on this please as it seems important.

    @tiran tiran added the type-bug An unexpected behavior, bug, or error label Nov 26, 2012
    @tiran
    Copy link
    Member Author

    tiran commented Oct 21, 2013

    ping :)

    @amauryfa
    Copy link
    Member

    Is the error still current? io.StringIO is now completely implemented in _io/textio.c, and should not have any Python-level __del__.

    @tiran
    Copy link
    Member Author

    tiran commented Oct 21, 2013

    I don't know ... Is somebody able to test it?

    @pitrou
    Copy link
    Member

    pitrou commented Oct 23, 2013

    The GC behaves gracefully in 3.4: the gc_bug script shows no uncollectable object. I don't think this is worth fixing in 3.3.

    @tiran
    Copy link
    Member Author

    tiran commented Oct 23, 2013

    I agree with not fixing 3.3.

    @pitrou pitrou closed this as completed Oct 23, 2013
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    interpreter-core (Objects, Python, Grammar, and Parser dirs) OS-windows tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    6 participants