Skip to content

Commit 5f454a0

Browse files
committed
Issue #1545463: Global variables caught in reference cycles are now garbage-collected at shutdown.
1 parent 1df37c6 commit 5f454a0

File tree

6 files changed

+56
-5
lines changed

6 files changed

+56
-5
lines changed

Include/pythonrun.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ PyAPI_FUNC(void) PyBytes_Fini(void);
217217
PyAPI_FUNC(void) PyByteArray_Fini(void);
218218
PyAPI_FUNC(void) PyFloat_Fini(void);
219219
PyAPI_FUNC(void) PyOS_FiniInterrupts(void);
220+
PyAPI_FUNC(void) _PyGC_DumpShutdownStats(void);
220221
PyAPI_FUNC(void) _PyGC_Fini(void);
221222
PyAPI_FUNC(void) PySlice_Fini(void);
222223
PyAPI_FUNC(void) _PyType_Fini(void);

Lib/test/test_gc.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import unittest
22
from test.support import (verbose, refcount_test, run_unittest,
33
strip_python_stderr)
4+
from test.script_helper import assert_python_ok, make_script, temp_dir
5+
46
import sys
57
import time
68
import gc
@@ -610,6 +612,40 @@ def run_command(code):
610612
stderr = run_command(code % "gc.DEBUG_SAVEALL")
611613
self.assertNotIn(b"uncollectable objects at shutdown", stderr)
612614

615+
def test_gc_main_module_at_shutdown(self):
616+
# Create a reference cycle through the __main__ module and check
617+
# it gets collected at interpreter shutdown.
618+
code = """if 1:
619+
import weakref
620+
class C:
621+
def __del__(self):
622+
print('__del__ called')
623+
l = [C()]
624+
l.append(l)
625+
"""
626+
rc, out, err = assert_python_ok('-c', code)
627+
self.assertEqual(out.strip(), b'__del__ called')
628+
629+
def test_gc_ordinary_module_at_shutdown(self):
630+
# Same as above, but with a non-__main__ module.
631+
with temp_dir() as script_dir:
632+
module = """if 1:
633+
import weakref
634+
class C:
635+
def __del__(self):
636+
print('__del__ called')
637+
l = [C()]
638+
l.append(l)
639+
"""
640+
code = """if 1:
641+
import sys
642+
sys.path.insert(0, %r)
643+
import gctest
644+
""" % (script_dir,)
645+
make_script(script_dir, 'gctest', module)
646+
rc, out, err = assert_python_ok('-c', code)
647+
self.assertEqual(out.strip(), b'__del__ called')
648+
613649
def test_get_stats(self):
614650
stats = gc.get_stats()
615651
self.assertEqual(len(stats), 3)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #1545463: Global variables caught in reference cycles are now
14+
garbage-collected at shutdown.
15+
1316
- Issue #17094: Clear stale thread states after fork(). Note that this
1417
is a potentially disruptive change since it may release some system
1518
resources which would otherwise remain perpetually alive (e.g. database

Modules/gcmodule.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1544,8 +1544,9 @@ PyGC_Collect(void)
15441544
return n;
15451545
}
15461546

1547+
15471548
void
1548-
_PyGC_Fini(void)
1549+
_PyGC_DumpShutdownStats(void)
15491550
{
15501551
if (!(debug & DEBUG_SAVEALL)
15511552
&& garbage != NULL && PyList_GET_SIZE(garbage) > 0) {
@@ -1574,6 +1575,11 @@ _PyGC_Fini(void)
15741575
Py_XDECREF(bytes);
15751576
}
15761577
}
1578+
}
1579+
1580+
void
1581+
_PyGC_Fini(void)
1582+
{
15771583
Py_CLEAR(callbacks);
15781584
}
15791585

Python/import.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,14 @@ PyImport_Cleanup(void)
403403
}
404404
}
405405

406+
/* Collect garbage remaining after deleting the modules. Mostly
407+
reference cycles created by classes. */
408+
PyGC_Collect();
409+
410+
/* Dump GC stats before it's too late, since it uses the warnings
411+
machinery. */
412+
_PyGC_DumpShutdownStats();
413+
406414
/* Next, delete sys and builtins (in that order) */
407415
value = PyDict_GetItemString(modules, "sys");
408416
if (value != NULL && PyModule_Check(value)) {

Python/pythonrun.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -544,10 +544,6 @@ Py_Finalize(void)
544544
while (PyGC_Collect() > 0)
545545
/* nothing */;
546546
#endif
547-
/* We run this while most interpreter state is still alive, so that
548-
debug information can be printed out */
549-
_PyGC_Fini();
550-
551547
/* Destroy all modules */
552548
PyImport_Cleanup();
553549

@@ -628,6 +624,7 @@ Py_Finalize(void)
628624
PyFloat_Fini();
629625
PyDict_Fini();
630626
PySlice_Fini();
627+
_PyGC_Fini();
631628

632629
/* Cleanup Unicode implementation */
633630
_PyUnicode_Fini();

0 commit comments

Comments
 (0)