在扩展模块中更正循环垃圾收集

时间:2012-09-04 00:53:14

标签: python c garbage-collection python-c-api python-extensions

Python 2.7的doc的两个部分提到为扩展模块中定义的容器对象添加循环垃圾收集(CGC)支持。

Python/C API Reference Manual给出了两条规则,即

  
      
  1. 必须使用PyObject_GC_New()PyObject_GC_NewVar()
  2. 分配对象的内存   
  3. 一旦初始化了可能包含对其他容器的引用的所有字段,就必须调用PyObject_GC_Track()
  4.   

而在Extending and Embedding the Python Interpreter中,对于Noddy示例,似乎添加Py_TPFLAGS_HAVE_GC标记并填充tp_traversetp_clear广告位就足以启用CGC支持。以上两条规则根本没有实施。

当我修改Noddy示例以实际遵循PyObject_GC_New() / PyObject_GC_Del()PyObject_Track() / PyObject_GC_UnTrack()的规则时,令人惊讶地提出断言错误说,

  

Modules / gcmodule.c:348:visit_decref:断言“gc-> gc.gc_refs!= 0”失败。 refcount太小了

这导致我对实施CGC的正确/安全方式感到困惑。任何人都可以提供建议,或者最好是一个容器对象的整洁的示例吗?

2 个答案:

答案 0 :(得分:3)

我自己在C API方面没有足够的经验给你任何建议,但Python容器实现本身有很多例子

就个人而言,我首先从元组实现开始,因为它是不可变的:Objects/tupleobject.c。然后转到dictlistset实现,以获取有关可变容器的更多说明:

我不禁注意到,PyObject_GC_New()PyObject_GC_NewVar()PyObject_GC_Track()的呼叫始终存在,并且设置了Py_TPFLAGS_HAVE_GC

答案 1 :(得分:1)

在大多数正常情况下,您不需要自己进行跟踪/取消跟踪。这在文档中有描述,但并未明确说明。在Noddy example的情况下,你绝对不会。

简短版本是TypeObject包含两个函数指针:tp_alloctp_free。默认情况下,tp_alloc会在创建类时调用所有正确的函数(如果已设置Py_TPFLAGS_HAVE_GC),并且tp_free会在销毁时取消该类。

Noddy documentation says(在本节末尾):

  

这就是它。如果我们编写了自定义tp_alloctp_free个插槽,我们需要修改它们以进行循环垃圾收集。大多数扩展程序将使用自动提供的版本。

不幸的是,一个不清楚你自己不需要这样做的地方是Supporting Cyclic Garbage Collection documentation

<强>详情:

使用Noddy_newtp_newTypeObject个插槽中的tp_alloc函数分配Noddy。根据{{​​3}},“新”函数应该做的主要事情是调用the documentation。您通常不会自己编写PyType_GenericAlloc(),而是默认为PyType_IS_GC(type)

查看tp_alloc slot会根据_PyObject_GC_Malloc显示更改的部分。首先,它调用PyObject_Malloc而不是_PyObject_GC_TRACK(obj),然后调用PyObject_New。 [请注意,PyObject_Malloc真正做的就是致电tp_init,然后致电Py_TPFLAGS_HAVE_GC。]

同样,在重新分配时,您调用PyType_GenericAlloc() in the Python source,对PyObject_GC_Del的类自动设置为tp_free slotPyObject_GC_UnTrack包含与package workspace; public class Fencing { public static void main(String[] args) { boolean b = true; double corner = 0; double center = 0; double side = 0; int i = 0; int j = 0; int[][] map = { { 0, 1, 1, 0 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 0, 1 } }; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { if (map[i][j] != 1 && map[i][j] != 0) { b = false; System.out.println("--> A value of " + map[i][j] + " was found at " + i + "," + j); } } } if (b == false && i == 3) { System.out.println("The map is invalid"); return; } else { System.out.println("Map is valid"); } if (map[0][0] == 1) { corner += 5; if (map[1][0] == 1 && map[0][1] == 1) { corner += 0; } if (map[1][0] != 1 && map[0][1] != 1) { corner += 5; } if (map[1][0] != 1 && map[0][1] == 1 || map[1][0] == 1 && map[0][1] != 1) { corner += 2.5; } } else { corner += 0; } if (map[0][3] == 1) { corner += 5; if (map[1][3] == 1 && map[0][2] == 1) { corner += 0; } if (map[1][3] != 1 && map[0][2] != 1) { corner += 5; } if (map[1][3] != 1 && map[0][2] == 1 || map[1][3] == 1 && map[0][2] != 1) { corner += 2.5; } } else { corner += 0; } if (map[3][0] == 1) { corner += 5; if (map[3][1] == 1 && map[2][0] == 1) { corner += 0; } if (map[3][1] != 1 && map[02][0] != 1) { corner += 5; } if (map[3][1] != 1 && map[2][0] == 1 || map[3][1] == 1 && map[2][0] != 1) { corner += 2.5; } } else { corner += 0; } if (map[3][3] == 1) { corner += 5; if (map[3][2] == 1 && map[2][3] == 1) { corner += 0; } if (map[3][2] != 1 && map[2][3] != 1) { corner += 5; } if (map[3][2] != 1 && map[2][3] == 1 || map[3][2] == 1 && map[2][3] != 1) { corner += 2.5; } } else { corner += 0; } System.out.println("Corner fencing = " + corner); i = 0; j = 0; for (j = 1; j < 3; j++) { // Top if (map[0][j] == 1) { side += 2.5; if (map[0][j + 1] == 1 && map[0][j - 1] == 1) { side += 0; } if (map[0][j + 1] != 1 || map[0][j - 1] != 1) { side += 2.5; } if (map[0][j + 1] != 1 && map[0][j - 1] != 1) { side += 5; } } else { side += 0; } } System.out.println("After the first row, fencing = " + side); i = 0; j = 0; for (j = 1; j < 3; j++) { // Bottom if (map[3][j] == 1) { side += 2.5; if (map[3][j + 1] == 1 && map[3][j - 1] == 1) { side += 0; } if (map[3][j + 1] != 1 || map[3][j - 1] != 1) { side += 2.5; } if (map[3][j + 1] != 1 && map[3][j - 1] != 1) { side += 5; } } else { side += 0; } } System.out.println("After the bottom row, fencing = " + side); i = 0; j = 0; for (i = 1; i < 3; i++) {// Right if (map[i][3] == 1) { side += 2.5; if (map[i + 1][3] == 1 && map[i - 1][3] == 1) { side += 0; } if (map[i + 1][3] != 1 || map[i - 1][3] != 1) { side += 2.5; } if (map[i + 1][3] != 1 && map[i - 1][3] != 1) { side += 5; } } else { side += 0; } } System.out.println("After the first column, fencing = " + side); i = 0; j = 0; for (i = 1; i < 3; i++) {// Left if (map[i][0] == 1) { side += 2.5; if (map[i + 1][0] == 1 && map[i - 1][0] == 1) { side += 0; } if (map[i + 1][0] != 1 || map[i - 1][0] != 1) { side += 2.5; } if (map[i + 1][0] != 1 && map[i - 1][0] != 1) { side += 5; } } else { side += 0; } } System.out.println("After the last column, fencing = " + side); } } 相同的代码,因此无需调用untrack。