Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c5007aa

Browse files
committed
final patches from Neil Schemenauer for garbage collection
1 parent 4e542a3 commit c5007aa

16 files changed

Lines changed: 956 additions & 18 deletions

File tree

Include/objimpl.h

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ extern DL_IMPORT(void) _PyObject_Del Py_PROTO((PyObject *));
203203
( (type *) PyObject_InitVar( \
204204
(PyVarObject *) PyObject_MALLOC( _PyObject_VAR_SIZE((typeobj),(n)) ),\
205205
(typeobj), (n)) )
206-
#define PyObject_DEL(op) PyObject_FREE(op)
207206

208207
/* This example code implements an object constructor with a custom
209208
allocator, where PyObject_New is inlined, and shows the important
@@ -234,11 +233,67 @@ extern DL_IMPORT(void) _PyObject_Del Py_PROTO((PyObject *));
234233
the 1st step is performed automatically for you, so in a C++ class
235234
constructor you would start directly with PyObject_Init/InitVar. */
236235

236+
/*
237+
* Garbage Collection Support
238+
* ==========================
239+
*/
237240

241+
/* To make a new object participate in garbage collection use
242+
PyObject_{New, VarNew, Del} to manage the memory. Set the type flag
243+
Py_TPFLAGS_GC and define the type method tp_recurse. You should also
244+
add the method tp_clear if your object is mutable. Include
245+
PyGC_INFO_SIZE in the calculation of tp_basicsize. Call
246+
PyObject_GC_Init after the pointers followed by tp_recurse become
247+
valid (usually just before returning the object from the allocation
248+
method. Call PyObject_GC_Fini before those pointers become invalid
249+
(usually at the top of the deallocation method). */
238250

239251
#ifndef WITH_CYCLE_GC
240-
#define PyGC_INFO_SIZE 0
241-
#endif
252+
253+
#define PyGC_HEAD_SIZE 0
254+
#define PyObject_GC_Init(op)
255+
#define PyObject_GC_Fini(op)
256+
#define PyObject_AS_GC(op) (op)
257+
#define PyObject_FROM_GC(op) (op)
258+
#define PyObject_DEL(op) PyObject_FREE(op)
259+
260+
#else
261+
262+
/* Add the object into the container set */
263+
extern DL_IMPORT(void) _PyGC_Insert Py_PROTO((PyObject *));
264+
265+
/* Remove the object from the container set */
266+
extern DL_IMPORT(void) _PyGC_Remove Py_PROTO((PyObject *));
267+
268+
#define PyObject_GC_Init(op) _PyGC_Insert((PyObject *)op)
269+
#define PyObject_GC_Fini(op) _PyGC_Remove((PyObject *)op)
270+
271+
/* Structure *prefixed* to container objects participating in GC */
272+
typedef struct _gc_head {
273+
struct _gc_head *gc_next;
274+
struct _gc_head *gc_prev;
275+
int gc_refs;
276+
} PyGC_Head;
277+
278+
#define PyGC_HEAD_SIZE sizeof(PyGC_Head)
279+
280+
/* Test if a type has a GC head */
281+
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_GC)
282+
283+
/* Test if an object has a GC head */
284+
#define PyObject_IS_GC(o) PyType_IS_GC((o)->ob_type)
285+
286+
/* Get an object's GC head */
287+
#define PyObject_AS_GC(o) ((PyGC_Head *)(o)-1)
288+
289+
/* Get the object given the PyGC_Head */
290+
#define PyObject_FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
291+
292+
#define PyObject_DEL(op) PyObject_FREE( PyObject_IS_GC(op) ? \
293+
(ANY *)PyObject_AS_GC(op) : \
294+
(ANY *)(op) )
295+
296+
#endif /* WITH_CYCLE_GC */
242297

243298
#ifdef __cplusplus
244299
}

Lib/test/output/test_gc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
test_gc
2+
list 0x831a754
3+
dict 0x831a754
4+
list 0x831a754
5+
tuple 0x831a734
6+
class 0x831a794
7+
<test_gc.A instance at 831a754>
8+
a <test_gc.A instance at 831a754>
9+
b <test_gc.B instance at 831a95c>
10+
dict 0x831a9bc
11+
func 0x831d9e4

Lib/test/test_gc.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import gc
2+
3+
def test_list():
4+
l = []
5+
l.append(l)
6+
print 'list 0x%x' % id(l)
7+
gc.collect()
8+
del l
9+
assert gc.collect() == 1
10+
11+
def test_dict():
12+
d = {}
13+
d[1] = d
14+
print 'dict 0x%x' % id(d)
15+
gc.collect()
16+
del d
17+
assert gc.collect() == 1
18+
19+
def test_tuple():
20+
l = []
21+
t = (l,)
22+
l.append(t)
23+
print 'list 0x%x' % id(l)
24+
print 'tuple 0x%x' % id(t)
25+
gc.collect()
26+
del t
27+
del l
28+
assert gc.collect() == 2
29+
30+
def test_class():
31+
class A:
32+
pass
33+
A.a = A
34+
print 'class 0x%x' % id(A)
35+
gc.collect()
36+
del A
37+
assert gc.collect() > 0
38+
39+
def test_instance():
40+
class A:
41+
pass
42+
a = A()
43+
a.a = a
44+
print repr(a)
45+
gc.collect()
46+
del a
47+
assert gc.collect() > 0
48+
49+
def test_method():
50+
class A:
51+
def __init__(self):
52+
self.init = self.__init__
53+
a = A()
54+
gc.collect()
55+
del a
56+
assert gc.collect() > 0
57+
58+
def test_finalizer():
59+
class A:
60+
def __del__(self): pass
61+
class B:
62+
pass
63+
a = A()
64+
a.a = a
65+
id_a = id(a)
66+
b = B()
67+
b.b = b
68+
print 'a', repr(a)
69+
print 'b', repr(b)
70+
gc.collect()
71+
gc.garbage[:] = []
72+
del a
73+
del b
74+
assert gc.collect() > 0
75+
assert id(gc.garbage[0]) == id_a
76+
77+
def test_function():
78+
d = {}
79+
exec("def f(): pass\n") in d
80+
print 'dict 0x%x' % id(d)
81+
print 'func 0x%x' % id(d['f'])
82+
gc.collect()
83+
del d
84+
assert gc.collect() == 2
85+
86+
87+
def test_all():
88+
debug = gc.get_debug()
89+
gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS)
90+
test_list()
91+
test_dict()
92+
test_tuple()
93+
test_class()
94+
test_instance()
95+
test_method()
96+
test_finalizer()
97+
test_function()
98+
gc.set_debug(debug)
99+
100+
test_all()

Modules/Setup.thread.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
# support threads.
1111

1212
@USE_THREAD_MODULE@thread threadmodule.c
13+
14+
# Garbage collection enabled with --with-cycle-gc
15+
@USE_GC_MODULE@gc gcmodule.c

Modules/cPickle.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2892,7 +2892,7 @@ Instance_New(PyObject *cls, PyObject *args) {
28922892
Py_DECREF(inst);
28932893
goto err;
28942894
}
2895-
2895+
PyObject_GC_Init(inst);
28962896
return (PyObject *)inst;
28972897
}
28982898
Py_DECREF(__getinitargs__);

0 commit comments

Comments
 (0)