mirror of
https://github.com/python/cpython.git
synced 2025-09-25 01:43:11 +00:00
[3.14] gh-133532: Run GC fast cycles test in subprocess. (gh-133533) (gh-133716)
This makes the test more reliable since there are not extra objects on the heap leftover
from other tests.
(cherry picked from commit 8598e57942
)
Co-authored-by: Neil Schemenauer <nas-github@arctrix.com>
This commit is contained in:
parent
da41ce6b69
commit
aaeca91b05
2 changed files with 54 additions and 56 deletions
48
Lib/test/_test_gc_fast_cycles.py
Normal file
48
Lib/test/_test_gc_fast_cycles.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Run by test_gc.
|
||||||
|
from test import support
|
||||||
|
import _testinternalcapi
|
||||||
|
import gc
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class IncrementalGCTests(unittest.TestCase):
|
||||||
|
|
||||||
|
# Use small increments to emulate longer running process in a shorter time
|
||||||
|
@support.gc_threshold(200, 10)
|
||||||
|
def test_incremental_gc_handles_fast_cycle_creation(self):
|
||||||
|
|
||||||
|
class LinkedList:
|
||||||
|
|
||||||
|
#Use slots to reduce number of implicit objects
|
||||||
|
__slots__ = "next", "prev", "surprise"
|
||||||
|
|
||||||
|
def __init__(self, next=None, prev=None):
|
||||||
|
self.next = next
|
||||||
|
if next is not None:
|
||||||
|
next.prev = self
|
||||||
|
self.prev = prev
|
||||||
|
if prev is not None:
|
||||||
|
prev.next = self
|
||||||
|
|
||||||
|
def make_ll(depth):
|
||||||
|
head = LinkedList()
|
||||||
|
for i in range(depth):
|
||||||
|
head = LinkedList(head, head.prev)
|
||||||
|
return head
|
||||||
|
|
||||||
|
head = make_ll(1000)
|
||||||
|
|
||||||
|
assert(gc.isenabled())
|
||||||
|
olds = []
|
||||||
|
initial_heap_size = _testinternalcapi.get_tracked_heap_size()
|
||||||
|
for i in range(20_000):
|
||||||
|
newhead = make_ll(20)
|
||||||
|
newhead.surprise = head
|
||||||
|
olds.append(newhead)
|
||||||
|
if len(olds) == 20:
|
||||||
|
new_objects = _testinternalcapi.get_tracked_heap_size() - initial_heap_size
|
||||||
|
self.assertLess(new_objects, 27_000, f"Heap growing. Reached limit after {i} iterations")
|
||||||
|
del olds[:]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -7,7 +7,7 @@ from test.support import (verbose, refcount_test,
|
||||||
Py_GIL_DISABLED)
|
Py_GIL_DISABLED)
|
||||||
from test.support.import_helper import import_module
|
from test.support.import_helper import import_module
|
||||||
from test.support.os_helper import temp_dir, TESTFN, unlink
|
from test.support.os_helper import temp_dir, TESTFN, unlink
|
||||||
from test.support.script_helper import assert_python_ok, make_script
|
from test.support.script_helper import assert_python_ok, make_script, run_test_script
|
||||||
from test.support import threading_helper, gc_threshold
|
from test.support import threading_helper, gc_threshold
|
||||||
|
|
||||||
import gc
|
import gc
|
||||||
|
@ -1127,64 +1127,14 @@ class GCTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class IncrementalGCTests(unittest.TestCase):
|
class IncrementalGCTests(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
# Reenable GC as it is disabled module-wide
|
|
||||||
gc.enable()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
gc.disable()
|
|
||||||
|
|
||||||
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
||||||
@requires_gil_enabled("Free threading does not support incremental GC")
|
@requires_gil_enabled("Free threading does not support incremental GC")
|
||||||
# Use small increments to emulate longer running process in a shorter time
|
|
||||||
@gc_threshold(200, 10)
|
|
||||||
def test_incremental_gc_handles_fast_cycle_creation(self):
|
def test_incremental_gc_handles_fast_cycle_creation(self):
|
||||||
|
# Run this test in a fresh process. The number of alive objects (which can
|
||||||
class LinkedList:
|
# be from unit tests run before this one) can influence how quickly cyclic
|
||||||
|
# garbage is found.
|
||||||
#Use slots to reduce number of implicit objects
|
script = support.findfile("_test_gc_fast_cycles.py")
|
||||||
__slots__ = "next", "prev", "surprise"
|
run_test_script(script)
|
||||||
|
|
||||||
def __init__(self, next=None, prev=None):
|
|
||||||
self.next = next
|
|
||||||
if next is not None:
|
|
||||||
next.prev = self
|
|
||||||
self.prev = prev
|
|
||||||
if prev is not None:
|
|
||||||
prev.next = self
|
|
||||||
|
|
||||||
def make_ll(depth):
|
|
||||||
head = LinkedList()
|
|
||||||
for i in range(depth):
|
|
||||||
head = LinkedList(head, head.prev)
|
|
||||||
return head
|
|
||||||
|
|
||||||
head = make_ll(1000)
|
|
||||||
count = 1000
|
|
||||||
|
|
||||||
# There will be some objects we aren't counting,
|
|
||||||
# e.g. the gc stats dicts. This test checks
|
|
||||||
# that the counts don't grow, so we try to
|
|
||||||
# correct for the uncounted objects
|
|
||||||
# This is just an estimate.
|
|
||||||
CORRECTION = 20
|
|
||||||
|
|
||||||
enabled = gc.isenabled()
|
|
||||||
gc.enable()
|
|
||||||
olds = []
|
|
||||||
initial_heap_size = _testinternalcapi.get_tracked_heap_size()
|
|
||||||
for i in range(20_000):
|
|
||||||
newhead = make_ll(20)
|
|
||||||
count += 20
|
|
||||||
newhead.surprise = head
|
|
||||||
olds.append(newhead)
|
|
||||||
if len(olds) == 20:
|
|
||||||
new_objects = _testinternalcapi.get_tracked_heap_size() - initial_heap_size
|
|
||||||
self.assertLess(new_objects, 27_000, f"Heap growing. Reached limit after {i} iterations")
|
|
||||||
del olds[:]
|
|
||||||
if not enabled:
|
|
||||||
gc.disable()
|
|
||||||
|
|
||||||
|
|
||||||
class GCCallbackTests(unittest.TestCase):
|
class GCCallbackTests(unittest.TestCase):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue